xref: /petsc/src/sys/classes/draw/utils/bars.c (revision 862e4a309d45a165aaa4da0d704ba733429d833a)
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 {
38   PetscDrawBar h;
39 
40   PetscFunctionBegin;
41   PetscValidHeaderSpecific(draw, PETSC_DRAW_CLASSID, 1);
42   PetscValidPointer(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(0);
61 }
62 
63 /*@C
64    PetscDrawBarSetData
65 
66    Logically Collective on bar
67 
68    Input Parameters:
69 +  bar - The bar graph context.
70 .  bins  - number of items
71 .  values - 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()`, `PetscDrawBar`, `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(0);
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(0);
115   PetscValidHeaderSpecific(*bar, PETSC_DRAWBAR_CLASSID, 1);
116   if (--((PetscObject)(*bar))->refct > 0) PetscFunctionReturn(0);
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(0);
124 }
125 
126 /*@
127   PetscDrawBarDraw - Redraws a bar graph.
128 
129   Collective on bar
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(0);
151   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)bar), &rank));
152 
153   if (bar->numBins < 1) PetscFunctionReturn(0);
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(0);
224 }
225 
226 /*@
227   PetscDrawBarSave - Saves a drawn bar graph
228 
229   Collective on bar
230 
231   Input Parameters:
232 . bar - The bar graph context
233 
234   Level: intermediate
235 
236 .seealso: `PetscDrawSave()`, `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBarGetDraw()`, `PetscDrawSetSave()`, `PetscDrawSave()`, `PetscDrawBarSetData()`
237 @*/
238 PetscErrorCode PetscDrawBarSave(PetscDrawBar bar)
239 {
240   PetscFunctionBegin;
241   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
242   PetscCall(PetscDrawSave(bar->win));
243   PetscFunctionReturn(0);
244 }
245 
246 /*@
247   PetscDrawBarSetColor - Sets the color the bars will be drawn with.
248 
249   Logically Collective on bar
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(0);
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 on bar
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()`, `PetscDrawBar`, `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(0);
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 on bar
297 
298   Input Parameters:
299 + bar - The bar graph context
300 - y_min,y_max - The limits
301 
302   Level: intermediate
303 
304 .seealso: `PetscDrawBar`, `PetscDrawBarCreate()`, `PetscDrawBar`, `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(0);
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()`, `PetscDrawBar`, `PetscDrawAxis`, `PetscDrawAxisCreate()`
332 @*/
333 PetscErrorCode PetscDrawBarGetAxis(PetscDrawBar bar, PetscDrawAxis *axis)
334 {
335   PetscFunctionBegin;
336   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
337   PetscValidPointer(axis, 2);
338   *axis = bar->axis;
339   PetscFunctionReturn(0);
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()`, `PetscDrawBar`, `PetscDrawBarDraw()`, `PetscDraw`
356 @*/
357 PetscErrorCode PetscDrawBarGetDraw(PetscDrawBar bar, PetscDraw *draw)
358 {
359   PetscFunctionBegin;
360   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
361   PetscValidPointer(draw, 2);
362   *draw = bar->win;
363   PetscFunctionReturn(0);
364 }
365 
366 /*@
367     PetscDrawBarSetFromOptions - Sets options related to the display of the `PetscDrawBar`
368 
369     Collective over bar
370 
371     Options Database Key:
372 .  -bar_sort - sort the entries before drawing the bar graph
373 
374     Level: intermediate
375 
376     Note:
377     Does not set options related to the underlying `PetscDraw` or `PetscDrawAxis`
378 
379 .seealso: `PetscDrawBar`, `PetscDrawBarDestroy()`, `PetscDrawBarCreate()`, `PetscDrawBarSort()`
380 @*/
381 PetscErrorCode PetscDrawBarSetFromOptions(PetscDrawBar bar)
382 {
383   PetscBool set;
384 
385   PetscFunctionBegin;
386   PetscValidHeaderSpecific(bar, PETSC_DRAWBAR_CLASSID, 1);
387 
388   PetscCall(PetscOptionsHasName(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &set));
389   if (set) {
390     PetscReal tol = bar->sorttolerance;
391     PetscCall(PetscOptionsGetReal(((PetscObject)bar)->options, ((PetscObject)bar)->prefix, "-bar_sort", &tol, NULL));
392     PetscCall(PetscDrawBarSort(bar, PETSC_TRUE, tol));
393   }
394   PetscFunctionReturn(0);
395 }
396