/* DFT++ is a density functional package developed by the research group of Professor Tomas Arias Copyright 1996-2003 Sohrab Ismail-Beigi This file is part of DFT++. DFT++ 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 of the License, or (at your option) any later version. DFT++ 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. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with DFT++; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Please see the file CREDITS for a list of authors. For academic users, we request that publications using results obtained with this software reference "New algebraic formulation of density functional calculation," by Sohrab Ismail-Beigi and T.A. Arias, Computer Physics Communications 128:1-2, 1-45 (June 2000). and, if using the wavelet basis, further reference "Multiresolution analysis of electronic structure: semicardinal and wavelet bases," T.A. Arias, Reviews of Modern Physics 71:1, 267-311 (January 1999). and "Robust ab initio calculation of condensed matter: transparent convergence through semicardinal multiresolution analysis,'' I.P. Daykov, T.A. Arias, and Torkel D. Engeness, Physical Review Letters, 90:21, 216402 (May 2003). For your convenience, preprints of the above articles may be obtained from http://arXiv.org/abs/cond-mat/9909130, 9805262, and 0204411, respectively. */ /* * Contains the parser (and related functions) that reads * the input file and figures out what it says, checks for errors, * and issues commands to do what the input files wants. * * The parser does not define any commands; that is done in the * files in the commands/ directory. * */ #include #include "header.h" #include "commands/command.h" // // Special errors from processing go into // this little buffer // char processing_error[DFT_LINE_LEN]; // Print out all the commands and their format static void print_commands(command *list) { if (list == NULL) die("\nCan't print a null command list!\n"); command *c = list; do { dft_log("%s\n",c->format); c = c->next_command; } while (c!=NULL); } // Read a non-empty, non-comment line from the input file // and put it into the string s. Up to n-1 chars are read. // Returns NULL if EOF or some other fgets error is encountered. static char * read_a_line(char *s,int n,dft_text_FILE *fp) { char key[DFT_MSG_LEN]; while (1) { if (dft_text_fgets(s,n,'\\',fp)==NULL) return NULL; else if (s[0]=='#' || sscanf(s,"%s",key)<1) continue; else return s; } } // Returns command pointer for command in string str // or NULL if it can't find it static command * find_command(char *str,command *list) { command *c = list; while(c!=NULL) { if (strcmp(str,c->name) == 0) return c; else c = c->next_command; } return NULL; } // Returns command pointer for command number // or NULL if it can't find it static command * find_command(int number,command *list) { command *c = list; while(c!=NULL) { if (number == c->number) return c; else c = c->next_command; } return NULL; } // Go through dependency list of command cmnd // and check to see if they are satisfied (return 0) // or return the failed dependency number static int check_dependencies(command *cmnd,command *list) { int idep,i=0; while( (idep=cmnd->dependencies[i]) != 0) { command *cdep = find_command(abs(idep),list); if (cdep==NULL) die("PROBLEM %d %d %s!!!\n",i,idep,cmnd->name); if ( (idep>0 && !cdep->found) || (idep<0 && cdep->found) ) return idep; else i++; } return 0; } // Print out the status of all found commands with comments and formats // for those commands preceding them static void print_all_command_status(Output *out,command *list,Everything &e) { command *c = list; while (c!=NULL) { if (c->found) { out->printf("%s\n# %s\n",c->comments,c->format); c->print_status(e,out); out->printf("\n"); } c = c->next_command; } out->flush(); } // // The parser! (Relatively) short and sweet. // void parse(dft_text_FILE *input_file,Everything &everything) { // Create the command list command *list = create_command_list(); // For now, let's say we have a max of 1000 lines... this // will be dynamically changed if there are more lines int max_lines = 200; command **line_status = (command **)mymalloc(sizeof(command *)*max_lines, "line_status","parse"); // Count lines, check for command validity, and store // the command number for that line char input_line[DFT_LINE_LEN]; int n_lines = 0; dft_text_rewind(input_file); while (read_a_line(input_line,DFT_LINE_LEN,input_file) != NULL) { char key[DFT_MSG_LEN]; // Is the command recognized? sscanf(input_line,"%s",key); command *cmnd = find_command(key,list); if (cmnd == NULL) { dft_log(DFT_SILENCE,"\nValid commands are:\n\n"); print_commands(list); die("\nCommand '%s' is unknown\n\n",key); } // Store the command and increment counter line_status[n_lines] = cmnd; n_lines++; // If we are going to run out of space, get some more! if (n_lines >= max_lines) { max_lines += 100; line_status = (command **)myrealloc(line_status, sizeof(command *)*max_lines, "line_status","parse"); } } // Go through all the commands we have and find the // ones not specified in the input. // For each such command, if it has a default, do the // default setup and mark it as found. // Also, print out the default corresponding command line. dft_log("\nIssuing the following default commands since they\n"); dft_log("were not explicitely specified.\n\n"); command *cmnd = list; while(cmnd != NULL) { int found=0; for (int l=0; l < n_lines; l++) if (cmnd==line_status[l]) found=1; if (!found && cmnd->has_default) { cmnd->set_default(everything); cmnd->print_status(everything,dft_global_log); cmnd->found = TRUE; } cmnd = cmnd->next_command; } dft_log("\n"); // Now we are ready to parse the lines. // We'll parse the file repeatedly until we're done, or // we stop because of an error. // We also assume that we have nothing to do, unless // we encounter an "overall_action" command. int nothing_todo = TRUE; int done = FALSE; do { // Go through the input lines and work on unprocessed lines dft_text_rewind(input_file); int n_failed = 0; int n_succeeded = 0; int line_counter = 0; while (read_a_line(input_line,DFT_LINE_LEN,input_file) != NULL) { // Get the command corresponding to this line command *cmnd = line_status[line_counter]; line_counter++; // If already processed, skip it if (cmnd == NULL) continue; // If you can't use it more than once, die if(cmnd->found && !cmnd->allow_multiple) die("\nCommand '%s' can not be used more than once\n\n", cmnd->name); // Check the dependencies int err=check_dependencies(cmnd,list); if (err!=0) { n_failed++; continue; } // Try to process the command err=cmnd->process_command(input_line,everything); if (err!=0) { n_failed++; continue; } // It succeeded! Mark it found and remove from list cmnd->found=1; line_status[line_counter-1]=NULL; n_succeeded++; // If this command is an overall activity to be performed, // then set the flag saying that there is something to do if (cmnd->is_overall_action) nothing_todo = FALSE; } // while (current pass) // The pass over the input has ended. // If all the lines we processed failed, we should die. if (n_failed>0 && n_succeeded==0) { dft_log(DFT_SILENCE,"\nConsistency problems:\n\n"); dft_text_rewind(input_file); line_counter = 0; while (read_a_line(input_line,DFT_LINE_LEN,input_file) != NULL) { command *cmnd = line_status[line_counter]; line_counter++; if (cmnd == NULL) continue; int err=check_dependencies(cmnd,list); if (err!=0) { dft_log(DFT_SILENCE, "Command '%s' failed because it\n",cmnd->name); if (err>0) dft_log(DFT_SILENCE, "requires command '%s' as well.\n\n\n", find_command(err,list)->name); else dft_log(DFT_SILENCE, "can not be used simultaneously with '%s'.\n\n\n", find_command(-err,list)->name); } else { cmnd->process_command(input_line,everything); dft_log(DFT_SILENCE, "Command '%s' had a processing error:\n", cmnd->name); dft_log(DFT_SILENCE,"%s",processing_error); dft_log(DFT_SILENCE, "Correct command format is:\n%s\n", cmnd->format); dft_log(DFT_SILENCE,"The input was:\n%s\n\n\n",input_line); } } die("Exiting.\n\n"); } // No failures: as long as we have something to do, we're done! if (n_failed==0) { // We sucessfuly parse everything, but were we given // something to do? if (nothing_todo) { dft_log("\nInput file does not specify an overall activity,\n"); dft_log("so there is nothing to do!\n\n"); dft_log("Issue at least one of the following commands in the input file:\n\n"); command *c=list; while (c!=NULL) { if (c->is_overall_action) dft_log("%s\n",c->name); c = c->next_command; } die("\nExiting.\n\n"); } else done = TRUE; } } while (!done); dft_log("The following is a template of all commands that have been\n"); dft_log("specified in the input file or have been called with\n"); dft_log("automatic defaults.\n"); dft_log("\n>>>>>>>>>>>>>>>>>>Template Begins<<<<<<<<<<<<<<<<<<<<\n\n"); dft_log("# Unit length is the Bohr radius. Energies in Hartrees.\n\n"); print_all_command_status(dft_global_log,list,everything); dft_log("\n>>>>>>>>>>>>>>>>>>Template Ends<<<<<<<<<<<<<<<<<<<<<<\n\n"); free_command_list(list); } // // Create a command list, execute default setups, and print // out the resulting template. Useful for when the user wants // a default template. // void print_default_template(Output *out,Everything &everything) { // Create the command list command *list = create_command_list(); // Go through all the commands and execute default setup // For those that have it command *c = list; while(c != NULL) { if (c->has_default) c->set_default(everything); c = c->next_command; } // Now print out the current status of all commands out->printf("\n>>>>>>>>>>>>>>>>>>Template Begins<<<<<<<<<<<<<<<<<<<<\n\n"); out->printf("# Unit length is the Bohr radius. Energies in Hartrees\n\n"); c = list; while (c!=NULL) { out->printf("%s\n# %s\n",c->comments,c->format); if (c->has_default) c->print_status(everything,out); out->printf("\n"); c = c->next_command; } out->printf("\n>>>>>>>>>>>>>>>>>>Template Ends<<<<<<<<<<<<<<<<<<<<<<\n\n"); out->flush(); // Free command list free_command_list(list); }