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:     bar->numBins = (int)bins;
     87:   }
     88:   PetscCall(PetscArraycpy(bar->values, data, bins));
     89:   bar->numBins = (int)bins;
     90:   if (labels) PetscCall(PetscStrArrayallocpy(labels, &bar->labels));
     91:   PetscFunctionReturn(PETSC_SUCCESS);
     92: }

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

     97:   Collective

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

    102:   Level: intermediate

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

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

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

    124:   Collective

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

    129:   Level: intermediate

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

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

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

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

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

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

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

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

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

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

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

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

    226:   Collective

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

    231:   Level: intermediate

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

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

    246:   Logically Collective

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

    253:   Level: intermediate

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

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

    268:   Logically Collective

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

    275:   Level: intermediate

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

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

    293:   Logically Collective

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

    300:   Level: intermediate

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

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

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

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

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

    327:   Level: intermediate

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

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

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

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

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

    351:   Level: intermediate

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

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

    367:   Collective

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

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

    375:   Level: intermediate

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

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

    386:   PetscFunctionBegin;

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