#!/usr/bin/perl -w ## ## frobnicate.pl Copyright (C) 1999 Bruce Ravel ## ## Author: Bruce Ravel ## Maintainer: Bruce Ravel ## Created: August 30 1999 ## Updated: ## Version: see variable $cvs_info near line 44 ## Keywords: gnuplot, plotting ########################################################################## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2, or (at your option) ## any later version. ## ## This lisp script is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ## ## Permission is granted to distribute copies of this perl program ## provided the copyright notice and this permission are preserved in ## all copies. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, you can either send email to this ## program's maintainer or write to: The Free Software Foundation, ## Inc.; 675 Massachusetts Avenue; Cambridge, MA 02139, USA. ########################################################################## ## ## This program is a simple hack which attempts to automate a large ## sequence of feff8 calculations, perhaps on a remote machine, and ## with an eye towards saving computation time and disk space. It ## works by parsing a specially marked-up feff.inp file and perfroming ## the sequence of calculations specified by the markup. At the end, ## the sequence of calculations is packed up in a tar.gz file and ## emailed back to the owner. ## ########################################################################## ## send bug reports to the authors (ravel@phys.washington.edu) ########################################################################## use strict; use Cwd; use File::Copy; use File::Find; use File::Path; use File::Spec; use Text::ParseWords; ## -- parse command line options --------------------------------- use Getopt::Long; use vars qw($run_feff $archive $mail $test $quiet $short_help $help); ($run_feff, $archive, $mail, $test, $quiet, $short_help, $help) = (1, 1, 1, 0, 0, 0, 0); GetOptions("feff!" => \$run_feff, "archive!" => \$archive, "mail!" => \$mail, "test!" => \$test, "q!" => \$quiet, 'help!' => \$help, 'h!' => \$short_help, ); (not $archive) and $mail = 0; ($test) and ($run_feff, $archive, $mail) = (0, 0, 0); ($short_help) and &short_help; if ($help) { require Pod::Text; $^W=0; Pod::Text::pod2text($0, *STDOUT); exit; }; ## --------------------------------------------------------------- ## -- set some global variables ---------------------------------- my $cvs_info = '$Id:$ '; #my $version = (split(' ', $cvs_info))[2] || "pre_release"; my $version = '0.0.0'; my $name = 'FROBNICATE'; my $spelled_out = 'FEFF Remote Operation By Network Interaction: ' . 'Calculate, Archive, Then Email'; my $inpfile = $ARGV[0] || "feff-template.inp"; my ($frob_comment, $frob_delimiter, $frob_skip) = ('^\s*\*\%\%', '^\s*\*%%\s+---', '^\s*\* \*'); my (%dos, %xanes, %exafs); my $start = localtime; my %params = ('molecule' => 0, 'id' => 0, 'email' => '', 'feff' => 'feff8', 'start' => $start); my %scf = ('r' => 4.0, ); my %phases = ('k' => 4.0, ); my @actions = (); my $done_a_xanes = 0; ## names of files for making symbolic links my @scf_files = qw(fms.bin info.bin pot.bin geom.dat potph.bin); my @phase_files = qw(phase.bin rkk.bin xsect.bin); my $logfile = 'frobnicate.log'; open LOG, '>'.$logfile or die $!; my @outhandles = ($quiet) ? (*LOG) : (*STDOUT, *LOG); ## --------------------------------------------------------------- ## -- read the template file ------------------------------------- open INP, $inpfile or die $!; my @input_data = ; close INP; ## --------------------------------------------------------------- ## +---- first chore -----------------------------------------------+ ## | read through the input file template and find all frobnicate | ## | keywords (which are maked by this comment string: ' *%% '). | ## | These ontain the instructions for the order and nature of each | ## | calculation to be made. | ## +----------------------------------------------------------------+ foreach my $fh (@outhandles) { print $fh $/, $name, ' version ', $version, $/ x 2; print $fh $spelled_out, $/ x 2; print $fh 'Reading from template file ', $inpfile, $/; }; foreach my $line (@input_data) { next unless ($line =~ /$frob_comment/); my @words = shellwords($line); shift @words; shift @words; # shift of the comment characters my $this = shift @words; # get this frobnicate keyword MODES: { ## params :molecule 0 :id '' :email '' ## SCF :r 4.0 ## PHASES :k 4.0 ## DOS :r 4.1 :emin -30 :emax 20 :eimag 0.1 ## XANES :r 4.1 :polarization z :ellipticity '' ## EXAFS :r 5.1 :polarization z :ellipticity '' ## params ($this =~ /params/i) && do { while (@words) { my $arg = shift @words; next unless ($arg =~ /^:/); if ($arg =~ /:molecule$/) { $params{'molecule'} = shift @words; } elsif ($arg =~ /:id/) { $params{'id'} = (shift @words) || &random_number; } elsif ($arg =~ /:email/) { $params{'email'} = shift @words; } elsif ($arg =~ /:feff/) { $params{'feff'} = (shift @words) || 'feff'; }; }; last MODES; }; ## SCF ($this =~ /scf/i) && do { my ($r, $k) = (4.0, 4.0); while (@words) { my $arg = shift @words; next unless ($arg =~ /^:/); if ($arg =~ /:r$/) { $r = shift @words; } elsif ($arg =~ /:k$/) { $k = shift @words; }; }; push @actions, ['scf', $r, $k]; last MODES; }; ## ## PHASES ## ($this =~ /phase/i) && do { ## my $k = 4.0; ## while (@words) { ## my $arg = shift @words; ## next unless ($arg =~ /^:/); ## if ($arg =~ /:k$/) { ## $k = shift @words; ## }; ## }; ## push @actions, ['phases', $k]; ## last MODES; ## }; ## DOS ($this =~ /dos/i) && do { my ($r, $k, $emin, $emax, $eimag) = (4.0, 4.0, -30, 20, 0.1); while (@words) { my $arg = shift @words; next unless ($arg =~ /^:/); if ($arg =~ /:r$/) { $r = shift @words; } elsif ($arg =~ /:k$/) { $k = shift @words; } elsif ($arg =~ /:emin$/) { $emin = shift @words; } elsif ($arg =~ /:emax$/) { $emax = shift @words; } elsif ($arg =~ /:eimag$/) { $eimag = shift @words; }; }; push @actions, ['dos', $r, $k, $emin, $emax, $eimag]; last MODES; }; ## XANES ($this =~ /xanes/i) && do { my ($r, $k, $polarization, $ellipticity) = (4.0, 4.0, '', ''); while (@words) { my $arg = shift @words; next unless ($arg =~ /^:/); if ($arg =~ /:r$/) { $r = shift @words; } elsif ($arg =~ /:k$/) { $k = shift @words; } elsif ($arg =~ /:polarization$/) { ($polarization, $ellipticity) = &set_polarization(shift @words); } elsif ($arg =~ /:ellipticity$/) { $ellipticity = &set_ellipticity(shift @words); }; }; push @actions, ['xanes', $r, $k, $polarization, $ellipticity]; last MODES; }; ## EXAFS ($this =~ /exafs/i) && do { my ($r, $k, $polarization, $ellipticity) = (4.0, 20, '', ''); while (@words) { my $arg = shift @words; next unless ($arg =~ /^:/); if ($arg =~ /:r$/) { $r = shift @words; } elsif ($arg =~ /:k$/) { $k = shift @words; } elsif ($arg =~ /:polarization$/) { ($polarization, $ellipticity) = &set_polarization(shift @words); } elsif ($arg =~ /:ellipticity$/) { $ellipticity = &set_ellipticity(shift @words); }; }; push @actions, ['exafs', $r, $k, $polarization, $ellipticity]; last MODES; }; }; }; foreach my $fh (@outhandles) { print $fh ' Done snarfing input file.', $/; print $fh ' The calculations to be performed in this frobnication are:', $/; my $count = 1; &check_first_action; foreach my $action (@actions) { printf $fh " %2d. %-6s with ", $count, uc($action->[0]); if ($action->[0] eq 'scf') { printf $fh "Rmax = %s, kmax = %s$/", $action->[1], $action->[2]; } elsif ($action->[0] eq 'phases') { printf $fh "kmax = %s$/", $action->[1]; } elsif ($action->[0] eq 'dos') { printf $fh "Rmax = %s, kmax = %s, emin = %s, emax = %s, eimag = %s$/", $action->[1], $action->[2], $action->[3], $action->[4], $action->[5]; } elsif ($action->[0] eq 'xanes') { printf $fh "Rmax = %s, kmax = %s, polariz. = %s, ellipt. = %s$/", $action->[1], $action->[2], $action->[3], $action->[4]; } elsif ($action->[0] eq 'exafs') { printf $fh "Rmax = %s, kmax = %s, polariz. = %s, ellipt. = %s$/", $action->[1], $action->[2], $action->[3], $action->[4]; }; ++$count; }; }; ## check parameters # cannot continue without email address if ( (not $params{'email'}) and $mail) { &die_no_email; }; # check to see if a directory named by # :id already exists. use it or create it if (-d $params{'id'}) { foreach my $fh (@outhandles) { print $fh ' Using "', $params{'id'}, '" as the base directory for this frobnication. ', $/; }; } else { foreach my $fh (@outhandles) { print $fh ' Creating "', $params{'id'}, '" as the base directory for this frobnication. ', $/; }; mkpath($params{'id'}, 0); }; foreach my $fh (@outhandles) { printf $fh " This is %sa molecule.$/", ($params{'molecule'}) ? '' : 'not '; }; ## require Data::Dumper; ## print Data::Dumper -> Dump([\@actions], [qw/*actions/]); ## print Data::Dumper -> Dump([\%params], [qw/*params/]); ## +---- second chore ----------------------------------------------+ ## | Step through list of actions, performing each calculation in | ## | sequence. This involves writing finding or creating the | ## | appropriate sub-directory, writing out the appropriate input | ## | file, then launching feff. This is the time consuming chore. | ## +----------------------------------------------------------------+ foreach my $fh (@outhandles) { print $fh $/, 'Beginning calculation loop. ', $/; print $fh ' FEFF8 will be invoked as "', $params{'feff'}, '".', $/; }; ## need to build the directory structure for each calculation and ## write the input file to that directory. then launch feff foreach my $action (@actions) { my $outfile; foreach my $fh (@outhandles) { print $fh ' ', uc($action->[0]), ' calculation.', $/; }; #### SCF #### if ($action->[0] eq 'scf') { ## just run module 1 my $thisdir = File::Spec -> catfile($params{'id'}, 'scf'); unless (-d $thisdir) { mkpath($thisdir,0) or &die_making_directory($thisdir); }; $outfile = File::Spec -> catfile($thisdir, 'feff.inp'); &write_file($outfile, \@input_data, \&write_SCF, $params{molecule}, $action->[1], $action->[2]); &run_feff($thisdir) if ($run_feff); ## } elsif ($action->[0] eq 'phases') { ## $outfile = 'phases.inp'; ## #&write_file($outfile, \@input_data, \&write_PHASES); #### DOS #### } elsif ($action->[0] eq "dos") { ## just run module 2 my $thisdir = File::Spec -> catfile($params{'id'}, 'dos'); unless (-d $thisdir) { mkpath($thisdir,0) or &die_making_directory($thisdir); }; $outfile = File::Spec -> catfile($thisdir, 'feff.inp'); &write_file($outfile, \@input_data, \&write_DOS, $params{molecule}, $action->[1], $action->[2], $action->[3], $action->[4], $action->[5]); &make_symlinks($params{'id'}, 'scf', 'dos', \@scf_files, 0); &run_feff($thisdir) if ($run_feff); #### XANES #### } elsif ($action->[0] eq "xanes") { ## run module 2 once and modules 3-6 several times ## presumably there will be many xanes runs, so do a single phases ## run and make symlinks to the necessary files for each of the ## xanes runs. ## compute phases if need be unless ($done_a_xanes) { my $thisdir = File::Spec -> catfile($params{'id'}, 'xanes', 'phases'); unless (-d $thisdir) { mkpath($thisdir,0) or &die_making_directory($thisdir); }; &make_symlinks($params{'id'}, 'scf', File::Spec -> catfile('xanes', 'phases'), \@scf_files, 1); $outfile = File::Spec -> catfile($thisdir, 'feff.inp'); &write_file($outfile, \@input_data, \&write_PHASES, $params{molecule}, $action->[1], $action->[2], $action->[3], $action->[4] ); &run_feff($thisdir) if ($run_feff); }; ## compute this xanes my $dirbase = &make_dir_name($action->[1], $action->[3]); my $thisdir = File::Spec -> catfile($params{'id'}, 'xanes', $dirbase); unless (-d $thisdir) { mkpath($thisdir,0) or &die_making_directory($thisdir); }; &make_symlinks($params{'id'}, 'scf', File::Spec -> catfile('xanes', $dirbase), \@scf_files, 1); &make_symlinks($params{'id'}, 'phases', File::Spec -> catfile('xanes', $dirbase), \@phase_files, 0); $outfile = File::Spec -> catfile($thisdir, 'feff.inp'); &write_file($outfile, \@input_data, \&write_XANES, $params{molecule}, $action->[1], $action->[2], $action->[3], $action->[4]); &run_feff($thisdir) if ($run_feff); ++$done_a_xanes; #### EXAFS #### } elsif ($action->[0] eq "exafs") { ## just run modules 2-6 this is usually fast enough that a ## separate phases run need not be done. that might need to be ## changed in the case of large numbers of exafs runs, but that ## doesn't seem too likely. my $dirbase = &make_dir_name($action->[1], $action->[3]); my $thisdir = File::Spec -> catfile($params{'id'}, 'exafs', $dirbase); unless (-d $thisdir) { mkpath($thisdir,0) or &die_making_directory($thisdir); }; &make_symlinks($params{'id'}, 'scf', File::Spec -> catfile('exafs', $dirbase), \@scf_files, 1); $outfile = File::Spec -> catfile($thisdir, 'feff.inp'); &write_file($outfile, \@input_data, \&write_EXAFS, $params{molecule}, $action->[1], $action->[2], $action->[3], $action->[4]); &run_feff($thisdir) if ($run_feff); }; }; ## +---- third chore -----------------------------------------------+ ## | Make an archive of the directory named by :id and mail it to | ## | the owner | ## +----------------------------------------------------------------+ foreach my $fh (@outhandles) { print $fh $/, 'Archiving results. ', $/; print $fh " Copying log and template file to $params{'id'}... ", $/; ($archive) and print $fh " Writing archive to $params{'id'}.tar.gz... ", $/; ($mail) and print $fh " Mailing archive to $params{'email'}...", $/; print $fh $/, $/, "FROBNICATE finished on $params{'id'}.", $/; }; close LOG; copy($logfile, $params{'id'}); copy($inpfile, $params{'id'}); ## build list of files if ($archive) { my @file_list; find(sub { push @file_list, $File::Find::name }, $params{'id'}); require Archive::Tar; Archive::Tar->create_archive ("$params{'id'}.tar.gz", 9, @file_list); }; $params{'end'} = localtime; if ($mail) { &mail_archive("$params{'id'}.tar.gz"); }; ##################################################################### ## subroutines for writing the feff input file sub write_file { my $file = shift; my $data = shift; my $function = shift; my $top = 0; open OUT, ">".$file or die $!; foreach my $line (@$data) { if ($line =~ /$frob_delimiter/) { print OUT $line; ($top) or print OUT &$function(@_); ++$top; } elsif ($line =~ /$frob_comment/) { 1; } elsif ($line =~ /$frob_skip/) { 1; } else { print OUT $line; }; }; close OUT; }; sub write_SCF { my ($molecule, $rscf, $k) = @_; return <'1 0 0', 'y' =>'0 1 0', 'z' =>'0 0 1', 'xy'=>'1 1 0', 'yz'=>'0 1 1', 'xz'=>'1 0 1',); my %ellip = ('x' =>'', 'y' =>'', 'z' =>'', 'xy'=>'1 0 0 1', 'yz'=>'1 1 0 0', 'xz'=>'1 0 1 0',); (exists($trans{$pol})) and return ($trans{$pol}, $ellip{$pol}); ($pol =~ /^([+-]?\d+)\s+([+-]?\d+)\s+([+-]?\d+)/) and return ("$1 $2 $3", ''); return ('', ''); }; sub set_ellipticity { my $ell = lc($_[0]); ($ell =~ /^([+-]?\d+)\s+([+-]?\d+)\s+([+-]?\d+)\s+([+-]?\d+)/) and return "$1 $2 $3 $4"; return ''; }; ## make a directory name for a xanes or exafs calculation based on the ## polarization and the rmax values sub make_dir_name { my ($r, $pol) = @_; unless ($pol) {return sprintf "r=%s",$r}; my @pol = split(" ", $pol); return sprintf "%d_%d_%d^r=%s", @pol, $r; }; sub random_number { srand; return int(10000 + rand(89999)); }; ##################################################################### ## these subroutines may take some tweaking on other platforms ## :id from to ref_to_list_of_files depth_flag sub make_symlinks { my ($id, $from, $to, $r_list, $xanes_or_exafs) = @_; $from = File::Spec -> catfile(File::Spec -> updir, $from); ($xanes_or_exafs) and $from = File::Spec -> catfile(File::Spec -> updir, $from); $to = File::Spec -> catfile($id, $to); foreach my $f (@$r_list) { symlink(File::Spec -> catfile($from, $f), File::Spec -> catfile($to, $f)) }; }; sub run_feff { my $dir = $_[0]; my $top = cwd(); foreach my $fh (@outhandles) { print $fh ' chdir down to ', File::Spec -> catfile($top, $dir), $/; print $fh ' see log.dat and/or logpot.dat in ', File::Spec -> catfile($top, $dir), $/; }; chdir $dir; foreach my $fh (@outhandles) { print $fh ' running feff ', $/; }; system $params{'feff'}; ##print " ---> ", $params{'feff'}, $/; foreach my $fh (@outhandles) { print $fh ' chdir back to ', $top, $/; }; chdir $top; }; sub mail_archive { my $archive = $_[0]; require MIME::Entity; my @message=( "Your calculation with id \"$params{'id'}\" has finished.$/", "The job started at $params{'start'} and$/", "finished at $params{'end'}.$/", "$/" ); my $mime = build MIME::Entity (From => 'bruce.ravel@nist.gov', To => 'bruce.ravel@nist.gov', Subject => "FROBNICATION [$params{'id'}] finished", Data => \@message); $mime->attach(Path => $archive, Type => "application/octet-stream", Encoding => "base64"); open MAIL, "| /usr/lib/sendmail -t -i" or die "open: $!"; $mime->print(\*MAIL); close MAIL; }; ##################################################################### ## subroutines for exiting gracefully sub die_no_email { foreach my $fh (@outhandles) { print $fh < catfile($params{'id'}, 'SCF', 'phase.bin'); (-e $scf_file) and return; ($actions[0]->[0] eq 'scf') and return; foreach my $fh (@outhandles) { print $fh <) { # of the pod exit if (/COMMAND LINE SWITCHES/); if (/=head1/) { $_=; next; }; print; }; }; __DATA__ =head1 NAME frobnicate - Run a sequence of feff8 calculations =head1 SYNOPSIS frobnicate [switches] [template_file] =head1 DESCRIPTION The template file is assumed to be feff-template.inp if not specified. command line switches: switch description default ----------------------------------------------------------------- -feff/-nofeff perform feff calculations yes -archive/-noarchive build archive file yes -mail/-nomail mail archive to user yes -test = -nofeff -noarchive -nomail no -q suppress screen messages no -h print short help to STDOUT -help print long help to STDOUT =head1 COMMAND LINE SWITCHES Most flags only serve diagnostic purposes. =over 4 =item C<-feff>/C<-nofeff> This tells frobnicate whether to actually run feff8. The default is to do so. =item C<-archive>/C<-noarchive> This tells frobnicate whether to actually build an archive from the driectory tree. The default is to do so. Specifying C<-noarchive> also toggle the C<-nomail> switch. =item C<-mail>/C<-nomail> This tells frobnicate whether to send the archive by email. The default is to do so. =item C<-test> Specifying this is the same as specifying C<-nofeff> C<-noarchive> C<-nomail> at the command line. =item C<-h> This causes a short help message to be printed to the screen. =item C<-help> This causes the full help message to be printed to the screen. =back =head1 CALCULATION STRATEGY The primary motivation for this program is to organize and automate a large sequence of feff8 calculations. This is done in the following steps =over 4 =item 1 Read in the template file and parse the frobnicate instructions. From these make a list of computational chores that need to be done and the order in which to do them. Also make a subdirectory in which all the calculations will be saved. =item 2 Step through the list. For each item in the list, make a subdirectory in the directory tree for this run of frobnicate. From the content of the template file and the instructions in it, generate a feff.inp file suitable for the calculation and store in the appropriate subdirectory. Then make a bunch of symbolic links as necessary to use relevant parts of previous calculations. Finally, change directories to the appropriate subdirectory and run the feff calculation. =item 3 Once all the items in the list have been done, pack the entire directory structure up into a tarred and gzipped archive. =item 4 Mail the archive back to the owner of the process as a MIME attachment. =back =head1 TEMPLATE FILE Most of the template file is just a feff.inp file. In particular, the template file contains the title lines and the potentials and atoms lists. The template file should not contains any of the following cards: CONTROL POLARIZATION ELLIPTICITY SCF XANES FMS LDOS RPATH EXAFS These are the cards that are written by frobnicate. If you explicitly specify these in the template file, you may interfere with the proper operation of frobnicate. =head2 FROBNICATE KEYWORDS Keywords are specified in the template file by lines beginning with C<*%%>. Each keyword takes arguments, some or all of which may be optional. A keyword argument always begins with a colon (C<:>) and the argument is followed by its value. White space separates argumnets and their values. If a value itself contains whitespace, then you need to quote the value with single quote marks. The order of the keywords is very important. The ordeer in which keywords are found in the template file is the order in which the calculations are performed. Thus C must come before C, C, or C. It is not useful to have more than one C line because subsequent SCF calculatiosn would just overwrite the previous one. However, it is useful -- in fact the entire point of frobnicate -- to have more than one of the other lines. =over 4 =item C This keyword is used to specify operational parameters for this frobnicate run. Its arguments are C<:molecule>, C<:id>, C<:email>, and C<:feff>. C<:molecule> takes a value of 0 or 1 depending on whether your material is a solid or a molecule. The default is 0, a solid. C<:id> is an identifier string used to describe this frobnicate run. Its most important function is as the name of the directory tree which will contain this frobnicate run. For example, a calculation on gallium nitride might use the :id string C. The default is a pseudorandom 5 digit number. C<:email> takes the email address where you would like the archive sent once the run is complete. C<:feff> takes the name by which feff8 is invoked on the remote host. The default is C. If feff8 is installed on that machine as something else, then you must specify this argument. You may have more than one C line and, unlike the other keywords, its order in the list of keywords does not matter. =item C This line tells frobnicate to compute self-consistent potentials. This is a prerequisite to any of the other calculations and should always be the first item in the list. Its argumnets are C<:r> and C<:k> and take the maximum values of those parameters in the calculation. =item C This line is used to compute the density of states functions for each of potential types. Its arguments are C<:r>, C<:k>, C<:emin>, C<:emax>, and C<:eimag>. The last three are the arguments for the LDOS card in the feff.inp file. =item C This line is used to compute a XANES spectrum. Its arguments are C<:r>, C<:k>, C<:polarization> and C<:ellipticity>. C<:polarization> can take any of I, I, I, indicating a polarization vector in that direction. It can also take one of I, I, I, indicating circular polarization averaged over that plane. In the case of circular polarization the ellipticity will be set appropriately. C<:polarization> can also be set explicitly using quotation marks, as in I<'1 1 1'>. C<:ellipticity> can also be explicitly set in that manner. A very common use of frobnicate is to make a sequence of XANES calculations with increasing values for C<:r> in order to study convergence with respect to radial cluster size. =item C This line is used to compute EXAFS fitting standards. It takes the same arguments as C. Please note that you should set the ff2chi flag in the PRINT card to 3 because you may be able to use the F on your home computer. The PRINT card is not one of the cards controlled by frobnicate and so must be set by you somewhere else in the template file. =back =head2 FROBNICATE COMMENT STRINGS The lines in the template file relevant to frobnicate are denoted by comment strings, i.e. lines which begin with the comment character recognized by FEFF, C<*>. =over 4 =item C< *%%> Lines starting with this string are interpreted by frobnicate as lines containing frobnicate instructions. These lines will not be written to the output F files. =item C< *%% ---> The two lines starting with this string denote the boundries of the region where frobnicate will insert FEFF cards appropriate to each calculation. You should have two such lines, no more and no fewer, and they should be contiguous (i.e. no lines should come between them). These lines will be written to the output F files. =item C< * *> These are frobnicate comment lines. They are ignored by frobnicate and are not written to the output F files. =back All other lines are passed verbatim to the output F files. =head2 EXAMPLE Here is an example of the template file for a sequence of calculations on gallium nitride. The XANES spectrum is calculated at each polarization and at each of four cluster sizes. * * frobnicate stanza for calculations on gallium nitride * * *%% begin FROBNICATE keywords *%% params :molecule 0 :id 'GaN' *%% params :email 'bruce.ravel@nist.gov' :feff 'feff8' *%% SCF :r 4.0 *%% DOS :r 5.0 :emin -30 :emax 20 :eimag 0.1 *%% XANES :polarization z :r 2.0 *%% XANES :polarization xy :r 2.0 *%% XANES :polarization z :r 3.0 *%% XANES :polarization xy :r 3.0 *%% XANES :polarization z :r 4.0 *%% XANES :polarization xy :r 4.0 *%% XANES :polarization z :r 5.1 *%% XANES :polarization xy :r 5.1 *%% EXAFS :r 5.1 *%% end FROBNICATE keywords * * The next few lines are just normal feff cards... TITLE GaN * Ga K edge energy = 10367.0 eV EDGE K S02 1.0 * pot xsph fms paths genfmt ff2chi PRINT 1 0 0 0 0 3 * ixc [ Vr Vi ] EXCHANGE 0 *%% --- FROBNICATE: inserted cards start here ------------ *%% --- end of cards inserted by FROBNICATE -------------- This is followed by the potentials and atoms lists which is have left out of this example for the sake of brevity. =head1 STUFF FROM CPAN Using all the features of this program requires that several files from CPAN be installed on the remote computer. These modules deal with archiving, MIME encoding, and mailing. You can get away with not installing any of the modules by always using the C<-noarchive> and C<-nomail> switches. Of course, you then loose a nifty feature of the program, but at least it runs the calculations. =head1 OTHER PLATFORMS The program could be made to run on something non-unix with a bit of work. In its current incarnation it makes heavy use of system calls and symbolic links. These might give non-unix platforms pause. Of course, you can use any computer as the local client -- that is the whole point! =head1 TO DO Lots of stuff, no doubt... =head1 AUTHOR Bruce Ravel, bruce@phys.washington.edu http://feff.phys.washington.edu/~ravel/ http://feff.phys.washington.edu/~ravel/frobnicate/ =cut