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