From 0cc40456bd1c97b8ede748c516b019ca75f18a35 Mon Sep 17 00:00:00 2001 From: sgtell Date: Mon, 18 Oct 2010 03:56:57 +0000 Subject: [PATCH] Merge Dan White's contributed additions to spicefile/sp2sp: -o option, numpy output format. git-svn-id: http://gwave.svn.sourceforge.net/svnroot/gwave/branches/guile-gnome-platform-branch@245 9168a026-1b44-0410-911b-d774ca5fe8d3 --- doc/Makefile.am | 2 +- spicefile/sp2sp.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 352 insertions(+), 29 deletions(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index 2d9fe79..b5f9829 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -39,7 +39,7 @@ pdf gwave.pdf: gwave.sgml SUFFIXES=.sgml .1 .5 .sgml.1: - docbook-to-man $*.sgml > $@ + docbook2man $*.sgml > $@ .PHONY: html tex dvi ps pdf diff --git a/spicefile/sp2sp.c b/spicefile/sp2sp.c index 9831392..901d73a 100644 --- a/spicefile/sp2sp.c +++ b/spicefile/sp2sp.c @@ -26,19 +26,38 @@ #include #include #include +#include #include "spicestream.h" #define SWEEP_NONE 0 #define SWEEP_PREPEND 1 #define SWEEP_HEAD 2 +static const char NPY_MAGIC[] = "\x93NUMPY"; +static const int NPY_MAJOR = 1; +static const int NPY_MINOR = 0; +static const int NPY_MAX_HDR_LEN = 256 * 256; +static const int NPY_PREAMBLE_LEN = 6 + 1 + 1 + 2; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +static const char NPY_ENDIAN_CHAR = '<'; +#else +static const char NPY_ENDIAN_CHAR = '>'; +#endif + + int g_verbose = 0; int sweep_mode = SWEEP_PREPEND; char *progname = "sp2sp"; -static void ascii_header_output(SpiceStream *sf, int *enab, int nidx); +static void ascii_header_output(SpiceStream *sf, int *enab, int nidx, FILE *of); static void ascii_data_output(SpiceStream *sf, int *enab, int nidx, - double begin_val, double end_val, int ndigits); + double begin_val, double end_val, int ndigits, FILE *of); +static void numpy_output(SpiceStream *sf, int *enab, int nidx, + double begin_val, double end_val, int ndigits, FILE *of); +static int numpy_header_output(int ndims, int* shape, int len, FILE *of); +static int numpy_footer_output(SpiceStream *sf, int *indices, int nidx, + int *sweeprows, FILE *of); static int parse_field_numbers(int **index, int *idxsize, int *nsel, char *list, int nfields); static int parse_field_names(int **index, int *idxsize, int *nsel, @@ -62,6 +81,7 @@ static void usage() fprintf(stderr, " -f f1,f2,... Output only fields named f1, f2, etc.\n"); fprintf(stderr, " -n n1,n2,... Output only fields n1, n2, etc;\n"); fprintf(stderr, " independent variable is field number 0\n"); + fprintf(stderr, " -o file Output to named file instead of default '-' stdout.\n"); fprintf(stderr, " -u U Output only variables with units of type; U\n"); fprintf(stderr, " U = volts, amps, etc.\n"); fprintf(stderr, " -s S Handle sweep parameters as S:\n"); @@ -75,6 +95,7 @@ static void usage() fprintf(stderr, " ascii - lines of space-seperated numbers, with header\n"); fprintf(stderr, " nohead - lines of space-seperated numbers, no headers\n"); fprintf(stderr, " cazm - CAzM format\n"); + fprintf(stderr, " numpy - .npy data followed by python str(dict) and footer len uint16\n"); fprintf(stderr, " input format types:\n"); i = 0; @@ -87,6 +108,7 @@ int main(int argc, char **argv) { SpiceStream *sf; + FILE *of; int i; int idx; @@ -96,6 +118,7 @@ main(int argc, char **argv) int errflg = 0; char *infiletype = "hspice"; char *outfiletype = "ascii"; + char *outfilename = "-"; char *fieldnamelist = NULL; char *fieldnumlist = NULL; int *out_indices = NULL; @@ -107,7 +130,7 @@ main(int argc, char **argv) double begin_val = -DBL_MAX; double end_val = DBL_MAX; - while ((c = getopt (argc, argv, "b:c:d:e:f:n:s:t:u:vx")) != EOF) { + while ((c = getopt (argc, argv, "b:c:d:e:f:n:s:t:u:o:vx")) != EOF) { switch(c) { case 'v': spicestream_msg_level = DBG; @@ -155,6 +178,9 @@ main(int argc, char **argv) spicestream_msg_level = DBG; x_flag = 1; break; + case 'o': + outfilename = optarg; + break; default: errflg = 1; break; @@ -199,6 +225,22 @@ main(int argc, char **argv) } } + if(strcmp(outfilename, "-") == 0) { + if(strcmp(outfiletype, "numpy") == 0) { + fprintf(stderr, "cannot output to stdout for 'numpy' format, use -o\n"); + exit(1); + } + of = stdout; + } else { + of = (FILE *)fopen64(outfilename, "w"); /* DJW: why is the cast needed? */ + if(!of) { + if(errno) + perror(outfilename); + fprintf(stderr, "unable to open output file\n"); + exit(1); + } + } + if(fieldnamelist == NULL && fieldnumlist == NULL) { out_indices = g_new0(int, sf->ndv+1); nsel = 0; @@ -229,16 +271,18 @@ main(int argc, char **argv) } if(strcmp(outfiletype, "cazm") == 0) { - printf("* CAZM-format output converted with sp2sp\n"); - printf("\n"); - printf("TRANSIENT ANALYSIS\n"); - ascii_header_output(sf, out_indices, nsel); - ascii_data_output(sf, out_indices, nsel, begin_val, end_val, ndigits); + fprintf(of, "* CAZM-format output converted with sp2sp\n"); + fprintf(of, "\n"); + fprintf(of, "TRANSIENT ANALYSIS\n"); + ascii_header_output(sf, out_indices, nsel, of); + ascii_data_output(sf, out_indices, nsel, begin_val, end_val, ndigits, of); } else if(strcmp(outfiletype, "ascii") == 0) { - ascii_header_output(sf, out_indices, nsel); - ascii_data_output(sf, out_indices, nsel, begin_val, end_val, ndigits); + ascii_header_output(sf, out_indices, nsel, of); + ascii_data_output(sf, out_indices, nsel, begin_val, end_val, ndigits, of); } else if(strcmp(outfiletype, "nohead") == 0) { - ascii_data_output(sf, out_indices, nsel, begin_val, end_val, ndigits); + ascii_data_output(sf, out_indices, nsel, begin_val, end_val, ndigits, of); + } else if(strcmp(outfiletype, "numpy") == 0) { + numpy_output(sf, out_indices, nsel, begin_val, end_val, ndigits, of); } else if(strcmp(outfiletype, "none") == 0) { /* do nothing */ } else { @@ -247,6 +291,7 @@ main(int argc, char **argv) } ss_close(sf); + close(of); exit(0); } @@ -257,35 +302,35 @@ main(int argc, char **argv) * consisting of the variable name plus a suffix. */ static void -ascii_header_output(SpiceStream *sf, int *indices, int nidx) +ascii_header_output(SpiceStream *sf, int *indices, int nidx, FILE *of) { int i, j; char buf[1024]; if((sf->nsweepparam > 0) && (sweep_mode == SWEEP_PREPEND)) { for(i = 0; i < sf->nsweepparam; i++) { - printf("%s ", sf->spar[i].name); + fprintf(of, "%s ", sf->spar[i].name); } } for(i = 0; i < nidx; i++) { if(i > 0) - putchar(' '); + fputc(' ', of); if(indices[i] == 0) { ss_var_name(sf->ivar, 0, buf, 1024); - printf("%s", buf); + fprintf(of, "%s", buf); } else { int varno = indices[i]-1; SpiceVar *dvar; dvar = ss_dvar(sf, varno); for(j = 0; j < dvar->ncols; j++) { if(j > 0) - putchar(' '); + fputc(' ', of); ss_var_name(dvar, j, buf, 1024); - printf("%s", buf); + fprintf(of, "%s", buf); } } } - putchar('\n'); + fputc('\n', of); } /* @@ -293,7 +338,7 @@ ascii_header_output(SpiceStream *sf, int *indices, int nidx) */ static void ascii_data_output(SpiceStream *sf, int *indices, int nidx, - double begin_val, double end_val, int ndigits) + double begin_val, double end_val, int ndigits, FILE *of) { int i, j, tab; int rc; @@ -314,11 +359,11 @@ ascii_data_output(SpiceStream *sf, int *indices, int nidx, break; } if(tab > 0 && sweep_mode == SWEEP_HEAD) { - printf("# sweep %d;", tab); + fprintf(of, "# sweep %d;", tab); for(i = 0; i < sf->nsweepparam; i++) { - printf(" %s=%g", sf->spar[i].name, spar[i]); + fprintf(of, " %s=%g", sf->spar[i].name, spar[i]); } - putchar('\n'); + fputc('\n', of); } while((rc = ss_readrow(sf, &ival, dvals)) > 0) { if(ival < begin_val) @@ -335,41 +380,316 @@ ascii_data_output(SpiceStream *sf, int *indices, int nidx, if((sf->nsweepparam > 0) && (sweep_mode == SWEEP_PREPEND)) { for(i = 0; i < sf->nsweepparam; i++) { - printf("%.*g ", ndigits, spar[i]); + fprintf(of, "%.*g ", ndigits, spar[i]); } } for(i = 0; i < nidx; i++) { if(i > 0) - putchar(' '); + fputc(' ', of); if(indices[i] == 0) - printf("%.*g", ndigits, ival); + fprintf(of, "%.*g", ndigits, ival); else { int varno = indices[i]-1; SpiceVar *dvar = ss_dvar(sf, varno); int dcolno = dvar->col - 1; for(j = 0; j < dvar->ncols; j++) { if(j > 0) - putchar(' '); - printf("%.*g", ndigits, + fputc(' ', of); + fprintf(of, "%.*g", ndigits, dvals[dcolno+j]); } } } - putchar('\n'); + fputc('\n', of); + } + if(rc == -2) { /* end of sweep, more follow */ + if(sf->nsweepparam == 0) + sweep_mode = SWEEP_HEAD; + tab++; + } else { /* EOF or error */ + done = 1; + } + } + g_free(dvals); + if(spar) + g_free(spar); +} + +static int +numpy_header_output(int ndims, int *shape, int len, FILE *of) +{ + /* More documentation about the npy format at numpy/lib/format.py */ + + char header[NPY_MAX_HDR_LEN]; + char descr[5]; + int i; + int hlen; + int nspaces = 0; + + strcpy(header, "{'descr':'"); + + descr[0] = NPY_ENDIAN_CHAR; + descr[1] = 'f'; + sprintf(descr+2, "%d", (int) sizeof(float)); + + strcat(header, descr); + strcat(header, "', 'fortran_order':False, 'shape': ("); + for(i=0; i < ndims; i++) { + hlen = strlen(header); + sprintf(header+hlen, "%d", shape[i]); + + if(i != ndims-1) { + strcat(header, ", "); + } + } + strcat(header, ") }"); + hlen = strlen(header); + + if(len == -1) { + /* bogus header values or just write the length needed */ + nspaces = 16 - ((NPY_PREAMBLE_LEN + hlen + 1) % 16); + + } else if(len < hlen) { + fprintf(stderr, "numpy_header_output: requested header len is too small\n"); + + } else if(((len + NPY_PREAMBLE_LEN) % 16) != 0) { + fprintf(stderr, "requested header len does not align to mult of 16\n"); + + } else { + /* pad to the (longer) header length */ + nspaces = len - hlen - 1; + } + + if(hlen + nspaces + 1 > NPY_MAX_HDR_LEN) { + fprintf(stderr, "npy header too long (%d)\n", hlen+nspaces+1); + return 0; + } + + for(i=0; i < nspaces; i++) { + strcat(header, " "); + } + strcat(header, "\n"); + hlen = strlen(header); + + /* preamble */ + fprintf(of, NPY_MAGIC); + fwrite(&NPY_MAJOR, sizeof(char), 1, of); + fwrite(&NPY_MINOR, sizeof(char), 1, of); + fputc((0xff & hlen), of); + fputc((0xff & (hlen >> 8)), of); + fwrite(header, sizeof(char), hlen, of); + + return hlen; +} + + +static int +numpy_footer_output(SpiceStream *sf, int *indices, int nidx, int *sweeprows, FILE *of) +{ + int i, j; + int foot_start = 0; + int foot_len = 0; + char buf[1024]; + double *spar = NULL; + + foot_start = ftell(of); + + /* + * make footer dict + */ + fprintf(of, "{ "); + + /* sweep variables, these values prepend the data columns for each row */ + fprintf(of, "'sweepvars':("); + /*for(i=0; i < sf->ntables; i++) {*/ + if(sf->ntables > 1) { + for(j = 0; j < sf->nsweepparam; j++) { + fprintf(of, "'%s'", sf->spar[j].name); + fprintf(of, ","); + } + } + fprintf(of, "), "); + + fprintf(of, "'sweeprows':("); + if(sf->ntables > 1) { + for(i=0; i < sf->ntables; i++) { + fprintf(of, "(%d,%d),", sweeprows[2*i], sweeprows[2*i+1]); + } + } + fprintf(of, "), "); + + fprintf(of, "'cols':("); + for(i = 0; i < nidx; i++) { + if(indices[i] == 0) { + ss_var_name(sf->ivar, 0, buf, 1024); + fprintf(of, "'%s', ", buf); + } else { + int varno = indices[i]-1; + SpiceVar *dvar = ss_dvar(sf, varno); + int dcolno = dvar->col - 1; + for(j = 0; j < dvar->ncols; j++) { + ss_var_name(dvar, j, buf, 1024); + fprintf(of, "'%s', ", buf); + } + } + } + fprintf(of, ") }"); + + /* space-pad to end on a 16 byte boundary */ + while(((ftell(of)+3) % 16) != 0) { + fputc(' ', of); + } + fputc('\n', of); + foot_len = ftell(of) - foot_start + 2; + fputc((0xFF & foot_len), of); + fputc((0xFF & (foot_len >> 8)), of); + + return foot_len; +} + + + +/* + * print data as a .npy format array + * See numpy/lib/format.py for details of the .npy file format. + */ +static void +numpy_output(SpiceStream *sf, int *indices, int nidx, + double begin_val, double end_val, int ndigits, FILE *of) +{ + int i, j, tab; + int rc; + int npy_hlen = 0; + int foot_len = 0; + int npy_end = 0; + int ndims = 0, ncols = 0, nrows = 0; + int *sweeprows; + int shape[3]; + char buf[1024]; + char npy_descr[5], npy_preamble[NPY_PREAMBLE_LEN], npy_header[NPY_MAX_HDR_LEN]; + float val; + double ival; + double *dvals; + double *spar = NULL; + int done; + + + /* + * write sham npy preamble + header to reserve space, don't know nrows yet + */ + ndims = 2; + shape[0] = INT_MAX; + shape[1] = INT_MAX; + npy_hlen = numpy_header_output(ndims, shape, -1, of); + + /* now write rows */ + dvals = g_new(double, sf->ncols); + sweeprows = g_new(int, 2 * sf->ntables); + if(sf->nsweepparam > 0) + spar = g_new(double, sf->nsweepparam); + + done = 0; + tab = 0; + nrows = 0; + sweeprows[2*tab] = nrows; + while(!done) { + if(sf->nsweepparam > 0) { + if(ss_readsweep(sf, spar) <= 0) + break; + } + + while((rc = ss_readrow(sf, &ival, dvals)) > 0) { + if(ival < begin_val) + continue; + if(ival > end_val) { + /* past end_val, but can only stop reading + early if if there is only one sweep-table + in the file. */ + if(sf->ntables == 1) + break; + else + continue; + } + + + if(sf->nsweepparam > 0) { + for(i = 0; i < sf->nsweepparam; i++) { + val = (float)spar[i]; + fwrite(&val, sizeof(float), 1, of); + } + } + for(i = 0; i < nidx; i++) { + if(indices[i] == 0) { + val = (float)ival; + fwrite(&val, sizeof(float), 1, of); + } else { + int varno = indices[i]-1; + SpiceVar *dvar = ss_dvar(sf, varno); + int dcolno = dvar->col - 1; + for(j = 0; j < dvar->ncols; j++) { + val = (float)dvals[dcolno+j]; + fwrite(&val, sizeof(float), 1, of); + } + } + } + nrows += 1; } if(rc == -2) { /* end of sweep, more follow */ if(sf->nsweepparam == 0) sweep_mode = SWEEP_HEAD; + + sweeprows[2*tab+1] = nrows-1; tab++; + sweeprows[2*tab] = nrows; } else { /* EOF or error */ done = 1; + sweeprows[2*tab+1] = nrows-1; + } + } + + npy_end = ftell(of); + + + /* write trailing string representation of a python dict describing the data */ + foot_len = numpy_footer_output(sf, indices, nidx, sweeprows, of); + + + /* + * go back and overwrite npy header with correct values + */ + fseek(of, 0, SEEK_SET); + + /* calc num cols of data, second dimension */ + ncols = sf->nsweepparam; + for(i = 0; i < nidx; i++) { + if(indices[i] == 0) { /* independent var */ + ncols += 1; + } else { + int varno = indices[i]-1; + SpiceVar *dvar = ss_dvar(sf, varno); + ncols += dvar->ncols; } } + shape[0] = nrows; + shape[1] = ncols; + int npy_hlen2 = numpy_header_output(ndims, shape, npy_hlen, of); + + if(npy_hlen2 != npy_hlen) { + fprintf(stderr, "numpy OOPS, inconsistent header lengths\n"); + } + + fclose(of); + g_free(dvals); + g_free(sweeprows); if(spar) g_free(spar); } + + + + static int parse_field_numbers(int **indices, int *idxsize, int *nidx, char *list, int nfields) { @@ -503,3 +823,6 @@ static VarType get_vartype_code(char *vartype) } return UNKNOWN; } +/* + * vim:tabstop=4 noexpandtab + */ -- 2.11.4.GIT