/* * elem 0.5 (C) 2001 Ivan Tubert (itubert@hotmail.com) * * You can do whatever you want with this program except claiming you wrote it. * * This program has ABSOLUTELY NO WARRANTY * */ #include #include #include #include #include "eprintf.h" enum{ TRUE = 1, FALSE = 0, MAXBLOCKS = 20, SYMLENGTH = 8, MAXELEM = 256, LINELENGTH = 128, MAXBEST = 1000 }; typedef int BOOL; typedef struct { char symbol[SYMLENGTH]; double mass; } ELEMENT; typedef struct { int type; double percentage; } COMPOSITION_ELEMENT; typedef struct { int n; COMPOSITION_ELEMENT *elem; } COMPOSITION; typedef struct { int elem; int index; } FORMULA_ITEM; typedef struct { double min, max, step, curr; FORMULA_ITEM *formula; int n_elem; // number of different elements in the formula double charge; char *formula_string; char *name; } BLOCK; typedef struct { COMPOSITION *comp, *rel_err; // rel_err[i] = (exp[i] - theo[i]) / theo[i] double sq_err; // sq_err = sum(sq(exp-theo)) double charge; double vec[MAXBLOCKS]; // vector describing the composition } BLOCKSUM; /*** GLOBAL VARIABLES ***/ ELEMENT elements[MAXELEM]; int NELEM; // Number of elements int NBLOCK = 0; // The number of components to be varied (aka blocks) int CHARGEREQ = FALSE; // Requirement to find only electrically neutral combinations; int NBEST = 10; int VERBOSITY = 0; /* Verbosity levels: 0 normal 1 verbose 2 very verbose 3 extremely verbose -1 silent */ /* Returns number of elements read from file fname * Entries are in the format "symbol mass ..." */ int read_elements(char *fname) { FILE *fp; char s[SYMLENGTH]; int n = 0; float f; memset(elements, 0, sizeof(elements)); fp = fopen(fname, "r"); if(fp) { while(fscanf(fp, "%s %f", s, &f) == 2) { // in God we trust elements[n].mass = f; strcpy(elements[n].symbol, s); ++n; } } else { printf("Could not open element file %s\n", fname); } return(n); } void parse(BLOCK *b) { int n; char *p, *endptr; int i, len, maxlen, z, charge = 0; p = b->formula_string; while(*p){ /*** Read symbol ***/ for(i = maxlen = 0; (i < NELEM); ++i){ len = strlen(elements[i].symbol); if((len>maxlen) && (strncmp(elements[i].symbol, p, len) == 0)){ maxlen = len; z = i; } } if(!maxlen) { printf("Unidentified symbol in %s\n", b->formula_string); printf(" "); for(i=0; i < p - b->formula_string; ++i) printf(" "); printf("^\n"); exit(1); } p += maxlen; if(VERBOSITY > 1) printf(" Symbol: %s\n", elements[z].symbol); /*** Read index ***/ if(*p != '+' && *p != '-') { // make sure that number is not intended as a charge n = strtol(p, &endptr, 10); if(endptr == p) { n = 1; } else p = endptr; } else n = 1; if(VERBOSITY > 1) printf(" Index: %i\n", n); if(*p == '+' || *p == '-') { charge = strtol(p, &endptr, 10); if(endptr == p) { charge = (*p == '+') ? 1 : -1; ++p; } else p = endptr; } if(VERBOSITY > 1) printf(" Charge: %i\n", charge); // Add to formula b->charge = charge; for(i=0; in_elem; ++i){ if(b->formula[i].elem == z){ b->formula[i].index += n; break; } } if(i==b->n_elem){ // add to list b->formula = realloc(b->formula, (b->n_elem+1)*sizeof(FORMULA_ITEM)); b->formula[i].elem = z; b->formula[i].index = n; ++b->n_elem; } } } int inc(BLOCK *b, int n) { if(n<0) return(FALSE); b[n].curr += b[n].step; if(b[n].curr - 1.0E-6 > b[n].max){ b[n].curr = b[n].min; return(inc(b, n-1)); } return(TRUE); } /* Calculates composition and returns newly allocated structure */ COMPOSITION *calc_comp(BLOCK *b) { COMPOSITION *comp; int i, j; double MW = 0.0; double fcomp[MAXELEM]; double f; comp = calloc(1, sizeof(COMPOSITION)); memset(&fcomp, 0, sizeof(fcomp)); for(i=0; i < NBLOCK; ++i){ for(j=0; j < b[i].n_elem; ++j) { f = b[i].formula[j].index * elements[b[i].formula[j].elem].mass * b[i].curr; MW += f; fcomp[b[i].formula[j].elem] += f; } } // compress the list for(i = 0; i < NELEM; ++i) { if(fcomp[i]){ comp->elem = realloc(comp->elem, (comp->n + 1) * sizeof(COMPOSITION_ELEMENT)); comp->elem[comp->n].type = i; comp->elem[comp->n].percentage = fcomp[i] / MW * 100; ++comp->n; } } return(comp); } double calc_charge(BLOCK *b) { double charge = 0.0; int i; for(i=0; i < NBLOCK; ++i) charge += b[i].charge * b[i].curr; return(charge); } COMPOSITION *error(COMPOSITION *exp, COMPOSITION *theo, double *sq_err) { double c1[MAXELEM]; double c2[MAXELEM]; double d = 0.0; int i; COMPOSITION *comp; comp = calloc(1, sizeof(COMPOSITION)); for(i = 0; i < NELEM; ++i) c1[i] = -1.0; // negative number indicates undefined element for(i = 0; i < exp->n; ++i) c1[exp->elem[i].type] = exp->elem[i].percentage; memset(&c2, 0, sizeof(c2)); for(i = 0; i < theo->n; ++i) c2[theo->elem[i].type] = theo->elem[i].percentage; for(i = 0; i < NELEM; ++i) if(c1[i] >= 0.0) { d += (c1[i] - c2[i]) * (c1[i] - c2[i]); comp->elem = realloc(comp->elem, (comp->n + 1) * sizeof(COMPOSITION_ELEMENT)); comp->elem[comp->n].type = i; comp->elem[comp->n].percentage = c1[i] = (c1[i] - c2[i]) / c2[i] * 100; ++comp->n; } if(sq_err) *sq_err = d; return(comp); } BOOL addtobest(BLOCKSUM *best, BLOCK *b, COMPOSITION *compteo, COMPOSITION *compexp) { double sq_err, charge; int i, j; COMPOSITION *rel_err; rel_err = error(compexp, compteo, &sq_err); // Theological line charge = calc_charge(b); for(i=0; ielem); free(c); return(NULL); } char *readline(char *line, FILE *fp) { int len; fgets(line, LINELENGTH, fp); if(feof(fp)) eprintf("Error: Unexpected end of file\n"); len = strlen(line); if((len == LINELENGTH) && (line[len-1] != '\n')) eprintf("Error: line too long.\n"); return(line); } void printcomp(COMPOSITION *c) { int i; for(i = 0; i < c->n; ++i){ printf("%s=%5.2f%%; ", elements[c->elem[i].type].symbol, c->elem[i].percentage); } } int main(int argc, char **argv) { FILE *fp; char *fname = NULL; COMPOSITION *exp, *curr; BLOCK b[MAXBLOCKS]; int i, j; BLOCKSUM *best; float percentage, min, max, step; char line[LINELENGTH]; char symbol[SYMLENGTH]; char name[100]; char formula_string[100]; int n = 0; /* Number of trials */ char *arg; memset(b, 0, sizeof(b)); /*** READ COMMAND LINE ARGUMENTS ***/ for(i = 1; i < argc; ++i) { arg = argv[i]; if(arg[0] == '-') { //options char *p; for(p = arg+1; *p; ++p) switch(*p){ case '0': // zero CHARGEREQ = TRUE; break; case 'h': printf("elem v. 0.5 by Ivan Tubert\n"); printf("use: elem [options...] [filename]\n"); printf("\nOptions:\n"); printf("-niii Display 'iii' results\n"); printf("-0 Show only electrically neutral results\n"); printf("-h Show this help and exit\n"); printf("-v Verbose mode\n"); printf("-vv Very verbose mode\n"); printf("-s Silent mode\n"); exit(0); break; case 'v': ++VERBOSITY; break; case 's': --VERBOSITY; break; case 'n': { int m; char *endptr; if(p[1]){ m = strtol(p+1, &endptr, 10); p = endptr; if(!*p) --p; } else { ++i; if(i 0 && m <= MAXBEST) NBEST = m; else weprintf("Expected a number in [1, %i] after 'n' option. Using default of %i.", MAXBEST, NBEST); }break; default: weprintf("Warning: unrecognized option '%c'\n", *p); break; } } else if(!fname) fname = arg; else weprintf("Ignoring multiple files: %s", arg); } best = calloc(NBEST, sizeof(BLOCKSUM)); for (i = 0; i < NBEST; ++i) best[i].sq_err = 1E20; NELEM = read_elements("elem.txt"); /*** READ INPUT FILE ***/ /* Open file */ if(fname) { if((fp = fopen(fname, "r")) == 0) eprintf("Error opening file %s\n", fname); } else { fp = stdin; fname = "STDIN"; } if(VERBOSITY > -1) printf("Input file: %s\n", fname); do{ /* Ignore everything up to first dot ".exp" */ readline(line, fp); } while(line[0] != '.'); /* Read experimental composition */ if(VERBOSITY > -1) printf("Experimental composition:\n"); exp = calloc(1, sizeof(COMPOSITION)); do{ readline(line, fp); if(line[0] != '.') { if(sscanf(line, "%s %f", symbol, &percentage) != 2) // In God we trust eprintf("Error scanning in .EXP block"); for(i = 0; i < NELEM; ++i){ if(strncmp(symbol, elements[i].symbol, SYMLENGTH) == 0) { exp->elem = erealloc(exp->elem, (exp->n + 1) * sizeof(COMPOSITION_ELEMENT)); exp->elem[exp->n].type = i; exp->elem[exp->n].percentage = percentage; ++exp->n; if(VERBOSITY > -1) printf("%s: %5.2f\n", elements[i].symbol, percentage); break; } } if(i == NELEM) eprintf("Unidentified element in .EXP block"); } } while(line[0] != '.'); if(VERBOSITY > 0) { printf(".EXP block done\n"); } while(fscanf(fp, "%s %s %f %f %f", name, formula_string, &min, &max, &step) == 5) { b[NBLOCK].min = min; b[NBLOCK].max = max; b[NBLOCK].step = step; b[NBLOCK].name = estrdup(name); b[NBLOCK].formula_string = estrdup(formula_string); if(VERBOSITY > -1) printf("%s (%s): from %5.2f to %5.2f step %5.2f\n", b[NBLOCK].name, b[NBLOCK].formula_string, b[NBLOCK].min, b[NBLOCK].max, b[NBLOCK].step); parse(&b[NBLOCK]); b[NBLOCK].curr = b[NBLOCK].min; ++NBLOCK; } if(VERBOSITY > 0) printf("N = %i\n\n", NBLOCK); if(VERBOSITY > 0) printf("\nStarting...\n"); /*** PERFORM SEARCH ***/ do{ curr = calc_comp(b); if(!addtobest(best, b, curr, exp)) destroy_comp(curr); ++n; } while(inc(b, NBLOCK-1)); /*** DISPLAY RESULTS ***/ if(VERBOSITY > 0) printf("Tried %i combinations.\n", n); if(VERBOSITY > -1) printf("BEST %i RESULTS\n", NBEST); for(i = 0; (i < NBEST) && (best[i].sq_err < 1E19); ++i) { for(j=0; j < NBLOCK; ++j){ printf("%s: %4.2f; ", b[j].name, best[i].vec[j]); } printf("Charge=%4.2f\n", best[i].charge); printcomp(best[i].comp); printf("\nTotal squared absolute error = %.4g\n", best[i].sq_err); printf("Relative error by element: "); printcomp(best[i].rel_err); printf("\n\n"); } return 0; }