xref: /petsc/src/sys/classes/draw/utils/bars.c (revision 0619917b5a674bb687c64e7daba2ab22be99af31)
1 
2 /*
3   Contains the data structure for plotting a bargraph in a window with an axis.
4 */
5 
6 #include <petsc/private/drawimpl.h> /*I "petscdraw.h" I*/
7 #include <petscviewer.h>            /*I "petscviewer.h" I*/
8 
9 PetscClassId PETSC_DRAWBAR_CLASSID = 0;
10 
11 /*@C
12   PetscDrawBarCreate - Creates a bar graph data structure.
13 
14   Collective over draw
15 
16   Input Parameter:
17 . draw - The window where the graph will be made
18 
19   Output Parameter:
20 . bar - The bar graph context
21 
22   Notes:
23   Call `PetscDrawBarSetData()` to provide the bins to be plotted and then `PetscDrawBarDraw()` to display the new plot
24 
25   The difference between a bar chart, `PetscDrawBar`, and a histogram, `PetscDrawHG`, is explained here https://stattrek.com/statistics/charts/histogram.aspx?Tutorial=AP
26 
27   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
28   zeroth MPI rank in the communicator. All MPI ranks in the communicator must call `PetscDrawBarDraw()` to display the updated graph.
29 
30   Level: intermediate
31 
32 .seealso: `PetscDrawBar`, `PetscDrawLGCreate()`, `PetscDrawLG`, `PetscDrawSPCreate()`, `PetscDrawSP`, `PetscDrawHGCreate()`, `PetscDrawHG`, `PetscDrawBarDestroy()`, `PetscDrawBarSetData()`,
33           `PetscDrawBarDraw()`, `PetscDrawBarSave()`, `PetscDrawBarSetColor()`, `PetscDrawBarSort()`, `PetscDrawBarSetLimits()`, `PetscDrawBarGetAxis()`, `PetscDrawAxis`,
34           `PetscDrawBarGetDraw()`, `PetscDrawBarSetFromOptions()`
35 @*/
36 PetscErrorCode PetscDrawBarCreate(PetscDraw draw, PetscDrawBar *bar)
37 {
38   PetscDrawBar h;
39 
40   PetscFunctionBegin;
41   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
42   PetscAssertPointer(bar, 2);
43 
44   PetscCall(PetscHeaderCreate(h, PETSC_DRAWBAR_CLASSID, "DrawBar", "Bar Graph", "Draw", PetscObjectComm((PetscObject)draw), PetscDrawBarDestroy, NULL));
45 
46   PetscCall(PetscObjectReference((PetscObject)draw));
47   h->win = draw;
48 
49   h->view    = NULL;
50   h->destroy = NULL;
51   h->color   = PETSC_DRAW_GREEN;
52   h->ymin    = 0.; /* if user has not set these then they are determined from the data */
53   h->ymax    = 0.;
54   h->numBins = 0;
55 
56   PetscCall(PetscDrawAxisCreate(draw, &h->axis));
57   h->axis->xticks = NULL;
58 
59   *bar = h;
60   PetscFunctionReturn(PETSC_SUCCESS);
61 }
62 
63 /*@C
64   PetscDrawBarSetData - Set the data for a bar graph
65 
66   Logically Collective
67 
68   Input Parameters:
69 + bar    - The bar graph context.
70 . bins   - number of items
71 . data   - values of each item
72 - labels - optional label for each bar, NULL terminated array of strings
73 
74   Level: intermediate
75 
76   Notes:
77   Call `PetscDrawBarDraw()` after this call to display the new plot
78 
79   The data is ignored on all ranks except zero
80 
81 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarDraw()`
82 @*/
83 PetscErrorCode PetscDrawBarSetData(PetscDrawBar bar, PetscInt bins, const PetscReal data[], const char *const *labels)
84 {
85   PetscFunctionBegin;
86   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
87 
88   if (bar->numBins != bins) {
89     PetscCall(PetscFree(bar->values));
90     PetscCall(PetscMalloc1(bins, &bar->values));
91     bar->numBins = bins;
92   }
93   PetscCall(PetscArraycpy(bar->values, data, bins));
94   bar->numBins = bins;
95   if (labels) PetscCall(PetscStrArrayallocpy(labels, &bar->labels));
96   PetscFunctionReturn(PETSC_SUCCESS);
97 }
98 
99 /*@C
100   PetscDrawBarDestroy - Frees all space taken up by bar graph data structure.
101 
102   Collective over bar
103 
104   Input Parameter:
105 . bar - The bar graph context
106 
107   Level: intermediate
108 
109 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`
110 @*/
111 PetscErrorCode PetscDrawBarDestroy(PetscDrawBar *bar)
112 {
113   PetscFunctionBegin;
114   if (!*bar) PetscFunctionReturn(PETSC_SUCCESS);
115   PetscValidHeaderSpecific(*bar, PETSC_DRAWBAR_CLASSID, 1);
116   if (--((PetscObject)(*bar))->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
117 
118   PetscCall(PetscFree((*bar)->values));
119   PetscCall(PetscStrArrayDestroy(&(*bar)->labels));
120   PetscCall(PetscDrawAxisDestroy(&(*bar)->axis));
121   PetscCall(PetscDrawDestroy(&(*bar)->win));
122   PetscCall(PetscHeaderDestroy(bar));
123   PetscFunctionReturn(PETSC_SUCCESS);
124 }
125 
126 /*@
127   PetscDrawBarDraw - Redraws a bar graph.
128 
129   Collective
130 
131   Input Parameter:
132 . bar - The bar graph context
133 
134   Level: intermediate
135 
136 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarSetData()`
137 @*/
138 PetscErrorCode PetscDrawBarDraw(PetscDrawBar bar)
139 {
140   PetscDraw   draw;
141   PetscBool   isnull;
142   PetscReal   xmin, xmax, ymin, ymax, *values, binLeft, binRight;
143   PetscInt    numValues, i, bcolor, color, idx, *perm, nplot;
144   PetscMPIInt rank;
145   char      **labels;
146 
147   PetscFunctionBegin;
148   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
149   PetscCall(PetscDrawIsNull(bar->win, &isnull));
150   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
151   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)bar), &rank));
152 
153   if (bar->numBins < 1) PetscFunctionReturn(PETSC_SUCCESS);
154 
155   color = bar->color;
156   if (color == PETSC_DRAW_ROTATE) bcolor = PETSC_DRAW_BLACK + 1;
157   else bcolor = color;
158 
159   numValues = bar->numBins;
160   values    = bar->values;
161   if (bar->ymin == bar->ymax) {
162     /* user has not set bounds on bars so set them based on the data */
163     ymin = PETSC_MAX_REAL;
164     ymax = PETSC_MIN_REAL;
165     for (i = 0; i < numValues; i++) {
166       ymin = PetscMin(ymin, values[i]);
167       ymax = PetscMax(ymax, values[i]);
168     }
169   } else {
170     ymin = bar->ymin;
171     ymax = bar->ymax;
172   }
173   nplot  = numValues; /* number of points to actually plot; if some are lower than requested tolerance */
174   xmin   = 0.0;
175   xmax   = nplot;
176   labels = bar->labels;
177 
178   if (bar->sort) {
179     PetscCall(PetscMalloc1(numValues, &perm));
180     for (i = 0; i < numValues; i++) perm[i] = i;
181     PetscCall(PetscSortRealWithPermutation(numValues, values, perm));
182     if (bar->sorttolerance) {
183       for (i = 0; i < numValues; i++) {
184         if (values[perm[numValues - i - 1]] < bar->sorttolerance) {
185           nplot = i;
186           break;
187         }
188       }
189     }
190   }
191 
192   draw = bar->win;
193   PetscCall(PetscDrawCheckResizedWindow(draw));
194   PetscCall(PetscDrawClear(draw));
195 
196   PetscCall(PetscDrawAxisSetLimits(bar->axis, xmin, xmax, ymin, ymax));
197   PetscCall(PetscDrawAxisDraw(bar->axis));
198 
199   PetscDrawCollectiveBegin(draw);
200   if (rank == 0) { /* Draw bins */
201     for (i = 0; i < nplot; i++) {
202       idx      = (bar->sort ? perm[numValues - i - 1] : i);
203       binLeft  = xmin + i;
204       binRight = xmin + i + 1;
205       PetscCall(PetscDrawRectangle(draw, binLeft, ymin, binRight, values[idx], bcolor, bcolor, bcolor, bcolor));
206       PetscCall(PetscDrawLine(draw, binLeft, ymin, binLeft, values[idx], PETSC_DRAW_BLACK));
207       PetscCall(PetscDrawLine(draw, binRight, ymin, binRight, values[idx], PETSC_DRAW_BLACK));
208       PetscCall(PetscDrawLine(draw, binLeft, values[idx], binRight, values[idx], PETSC_DRAW_BLACK));
209       if (labels) {
210         PetscReal h;
211         PetscCall(PetscDrawStringGetSize(draw, NULL, &h));
212         PetscCall(PetscDrawStringCentered(draw, .5 * (binLeft + binRight), ymin - 1.5 * h, bcolor, labels[idx]));
213       }
214       if (color == PETSC_DRAW_ROTATE) bcolor++;
215       if (bcolor > PETSC_DRAW_BASIC_COLORS - 1) bcolor = PETSC_DRAW_BLACK + 1;
216     }
217   }
218   PetscDrawCollectiveEnd(draw);
219   if (bar->sort) PetscCall(PetscFree(perm));
220 
221   PetscCall(PetscDrawFlush(draw));
222   PetscCall(PetscDrawPause(draw));
223   PetscFunctionReturn(PETSC_SUCCESS);
224 }
225 
226 /*@
227   PetscDrawBarSave - Saves a drawn bar graph
228 
229   Collective
230 
231   Input Parameter:
232 . bar - The bar graph context
233 
234   Level: intermediate
235 
236 .seealso: `PetscDrawSave()`, `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetDraw()`, `PetscDrawSetSave()`, `PetscDrawBarSetData()`
237 @*/
238 PetscErrorCode PetscDrawBarSave(PetscDrawBar bar)
239 {
240   PetscFunctionBegin;
241   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
242   PetscCall(PetscDrawSave(bar->win));
243   PetscFunctionReturn(PETSC_SUCCESS);
244 }
245 
246 /*@
247   PetscDrawBarSetColor - Sets the color the bars will be drawn with.
248 
249   Logically Collective
250 
251   Input Parameters:
252 + bar   - The bar graph context
253 - color - one of the colors defined in petscdraw.h or `PETSC_DRAW_ROTATE` to make each bar a
254           different color
255 
256   Level: intermediate
257 
258 .seealso: `PetscDrawBarCreate()`, `PetscDrawBar`, `PetscDrawBarSetData()`, `PetscDrawBarDraw()`, `PetscDrawBarGetAxis()`
259 @*/
260 PetscErrorCode PetscDrawBarSetColor(PetscDrawBar bar, int color)
261 {
262   PetscFunctionBegin;
263   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
264   bar->color = color;
265   PetscFunctionReturn(PETSC_SUCCESS);
266 }
267 
268 /*@
269   PetscDrawBarSort - Sorts the values before drawing the bar chart, the bars will be in ascending order from left to right
270 
271   Logically Collective
272 
273   Input Parameters:
274 + bar       - The bar graph context
275 . sort      - `PETSC_TRUE` to sort the values
276 - tolerance - discard values less than tolerance
277 
278   Level: intermediate
279 
280 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarSetData()`, `PetscDrawBarSetColor()`, `PetscDrawBarDraw()`, `PetscDrawBarGetAxis()`
281 @*/
282 PetscErrorCode PetscDrawBarSort(PetscDrawBar bar, PetscBool sort, PetscReal tolerance)
283 {
284   PetscFunctionBegin;
285   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
286   bar->sort          = sort;
287   bar->sorttolerance = tolerance;
288   PetscFunctionReturn(PETSC_SUCCESS);
289 }
290 
291 /*@
292   PetscDrawBarSetLimits - Sets the axis limits for a bar graph. If more
293   points are added after this call, the limits will be adjusted to
294   include those additional points.
295 
296   Logically Collective
297 
298   Input Parameters:
299 + bar   - The bar graph context
300 . y_min - The lower limit
301 - y_max - The upper limit
302 
303   Level: intermediate
304 
305 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetAxis()`, `PetscDrawBarSetData()`, `PetscDrawBarDraw()`
306 @*/
307 PetscErrorCode PetscDrawBarSetLimits(PetscDrawBar bar, PetscReal y_min, PetscReal y_max)
308 {
309   PetscFunctionBegin;
310   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
311   bar->ymin = y_min;
312   bar->ymax = y_max;
313   PetscFunctionReturn(PETSC_SUCCESS);
314 }
315 
316 /*@C
317   PetscDrawBarGetAxis - Gets the axis context associated with a bar graph.
318   This is useful if one wants to change some axis property, such as
319   labels, color, etc. The axis context should not be destroyed by the
320   application code.
321 
322   Not Collective, axis is parallel if bar is parallel
323 
324   Input Parameter:
325 . bar - The bar graph context
326 
327   Output Parameter:
328 . axis - The axis context
329 
330   Level: intermediate
331 
332 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawAxis`, `PetscDrawAxisCreate()`
333 @*/
334 PetscErrorCode PetscDrawBarGetAxis(PetscDrawBar bar, PetscDrawAxis *axis)
335 {
336   PetscFunctionBegin;
337   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
338   PetscAssertPointer(axis, 2);
339   *axis = bar->axis;
340   PetscFunctionReturn(PETSC_SUCCESS);
341 }
342 
343 /*@C
344   PetscDrawBarGetDraw - Gets the draw context associated with a bar graph.
345 
346   Not Collective, draw is parallel if bar is parallel
347 
348   Input Parameter:
349 . bar - The bar graph context
350 
351   Output Parameter:
352 . draw - The draw context
353 
354   Level: intermediate
355 
356 .seealso: `PetscDrawBar`, `PetscDraw`, `PetscDrawBarCreate()`, `PetscDrawBarDraw()`
357 @*/
358 PetscErrorCode PetscDrawBarGetDraw(PetscDrawBar bar, PetscDraw *draw)
359 {
360   PetscFunctionBegin;
361   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
362   PetscAssertPointer(draw, 2);
363   *draw = bar->win;
364   PetscFunctionReturn(PETSC_SUCCESS);
365 }
366 
367 /*@
368   PetscDrawBarSetFromOptions - Sets options related to the display of the `PetscDrawBar`
369 
370   Collective over bar
371 
372   Input Parameter:
373 . bar - the bar graph context
374 
375   Options Database Key:
376 . -bar_sort - sort the entries before drawing the bar graph
377 
378   Level: intermediate
379 
380   Note:
381   Does not set options related to the underlying `PetscDraw` or `PetscDrawAxis`
382 
383 .seealso: `PetscDrawBar`, `PetscDrawBarDestroy()`, `PetscDrawBarCreate()`, `PetscDrawBarSort()`
384 @*/
385 PetscErrorCode PetscDrawBarSetFromOptions(PetscDrawBar bar)
386 {
387   PetscBool set;
388 
389   PetscFunctionBegin;
390   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
391 
392   PetscCall(PetscOptionsHasName(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &set));
393   if (set) {
394     PetscReal tol = bar->sorttolerance;
395     PetscCall(PetscOptionsGetReal(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &tol, NULL));
396     PetscCall(PetscDrawBarSort(bar, PETSC_TRUE, tol));
397   }
398   PetscFunctionReturn(PETSC_SUCCESS);
399 }
400