Actual source code: bars.c

      1: /*
      2:   Contains the data structure for plotting a bargraph in a window with an axis.
      3: */

      5: #include <petsc/private/drawimpl.h>
      6: #include <petscviewer.h>

      8: PetscClassId PETSC_DRAWBAR_CLASSID = 0;

     10: /*@
     11:   PetscDrawBarCreate - Creates a bar graph data structure.

     13:   Collective

     15:   Input Parameter:
     16: . draw - The window where the graph will be made

     18:   Output Parameter:
     19: . bar - The bar graph context

     21:   Notes:
     22:   Call `PetscDrawBarSetData()` to provide the bins to be plotted and then `PetscDrawBarDraw()` to display the new plot

     24:   The difference between a bar chart, `PetscDrawBar`, and a histogram, `PetscDrawHG`, is explained here <https://stattrek.com/statistics/charts/histogram.aspx?Tutorial=AP>

     26:   The MPI communicator that owns the `PetscDraw` owns this `PetscDrawBar`, but the calls to set options and add data are ignored on all processes except the
     27:   zeroth MPI process in the communicator. All MPI processes in the communicator must call `PetscDrawBarDraw()` to display the updated graph.

     29:   Level: intermediate

     31: .seealso: `PetscDrawBar`, `PetscDrawLGCreate()`, `PetscDrawLG`, `PetscDrawSPCreate()`, `PetscDrawSP`, `PetscDrawHGCreate()`, `PetscDrawHG`, `PetscDrawBarDestroy()`, `PetscDrawBarSetData()`,
     32:           `PetscDrawBarDraw()`, `PetscDrawBarSave()`, `PetscDrawBarSetColor()`, `PetscDrawBarSort()`, `PetscDrawBarSetLimits()`, `PetscDrawBarGetAxis()`, `PetscDrawAxis`,
     33:           `PetscDrawBarGetDraw()`, `PetscDrawBarSetFromOptions()`
     34: @*/
     35: PetscErrorCode PetscDrawBarCreate(PetscDraw draw, PetscDrawBar *bar)
     36: {
     37:   PetscDrawBar h;

     39:   PetscFunctionBegin;
     41:   PetscAssertPointer(bar, 2);

     43:   PetscCall(PetscHeaderCreate(h, PETSC_DRAWBAR_CLASSID, "DrawBar", "Bar Graph", "Draw", PetscObjectComm((PetscObject)draw), PetscDrawBarDestroy, NULL));
     44:   PetscCall(PetscObjectReference((PetscObject)draw));
     45:   h->win     = draw;
     46:   h->view    = NULL;
     47:   h->destroy = NULL;
     48:   h->color   = PETSC_DRAW_GREEN;
     49:   h->ymin    = 0.; /* if user has not set these then they are determined from the data */
     50:   h->ymax    = 0.;
     51:   h->numBins = 0;
     52:   PetscCall(PetscDrawAxisCreate(draw, &h->axis));
     53:   h->axis->xticks = NULL;
     54:   *bar            = h;
     55:   PetscFunctionReturn(PETSC_SUCCESS);
     56: }

     58: /*@C
     59:   PetscDrawBarSetData - Set the data for a bar graph

     61:   Logically Collective

     63:   Input Parameters:
     64: + bar    - The bar graph context.
     65: . bins   - number of items
     66: . data   - values of each item
     67: - labels - optional label for each bar, `NULL` terminated array of strings

     69:   Level: intermediate

     71:   Notes:
     72:   Call `PetscDrawBarDraw()` after this call to display the new plot

     74:   The data is ignored on all MPI processes except rank zero

     76: .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarDraw()`
     77: @*/
     78: PetscErrorCode PetscDrawBarSetData(PetscDrawBar bar, PetscInt bins, const PetscReal data[], const char *const labels[])
     79: {
     80:   PetscFunctionBegin;

     83:   if (bar->numBins != bins) {
     84:     PetscCall(PetscFree(bar->values));
     85:     PetscCall(PetscMalloc1(bins, &bar->values));
     86:   }
     87:   PetscCall(PetscArraycpy(bar->values, data, bins));
     88:   PetscCall(PetscCIntCast(bins, &bar->numBins));
     89:   if (labels) PetscCall(PetscStrArrayallocpy(labels, &bar->labels));
     90:   PetscFunctionReturn(PETSC_SUCCESS);
     91: }

     93: /*@
     94:   PetscDrawBarDestroy - Frees all space taken up by bar graph data structure.

     96:   Collective

     98:   Input Parameter:
     99: . bar - The bar graph context

    101:   Level: intermediate

    103: .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`
    104: @*/
    105: PetscErrorCode PetscDrawBarDestroy(PetscDrawBar *bar)
    106: {
    107:   PetscFunctionBegin;
    108:   if (!*bar) PetscFunctionReturn(PETSC_SUCCESS);
    110:   if (--((PetscObject)*bar)->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);

    112:   PetscCall(PetscFree((*bar)->values));
    113:   PetscCall(PetscStrArrayDestroy(&(*bar)->labels));
    114:   PetscCall(PetscDrawAxisDestroy(&(*bar)->axis));
    115:   PetscCall(PetscDrawDestroy(&(*bar)->win));
    116:   PetscCall(PetscHeaderDestroy(bar));
    117:   PetscFunctionReturn(PETSC_SUCCESS);
    118: }

    120: /*@
    121:   PetscDrawBarDraw - Redraws a bar graph.

    123:   Collective

    125:   Input Parameter:
    126: . bar - The bar graph context

    128:   Level: intermediate

    130: .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarSetData()`
    131: @*/
    132: PetscErrorCode PetscDrawBarDraw(PetscDrawBar bar)
    133: {
    134:   PetscDraw   draw;
    135:   PetscBool   isnull;
    136:   PetscReal   xmin, xmax, ymin, ymax, *values, binLeft, binRight;
    137:   PetscInt    numValues, i, idx, *perm, nplot;
    138:   PetscMPIInt rank;
    139:   char      **labels;
    140:   int         bcolor, color;

    142:   PetscFunctionBegin;
    144:   PetscCall(PetscDrawIsNull(bar->win, &isnull));
    145:   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
    146:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)bar), &rank));

    148:   if (bar->numBins < 1) PetscFunctionReturn(PETSC_SUCCESS);

    150:   color = bar->color;
    151:   if (color == PETSC_DRAW_ROTATE) bcolor = PETSC_DRAW_BLACK + 1;
    152:   else bcolor = color;

    154:   numValues = bar->numBins;
    155:   values    = bar->values;
    156:   if (bar->ymin == bar->ymax) {
    157:     /* user has not set bounds on bars so set them based on the data */
    158:     ymin = PETSC_MAX_REAL;
    159:     ymax = PETSC_MIN_REAL;
    160:     for (i = 0; i < numValues; i++) {
    161:       ymin = PetscMin(ymin, values[i]);
    162:       ymax = PetscMax(ymax, values[i]);
    163:     }
    164:   } else {
    165:     ymin = bar->ymin;
    166:     ymax = bar->ymax;
    167:   }
    168:   nplot  = numValues; /* number of points to actually plot; if some are lower than requested tolerance */
    169:   xmin   = 0.0;
    170:   xmax   = (PetscReal)nplot;
    171:   labels = bar->labels;

    173:   if (bar->sort) {
    174:     PetscCall(PetscMalloc1(numValues, &perm));
    175:     for (i = 0; i < numValues; i++) perm[i] = i;
    176:     PetscCall(PetscSortRealWithPermutation(numValues, values, perm));
    177:     if (bar->sorttolerance) {
    178:       for (i = 0; i < numValues; i++) {
    179:         if (values[perm[numValues - i - 1]] < bar->sorttolerance) {
    180:           nplot = i;
    181:           break;
    182:         }
    183:       }
    184:     }
    185:   }

    187:   draw = bar->win;
    188:   PetscCall(PetscDrawCheckResizedWindow(draw));
    189:   PetscCall(PetscDrawClear(draw));

    191:   PetscCall(PetscDrawAxisSetLimits(bar->axis, xmin, xmax, ymin, ymax));
    192:   PetscCall(PetscDrawAxisDraw(bar->axis));

    194:   PetscDrawCollectiveBegin(draw);
    195:   if (rank == 0) { /* Draw bins */
    196:     for (i = 0; i < nplot; i++) {
    197:       idx      = (bar->sort ? perm[numValues - i - 1] : i);
    198:       binLeft  = xmin + (PetscReal)i;
    199:       binRight = xmin + (PetscReal)i + 1;
    200:       PetscCall(PetscDrawRectangle(draw, binLeft, ymin, binRight, values[idx], bcolor, bcolor, bcolor, bcolor));
    201:       PetscCall(PetscDrawLine(draw, binLeft, ymin, binLeft, values[idx], PETSC_DRAW_BLACK));
    202:       PetscCall(PetscDrawLine(draw, binRight, ymin, binRight, values[idx], PETSC_DRAW_BLACK));
    203:       PetscCall(PetscDrawLine(draw, binLeft, values[idx], binRight, values[idx], PETSC_DRAW_BLACK));
    204:       if (labels) {
    205:         PetscReal h;

    207:         PetscCall(PetscDrawStringGetSize(draw, NULL, &h));
    208:         PetscCall(PetscDrawStringCentered(draw, .5 * (binLeft + binRight), ymin - 1.5 * h, bcolor, labels[idx]));
    209:       }
    210:       if (color == PETSC_DRAW_ROTATE) bcolor++;
    211:       if (bcolor > PETSC_DRAW_BASIC_COLORS - 1) bcolor = PETSC_DRAW_BLACK + 1;
    212:     }
    213:   }
    214:   PetscDrawCollectiveEnd(draw);
    215:   if (bar->sort) PetscCall(PetscFree(perm));

    217:   PetscCall(PetscDrawFlush(draw));
    218:   PetscCall(PetscDrawPause(draw));
    219:   PetscFunctionReturn(PETSC_SUCCESS);
    220: }

    222: /*@
    223:   PetscDrawBarSave - Saves a drawn bar graph

    225:   Collective

    227:   Input Parameter:
    228: . bar - The bar graph context

    230:   Level: intermediate

    232: .seealso: `PetscDrawSave()`, `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetDraw()`, `PetscDrawSetSave()`, `PetscDrawBarSetData()`
    233: @*/
    234: PetscErrorCode PetscDrawBarSave(PetscDrawBar bar)
    235: {
    236:   PetscFunctionBegin;
    238:   PetscCall(PetscDrawSave(bar->win));
    239:   PetscFunctionReturn(PETSC_SUCCESS);
    240: }

    242: /*@
    243:   PetscDrawBarSetColor - Sets the color the bars will be drawn with.

    245:   Logically Collective

    247:   Input Parameters:
    248: + bar   - The bar graph context
    249: - color - one of the colors defined in petscdraw.h or `PETSC_DRAW_ROTATE` to make each bar a
    250:           different color

    252:   Level: intermediate

    254: .seealso: `PetscDrawBarCreate()`, `PetscDrawBar`, `PetscDrawBarSetData()`, `PetscDrawBarDraw()`, `PetscDrawBarGetAxis()`
    255: @*/
    256: PetscErrorCode PetscDrawBarSetColor(PetscDrawBar bar, int color)
    257: {
    258:   PetscFunctionBegin;
    260:   bar->color = color;
    261:   PetscFunctionReturn(PETSC_SUCCESS);
    262: }

    264: /*@
    265:   PetscDrawBarSort - Sorts the values before drawing the bar chart, the bars will be in ascending order from left to right

    267:   Logically Collective

    269:   Input Parameters:
    270: + bar       - The bar graph context
    271: . sort      - `PETSC_TRUE` to sort the values
    272: - tolerance - discard values less than tolerance

    274:   Level: intermediate

    276: .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarSetData()`, `PetscDrawBarSetColor()`, `PetscDrawBarDraw()`, `PetscDrawBarGetAxis()`
    277: @*/
    278: PetscErrorCode PetscDrawBarSort(PetscDrawBar bar, PetscBool sort, PetscReal tolerance)
    279: {
    280:   PetscFunctionBegin;
    282:   bar->sort          = sort;
    283:   bar->sorttolerance = tolerance;
    284:   PetscFunctionReturn(PETSC_SUCCESS);
    285: }

    287: /*@
    288:   PetscDrawBarSetLimits - Sets the axis limits for a bar graph. If more
    289:   points are added after this call, the limits will be adjusted to
    290:   include those additional points.

    292:   Logically Collective

    294:   Input Parameters:
    295: + bar   - The bar graph context
    296: . y_min - The lower limit
    297: - y_max - The upper limit

    299:   Level: intermediate

    301: .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetAxis()`, `PetscDrawBarSetData()`, `PetscDrawBarDraw()`
    302: @*/
    303: PetscErrorCode PetscDrawBarSetLimits(PetscDrawBar bar, PetscReal y_min, PetscReal y_max)
    304: {
    305:   PetscFunctionBegin;
    307:   bar->ymin = y_min;
    308:   bar->ymax = y_max;
    309:   PetscFunctionReturn(PETSC_SUCCESS);
    310: }

    312: /*@
    313:   PetscDrawBarGetAxis - Gets the axis context associated with a bar graph.
    314:   This is useful if one wants to change some axis property, such as
    315:   labels, color, etc. The axis context should not be destroyed by the
    316:   application code.

    318:   Not Collective, axis is parallel if bar is parallel

    320:   Input Parameter:
    321: . bar - The bar graph context

    323:   Output Parameter:
    324: . axis - The axis context

    326:   Level: intermediate

    328: .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawAxis`, `PetscDrawAxisCreate()`
    329: @*/
    330: PetscErrorCode PetscDrawBarGetAxis(PetscDrawBar bar, PetscDrawAxis *axis)
    331: {
    332:   PetscFunctionBegin;
    334:   PetscAssertPointer(axis, 2);
    335:   *axis = bar->axis;
    336:   PetscFunctionReturn(PETSC_SUCCESS);
    337: }

    339: /*@
    340:   PetscDrawBarGetDraw - Gets the draw context associated with a bar graph.

    342:   Not Collective, draw is parallel if bar is parallel

    344:   Input Parameter:
    345: . bar - The bar graph context

    347:   Output Parameter:
    348: . draw - The draw context

    350:   Level: intermediate

    352: .seealso: `PetscDrawBar`, `PetscDraw`, `PetscDrawBarCreate()`, `PetscDrawBarDraw()`
    353: @*/
    354: PetscErrorCode PetscDrawBarGetDraw(PetscDrawBar bar, PetscDraw *draw)
    355: {
    356:   PetscFunctionBegin;
    358:   PetscAssertPointer(draw, 2);
    359:   *draw = bar->win;
    360:   PetscFunctionReturn(PETSC_SUCCESS);
    361: }

    363: /*@
    364:   PetscDrawBarSetFromOptions - Sets options related to the display of the `PetscDrawBar`

    366:   Collective

    368:   Input Parameter:
    369: . bar - the bar graph context

    371:   Options Database Key:
    372: . -bar_sort - sort the entries before drawing the bar graph

    374:   Level: intermediate

    376:   Note:
    377:   Does not set options related to the underlying `PetscDraw` or `PetscDrawAxis`

    379: .seealso: `PetscDrawBar`, `PetscDrawBarDestroy()`, `PetscDrawBarCreate()`, `PetscDrawBarSort()`
    380: @*/
    381: PetscErrorCode PetscDrawBarSetFromOptions(PetscDrawBar bar)
    382: {
    383:   PetscBool set;

    385:   PetscFunctionBegin;

    388:   PetscCall(PetscOptionsHasName(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &set));
    389:   if (set) {
    390:     PetscReal tol = bar->sorttolerance;
    391:     PetscCall(PetscOptionsGetReal(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &tol, NULL));
    392:     PetscCall(PetscDrawBarSort(bar, PETSC_TRUE, tol));
    393:   }
    394:   PetscFunctionReturn(PETSC_SUCCESS);
    395: }