xref: /petsc/src/sys/classes/draw/utils/bars.c (revision 750b007cd8d816cecd9de99077bb0a703b4cf61a)
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 Parameters:
17 .  draw  - The window where the graph will be made
18 
19    Output Parameters:
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           `PetscDrawBar`, `PetscDrawBarDraw()`, `PetscDrawBarSave()`, `PetscDrawBarSetColor()`, `PetscDrawBarSort()`, `PetscDrawBarSetLimits()`, `PetscDrawBarGetAxis()`, `PetscDrawAxis`,
34           `PetscDrawBarGetDraw()`, `PetscDrawBarSetFromOptions()`
35 @*/
36 PetscErrorCode PetscDrawBarCreate(PetscDraw draw, PetscDrawBar *bar) {
37   PetscDrawBar h;
38 
39   PetscFunctionBegin;
40   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
41   PetscValidPointer(bar, 2);
42 
43   PetscCall(PetscHeaderCreate(h, PETSC_DRAWBAR_CLASSID, "DrawBar", "Bar Graph", "Draw", PetscObjectComm((PetscObject)draw), PetscDrawBarDestroy, NULL));
44 
45   PetscCall(PetscObjectReference((PetscObject)draw));
46   h->win = draw;
47 
48   h->view    = NULL;
49   h->destroy = NULL;
50   h->color   = PETSC_DRAW_GREEN;
51   h->ymin    = 0.; /* if user has not set these then they are determined from the data */
52   h->ymax    = 0.;
53   h->numBins = 0;
54 
55   PetscCall(PetscDrawAxisCreate(draw, &h->axis));
56   h->axis->xticks = NULL;
57 
58   *bar = h;
59   PetscFunctionReturn(0);
60 }
61 
62 /*@C
63    PetscDrawBarSetData
64 
65    Logically Collective on bar
66 
67    Input Parameters:
68 +  bar - The bar graph context.
69 .  bins  - number of items
70 .  values - values of each item
71 -  labels - optional label for each bar, NULL terminated array of strings
72 
73    Level: intermediate
74 
75    Notes:
76     Call `PetscDrawBarDraw()` after this call to display the new plot
77 
78    The data is ignored on all ranks except zero
79 
80 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBar`, `PetscDrawBarDraw()`
81 @*/
82 PetscErrorCode PetscDrawBarSetData(PetscDrawBar bar, PetscInt bins, const PetscReal data[], const char *const *labels) {
83   PetscFunctionBegin;
84   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
85 
86   if (bar->numBins != bins) {
87     PetscCall(PetscFree(bar->values));
88     PetscCall(PetscMalloc1(bins, &bar->values));
89     bar->numBins = bins;
90   }
91   PetscCall(PetscArraycpy(bar->values, data, bins));
92   bar->numBins = bins;
93   if (labels) PetscCall(PetscStrArrayallocpy(labels, &bar->labels));
94   PetscFunctionReturn(0);
95 }
96 
97 /*@C
98   PetscDrawBarDestroy - Frees all space taken up by bar graph data structure.
99 
100   Collective over bar
101 
102   Input Parameter:
103 . bar - The bar graph context
104 
105   Level: intermediate
106 
107 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`
108 @*/
109 PetscErrorCode PetscDrawBarDestroy(PetscDrawBar *bar) {
110   PetscFunctionBegin;
111   if (!*bar) PetscFunctionReturn(0);
112   PetscValidHeaderSpecific(*bar, PETSC_DRAWBAR_CLASSID, 1);
113   if (--((PetscObject)(*bar))->refct > 0) PetscFunctionReturn(0);
114 
115   PetscCall(PetscFree((*bar)->values));
116   PetscCall(PetscStrArrayDestroy(&(*bar)->labels));
117   PetscCall(PetscDrawAxisDestroy(&(*bar)->axis));
118   PetscCall(PetscDrawDestroy(&(*bar)->win));
119   PetscCall(PetscHeaderDestroy(bar));
120   PetscFunctionReturn(0);
121 }
122 
123 /*@
124   PetscDrawBarDraw - Redraws a bar graph.
125 
126   Collective on bar
127 
128   Input Parameter:
129 . bar - The bar graph context
130 
131   Level: intermediate
132 
133 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarSetData()`
134 @*/
135 PetscErrorCode PetscDrawBarDraw(PetscDrawBar bar) {
136   PetscDraw   draw;
137   PetscBool   isnull;
138   PetscReal   xmin, xmax, ymin, ymax, *values, binLeft, binRight;
139   PetscInt    numValues, i, bcolor, color, idx, *perm, nplot;
140   PetscMPIInt rank;
141   char      **labels;
142 
143   PetscFunctionBegin;
144   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
145   PetscCall(PetscDrawIsNull(bar->win, &isnull));
146   if (isnull) PetscFunctionReturn(0);
147   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)bar), &rank));
148 
149   if (bar->numBins < 1) PetscFunctionReturn(0);
150 
151   color = bar->color;
152   if (color == PETSC_DRAW_ROTATE) bcolor = PETSC_DRAW_BLACK + 1;
153   else bcolor = color;
154 
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   = nplot;
172   labels = bar->labels;
173 
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   }
187 
188   draw = bar->win;
189   PetscCall(PetscDrawCheckResizedWindow(draw));
190   PetscCall(PetscDrawClear(draw));
191 
192   PetscCall(PetscDrawAxisSetLimits(bar->axis, xmin, xmax, ymin, ymax));
193   PetscCall(PetscDrawAxisDraw(bar->axis));
194 
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 + i;
200       binRight = xmin + 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;
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));
216 
217   PetscCall(PetscDrawFlush(draw));
218   PetscCall(PetscDrawPause(draw));
219   PetscFunctionReturn(0);
220 }
221 
222 /*@
223   PetscDrawBarSave - Saves a drawn bar graph
224 
225   Collective on bar
226 
227   Input Parameters:
228 . bar - The bar graph context
229 
230   Level: intermediate
231 
232 .seealso: `PetscDrawSave()`, `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetDraw()`, `PetscDrawSetSave()`, `PetscDrawSave()`, `PetscDrawBarSetData()`
233 @*/
234 PetscErrorCode PetscDrawBarSave(PetscDrawBar bar) {
235   PetscFunctionBegin;
236   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
237   PetscCall(PetscDrawSave(bar->win));
238   PetscFunctionReturn(0);
239 }
240 
241 /*@
242   PetscDrawBarSetColor - Sets the color the bars will be drawn with.
243 
244   Logically Collective on bar
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   PetscFunctionBegin;
257   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
258   bar->color = color;
259   PetscFunctionReturn(0);
260 }
261 
262 /*@
263   PetscDrawBarSort - Sorts the values before drawing the bar chart, the bars will be in ascending order from left to right
264 
265   Logically Collective on bar
266 
267   Input Parameters:
268 + bar - The bar graph context
269 . sort - `PETSC_TRUE` to sort the values
270 - tolerance - discard values less than tolerance
271 
272   Level: intermediate
273 
274 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBar`, `PetscDrawBarSetData()`, `PetscDrawBarSetColor()`, `PetscDrawBarDraw()`, `PetscDrawBarGetAxis()`
275 @*/
276 PetscErrorCode PetscDrawBarSort(PetscDrawBar bar, PetscBool sort, PetscReal tolerance) {
277   PetscFunctionBegin;
278   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
279   bar->sort          = sort;
280   bar->sorttolerance = tolerance;
281   PetscFunctionReturn(0);
282 }
283 
284 /*@
285   PetscDrawBarSetLimits - Sets the axis limits for a bar graph. If more
286   points are added after this call, the limits will be adjusted to
287   include those additional points.
288 
289   Logically Collective on bar
290 
291   Input Parameters:
292 + bar - The bar graph context
293 - y_min,y_max - The limits
294 
295   Level: intermediate
296 
297 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBar`, `PetscDrawBarGetAxis()`, `PetscDrawBarSetData()`, `PetscDrawBarDraw()`
298 @*/
299 PetscErrorCode PetscDrawBarSetLimits(PetscDrawBar bar, PetscReal y_min, PetscReal y_max) {
300   PetscFunctionBegin;
301   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
302   bar->ymin = y_min;
303   bar->ymax = y_max;
304   PetscFunctionReturn(0);
305 }
306 
307 /*@C
308   PetscDrawBarGetAxis - Gets the axis context associated with a bar graph.
309   This is useful if one wants to change some axis property, such as
310   labels, color, etc. The axis context should not be destroyed by the
311   application code.
312 
313   Not Collective, axis is parallel if bar is parallel
314 
315   Input Parameter:
316 . bar - The bar graph context
317 
318   Output Parameter:
319 . axis - The axis context
320 
321   Level: intermediate
322 
323 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBar`, `PetscDrawAxis`, `PetscDrawAxisCreate()`
324 @*/
325 PetscErrorCode PetscDrawBarGetAxis(PetscDrawBar bar, PetscDrawAxis *axis) {
326   PetscFunctionBegin;
327   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
328   PetscValidPointer(axis, 2);
329   *axis = bar->axis;
330   PetscFunctionReturn(0);
331 }
332 
333 /*@C
334   PetscDrawBarGetDraw - Gets the draw context associated with a bar graph.
335 
336   Not Collective, draw is parallel if bar is parallel
337 
338   Input Parameter:
339 . bar - The bar graph context
340 
341   Output Parameter:
342 . draw  - The draw context
343 
344   Level: intermediate
345 
346 .seealso: `PetscDrawBar`, `PetscDraw`, `PetscDrawBarCreate()`, `PetscDrawBar`, `PetscDrawBarDraw()`, `PetscDraw`
347 @*/
348 PetscErrorCode PetscDrawBarGetDraw(PetscDrawBar bar, PetscDraw *draw) {
349   PetscFunctionBegin;
350   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
351   PetscValidPointer(draw, 2);
352   *draw = bar->win;
353   PetscFunctionReturn(0);
354 }
355 
356 /*@
357     PetscDrawBarSetFromOptions - Sets options related to the display of the `PetscDrawBar`
358 
359     Collective over bar
360 
361     Options Database Key:
362 .  -bar_sort - sort the entries before drawing the bar graph
363 
364     Level: intermediate
365 
366     Note:
367     Does not set options related to the underlying `PetscDraw` or `PetscDrawAxis`
368 
369 .seealso: `PetscDrawBar`, `PetscDrawBarDestroy()`, `PetscDrawBarCreate()`, `PetscDrawBarSort()`
370 @*/
371 PetscErrorCode PetscDrawBarSetFromOptions(PetscDrawBar bar) {
372   PetscBool set;
373 
374   PetscFunctionBegin;
375   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
376 
377   PetscCall(PetscOptionsHasName(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &set));
378   if (set) {
379     PetscReal tol = bar->sorttolerance;
380     PetscCall(PetscOptionsGetReal(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &tol, NULL));
381     PetscCall(PetscDrawBarSort(bar, PETSC_TRUE, tol));
382   }
383   PetscFunctionReturn(0);
384 }
385