/* 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. */ /* * Tairan Wang, November 18 1997 * * Define our own version of IO functions. * * dft_text_FILE* dft_text_fopen(...); (done, wait for checking) * int dft_text_fclose(dft_text_FILE*, ...); (done, wait for checking) * * dft_text_fgets(dft_text_FILE*, ...); (done, wait for checking) * dft_text_fscanf(dft_text_FILE*, ...); (done, wait for checking) * dft_text_rewind(dft_text_FILE*); * dft_text_fputc_old(char c, dft_text_FILE *file); * */ /* $Id: dft_text_FILE.cpp,v 1.7.2.2 2003/05/29 18:54:17 ivan Exp $ */ #include #include #include "header.h" #include "parallel.h" // Member function definitions for class dft_text_FILE: dft_text_FILE::dft_text_FILE() { my_id = System::Get_procID(); io_id = System::Get_IOprocID(); file_length = 0L; file_string = current_pointer = old_pointer = (char*) 0; file_handle = (FILE*) 0; } dft_text_FILE::~dft_text_FILE() { if (file_string != NULL) myfree(file_string); if (file_handle != NULL) fclose(file_handle); } void dft_text_FILE::init() { current_pointer = old_pointer = file_string = (char*) mymalloc(file_length,"file_string","dft_text_FILE"); } /////////////////////////////////////////////////////// dft_text_FILE * dft_text_fopen(const char *filename, const char *type) { dft_text_FILE *file; // Need to consider what we do if type is not "r" if (strncmp(type,"r",1)!=0) die("dft_text_fopen don't know what to do with non-read opens."); file = new dft_text_FILE; #ifdef DFT_MPI int flag; if (file->my_id == file->io_id) { // get the length of file if ((file->file_handle = fopen(filename, "rb"))==NULL) { // open failed. flag = 0; // inform all other processors of failure. MPI_Bcast(&flag, 1, MPI_INT, file->io_id, MPI_COMM_WORLD); delete (file); return ( (dft_text_FILE*) 0 ); } flag = 1; fseek(file->file_handle, 0L, SEEK_END); // move to the end file->file_length = ftell(file->file_handle); // get the length // inform all other processors of success. MPI_Bcast(&flag, 1, MPI_INT, file->io_id, MPI_COMM_WORLD); } else { MPI_Bcast(&flag, 1, MPI_INT, file->io_id, MPI_COMM_WORLD); if (flag == 0) { // open failure delete (file); return ( (dft_text_FILE*) 0 ); } } MPI_Bcast(&(file->file_length), 1, MPI_LONG, file->io_id, MPI_COMM_WORLD); #else // DFT_MPI // get the length of file if ((file->file_handle = fopen(filename, "rb"))==NULL) { // open failed. delete (file); return ( (dft_text_FILE*) 0 ); } fseek(file->file_handle, 0L, SEEK_END); // move to the end file->file_length = ftell(file->file_handle); // get the length #endif // DFT_MPI if (file->file_length == 0) { // file has no content delete(file); return ( (dft_text_FILE*) 0 ); } // allocate file_string; file->init(); if (file->my_id == file->io_id) { rewind(file->file_handle); fread(file->file_string,file->file_length,1,file->file_handle); } #ifdef DFT_MPI MPI_Bcast(file->file_string, file->file_length, MPI_CHAR, file->io_id, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); #endif // DFT_MPI return (file); } /* * dft_text_FILE::expand_include() * * expand the includes to read all include files too. * not circular include checks!!! Be careful. */ void dft_text_FILE::expand_include() { if (my_id == io_id) { // file_string is already pointed at a character string of the file. int done = FALSE, n_includes = 0, i, found = FALSE; FILE ** inc_file; long int *inc_file_length, tot_length = file_length; char file_name[DFT_FILENAME_LEN], **inc_file_ptr0, **inc_file_ptr1, *p; char line[DFT_LINE_LEN], key[DFT_MSG_LEN]; while (! done) { n_includes = 0; // go and count all includes, while (dft_text_fgets(line,DFT_LINE_LEN,'\\',this) != NULL) { if (sscanf(line,"%s",key) > 0) { if ( MATCH(key,"include") ) { n_includes++; } } } dft_text_rewind(this); if ( n_includes > 0 ) { inc_file = (FILE**) mymalloc(sizeof(FILE*)*n_includes, "inc_file", "dft_text_FILE::expand_include"); inc_file_length = (long int*)mymalloc(sizeof(long int)*n_includes, "inc_file_length", "dft_text_FILE::expand_include"); inc_file_ptr0 = (char**) mymalloc(sizeof(char*)*n_includes, "inc_file_ptr0", "dft_text_FILE::expand_include"); inc_file_ptr1 = (char**) mymalloc(sizeof(char*)*n_includes, "inc_file_ptr1", "dft_text_FILE::expand_include"); for (i = 0; i < n_includes; i++) { // find next include, get name to file_name, inc_file_ptr0/1[i] set. found = FALSE; while ( (!found ) && (dft_text_fgets(line,DFT_LINE_LEN,'\\',this) != NULL)) { if (sscanf(line,"%s",key) > 0) if ( MATCH(key,"include") ) if (sscanf(line,"%*s %s",file_name) > 0) { inc_file_ptr0[i] = old_pointer; inc_file_ptr1[i] = current_pointer; found = TRUE; } } if ( (inc_file[i] = fopen(file_name,"rb")) != NULL) { fseek(inc_file[i], 0L, SEEK_END); inc_file_length[i] = ftell(inc_file[i]); tot_length += inc_file_length[i] - (inc_file_ptr1[i] - inc_file_ptr0[i]); rewind(inc_file[i]); } else { inc_file_length[i] = 0L; } } dft_text_rewind(this); // now has the tot_length; long int section_length; p = (char*) mymalloc(tot_length,"",""); file_string = p; for (i = 0; i < n_includes; i++) { section_length = inc_file_ptr0[i] - current_pointer; strncpy(p, current_pointer, section_length); p += section_length; current_pointer = inc_file_ptr1[i]; if (inc_file_length[i] > 0L) fread(p,inc_file_length[i],1,inc_file[i]); p += inc_file_length[i]; fclose(inc_file[i]); } // now the last chunk: strncpy(p,current_pointer, old_pointer+file_length-current_pointer-1); // release temporary variables myfree(inc_file); myfree(inc_file_length); myfree(inc_file_ptr0); myfree(inc_file_ptr1); // now can deallocate old string, myfree(old_pointer); dft_text_rewind(this); file_length = tot_length; } else { done = TRUE; } } } #ifdef DFT_MPI // now also need to tell all other processors to // receive this new file string. MPI_Bcast(&file_length, 1, MPI_LONG, io_id, MPI_COMM_WORLD); if (file_length <= 0L) { // file has no content. How does this happen? die("Fatal error in dft_text_FILE::expand_include, file length <=0\n"); } if (my_id != io_id) { if (file_string != NULL) myfree(file_string); init(); } MPI_Bcast(file_string, file_length, MPI_CHAR, io_id, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); #endif // DFT_MPI } int dft_text_fclose(dft_text_FILE* file) { delete file; return 0; // assume success. } /* * dft_text_fgets() reads in at most one less than n characters from * stream and stores them into the buffer pointed to by s. * Reading stops after an EOF or a newline. If a newline is * read, it is stored into the buffer. A '\0' is stored * after the last character in the buffer. */ char * dft_text_fgets(char * s, int n, dft_text_FILE * file) { int i; file->old_pointer = file->current_pointer; // trickery to track the beginning. if (file->current_pointer + n - 1 > file->file_string + file->file_length) { n = file->file_string + file->file_length - file->current_pointer + 1; } for (i = 0; i < n-1; i++) { s[i] = file->current_pointer[i]; if (file->current_pointer[i] == '\n') { // end of line i ++; break; // break the for loop, stop reading. } else if (file->current_pointer[i] == '\0') { // encounter string termination character break; // break the for loop, stop reading. } } if (i > 0) { s[i] = '\0'; // put terminating character in s; file->current_pointer += i; // increment pointer return s; // upon success } else { return ( (char *) 0 ); // upon failure } } /* * This function reads in at most one less than n characters from * stream and stores them into the buffer pointed to by s. * * Reading will continue across newlines if they are terminated with * special character 'c', say '\'. * * Reading stops after an EOF or a newline. If a newline is * read, it is stored into the buffer. A '\0' is stored * after the last character in the buffer. */ char * dft_text_fgets(char * s, int n, char c, dft_text_FILE * file) { int i, j, m = n; file->old_pointer = file->current_pointer; // trickery to track the beginning. if (file->current_pointer + m - 1 > file->file_string + file->file_length) { m = file->file_string + file->file_length - file->current_pointer + 1; } for (i = j = 0; i < m-1; i++, j++) { s[i] = file->current_pointer[j]; if (file->current_pointer[j] == '\n') { if ( (j == 0) || (file->current_pointer[j-1] != c) ) { // end of line i++; j++; break; // break the for loop, stop reading. } else { // read line continuation marker // back track i; i -= 2; } } else if (file->current_pointer[j] == '\0') { // encounter string termination character break; // break the for loop, stop reading. } } if (i == n-1) { die(">>Within dft_text_FILE dft_text_fgets(). Line too long. Increase line length"); } if (i > 0) { s[i] = '\0'; // put terminating character in s; file->current_pointer += j; // increment pointer return s; // upon success } else { return ( (char *) 0 ); // upon failure } } void dft_text_rewind(dft_text_FILE * file) { file->current_pointer = file->old_pointer = file->file_string; } // MODIFIES input stream!! USE WITH CAUTION!! void dft_text_fputc_old(char c, dft_text_FILE * file) { file->old_pointer[0] = c; } /* Prof Arias's scanf, "pascanf". It should would just like dft_text_fscanf except that the first argument is a pointer to the string holding the input stream, rather than a file-pointer. (By string, I mean a pointer to the first character of the string.) See below for an example for the use of this program. NOTE: the string is updated as the input is read so that the next call will start reading at the appropriate place. This is convenient, but it does mean that the FIRST ARGUMENT IS DESROYED by the time you are done reading the file. ERRORS generated here by sscanf are passed back as the value EOF for the function call. BUGS: The code should work, except for those special cases for which it traps and complains about, before terminating execution. Hopefully, the cases I haven't handled are rare. We could easily take care of those cases if they start comming up a lot. */ int dft_text_fscanf( dft_text_FILE *file, const char *fmtin, ...) { #define Lenmx 1024 /* Maximum allowable length for the format specifier */ /* Variable argument list pointer */ va_list ap; /* Internal temporary variables */ char format[Lenmx]; /* Stores writable copy of input conversion specifier */ char fmt2[Lenmx]; /* Used to store input conversions one item at a time */ char *fmt,*fmt1; /* Pointers into format for parsing each specifier */ char c; /* Temp storage for characters we set to \0 in while parsing */ void *item; /* Used with va_arg to hold pointers poped off the stack */ int assigned_items = 0; /* Keep track of number of successful conversion made */ int used; /* Used with sscanf to determine length of input stream consumed */ int len; /* Counts up string length for safety */ char** stream; file->old_pointer = file->current_pointer; // trickery to track the beginning. stream = &(file->current_pointer); /* Copy format string into local writable space (fomat), and check for length and presence of unwated *'s (a smarter version could handle the *'s */ strncpy(format,fmtin,Lenmx); for (len=0; len=Lenmx) die("\n\nError: not enough space (Lenmx) in pascanf for input conversion string!\n(conversion string=\"%s\")\n\n",format); fmt=format; while (*fmt) /* Check for '*' characters and length */ if (*(fmt++)=='*') die("\n\nError: pascanf does not treat '*' in input conversion strings!\n(conversion string=\"%s\")\n\n",format); fmt=format; /* printf("INPUT: %d character format: \"%s\"\n",len,fmt); */ /* Initialize variable argument list to proper point in the stack */ va_start(ap, fmtin); /* This loop parses the format, picking out each %-field */ while (*fmt) { fmt1=fmt; fmt++; while (*fmt != '\0' && *fmt != '%') fmt++; /* Locate % or "EOF" */ /* We have now identified a single input field, prepare to use sscanf to read it in. Note that to do this, we have to update the pointer into the input stream. To do this we use the %n option on sscanf and put the number of characters consumed from the stream into the variable "used". */ if (*fmt=='%' && *fmt1=='%' && fmt==fmt1+1) { /* %% constitutes a special case where the input field contains two %'s and which is the only field beginning with % which doesn't consume a pointer from the stack. */ fmt++; /* Claim the second % as part of this input field */ strcpy(fmt2,"%% %n"); /* Build format string, note we need the %n to get the amount of stream consumed */ /* printf("Input stream: \"%s\" / Format: \"%s\"\n",(*stream),fmt2); */ /* Scan stream with error check */ if (sscanf((*stream),fmt2,&used)!=0) return(EOF); } else { /* Build into fmt2 format string for only the field from [fmt,fmt1] including the %n which we need for acounting consumption of the stream */ c=*fmt; *fmt='\0'; strcpy(fmt2,fmt1); *fmt=c; strcat(fmt2,"%n"); /* printf("Input stream: \"%s\" / Format: \"%s\"\n",(*stream),fmt2); */ if (*fmt2=='%') { /* An actual input item! Consume one argument from stack!!! */ item=va_arg(ap, void *); /* Scan stream with error check */ if (sscanf((*stream),fmt2,item,&used)!=1) return(EOF); assigned_items ++; } else { /* Simple text advance through stream (no argument from stack) */ /* Scan stream with error check */ if (sscanf((*stream),fmt2,&used)!=0) return(EOF); } } /* Advance stream pointer */ (*stream)+=used; } return assigned_items; }