[ previous ] [ contents ] [ next ]

Overview and usage

Program structure

As discussed at some detail in [17, 19], the program is an application of the simple meta language arfg for building Fortran 90 code from any selection of source files each of which corresponds to a specific functional concept («main part») and implements a given set of approximations. By applying arfg in fixed-point mode to the collection of source files, information is allowed to flow freely between the main parts and effectively gives rise to Fortran 90 code incorporating as much information about the target system and the approximations adopted as is feasible; compiling the Fortran 90 code then produces an executable that reads any further data from options files, solves the HRT equations in the approximation chosen all the way to Q=0, and saves the results in one of several file formats.

Files and directory structure

The arfg source code as well as the Fortran 90 code and the compiled executable are all organized in or below the three code directories iso.arfg, share.arfg, and util.arfg. Of these, share.arfg is only meant to hold files that must be shared between all the HRT programs, ar-HRT-1 and the utilities in particular. If you plan to change any of the files customarily located in or below share.arfg and your file system does not support links, it is of utmost importance that you maintain synchronization; otherwise, you run the risk of not being able to access the data calculated.

In the two directories meant to be used for constructing and compiling programs, viz. iso.arfg and util.arfg, the arfg source files corresponding to a main part with several different incarnations are contained in sub-directories the names of which are derived from the names of the main parts by a prepended dot and the appended ending .arfg; on the other hand, main parts with only one version are stored (or linked to; cf. share.arfg) in iso.arfg or util.arfg directly, with filenames consisting of the name of the main part and the ending .arfg.

Under iso.arfg and util.arfg you will find directories .f90: this is where the Fortran 90 code as well as auxiliary files and the executable go; with a definition for sub compile_f90 similar to the one given in the installation instructions, an additional symbolic link to the executable will be placed in the same directory where the arfg source resides, provided the Makefile is used so that the compilation proceeds via the script compile-program placed in the .f90 directory. - In the .opt subdirectory to directory .f90 you will find templates for the options files and void definitions of all hooks present in the current code base.

The directory share.arfg/.m4 (linked into iso.arfg and util.arfg) holds a set of files with m4 definitions that are used by arfg.

The meta language

As noted in its description, arfg is a framework for defining a simple meta language implemented via the combination of various filters. In the following we only give a rudimentary overview of the definitions used in order to allow for easy orientation of where some feature is implemented; those proficient in all the languages combined should find this a sufficient starting point for reading the code.

In accordance with the documentation of arfg, in ar-HRT-1 the definition of the meta language is accomplished via the following files:

This reads the m4 definitions of share.arfg/.m4/ as well as files .compile-time-parameters, .hooks, and .local. It also implements Fortran 90's KIND parameters (cf. the pseudo data types #real and #complex), a diversion for the current block's private variables, logging, error messages, safe memory allocation, generation of PUBLIC statements and PARAMETER definitions, handling of the pseudo data type #string for storing textual information of arbitrary length. It also defines the list of main parts and fixes their order, indicates the list of main parts that access nodes and node lists, commences the generation of derived data types tQ, tRho and tNode and of the corresponding initialization code. It also defines treatment of compile-time parameters vs. default values, generates code for the program's self-documentation (descriptions, hook lists), and it prevents some kinds of arfg error checks from being diverted into the null diversion. Auxiliary routines or macros for logging, for fatal error messages, for accessing yacas either through ePerl or at the m4 step are also defined here.
This exports the modifications to the derived data types tQ, tRho and tNode and to the corresponding initialization code, accumulates the information of which solution components are to be interpolated upon node insertion, and saves interfacing information for the code implementing documentation of the program and saving of the solution.
This defines convenient syntax for comments stripped before or after m4 expansion, accumulation of lists of public identifiers, program units, functions, subroutines, and type declarations, simplifies the use of pseudo data types, calls yacas for evaluation of numerical constants, keeps track of program units and provides simple means of starting and ending them, keeps track of output file names and provides simple means of opening and closing them, supports various modes of including files, generates data underlying module dependency analysis, and supports test code. It also adds special syntax for certain loops over nodes, m4 quoting and for saving of m4 definitions; also, it implements the hook mechanism, generates a comprehensive per-main-part list of hooks, and auto-declares a host of hooks.

Pseudo data types

The pseudo data types mentioned are

for REAL quantities with fixed KIND, usually double precision (cf. main part «basics»)
for COMPLEX quantities with fixed KIND, usually double precision (cf. main part «basics»)
for CHARACTER(:) arrays of fixed length; note that it is of vital importance that the string length (m4 variable `__StringLength__') not be changed once it has been used, or else any earlier results will be rendered unreadable.

Test code

In several cases, special test code is included; use the commands




to turn test code on or off, respectively; for simple commands, a line like

#? code

will expand to code or yield a blank line. According to .arfg-rewrite, #? works by calling the m4 macro m4_testcode() that is convenient for more complicated constructions.

Compile time parameters

As opposed to options, compile time parameters provide customization settings that are not likely to change once a satisfactory setting has been found (e. g., the customization factors to NumericalPrecision (denoted ε# in [17, 18, 19])) or that have to be taken into account already during code construction for reasons of simplicity and efficiency (such as, e. g., the number of basis functions in a LOGA/ORPA-like closure); usually, some (hopefully) reasonable default behavior is provided. Conventionally, these definitions are placed in file .compile-time-parameters.

The precise set of compile time parameters that can meaningfully be used depends, of course, on the set of main part versions chosen. In order to find out about them, look at the calls to m4 macro ctp_def() in the arfg code; the compile time parameters used are also saved as part of the automatic documentation.

In addition, the following parameters are always considered by the underlying m4 macro packages and the definition of the meta language:

A setting of m4_define(`__useCache__', `no') disallows installation of local caching; the default is to use `yes' if `__useCache__' is not defined; if you set `__useCache__' to some value other than `yes' or `no', an error will result during code construction. - Note that a setting of `yes' only allows, but does not force, caching to take place; hence, it is generally a safe bet to never disallow caching.
If this is defined (recommended for production use; to do this, put m4_define(`__quiet__') into your .compile-time-parameters), the program will not print out any information during execution. Note that this is much more efficient than giving similar options as no subroutine call will be generated and no flags have to be checked at run-time. Also, suppression of output will similarly affect your hooks provided you make use of the log() facility defined in .arfg-pre; on the other hand, if your hooks directly call PRINT, the corresponding code is obviously executed.

In addition, any logarithmic digests you might want to generate are to be declared here; e. g., for producing a digest based upon the variation of the inverse compressibility you might add the following:

 `kappa', `$1%rho%ansatzResults%invKappaT')

This will result in a vastly expanded options template file .f90/.opt/run.tmpl documenting the file format; q. v. m4-file logdigest.m4.

Compile time parameters are, of course, just some m4 macros that may or may not be defined; consequently, you may want to introduce additional ones. This is, indeed, the case with the sample definition of the Perl sub compile_f90 given earlier: as noted there, this introduces an additional flag compile__with_debugging (that has to be one of either `yes' or `no') to determine which set of compiler flags should be used in compiling the program.


The hook mechanism is a very simple but powerful tool for minimally invasive modification of the resulting Fortran code: whereever a hook is declared, the contents of a specific m4 macro, if defined, are inserted into the program text at the m4 stage. This means that further macro expansion may take place; on the other hand, the special syntax provided by .arfg-rewrite is not available. Hook definitions are customarily stored in .hooks; this file is read as part of a comment so that only the m4 commands in there have an effect provided you refrain from direct manipulation of the diversions.

Thus if, e. g., you want to add some code at a specific position in the code for, say, testing, rather than inserting the corresponding statements directly into the arfg file, you would only declare a hook there; as hooks without a defined replacement text are ignored, there is no need to remove the hook declaration once you no longer need the additional code. If this affects, say, main part «ansatz», you would insert

#hook label

into ansatz.arfg, where label is an identifier that is unique to ansatz.arfg. Application of .arfg-rewrite will then translate this into m4 code that instead dumps the contents of m4 macro `hook ansatz:label'. To use this to print, say, the value of some #real variable var, you might put the following definition into your .hooks:

m4_define(`hook ansatz:label',
 `print *, var')

or even the more elaborate (and somewhat baroque)

m4_define(`hook ansatz:label',
 `log([Hello], `The value of var at this point of the program &
                &happens to be "//trim(realString(var))//".')')

This example also serves to illustrate some basic features of log(); for the details, see .arfg-pre.

In addition to the manual declaration of hooks, there is also a host of automatically declared hooks (cf. .arfg-rewrite). These behave, and are to be used, in exactly the same way as manually declared ones; the only difference is that there is no #hook statement.

Hook names should be unique; however, the system has no simple way of enforcing this condition, so it is up to you to check that your hook label is, indeed, unique within that main part; using a descriptive label will help with this.

As part of the information compiled during code construction, the file .f90/.opt/hooks-template.m4 will be built; it contains empty definitions for all the hooks - automatically or manually declared ones alike - in the code base; in the above example, the entry

m4_define(`hook ansatz:label')

would be listed. In a typical situation this will contain several hundred hooks, only few of which are likely to be used at any given instance. Also bear in mind that some facilities, caching in particular, are implemented via hooks and thus may interfere with your definitions.

Capabilities and properties

One problem that sometimes arises with the hooks mechanism just sketched stems from the wish to safely and automatically generate alternative code that is valid only for certain combinations of main parts: while this can easily be implemented manually via hooks [17], and some automatic declaration based on the presence of a specific version of some other main part is feasible, it does not sit well with the main-part locality of simple hooks and soon becomes cumbersome.

In order to overcome this difficulty, a «properties / capabilities» mechanism was introduced (cf. m4-file capabilities.m4): Now any main part may export properties, and any main part may check any other main part's properties. If some main part maintains a list of capabilities, it is then simple to check whether the capabilities present match the properties exported by any of the other main parts, and code can be modified accordingly.

Preparing Fortran 90 code construction

In order to be able to build the Fortran code, you first have to select the main part versions to be used and provide whatever information is to enter the code construction phase. Note that the following assumes that code construction will always take place in the directory iso.arfg produced during installation, or in a directory mirroring the structure set up there.

Main part versions

The complete list of main parts of ar-HRT-1's version 5.20030128a can be found elsewhere. While those of the main parts that are provided in only one version should permanently reside in iso.arfg or share.arfg, in those cases where several versions may be selected you first have to copy or link the correct files in place. Thus, e. g., in order to use version 20010313-ar of main part «ansatz», you might issue the command:

ln -sf .ansatz.arfg/20010313-ar ansatz.arfg

In the end, some version of every main part must be present in the build directory.

Other settings

As described above, you should define all the hooks and set all the compile-time parameters you need.

Code construction

Code construction proceeds by repeated application of the arfg script to all the arfg files until the output files in .f90 no longer change; the latter criterion is usually checked via the files' md5 checksums.

As the system works by accumulating information in a host of auxiliary files and missing files generally indicate some failure of the scripts, starting from an empty .f90 subdirectory (as in make code, make program, or after make clean) means that you will get a large number of error messages during the first round of applications of arfg; if these error messages do not go away after one or two cycles, this indicates a true error and you should investigate its reason.

If error conditions are detected during code construction, you will often see a short notice being issued in the process. Usually, however, this will also result in some text being inserted into the Fortran code that does not conform to Fortran 90 syntax rules and is thus guaranteed to make compilation fail.

After code construction the subdirectory .f90/.opt/ will hold files with some of the information accumulated in the course of the procedure; this comprises the list hooks.m4 of hooks mentioned earlier as well as the template files master.tmpl, system.tmpl, isotherm.tmpl, and run.tmpl for the master, system, isotherm, and run options files; the latter hold some commentary and a list of all valid options understood by the program, occasionally amended by some instructions to their use. - Similarly, using your definition of the Perl sub compile_f90, a file .f90/compile-program is written containing the commands necessary for compiling the program.

Provided a suitable make program is installed, you can use the command

make code

to construct the Fortran 90 code in the .f90 subdirectory. Alternatively, a previous (partial) build of the Fortran code can be updated by issuing

make update

If you want to explicitly clean out the files under .f90/, use

make clean

Other useful targets can be found in the Makefile (share.arfg/Makefile).

Compiling the Fortran 90 program

The simplest way to compile the Fortran 90 code is to call

make compile


make program

combines the functionality of make code and make compile. The compiling step works by calling the shell script written during code construction.

If you are unable to compile the Fortran 90 code produced by arfg, you may have a wrong definition of the Perl sub compile_f90 (be sure to check the compiler flags); try to compile manually and adapt its definition. Otherwise, the problem must already have occurred before; if you get syntax errors, check the Fortran files for any errors of the code construction process and fix them in the arfg source. Note that the Fortran code is not intended for human inspection; consequently, it is largely unreadable and undocumented, and unsafe techniques like variable re-use in different contexts etc. may be used; simply changing the Fortran code without a full understanding of the whole program is an almost certain way to desaster. Problems must be fixed in the arfg source and never in the Fortran code. If you modify or complement the arfg source, be sure to first understand the way the meta language works; reading through the files listed above is a good starting point, and a good working knowledge of all the languages used by arfg is of paramount importance.

Running ar-HRT-1

At any invocation, the ar-HRT-1 executable will read a number of options files containing additional information; the syntax is documented in Module OptionsHandler in main part «basics» (file share.arfg/basics.arfg) but should be obvious from the template files; the usual sequence of reading the options files, as well as a set up often more convenient, are shortly described in the installation instructions. These files hold some information to be read at run time such as, e. g., the temperature or some potential parameters. The system template file is intended for settings specific to the physical model used; the isotherm template file is intended for settings specific to the isotherm considered, most importantly the temperature and the output file name; and the run options file is intended for information governing the run-time behavior, the amount and kind of information logged in particular. The master options file indicates the locations of the other options files.

The options so found are then handed to all the main parts in turn for initialization. Equipped with this information, the program then aspires to solve the HRT equations in an iterated full approximation scheme as discussed in [17, 19]; if an error condition occurrs, some error message indicating the code block and the arfg source file where it is defined is handed to log() and will be displayed or suppressed, depending on your setting for compile time parameter `__quiet__' and the run-time options you used.

Output files

If you defined a file name, at the end of the calculation the results will be saved. Based on the low-level module used for processing the file we have to distinguish between the following formats

This is the file format used exclusively in version 4: The routines relie on unformatted I/O, which results in low CPU processing demands (which may well be outweighed by far by disk performance, of course) and completely eliminates the problem of a possible loss of information: after all, the system just saves the binary representation as it sits in memory. On the other hand, the resulting files are clearly compiler- and architecture dependent, and quite often they will not be very compact. For most applications, this format is no longer recommended.
This is the current default format. It was introduced in version 5.20030128a and relies on BaseEncode to achieve portability and compactness. While this comes with a higher CPU load for processing the files, the smaller size of the files and the low speed of typical disks [not to mention networked file systems] actually renders this faster in many cases than the NonPortable format. On the other hand, file corruption is unlikely to be recoverable, and due to the current reliance on Fortran intrinsics for extracting floating-point numbers from a string some of the information stored in the file may not be accessible via Fortran; note, however, that the full information is still available and can easily be extracted for processing in, e. g., Mathematica.

The information actually saved in such a file depends on whether or not you define an extra file for the system's description: a complete account of all relevant settings and documentation strings is either written to that file or saved together in the header of the any isotherm file. In addition to this general information, selected components of the solution vector will be saved, where the following cases must be distinguished:

full results:
The data will be saved at each and every density used in the calculation. Obviously, this is the most comprehensive data set possible, and high-resolution computations will result in tremendously large files.
logarithmic digests:
The data is stored only at densities selected so as to relative changes as well as local extrema of some quantity; actually, the details of the selection criteria are a bit more complicated, m4-file logdigest.m4 and arfg-file hrt.arfg. A logarithmic digest must be defined at compile time; an example was given earlier. More than one logarithmic digest may be defined at any time; these will be written consecutively, and after any full results should these be requested, hrt.arfg.

The file names for the full data and the logarithmic digests are derived from some base name; the run options file provides ample documentation on this point.

Note that the calculation's results will silently be discarded if you do not define a file to save them; on the other hand, existing files will be overwritten mercilessly.

[ previous ] [ contents ] [ next ]

Copyright © 2002-2003 by Albert REINER. All rights reserved.

URL: http://purl.oclc.org/NET/ar-hrt-1/current/usage.html

URL: http://purl.oclc.org/NET/ar-hrt-1/5.20030128a/usage.html

2003-01-28 17:55:18