FORTEX is a Fortran tool with a flexible UI that can be incorporated into applications.
June 01, 1992
URL:http://www.drdobbs.com/tools/fortex-a-fortran-runtime-executive/184408786
Copyright © 1992, Dr. Dobb's Journal
Harold is the manager of a simulation-development and software-engineering group for Dynetics. He can be contacted at P.O. Drawer B, Huntsville, AL 35814.
Fortran is the language of choice for many scientists and engineers, especially for simulation programs. Although languages such as C, Pascal, Ada, and special-purpose simulation languages like the Advanced Continuous Simulation Language (ACSL) and Simulation Language for Alternative Modeling (SLAM) have made inroads into scientific programs, Fortran's popularity for scientific and engineering applications has remained unabated.
Fortran provides I/O facilities in the form of read/write (or print) statements used as primitives to develop the user interface (UI). The application developer can easily spend a large amount of development time programming the UI with hardcoded read/write and format statements. Most Fortran environments make available a symbolic I/O method called NAMELIST. Although NAMELIST provides a symbolic means to set the values of the variables specified in the NAMELIST block, its use is generally restricted to reading the values from a file rather than giving you the flexibility to enter the values interactively. The output capabilities of NAMELIST are limited to every variable in the NAMELIST block or nothing. NAMELIST lacks facilities to selectively output a sublist of variables.
Since many Fortran users are not interested in the programming aspects, per se, but in developing a program that will solve a particular problem, the tedium of programming the UI is not appealing. On the other hand, the UI must be seriously considered for those programs that will be used by anyone other than the programmer.
This article discusses a set of I/O options provided by an application tool called the Fortran Runtime Executive (FORTEX). I developed FORTEX to put a friendly UI on a simulation program that had a decidedly unfriendly interface provided by hardcoded I/O statements. The simulation program designer had previously developed several simulations in ACSL and had become accustomed to its rich set of I/O capabilities. He was proud of the algorithmic code but unhappy with the rigid method of communication with the program. Thus, FORTEX was conceived.
FORTEX is compliant with ANSI Fortran 77 and has been ported to several platforms from PCs to mainframes with very few differences in the code.
FORTEX, a runtime environment for Fortran, features a Command-Line Interface (CLI) that uses symbolic commands to control I/O and the order of program operation. It provides facilities for setting and displaying values of the program variables, selectively listing variable values during a run, and the selective recording of these values for post-processing. FORTEX provides a powerful macro feature that allows you to name a block of frequently used commands and invoke these commands by merely entering the macro name. FORTEX will read application command files that can be used to initialize data, define macros, and specify FORTEX system parameters such as the frequency at which data are recorded. FORTEX can also be used to control a purely batch application.
FORTEX provides access to any program variable specified in a labeled common block. Local subroutine and function variables, however, cannot be accessed by FORTEX. A preprocessor parses one or more of the application's common blocks and associated specification statements and builds a subroutine that contains data structures encapsulating all necessary information about the common-block members. This subroutine is used to symbolically access the individual common-block members during the execution of the application program.
FORTEX can be added to an existing program with only a few additions to the code in the form of subroutine calls. Existing I/O statements can be left in the program or removed. Local variables must be placed in common in order to be accessed by FORTEX. You are strongly urged to place each common block and the associated specification statements in a separate include file, but this is not a requirement. The common and specification statements are placed in an input file for the preprocessor in the form of include statements, source statements, or a combination of include and source statements. Include files may contain embedded include statements.
After additions are made to the application program and the preprocessor is executed to build the symbolic access routine, the application code and symbolic access routine are compiled and linked with the FORTEX library routines to build the application program. A makefile is normally used to automate these steps.
The application program is controlled during execution with FORTEX commands; see Table 1. Each command can be abbreviated to three characters, and the frequently used commands SET and DISPLAY can be specified with their first letter. FORTEX considers lowercase to be equivalent to uppercase.
Command Description ------------------------------------------------------------------------ action Sets a constant or variable during execution. close Closes a previously opened file. contin Continues execution of the program after an interrupt. display Displays the value of an interface constant or variable. dump Displays the values of all interface constants and variables. dynetx Invokes the graphical user interface (Dynet-X); not active for stand-alone FORTEX. echo Prints a message to the display. end Defines the end of a runtime macro. exit Terminates execution of the program (same as "stop"). help Invokes the FORTEX help facility. history Lists previous commands. include Loads the contents of a command file (same as "read"). inquire Checks whether a Fortran unit number is in use. macro Defines use commands by combining other FORTEX commands. open Opens and alternate, user-supplied, command file or data file. output Writes specified variables to the screen during execution. plot Transfers control to a plotting program. prepare Records specified variables for printing or plotting. print Prints recorded variables in tabular form (post-processing). quit Terminates execution of the program (same as "stop"). range Finds minimum and maximum values of prepared variables. read Loads the contents of a command file (same as "include"). reinit Transfers control to application program to reinitalize state variables to current values. restore Restores values previously saved with the save command. return Returns control to the calling routine. run Begins execution of the program (same as "start"). save Saves values of the interface constant and variables to a binary file. set Sets the value of an interface constant or variable.\ show Shows a variable's type, dimension, and location within its common block. spare Calls a user-supplied subroutine. start Begins execution of the program (run may also be used). stop Terminates execution of the program (quit or exit may also be used). xdisplay Displays the value of an integer constant or variable in hexadecimal. xset Set the value of an integer constant or variable in hexadecimal. !! Repeat the previous command. !an Repeat the most recent command that begins with the string "an." !n Repeat the nth command. "comment" Any text enclosed in quotes is a comment. $ Command separator. ... Continuation characters for continuing the present command to the next line.
The FORTEX preprocessor builds a subroutine that contains data structures collectively referred to as the FORTEX dictionary. The dictionary contains the variable name, common-block name, location in the common, dimension, and the type for each variable presented as input to the preprocessor. This subroutine allows FORTEX to symbolically access the individual common-block members. The preprocessor also generates an output file containing a special form of each common block input to the preprocessor.
The input file, named clicom.def, contains common-block statements and associated specification statements or include statements that name a file containing common and specification statements. The file clicom.def should contain all common blocks and specification statements required for FORTEX interface access, but it need not contain all commons used in the Fortran application.
The preprocessor builds two output files: clidict.f and clicom.out. clidict.f contains the dictionary subroutine which provides access to the information needed to symbolically address the variables in common blocks. clicom.out contains a special form of each common block parsed by the preprocessor. The common blocks in clicom.out contain only one member for each common block. For noncharacter commons, the single member is a real array with a single dimension equal to the number of single-precision memory words occupied by the common. For character common blocks, the single member is a character variable, the length of which is equal to the total length of all the character members of that common.
Once the interface between the Fortran application and FORTEX is defined by the preprocessor, you can add calls to the FORTEX routines to your code and link with the FORTEX library.
Adding a FORTEX interface to a Fortran program involves the following steps: Identify UI variables and place them into common blocks; place the common blocks (or include files) in clicom.def for the FORTEX dictionary builder; add FORTEX executive and data-recording calls to the program; execute the preprocessor; compile the application routines and clidict.f; and link the application to the FORTEX library.
The core of the FORTEX interface is a program structure that calls the command-line executive (clexec) and data-recording subroutine (clrecd) at appropriate points. Separating the structure from the application code can be achieved with a simple main program that calls the top-level application subroutine. Calls can be issued from the main program to perform each of the following functions: initialize variables used in the application; run the interface application; continue execution of the interface application following an intermediate termination; and calculate terminal conditions (for example, final values at the end of a FORTEX application run).
The FORTEX preprocessor (clibdict) must be executed before any FORTEX application can be built. clibdict determines (from external files, as previously explained) information about the number, type, size, and order of variables declared in the Fortran application. clibdict then creates a dictionary of storage locations in memory (corresponding to each variable. This dictionary is used by FORTEX to symbolically access the program variables.
All subroutines used with the FORTEX interface can be compiled with a standard Fortran compiler. FORTEX provides a library designed to be linked with application-specific subroutines. This linking process generates an executable file for the interface application.
Integrating FORTEX with the application is independent of the application-program size. We've used FORTEX with programs having a few variables (less than 25) to very large simulation programs with more than 1000. The integration time depends heavily on the program's use of common blocks. If all variables you wish to access are already in common, and the commons are stored in include files, the integration time may take only a few minutes.
To illustrate, I'll use a trajectory-analysis program called MONTE to show how FORTEX is added to an application and how the application is controlled by the FORTEX commands. The analysis program reads a data file generated by a missile-trajectory program that was repeatedly executed in a Monte Carlo fashion. Monte Carlo refers to a simulation method whereby selected simulation variables are varied for each run. The resulting deviations in the time histories of the missile trajectories indicate the performance boundaries of the missile. The simulation analyst is interested in the expected or average trajectory and in the predicted variations in the trajectories, usually in the form of the 3-standard deviation (3-sigma) trajectory. MONTE computes the average and n sigma trajectories, where n is user selected through FORTEX. A trajectory is composed of the missile downrange, cross-range, and altitude components. The user may select which component to analyze and after analysis is completed on the selected component, another component may be selected.
The main program of an application should normally be a short, executive-type routine that controls the program flow. Listing One (page 116) is the main program. Listing Two (page 116) is the application code, which consists of subroutines dynxrun, mcsig, mcavg, and block-data routine mondat. Listing Three (page 116) is the common-block and associated-specification statements contained in the include file monte.h. The uppercase executable statements in Listings One and Two are subroutine calls that are necessary to invoke the FORTEX CLI and the data-recording routine. This example requires the addition of only two subroutine calls to invoke the FORTEX interface. More sophisticated applications may need additional calls to the data recording routine and to the ACTION routine if runtime actions are desired.
The main program (climain) provides a loop that will repeatedly call the CLI executive (clexec) and the application routines until the user enters the STOP command. The call to clexec invokes the command-line interface which executes FORTEX commands. After a START command is issued, control returns to climain and dynxrun is called to execute the analysis routines. If a STOP command is issued, climain causes the program to terminate.
The call to clrecd is somewhat arbitrary. Generally, it should be placed where data can be recorded for each update of the program with appropriate logic to limit calls as desired by the user at run time. For data that will be plotted, you generally want a higher recording rate than for printed output. FORTEX provides two system parameters to control the amount of output: nciout controls the rate of output as the run executes, and nciprn controls the rate at which post-processing results are printed. The call to clrecd in MONTE is placed so that every computed point is also recorded.
FORTEX accepts commands from the keyboard during an interactive session, and from a command file during batch execution. Command files are frequently used in interactive sessions as well. Command files are very useful in setting up standard scenarios and for defining customized commands as macros. Included with the electronic version of the listings is the command (or setup) file used with MONTE. The command file uses two macros called LATDAT and VERDAT. LATDAT sets up the parameters to analyze the lateral or cross-range component, and VERDAT sets up the parameters so that the vertical component will be analyzed.
Also available electronically is a partial example runtime session of MONTE. I've placed comments in the (command stream to help explain the commands. After the run is completed, control is passed to the plot program. The resulting trajectory plot for the vertical component is shown in Figure 1.
FORTEX is a Fortran enhancement tool that provides a flexible and easily mastered UI that can be quickly added to an existing program or incorporated into a new application by following a simple set of programming rules. The primary benefit of FORTEX is that the user is put completely in charge of the application program. Any or all variables can be monitored during a run without having to change the code. Traditional "what-if" runs can be made with simple command sequences. Furthermore, FORTEX is useful during the debugging stage since any variable can be monitored without adding print statements.
_FORTEX: A Fortran Runtime Executive_ by Harold R. Justice[LISTING ONE]
c- climain.f -- top-level calling system utilized by FORTEX CLI. c- The main program calls user routine "dynxrun". For CLI-only, this routine c- the name is arbitrary. When linked to GUI library, "start" button calls c- the subroutine "dynxrun", a reserved name that MUST be used with the GUI. c- Dynetics, Inc. 1000 Explorer Blvd. Huntsville, AL 35806 (205) 922-9230 program climain integer icntl 100 continue c-------CLEXEC sends the program to the CLI> prompt CALL CLEXEC(icntl) c-------check the return code to see whether the user typed 'stop' if( icntl.lt. 0 ) then write(*,*)'Normal exit from program' stop endif c-------call the user program to do computations, etc. call dynxrun c-----return to the user CLI> prompt so that we may run again goto 100 end[LISTING TWO]
c- monte.f -- bob graves -- (c) 1992, dynetics, inc., huntsville, al c- name type description c- ---- ---- ----------- c- dbldat dp array to read line of data from file c- idat int data counter c- irun int run counter c- nmnvec int number of points in shortest run c- nmxvec int number of points in longest run c- numrun int number of runs read from file c- there log boolean file exists (t) or not (f) c- clrecd sub FORTEX data recording routine c- dynxrun sub main number cruncher for monte program c- mcavg sub compute average for all points, all runs c- mcsig sub compute sigma (std dev) for single point subroutine dynxrun c- include file listed in clicom.def for FORTEX dictionary include 'monte.h' double precision dbldat(100) logical there c- error checking if( nsigma .lt. 0.0 ) then write(*,*)'err: nsigma negative: defaults to 3.0' nsigma = 3.0 endif c- user may access the file name through FORTEX set/display inquire( file = fname, exist = there ) if( .not. there ) then write(*, *) 'file not found : ', fname return endif c- file exists, so open. open( lfdat, file=fname, form='unformatted', status='old') c- initialize counters. idat = 1 irun = 1 c- data read loop reads from a FORTEX "clirrr" type file and c- presumes that time (independent variable) has been prepared c- first on the list. other parameters are data (nparm of them) 100 continue read(lfdat,err=110,end=110) t(idat,irun),(dbldat(i),i=1,nparm) c- pick out the column of data for analysis work(idat,irun) = dbldat(ivar) if( idat .gt. 1 ) then c- test whether we have entered into another run. if( t(idat, irun ) .lt. t(idat-1,irun) ) then c- set the number of data points in the run. num(irun) = idat - 1 irun = irun + 1 if( irun .gt. 1 ) then c- already into next run, so copy it to first element of next run t(1, irun ) = t( idat, irun - 1) work(1, irun ) = work( idat, irun - 1) endif idat = 2 goto 100 endif endif idat = idat + 1 goto 100 110 continue c- close the file in case we run again close(lfdat) c- assign the number of data points in the last run. num(irun) = idat - 1 numrun = irun c- find the length of the shortest and longest runs. nmxvec = -1 nmnvec = mxdat do 300 irun = 1, numrun nmxvec = max ( num(irun), nmxvec ) nmnvec = min ( num(irun), nmnvec ) 300 continue c- determine data extend or truncate mode: trunc=.t., truncate long runs; c- trunc=.f., extend all shorter runs to be identical to longest run if ( trunc) then numvec = nmnvec else numvec = nmxvec imxrun = 1 c- load shorter runs with longest run data. mechanism drives std dev to 0.0 do 350 irun = 1, numrun if( nmxvec .eq. num(irun) ) then imxrun = irun endif 350 continue c- assign longest run data to shorter run data. do 410 idat = nmnvec, nmxvec do 400 irun = 1, numrun work(idat, irun ) = work(idat, imxrun ) 410 continue 400 continue endif c- analysis section. looping for FORTEX output. c- do average and +/- n sigma computations. call mcavg( avgvec, numvec, numrun, mxdat, work ) do 450 idat = 1, numvec avg = avgvec(idat) time = t(idat,1) call mcsig( avg, idat, numrun, mxdat, work, . nsigma, sigpos, signeg, sig ) c- FORTEX data recording in clrecd CALL CLRECD(idat-1) 450 continue return end subroutine mcsig( avg, idat, nrun, mxdat, work, . nsigma, ps3, ns3, sig ) c- compute +/- n sigma for data passed in. save double precision avg, ps3, ns3, sig, work(mxdat,*), sum c- initialize. sum = 0.0 c- loop through points. do 100 irun = 1, nrun sum = sum + ( work(idat, irun ) - avg )**2 100 continue if(nrun .gt. 0) then sig = sqrt(sum / dble( nrun - 1 )) else sig = 0.0 endif c- compute + and - N sigma vectors. ps3 = avg + nsigma * sig ns3 = avg - nsigma * sig return end subroutine mcavg( avg, ndat, nrun, mxdat, work ) c- compute the average of nruns of data. save double precision avg(*), work(mxdat,*), sum c- initialize. do 110 idat = 1, ndat sum = 0.0 c- loop the nrun loop. do 100 irun = 1, nrun sum = sum + work( idat, irun ) 100 continue c- compute the average for this parameter. avg( idat ) = sum / dble( nrun ) 110 continue c- return. return end block data mondat include 'monte.h' c- initialize through data statements, may also use the monte.setup file data nsigma /3/ data fname /'monte.dat'/ data nparm /2/ data ivar /2/ end[LISTING THREE]
c- monte.h / dynetics, inc., huntsville, al c- name type description c- ---- ---- ----------- c- mxdat parm maximum number of data elements per run c- mxrun parm maximum number of runs c- work dp memory copy of raw data for use in statistics c- avg dp average for a given time point c- avgvec dp average vector (keep the averages for std dev calc) c- signeg dp minus n-sigma value for a given time point c- sigpos dp plus n-sigma value for a given time point c- sig dp sigma (std dev) for a given time point c- nsigma int 1,2,or 3 for the +/- N sigma computation c- t dp time array c- time dp time for a given point (independent variable) c- nparm int number of data columns (not counting time) in file c- lfdat int logical file number of the data c- fname char name of the input data file (binary) c- num int array of number of data points per run c- ivar int which data column to analyze c- trunc log whether to truncate to shortest run (T) or extrapolate parameter( mxdat = 1000 ) parameter( mxrun = 100 ) double precision work( mxdat, mxrun ) double precision avg, avgvec(mxdat), signeg, sigpos, sig double precision t(mxdat, mxrun ), time common /monted/ work, avg, avgvec, signeg, sigpos, sig, time, t integer numvar, nsigma, lfdat, nparm, ivar, num(mxrun) logical trunc common /monte/ numvar, lfdat, nsigma, nparm, ivar, num, trunc character*64 fname common /montec/ fname [Example 1: MONTE command file] echo Setup for MONTE program set fname='traj.rrr' set ivar = 2 set lfdat = 30 set nparm = 2 prepar time,avg,sigpos,signeg,sig display 'prepare' macro facts echo File name with trajectory data display fname echo Number of data columns display nparm echo Column of data to analyze display ivar end macro sig3 echo 3 Sigma (nsigma=3) set nsigma = 3 end sig3 macro latdat echo Analyze the crossrange data set ivar=2 set trunc=.t. set fname='../inmonte/clirrr' set nparm=3 $ "number of data columns in missile simulation clirrr file" facts output 'clear' output time,avg,sigpos,signeg start plot end macro verdat echo Analyze the altitude data set ivar=3 set trunc=.t. set fname='../inmonte/clirrr' set nparm=3 $ "number of data columns in missile simulation clirrr file" facts output 'clear' output time,avg,sigpos,signeg start plot end set nciout=10 s cmd=5 [Example 2: Partial output from example run-time session] riddler77% DYNET-X (C) 1991 FORTRAN RUN-TIME EXECUTIVE V3.06 DYNETICS, INC. CLI> read 'monte.setup' Setup for MONTE program Prepare List TIME AVG SIGPOS SIGNEG SIG End Prepare List 3 Sigma (nsigma=3) CLI> "Run program to analyze vertical data" CLI> display nciout NCIOUT 10 CLI> "Every 10-th data point will be printed to the screen" CLI> verdat Analyze the altitude data File name with trajectory data FNAME '../inmonte/clirrr ' Number of data columns NPARM 3 Column of data to analyze IVAR 3 TIME 0.00000000 AVG 0.00000000 SIGPOS 0.00000000 SIGNEG 0.00000000 TIME 0.09999999 AVG 18.96214371 SIGPOS 20.76645258 SIGNEG 17.15783483 TIME 0.20000002 AVG 32.36762810 SIGPOS 36.44571089 SIGNEG 28.28954530 . . . TIME 4.80001497 AVG 719.5072083 SIGPOS 784.4273663 SIGNEG 654.5870502 TIME 4.90001726 AVG 738.4712952 SIGPOS 805.1422782 SIGNEG 671.8003122 CLI> "Only output every 100th point" CLI> set nciout=100 CLI> "Run program to analyze the lateral data" CLI> latdat Analyze the crossrange data File name with trajectory data FNAME '../inmonte/clirrr ' Number of data columns NPARM 3 Column of data to analyze IVAR 2 TIME 0.00000000 AVG 0.00000000 SIGPOS 0.00000000 SIGNEG 0.00000000 TIME 0.99999934 AVG 13.08728914 SIGPOS 15.78440254 SIGNEG 10.39017575 TIME 1.99999845 AVG 25.58025684 SIGPOS 28.22318408 SIGNEG 22.93732961 TIME 2.99999762 AVG 37.22265167 SIGPOS 40.50114335 SIGNEG 33.94416000 TIME 3.99999666 AVG 50.43453903 SIGPOS 54.86466342 SIGNEG 46.00441465 CLI> display nsigma NSIGMA 3 CLI> "Change sigma level to 2" CLI> set nsigma = 2 CLI> "Run lateral case again to obtain 2-sigma trajectories" CLI> "Normally, you would type 'start' to run the program" CLI> "but we would like to illustrate the repeat command" CLI> !20 latdat Analyze the crossrange data File name with trajectory data FNAME '../inmonte/clirrr ' Number of data columns NPARM 3 Column of data to analyze IVAR 2 TIME 0.00000000 AVG 0.00000000 SIGPOS 0.00000000 SIGNEG 0.00000000 TIME 0.99999934 AVG 13.08728914 SIGPOS 14.88536474 SIGNEG 11.28921355 TIME 1.99999845 AVG 25.58025684 SIGPOS 27.34220833 SIGNEG 23.81830535 TIME 2.99999762 AVG 37.22265167 SIGPOS 39.40831279 SIGNEG 35.03699056 TIME 3.99999666 AVG 50.43453903 SIGPOS 53.38795529 SIGNEG 47.48112278 CLI> stop
Copyright © 1992, Dr. Dobb's Journal
Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.