xref: /petsc/src/sys/classes/draw/utils/bars.c (revision 357d8704e7b71e6f14cfde34e80d63f499af80c0)
1 /*
2   Contains the data structure for plotting a bargraph in a window with an axis.
3 */
4 
5 #include <petsc/private/drawimpl.h> /*I "petscdraw.h" I*/
6 #include <petscviewer.h>            /*I "petscviewer.h" I*/
7 
8 PetscClassId PETSC_DRAWBAR_CLASSID = 0;
9 
10 /*@
11   PetscDrawBarCreate - Creates a bar graph data structure.
12 
13   Collective
14 
15   Input Parameter:
16 . draw - The window where the graph will be made
17 
18   Output Parameter:
19 . bar - The bar graph context
20 
21   Notes:
22   Call `PetscDrawBarSetData()` to provide the bins to be plotted and then `PetscDrawBarDraw()` to display the new plot
23 
24   The difference between a bar chart, `PetscDrawBar`, and a histogram, `PetscDrawHG`, is explained here <https://stattrek.com/statistics/charts/histogram.aspx?Tutorial=AP>
25 
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.
28 
29   Level: intermediate
30 
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;
38 
39   PetscFunctionBegin;
40   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
41   PetscAssertPointer(bar, 2);
42 
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 }
57 
58 /*@C
59   PetscDrawBarSetData - Set the data for a bar graph
60 
61   Logically Collective
62 
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
68 
69   Level: intermediate
70 
71   Notes:
72   Call `PetscDrawBarDraw()` after this call to display the new plot
73 
74   The data is ignored on all MPI processes except rank zero
75 
76 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarDraw()`
77 @*/
78 PetscErrorCode PetscDrawBarSetData(PetscDrawBar bar, PetscInt bins, const PetscReal data[], const char *const labels[])
79 {
80   PetscFunctionBegin;
81   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
82 
83   if (bar->numBins != bins) {
84     PetscCall(PetscFree(bar->values));
85     PetscCall(PetscMalloc1(bins, &bar->values));
86     bar->numBins = bins;
87   }
88   PetscCall(PetscArraycpy(bar->values, data, bins));
89   bar->numBins = bins;
90   if (labels) PetscCall(PetscStrArrayallocpy(labels, &bar->labels));
91   PetscFunctionReturn(PETSC_SUCCESS);
92 }
93 
94 /*@
95   PetscDrawBarDestroy - Frees all space taken up by bar graph data structure.
96 
97   Collective
98 
99   Input Parameter:
100 . bar - The bar graph context
101 
102   Level: intermediate
103 
104 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`
105 @*/
106 PetscErrorCode PetscDrawBarDestroy(PetscDrawBar *bar)
107 {
108   PetscFunctionBegin;
109   if (!*bar) PetscFunctionReturn(PETSC_SUCCESS);
110   PetscValidHeaderSpecific(*bar, PETSC_DRAWBAR_CLASSID, 1);
111   if (--((PetscObject)*bar)->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
112 
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 }
120 
121 /*@
122   PetscDrawBarDraw - Redraws a bar graph.
123 
124   Collective
125 
126   Input Parameter:
127 . bar - The bar graph context
128 
129   Level: intermediate
130 
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, bcolor, color, idx, *perm, nplot;
139   PetscMPIInt rank;
140   char      **labels;
141 
142   PetscFunctionBegin;
143   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
144   PetscCall(PetscDrawIsNull(bar->win, &isnull));
145   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
146   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)bar), &rank));
147 
148   if (bar->numBins < 1) PetscFunctionReturn(PETSC_SUCCESS);
149 
150   color = bar->color;
151   if (color == PETSC_DRAW_ROTATE) bcolor = PETSC_DRAW_BLACK + 1;
152   else bcolor = color;
153 
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   = nplot;
171   labels = bar->labels;
172 
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   }
186 
187   draw = bar->win;
188   PetscCall(PetscDrawCheckResizedWindow(draw));
189   PetscCall(PetscDrawClear(draw));
190 
191   PetscCall(PetscDrawAxisSetLimits(bar->axis, xmin, xmax, ymin, ymax));
192   PetscCall(PetscDrawAxisDraw(bar->axis));
193 
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 + i;
199       binRight = xmin + 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;
206         PetscCall(PetscDrawStringGetSize(draw, NULL, &h));
207         PetscCall(PetscDrawStringCentered(draw, .5 * (binLeft + binRight), ymin - 1.5 * h, bcolor, labels[idx]));
208       }
209       if (color == PETSC_DRAW_ROTATE) bcolor++;
210       if (bcolor > PETSC_DRAW_BASIC_COLORS - 1) bcolor = PETSC_DRAW_BLACK + 1;
211     }
212   }
213   PetscDrawCollectiveEnd(draw);
214   if (bar->sort) PetscCall(PetscFree(perm));
215 
216   PetscCall(PetscDrawFlush(draw));
217   PetscCall(PetscDrawPause(draw));
218   PetscFunctionReturn(PETSC_SUCCESS);
219 }
220 
221 /*@
222   PetscDrawBarSave - Saves a drawn bar graph
223 
224   Collective
225 
226   Input Parameter:
227 . bar - The bar graph context
228 
229   Level: intermediate
230 
231 .seealso: `PetscDrawSave()`, `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetDraw()`, `PetscDrawSetSave()`, `PetscDrawBarSetData()`
232 @*/
233 PetscErrorCode PetscDrawBarSave(PetscDrawBar bar)
234 {
235   PetscFunctionBegin;
236   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
237   PetscCall(PetscDrawSave(bar->win));
238   PetscFunctionReturn(PETSC_SUCCESS);
239 }
240 
241 /*@
242   PetscDrawBarSetColor - Sets the color the bars will be drawn with.
243 
244   Logically Collective
245 
246   Input Parameters:
247 + bar   - The bar graph context
248 - color - one of the colors defined in petscdraw.h or `PETSC_DRAW_ROTATE` to make each bar a
249           different color
250 
251   Level: intermediate
252 
253 .seealso: `PetscDrawBarCreate()`, `PetscDrawBar`, `PetscDrawBarSetData()`, `PetscDrawBarDraw()`, `PetscDrawBarGetAxis()`
254 @*/
255 PetscErrorCode PetscDrawBarSetColor(PetscDrawBar bar, int color)
256 {
257   PetscFunctionBegin;
258   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
259   bar->color = color;
260   PetscFunctionReturn(PETSC_SUCCESS);
261 }
262 
263 /*@
264   PetscDrawBarSort - Sorts the values before drawing the bar chart, the bars will be in ascending order from left to right
265 
266   Logically Collective
267 
268   Input Parameters:
269 + bar       - The bar graph context
270 . sort      - `PETSC_TRUE` to sort the values
271 - tolerance - discard values less than tolerance
272 
273   Level: intermediate
274 
275 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarSetData()`, `PetscDrawBarSetColor()`, `PetscDrawBarDraw()`, `PetscDrawBarGetAxis()`
276 @*/
277 PetscErrorCode PetscDrawBarSort(PetscDrawBar bar, PetscBool sort, PetscReal tolerance)
278 {
279   PetscFunctionBegin;
280   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
281   bar->sort          = sort;
282   bar->sorttolerance = tolerance;
283   PetscFunctionReturn(PETSC_SUCCESS);
284 }
285 
286 /*@
287   PetscDrawBarSetLimits - Sets the axis limits for a bar graph. If more
288   points are added after this call, the limits will be adjusted to
289   include those additional points.
290 
291   Logically Collective
292 
293   Input Parameters:
294 + bar   - The bar graph context
295 . y_min - The lower limit
296 - y_max - The upper limit
297 
298   Level: intermediate
299 
300 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetAxis()`, `PetscDrawBarSetData()`, `PetscDrawBarDraw()`
301 @*/
302 PetscErrorCode PetscDrawBarSetLimits(PetscDrawBar bar, PetscReal y_min, PetscReal y_max)
303 {
304   PetscFunctionBegin;
305   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
306   bar->ymin = y_min;
307   bar->ymax = y_max;
308   PetscFunctionReturn(PETSC_SUCCESS);
309 }
310 
311 /*@
312   PetscDrawBarGetAxis - Gets the axis context associated with a bar graph.
313   This is useful if one wants to change some axis property, such as
314   labels, color, etc. The axis context should not be destroyed by the
315   application code.
316 
317   Not Collective, axis is parallel if bar is parallel
318 
319   Input Parameter:
320 . bar - The bar graph context
321 
322   Output Parameter:
323 . axis - The axis context
324 
325   Level: intermediate
326 
327 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawAxis`, `PetscDrawAxisCreate()`
328 @*/
329 PetscErrorCode PetscDrawBarGetAxis(PetscDrawBar bar, PetscDrawAxis *axis)
330 {
331   PetscFunctionBegin;
332   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
333   PetscAssertPointer(axis, 2);
334   *axis = bar->axis;
335   PetscFunctionReturn(PETSC_SUCCESS);
336 }
337 
338 /*@
339   PetscDrawBarGetDraw - Gets the draw context associated with a bar graph.
340 
341   Not Collective, draw is parallel if bar is parallel
342 
343   Input Parameter:
344 . bar - The bar graph context
345 
346   Output Parameter:
347 . draw - The draw context
348 
349   Level: intermediate
350 
351 .seealso: `PetscDrawBar`, `PetscDraw`, `PetscDrawBarCreate()`, `PetscDrawBarDraw()`
352 @*/
353 PetscErrorCode PetscDrawBarGetDraw(PetscDrawBar bar, PetscDraw *draw)
354 {
355   PetscFunctionBegin;
356   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
357   PetscAssertPointer(draw, 2);
358   *draw = bar->win;
359   PetscFunctionReturn(PETSC_SUCCESS);
360 }
361 
362 /*@
363   PetscDrawBarSetFromOptions - Sets options related to the display of the `PetscDrawBar`
364 
365   Collective
366 
367   Input Parameter:
368 . bar - the bar graph context
369 
370   Options Database Key:
371 . -bar_sort - sort the entries before drawing the bar graph
372 
373   Level: intermediate
374 
375   Note:
376   Does not set options related to the underlying `PetscDraw` or `PetscDrawAxis`
377 
378 .seealso: `PetscDrawBar`, `PetscDrawBarDestroy()`, `PetscDrawBarCreate()`, `PetscDrawBarSort()`
379 @*/
380 PetscErrorCode PetscDrawBarSetFromOptions(PetscDrawBar bar)
381 {
382   PetscBool set;
383 
384   PetscFunctionBegin;
385   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
386 
387   PetscCall(PetscOptionsHasName(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &set));
388   if (set) {
389     PetscReal tol = bar->sorttolerance;
390     PetscCall(PetscOptionsGetReal(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &tol, NULL));
391     PetscCall(PetscDrawBarSort(bar, PETSC_TRUE, tol));
392   }
393   PetscFunctionReturn(PETSC_SUCCESS);
394 }
395