From 38848259e4483cd14b888a6ae025751791848e66 Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Sat, 9 Oct 2021 10:57:14 -0700 Subject: [PATCH] With the ngspice gnuplot command, enable x/y contour plots for 2d Cider save file data. Usage: gnuplot xycontour . xycontour is a new flag which is ignored if the plot data is not from 2d Cider. For contours, only a single plotarg is allowed. With vs , only is plotted and is ignored. --- src/frontend/plotting/gnuplot.c | 522 +++++++++++++++++++++++--------- src/frontend/plotting/gnuplot.h | 2 +- src/frontend/plotting/plotit.c | 6 +- src/frontend/rawfile.c | 10 + src/include/ngspice/plot.h | 2 + 5 files changed, 403 insertions(+), 139 deletions(-) diff --git a/src/frontend/plotting/gnuplot.c b/src/frontend/plotting/gnuplot.c index 603e550be..2c00be612 100644 --- a/src/frontend/plotting/gnuplot.c +++ b/src/frontend/plotting/gnuplot.c @@ -50,22 +50,225 @@ quote_gnuplot_string(FILE *stream, char *s) } +static double **dmatrix(int nrow, int ncol) +{ + double **d; + int i; + if (nrow < 2 && ncol < 2) { + /* Who could want a 1x1 matrix? */ + return NULL; + } + d = TMALLOC(double *, nrow); + for (i = 0; i < nrow; i++) { + d[i] = TMALLOC(double, ncol); + } + return d; +} + + +static void dmatrix_free(double **d, int nrow, int ncol) +{ + int i; + (void) ncol; + if (d && nrow > 1) { + for (i = 0; i < nrow; i++) { + tfree(d[i]); + } + tfree(d); + } +} + + +static bool has_contour_data(struct dvec *vecs) +{ + struct plot *curpl = NULL; + struct dvec *v = NULL, *xvec = NULL, *yvec = NULL; + int xdim, ydim, i, npoints; + bool len_mismatch = FALSE, wrong_type = FALSE; + + if (!vecs) { + return FALSE; + } + curpl = vecs->v_plot; + if (!curpl) { + return FALSE; + } + xdim = curpl->pl_xdim2d; + ydim = curpl->pl_ydim2d; + if (xdim < 2 || ydim < 2) { + return FALSE; + } + + for (v = vecs, i = 0; v; v = v->v_link2) { + i++; + } + if (i > 1) { + printf("Specify only one expr for an xycontour plot:"); + for (v = vecs; v; v = v->v_link2) { + printf(" '%s'", v->v_name); + } + printf("\n"); + return FALSE; + } else if (i < 1) { + return FALSE; + } + if (!(vecs->v_flags & VF_REAL)) { + wrong_type = TRUE; + } + npoints = xdim * ydim; + if (vecs->v_length != npoints) { + len_mismatch = TRUE; + } + +#ifdef VERBOSE_GNUPLOT + printf("curpl vecs:"); + for (v = curpl->pl_dvecs; v; v = v->v_next) { + printf(" '%s'", v->v_name); + } + printf("\n"); + printf("vecs: "); + for (v = vecs; v; v = v->v_next) { + printf(" '%s'", v->v_name); + } + printf("\n"); +#endif + + for (v = vecs; v; v = v->v_next) { + /* Find the x and y vectors from the last part of the list. + Passing by the the plotarg expr parsing elements at the front. + */ + if (!(v->v_flags & VF_REAL)) { + /* Only real types allowed */ + wrong_type = TRUE; + } + if (v->v_length != npoints) { + /* Assume length 1 is a constant number */ + if (v->v_length != 1) { + len_mismatch = TRUE; + } + } + if (eq(v->v_name, "y")) { + yvec = v; + continue; + } + if (eq(v->v_name, "x")) { + xvec = v; + } + } + if (len_mismatch) { + printf("Vector lengths mismatch, ignoring xycontour\n"); + } + if (wrong_type) { + printf("Non-real expr or constant, ignoring xycontour\n"); + } + if (!xvec || !yvec || len_mismatch || wrong_type) { + return FALSE; + } + return TRUE; +} + + +/* Precondition: has_contour_data was called and returned TRUE */ +static int write_contour_data(FILE *filed, struct dvec *vecs) +{ + struct plot *curpl = NULL; + struct dvec *v = NULL, *xvec = NULL, *yvec = NULL; + int xdim, ydim, i, j, npoints, idx; + double *ycol; + double **zmat; + + if (!filed || !vecs) { + return 1; + } + curpl = vecs->v_plot; + if (!curpl) { + return 1; + } + xdim = curpl->pl_xdim2d; + ydim = curpl->pl_ydim2d; + if (xdim < 2 || ydim < 2) { + return 1; + } + + npoints = xdim * ydim; + for (v = vecs; v; v = v->v_next) { + /* Use the x and y vectors from the last part of the list. */ + if (eq(v->v_name, "y")) { + yvec = v; + continue; + } + if (eq(v->v_name, "x")) { + xvec = v; + } + } + if (!xvec || !yvec) { + return 1; + } + + /* First output row has the x vector values */ + fprintf(filed, "%d", xdim); + for (i = 0, j = 0; i < xdim; j += ydim) { + if (j >= xvec->v_length) { + return 1; + } + fprintf(filed, " %e", 1.0e6 * xvec->v_realdata[j]); + i++; + } + fprintf(filed, "\n"); + + ycol = TMALLOC(double, ydim); + for (i = 0; i < ydim; i++) { + ycol[i] = 1.0e6 * yvec->v_realdata[i]; + } + zmat = dmatrix(ydim, xdim); + idx = 0; + for (i = 0; i < xdim; i++) { + for (j = 0; j < ydim; j++) { + zmat[j][i] = vecs->v_realdata[idx]; + idx++; + } + } + if (idx != npoints) { + tfree(ycol); + dmatrix_free(zmat, ydim, xdim); + return 1; + } + + /* Subsequent output rows have a y vector value and the z matrix + values corresponding to that y vector. There is a z matrix value + for each x vector column. + */ + for (i = 0; i < ydim; i++) { + fprintf(filed, "%e", ycol[i]); + for (j = 0; j < xdim; j++) { + fprintf(filed, " %e", zmat[i][j]); + } + fprintf(filed, "\n"); + } + + tfree(ycol); + dmatrix_free(zmat, ydim, xdim); + return 0; +} + + void ft_gnuplot(double *xlims, double *ylims, double xdel, double ydel, const char *filename, const char *title, const char *xlabel, const char *ylabel, GRIDTYPE gridtype, PLOTTYPE plottype, - struct dvec *vecs) + struct dvec *vecs, bool xycontour) { FILE *file, *file_data; struct dvec *v, *scale = NULL; double xval, yval, prev_xval, extrange; int i, dir, numVecs, linewidth, gridlinewidth, err, terminal_type; - bool xlog, ylog, nogrid, markers, nolegend; + bool xlog, ylog, nogrid, markers, nolegend, contours = FALSE; char buf[BSIZE_SP], pointstyle[BSIZE_SP], *text, plotstyle[BSIZE_SP], terminal[BSIZE_SP]; char filename_data[128]; char filename_plt[128]; + char *vtypename = NULL; #ifdef SHARED_MODULE char* llocale = setlocale(LC_NUMERIC, NULL); @@ -92,6 +295,10 @@ void ft_gnuplot(double *xlims, double *ylims, return; } + if (xycontour) { + contours = has_contour_data(vecs); + } + extrange = 0.05 * (ylims[1] - ylims[0]); if (!cp_getvar("gnuplot_terminal", CP_STRING, @@ -190,86 +397,104 @@ void ft_gnuplot(double *xlims, double *ylims, #endif fprintf(file, "set termoption noenhanced\n"); #endif - if (title) { - text = cp_unquote(title); - fprintf(file, "set title "); - quote_gnuplot_string(file, text); - fprintf(file, "\n"); - tfree(text); - } - if (xlabel) { - text = cp_unquote(xlabel); - fprintf(file, "set xlabel "); - quote_gnuplot_string(file, text); - fprintf(file, "\n"); - tfree(text); - } - if (ylabel) { - text = cp_unquote(ylabel); - fprintf(file, "set ylabel "); - quote_gnuplot_string(file, text); - fprintf(file, "\n"); - tfree(text); - } - if (!nogrid) { - if (gridlinewidth > 1) - fprintf(file, "set grid lw %d \n" , gridlinewidth); - else - fprintf(file, "set grid\n"); - } - if (xlog) { - fprintf(file, "set logscale x\n"); - if (xlims) - fprintf(file, "set xrange [%1.0e:%1.0e]\n", - pow(10, floor(log10(xlims[0]))), pow(10, ceil(log10(xlims[1])))); - fprintf(file, "set mxtics 10\n"); - fprintf(file, "set grid mxtics\n"); - } else { - fprintf(file, "unset logscale x \n"); - if (xlims) - fprintf(file, "set xrange [%e:%e]\n", xlims[0], xlims[1]); - } - if (ylog) { - fprintf(file, "set logscale y \n"); - if (ylims) - fprintf(file, "set yrange [%1.0e:%1.0e]\n", - pow(10, floor(log10(ylims[0]))), pow(10, ceil(log10(ylims[1])))); - fprintf(file, "set mytics 10\n"); - fprintf(file, "set grid mytics\n"); + if (contours) { + fprintf(file, "set view map\n"); + fprintf(file, "set contour\n"); + fprintf(file, "unset surface\n"); + fprintf(file, "set cntrparam levels 20\n"); + fprintf(file, "set xlabel 'X microns'\n"); + fprintf(file, "set ylabel 'Y microns'\n"); + fprintf(file, "set key outside right\n"); + fprintf(file, "set title '%s - %s", vecs->v_plot->pl_title, + vecs->v_name); + vtypename = ft_typabbrev(vecs->v_type); + if (vtypename) { + fprintf(file, " %s'\n", vtypename); + } else { + fprintf(file, "'\n"); + } } else { - fprintf(file, "unset logscale y \n"); - if (ylims) - fprintf(file, "set yrange [%e:%e]\n", ylims[0] - extrange, ylims[1] + extrange); - } - - if (xdel > 0.) - fprintf(file, "set xtics %e\n", xdel); - else - fprintf(file, "#set xtics 1\n"); - fprintf(file, "#set x2tics 1\n"); - if (ydel > 0.) - fprintf(file, "set ytics %e\n", ydel); - else - fprintf(file, "#set ytics 1\n"); - fprintf(file, "#set y2tics 1\n"); - - if (gridlinewidth > 1) - fprintf(file, "set border lw %d\n", gridlinewidth); + if (title) { + text = cp_unquote(title); + fprintf(file, "set title "); + quote_gnuplot_string(file, text); + fprintf(file, "\n"); + tfree(text); + } + if (xlabel) { + text = cp_unquote(xlabel); + fprintf(file, "set xlabel "); + quote_gnuplot_string(file, text); + fprintf(file, "\n"); + tfree(text); + } + if (ylabel) { + text = cp_unquote(ylabel); + fprintf(file, "set ylabel "); + quote_gnuplot_string(file, text); + fprintf(file, "\n"); + tfree(text); + } + if (!nogrid) { + if (gridlinewidth > 1) + fprintf(file, "set grid lw %d \n" , gridlinewidth); + else + fprintf(file, "set grid\n"); + } + if (xlog) { + fprintf(file, "set logscale x\n"); + if (xlims) + fprintf(file, "set xrange [%1.0e:%1.0e]\n", + pow(10, floor(log10(xlims[0]))), pow(10, ceil(log10(xlims[1])))); + fprintf(file, "set mxtics 10\n"); + fprintf(file, "set grid mxtics\n"); + } else { + fprintf(file, "unset logscale x \n"); + if (xlims) + fprintf(file, "set xrange [%e:%e]\n", xlims[0], xlims[1]); + } + if (ylog) { + fprintf(file, "set logscale y \n"); + if (ylims) + fprintf(file, "set yrange [%1.0e:%1.0e]\n", + pow(10, floor(log10(ylims[0]))), pow(10, ceil(log10(ylims[1])))); + fprintf(file, "set mytics 10\n"); + fprintf(file, "set grid mytics\n"); + } else { + fprintf(file, "unset logscale y \n"); + if (ylims) + fprintf(file, "set yrange [%e:%e]\n", ylims[0] - extrange, ylims[1] + extrange); + } - if(nolegend) - fprintf(file, "set key off\n"); + if (xdel > 0.) + fprintf(file, "set xtics %e\n", xdel); + else + fprintf(file, "#set xtics 1\n"); + fprintf(file, "#set x2tics 1\n"); + if (ydel > 0.) + fprintf(file, "set ytics %e\n", ydel); + else + fprintf(file, "#set ytics 1\n"); + fprintf(file, "#set y2tics 1\n"); - if (plottype == PLOT_COMB) { - strcpy(plotstyle, "boxes"); - } else if (plottype == PLOT_POINT) { - if (markers) { - // fprintf(file, "Markers: True\n"); + if (gridlinewidth > 1) + fprintf(file, "set border lw %d\n", gridlinewidth); + + if(nolegend) + fprintf(file, "set key off\n"); + + if (plottype == PLOT_COMB) { + strcpy(plotstyle, "boxes"); + } else if (plottype == PLOT_POINT) { + if (markers) { + // fprintf(file, "Markers: True\n"); + } else { + // fprintf(file, "LargePixels: True\n"); + } + strcpy(plotstyle, "points"); } else { - // fprintf(file, "LargePixels: True\n"); + strcpy(plotstyle, "lines"); } - strcpy(plotstyle, "points"); - } else { - strcpy(plotstyle, "lines"); } /* Open the output gnuplot data file. */ @@ -277,25 +502,33 @@ void ft_gnuplot(double *xlims, double *ylims, perror(filename); return; } - fprintf(file, "set format y \"%%g\"\n"); - fprintf(file, "set format x \"%%g\"\n"); - - if ((terminal_type != 3) && (terminal_type != 5)) { - fprintf(file, "plot "); - i = 0; - - /* Write out the gnuplot command */ - for (v = vecs; v; v = v->v_link2) { - scale = v->v_scale; - if (v->v_name) { - i = i + 2; - if (i > 2) fprintf(file, ",\\\n"); - fprintf(file, "\'%s\' using %d:%d with %s lw %d title ", - filename_data, i - 1, i, plotstyle, linewidth); - quote_gnuplot_string(file, v->v_name); + if (contours) { + if ((terminal_type != 3) && (terminal_type != 5)) { + fprintf(file, + "splot '%s' nonuniform matrix using 1:2:3 with lines lw 2 " + "title ' '\n", filename_data); + } + } else { + fprintf(file, "set format y \"%%g\"\n"); + fprintf(file, "set format x \"%%g\"\n"); + + if ((terminal_type != 3) && (terminal_type != 5)) { + fprintf(file, "plot "); + i = 0; + + /* Write out the gnuplot command */ + for (v = vecs; v; v = v->v_link2) { + scale = v->v_scale; + if (v->v_name) { + i = i + 2; + if (i > 2) fprintf(file, ",\\\n"); + fprintf(file, "\'%s\' using %d:%d with %s lw %d title ", + filename_data, i - 1, i, plotstyle, linewidth); + quote_gnuplot_string(file, v->v_name); + } } + fprintf(file, "\n"); } - fprintf(file, "\n"); } /* terminal_type @@ -321,60 +554,77 @@ void ft_gnuplot(double *xlims, double *ylims, fprintf(file, "replot\n"); } - if ((terminal_type == 3) || (terminal_type == 5)) { - fprintf(file, "plot "); - i = 0; - - /* Write out the gnuplot command */ - for (v = vecs; v; v = v->v_link2) { - scale = v->v_scale; - if (v->v_name) { - i = i + 2; - if (i > 2) fprintf(file, ",\\\n"); - fprintf(file, "\'%s\' using %d:%d with %s lw %d title ", - filename_data, i - 1, i, plotstyle, linewidth); - quote_gnuplot_string(file, v->v_name); + if (contours) { + if ((terminal_type == 3) || (terminal_type == 5)) { + fprintf(file, + "splot '%s' nonuniform matrix using 1:2:3 with lines lw 2 " + "title ' '\n", filename_data); + fprintf(file, "exit\n"); + } + } else { + if ((terminal_type == 3) || (terminal_type == 5)) { + fprintf(file, "plot "); + i = 0; + + /* Write out the gnuplot command */ + for (v = vecs; v; v = v->v_link2) { + scale = v->v_scale; + if (v->v_name) { + i = i + 2; + if (i > 2) fprintf(file, ",\\\n"); + fprintf(file, "\'%s\' using %d:%d with %s lw %d title ", + filename_data, i - 1, i, plotstyle, linewidth); + quote_gnuplot_string(file, v->v_name); + } } + fprintf(file, "\n"); + fprintf(file, "exit\n"); } - fprintf(file, "\n"); - fprintf(file, "exit\n"); } (void) fclose(file); - /* Write out the data and setup arrays */ - bool mono = (plottype != PLOT_RETLIN); - dir = 0; - prev_xval = NAN; - for (i = 0; i < scale->v_length; i++) { - for (v = vecs; v; v = v->v_link2) { - scale = v->v_scale; - - xval = isreal(scale) ? - scale->v_realdata[i] : realpart(scale->v_compdata[i]); - - yval = isreal(v) ? - v->v_realdata[i] : realpart(v->v_compdata[i]); - - if (i > 0 && (mono || (scale->v_plot && scale->v_plot->pl_scale == scale))) { - if (dir * (xval - prev_xval) < 0) { - /* direction reversal, start a new graph */ - fprintf(file_data, "\n"); - dir = 0; - } else if (!dir && xval > prev_xval) { - dir = 1; - } else if (!dir && xval < prev_xval) { - dir = -1; + if (contours) { + if (write_contour_data(file_data, vecs) != 0) { + fprintf(stderr, "Error when writing contour data file\n"); + (void) fclose(file_data); + return; + } + } else { + /* Write out the data and setup arrays */ + bool mono = (plottype != PLOT_RETLIN); + dir = 0; + prev_xval = NAN; + for (i = 0; i < scale->v_length; i++) { + for (v = vecs; v; v = v->v_link2) { + scale = v->v_scale; + + xval = isreal(scale) ? + scale->v_realdata[i] : realpart(scale->v_compdata[i]); + + yval = isreal(v) ? + v->v_realdata[i] : realpart(v->v_compdata[i]); + + if (i > 0 && (mono || (scale->v_plot && scale->v_plot->pl_scale == scale))) { + if (dir * (xval - prev_xval) < 0) { + /* direction reversal, start a new graph */ + fprintf(file_data, "\n"); + dir = 0; + } else if (!dir && xval > prev_xval) { + dir = 1; + } else if (!dir && xval < prev_xval) { + dir = -1; + } } - } - fprintf(file_data, "%e %e ", xval, yval); + fprintf(file_data, "%e %e ", xval, yval); - prev_xval = xval; + prev_xval = xval; + } + fprintf(file_data, "\n"); } - fprintf(file_data, "\n"); } (void) fclose(file_data); diff --git a/src/frontend/plotting/gnuplot.h b/src/frontend/plotting/gnuplot.h index 16c8676d4..0fe5194e6 100644 --- a/src/frontend/plotting/gnuplot.h +++ b/src/frontend/plotting/gnuplot.h @@ -11,7 +11,7 @@ void ft_gnuplot(double *xlims, double *ylims, const char *filename, const char *title, const char *xlabel, const char *ylabel, GRIDTYPE gridtype, PLOTTYPE plottype, - struct dvec *vecs); + struct dvec *vecs, bool xycontour); void ft_writesimple(double *xlims, double *ylims, diff --git a/src/frontend/plotting/plotit.c b/src/frontend/plotting/plotit.c index 43a28797b..84a8bc8d8 100644 --- a/src/frontend/plotting/plotit.c +++ b/src/frontend/plotting/plotit.c @@ -287,7 +287,7 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) static GRIDTYPE gtype = GRID_LIN; static PLOTTYPE ptype = PLOT_LIN; - bool gfound = FALSE, pfound = FALSE, oneval = FALSE; + bool gfound = FALSE, pfound = FALSE, oneval = FALSE, contour2d = FALSE; double ylims[2], xlims[2]; struct pnode *pn, *names = NULL; struct dvec *d = NULL, *vecs = NULL, *lv = NULL, *lastvs = NULL; @@ -361,6 +361,8 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) goto quit1; } + /* See if contours for 2D Cider data can be plotted with gnuplot */ + contour2d = getflag(wl, "xycontour"); /* Now extract all the parameters. */ sameflag = getflag(wl, "samep"); @@ -1149,7 +1151,7 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname) title ? title : vecs->v_plot->pl_title, xlabel ? xlabel : ft_typabbrev(vecs->v_scale->v_type), ylabel ? ylabel : ft_typabbrev(y_type), - gtype, ptype, vecs); + gtype, ptype, vecs, contour2d); rtn = TRUE; goto quit; } diff --git a/src/frontend/rawfile.c b/src/frontend/rawfile.c index dda6f2e5c..a07b23a6e 100644 --- a/src/frontend/rawfile.c +++ b/src/frontend/rawfile.c @@ -390,6 +390,8 @@ raw_read(char *name) { if (!title) title = copy("default title"); curpl->pl_title = title; + curpl->pl_xdim2d = -1; + curpl->pl_ydim2d = -1; date = NULL; title = NULL; flags = VF_PERMANENT; @@ -606,6 +608,14 @@ raw_read(char *name) { } } + if ((numdims == 2) && (flags & VF_REAL) && + eq(curpl->pl_name, "Device Cross Section")) { + if ((dims[0] > 1) && (dims[1] > 1) && + (npoints == dims[0] * dims[1])) { + curpl->pl_xdim2d = dims[0]; + curpl->pl_ydim2d = dims[1]; + } + } if ((*buf == 'v') || (*buf == 'V')) is_ascii = TRUE; else diff --git a/src/include/ngspice/plot.h b/src/include/ngspice/plot.h index 392f6dcf4..0299867b9 100644 --- a/src/include/ngspice/plot.h +++ b/src/include/ngspice/plot.h @@ -25,6 +25,8 @@ struct plot { bool pl_written; /* Some or all of the vecs have been saved. */ bool pl_lookup_valid; /* vector lookup table valid */ int pl_ndims; /* Number of dimensions */ + int pl_xdim2d; /* 2D Cider plot x dimension */ + int pl_ydim2d; /* 2D Cider plot y dimension */ } ;