1 /* _______________________________________________________________________
2
3 DAKOTA: Design Analysis Kit for Optimization and Terascale Applications
4 Copyright 2014-2020 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
5 This software is distributed under the GNU Lesser General Public License.
6 For more information, see the README file in the top Dakota directory.
7 _______________________________________________________________________ */
8
9 //- Class: Graphics
10 //- Description: Implementation code for the Graphics class
11 //- Owner: Brian Dennis, Sandia National Laboratories
12
13 #include "DakotaGraphics.hpp"
14
15 #ifdef HAVE_X_GRAPHICS
16 #include "Graphics.hpp"
17 #endif // HAVE_X_GRAPHICS
18 #include "DakotaVariables.hpp"
19 #include "DakotaResponse.hpp"
20
21
22 namespace Dakota {
23
Graphics()24 Graphics::Graphics():
25 #ifdef HAVE_X_GRAPHICS
26 graphics2D(NULL),
27 #endif
28 win2dOn(false) //, win3dOn(false)
29 { }
30
31
32 // Since the Graphics instance is global, it's destruction occurs after
33 // main(). Similar to CommonIO within UTILIB, use of system resources in this
34 // destructor can lead to problems (e.g., hanging of the graphics thread).
35 // Therefore, Graphics::close() is now called explicitly (from the
36 // ParallelLibrary destructor which is invoked at the bottom of main()).
~Graphics()37 Graphics::~Graphics()
38 { }
39
40
41 /** Sets up a single event loop for duration of the dakotaGraphics object,
42 continuously adding data to a single window. There is no reset.
43 To start over with a new data set, you need a new object (delete old and
44 instantiate new). */
create_plots_2d(const Variables & vars,const Response & response)45 void Graphics::create_plots_2d(const Variables& vars, const Response& response)
46 {
47 #ifdef HAVE_X_GRAPHICS
48 StringMultiArrayConstView cv_labels = vars.continuous_variable_labels();
49 StringMultiArrayConstView div_labels = vars.discrete_int_variable_labels();
50 StringMultiArrayConstView drv_labels = vars.discrete_real_variable_labels();
51 const StringArray& fn_labels = response.function_labels();
52 int i, num_cv = vars.cv(), num_div = vars.div(), num_drv = vars.drv(),
53 num_fns = response.num_functions();
54
55 if (win2dOn) { // graphics already active
56 // TO DO: sanity checking for change in number of plot windows
57 }
58 else { // protect multple calls to create_plots_2d
59 graphics2D = new Graphics2D; // instantiate the graphics object
60 graphics2D->create_plots2d(num_fns+num_cv+num_div+num_drv);// set # of plots
61 }
62
63 // set title and plot line RGB color codes (see /usr/lib/X11/rgb.txt)
64 // for all fns and all vars.
65 int num_obj_fns = 0, num_nln_ineq_con = 0, num_nln_eq_con = 0,
66 num_lsq_terms = 0, num_resp_fns = 0;
67 char title[25];
68 // Since the user can specify arbitrary response labels, regular
69 // expression matching is preferable to using label.contains().
70 boost::regex obj_re("obj_fn_[1-9][0-9]*"),lsq_re("least_sq_term_[1-9][0-9]*"),
71 resp_re( "response_fn_[1-9][0-9]*"), ineq_re( "nln_ineq_con_[1-9][0-9]*"),
72 eq_re( "nln_eq_con_[1-9][0-9]*");
73 for (i=0; i<num_fns; ++i) {
74 const std::string& label = fn_labels[i];
75 if (label == "obj_fn") {
76 std::strcpy(title, "Objective Fn");
77 graphics2D->change_line_color2d(i, 0, 0, 192); // blue
78 }
79 else if (label == re_match(label, obj_re)) {
80 std::sprintf(title, "Objective %d", ++num_obj_fns);
81 graphics2D->change_line_color2d(i, 0, 0, 192); // blue
82 }
83 else if (label == re_match(label, lsq_re)) {
84 std::sprintf(title, "Least Square Term %d", ++num_lsq_terms);
85 graphics2D->change_line_color2d(i, 0, 0, 192); // blue
86 }
87 else if (label == re_match(label, resp_re)) {
88 std::sprintf(title, "Response Function %d", ++num_resp_fns);
89 graphics2D->change_line_color2d(i, 0, 0, 192); // blue
90 }
91 else if (label == re_match(label, ineq_re)) {
92 std::sprintf(title, "Ineq Constraint %d", ++num_nln_ineq_con);
93 graphics2D->change_line_color2d(i, 0, 192, 0); // green
94 }
95 else if (label == re_match(label, eq_re)) {
96 std::sprintf(title, "Eq Constraint %d", ++num_nln_eq_con);
97 graphics2D->change_line_color2d(i, 0, 192, 0); // green
98 }
99 else { // user-defined response descriptor
100 std::strcpy(title, fn_labels[i].c_str());
101 graphics2D->change_line_color2d(i, 0, 0, 192); // blue
102 }
103 graphics2D->set_y_label2d(i, label.data());
104 graphics2D->set_title2d(i, title);
105 }
106 int num_cdv = 0, num_cuv = 0, num_csv = 0, num_ddriv = 0, num_ddsiv = 0,
107 num_ddsrv = 0, num_duiv = 0, num_durv = 0, num_dsriv = 0, num_dssiv = 0,
108 num_dssrv = 0;
109 // Since the user can specify arbitrary variable labels, regular
110 // expression matching is preferable to using label.contains().
111 boost::regex cdv_re( "cdv_[1-9][0-9]*"), ddriv_re("ddriv_[1-9][0-9]*"),
112 ddsiv_re("ddsiv_[1-9][0-9]*"), ddsrv_re("ddsrv_[1-9][0-9]*"),
113 uv_re( "uv_[1-9][0-9]*"), csv_re( "csv_[1-9][0-9]*"),
114 dsriv_re("dsriv_[1-9][0-9]*"), dssiv_re("dssiv_[1-9][0-9]*"),
115 dssrv_re("dssrv_[1-9][0-9]*"); // TO DO
116 for(i=0; i<num_cv; ++i) {
117 const std::string& label = cv_labels[i];
118 if (label == re_match(label, cdv_re))
119 std::sprintf(title, "Cont Des Variable %d", ++num_cdv);
120 else if (label == re_match(label, uv_re))
121 std::sprintf(title, "Cont Unc Variable %d", ++num_cuv);
122 else if (label == re_match(label, csv_re))
123 std::sprintf(title, "Cont State Variable %d", ++num_csv);
124 else
125 std::strcpy(title, label.c_str());
126 graphics2D->set_title2d(i+num_fns, title);
127 graphics2D->set_y_label2d(i+num_fns, label.data());
128 graphics2D->change_line_color2d(i+num_fns, 192, 0, 0); // red
129 }
130 for(i=0; i<num_div; ++i) {
131 const std::string& label = div_labels[i];
132 if (label == re_match(label, ddriv_re))
133 std::sprintf(title, "Disc Range Des Var %d", ++num_ddriv);
134 else if (label == re_match(label, ddsiv_re))
135 std::sprintf(title, "Disc Set Int Des Var %d", ++num_ddsiv);
136 else if (label == re_match(label, uv_re))
137 std::sprintf(title, "Disc Int Unc Var %d", ++num_duiv);
138 else if (label == re_match(label, dsriv_re))
139 std::sprintf(title, "Disc Range State Var %d", ++num_dsriv);
140 else if (label == re_match(label, dssiv_re))
141 std::sprintf(title, "Disc Set Int State Var %d", ++num_dssiv);
142 else
143 std::strcpy(title, label.c_str());
144 graphics2D->set_title2d(i+num_fns+num_cv, title);
145 graphics2D->set_y_label2d(i+num_fns+num_cv, label.data());
146 graphics2D->change_line_color2d(i+num_fns+num_cv, 192, 0, 0); // red
147 }
148 for(i=0; i<num_drv; ++i) {
149 const std::string& label = drv_labels[i];
150 if (label == re_match(label, ddsrv_re))
151 std::sprintf(title, "Disc Set Real Des Var %d", ++num_ddsrv);
152 else if (label == re_match(label, uv_re))
153 std::sprintf(title, "Disc Real Unc Var %d", ++num_durv);
154 else if (label == re_match(label, dssrv_re))
155 std::sprintf(title, "Disc Set Real State Var %d", ++num_dssrv);
156 else
157 std::strcpy(title, label.c_str());
158 graphics2D->set_title2d(i+num_fns+num_cv+num_div, title);
159 graphics2D->set_y_label2d(i+num_fns+num_cv+num_div, label.data());
160 graphics2D->change_line_color2d(i+num_fns+num_cv+num_div, 192, 0, 0); // red
161 }
162
163 // spawn thread to create window; only call go() once per object.
164 // The go function calls pthread_create, which causes a separate thread to be
165 // created which is configured with a 2 sec callback. So every 2 sec, data
166 // from arrays published in add_datapoint2d will be added to the plots.
167 if (!win2dOn) {
168 graphics2D->go();
169 win2dOn = true;
170 }
171 #else
172 Cerr << "\nWarning: Dakota not compiled with X Windows support; consider "
173 << "removing\n \"graphics\" keyword from input file."
174 << std::endl;
175 #endif // HAVE_X_GRAPHICS
176 }
177
178
179
180 /** Adds data to each 2d plot and each tabular data column (one for
181 each active variable and for each response function).
182 graphicsCntr is used for the x axis in the graphics and the first
183 column in the tabular data. */
add_datapoint(int graphics_cntr,const Variables & vars,const Response & response)184 void Graphics::add_datapoint(int graphics_cntr,
185 const Variables& vars, const Response& response)
186 {
187 #ifdef HAVE_X_GRAPHICS
188 if (win2dOn) {
189 const RealVector& c_vars = vars.continuous_variables();
190 const IntVector& di_vars = vars.discrete_int_variables();
191 const RealVector& dr_vars = vars.discrete_real_variables();
192 const ShortArray& asv = response.active_set_request_vector();
193 const RealVector& fn_vals = response.function_values();
194 int i, num_cv = c_vars.length(), num_div = di_vars.length(),
195 num_drv = dr_vars.length(), num_fns = asv.size();
196 for (i=0; i<num_fns; ++i) // add to each function graph
197 if (asv[i] & 1) // better to skip a value than have a meaningless 0
198 graphics2D->add_datapoint2d(i, (double)graphics_cntr, fn_vals[i]);
199 for (i=0; i<num_cv; ++i) // add to each variable graph
200 graphics2D->add_datapoint2d(i+num_fns, (double)graphics_cntr, c_vars[i]);
201 for (i=0; i<num_div; ++i) // add to each variable graph
202 graphics2D->add_datapoint2d(i+num_fns+num_cv, (double)graphics_cntr,
203 (double)di_vars[i]);
204 for (i=0; i<num_drv; ++i) // add to each variable graph
205 graphics2D->add_datapoint2d(i+num_fns+num_cv+num_div,
206 (double)graphics_cntr, dr_vars[i]);
207 }
208 #endif // HAVE_X_GRAPHICS
209 }
210
211
212 /** Adds data to a single 2d plot. Allows complete flexibility in
213 defining other kinds of x-y plotting in the 2D graphics. */
add_datapoint(int i,double x,double y)214 void Graphics::add_datapoint(int i, double x, double y)
215 {
216 #ifdef HAVE_X_GRAPHICS
217 if (win2dOn)
218 graphics2D->add_datapoint2d(i, x, y);
219 #endif // HAVE_X_GRAPHICS
220
221 // Problem with this is that calls to this function may fill in the table
222 // by columns -> data may require caching or a reorganization of the table
223 //if (tabularDataFlag)
224 // tabularDataFStream << setw(8) << i << ' ' << x << ' ' << y;
225 }
226
227
228 /** Used for displaying multiple data sets within the same plot. */
new_dataset(int i)229 void Graphics::new_dataset(int i)
230 {
231 #ifdef HAVE_X_GRAPHICS
232 if (win2dOn)
233 graphics2D->new_dataset2d(i);
234 #endif // HAVE_X_GRAPHICS
235 }
236
237
238 /* 3D plotting clears data set and builds from scratch each time show_data3d
239 is called. This still involves an event loop waiting for a mouse click
240 (right button) to continue. X = 1-D x grid values only and
241 Y = 1-D Y grid values only [X and Y are _not_ (X,Y) pairs].
242 F = 2-d grid of values for a single function for all (X,Y) combinations.
243 void Graphics::
244 show_data_3d(const RealVector& X, const RealVector& Y,
245 const RealMatrix& F)
246 {
247 int num_axis_pts = X.length();
248 if (!num_axis_pts)
249 return;
250
251 #ifdef HAVE_X_GRAPHICS
252 if (!win3dOn) {
253 plsdev("xwin");
254 plinit();
255 win3dOn = true;
256 }
257
258 // create a matrix to store the values to be displayed
259 int i, j;
260 PLFLT **vis_mat = new PLFLT * [num_axis_pts];
261 for (i=0; i<num_axis_pts; ++i)
262 vis_mat[i] = new PLFLT [num_axis_pts];
263 PLFLT *x = new PLFLT [num_axis_pts];
264 PLFLT *y = new PLFLT [num_axis_pts];
265
266 // load matrix with points
267 PLFLT zhigh = -DBL_MAX, zlow = DBL_MAX; // PLFLT typedef defined in plplot.h
268 for (i=0; i<num_axis_pts; ++i) {
269 x[i] = X[i];
270 y[i] = Y[i];
271 for (j=0; j<num_axis_pts; ++j) {
272 vis_mat[i][j] = F(j,i);
273 if (vis_mat[i][j] > zhigh)
274 zhigh = vis_mat[i][j];
275 if (vis_mat[i][j] < zlow)
276 zlow = vis_mat[i][j];
277 }
278 }
279 // ensure that a suitable plot range is defined for z
280 // (important when plotting z = constant).
281 if ( std::fabs(zhigh-zlow) < 1.0e-6 ) {
282 zhigh += 1.0e-6;
283 zlow -= 1.0e-6;
284 }
285
286 // X and Y are monotonic so their min/max values are at the ends
287 PLFLT xl_ = (X[0] < X[num_axis_pts-1]) ? X[0] : X[num_axis_pts-1];
288 PLFLT xu_ = (X[0] < X[num_axis_pts-1]) ? X[num_axis_pts-1] : X[0];
289 PLFLT yl_ = (Y[0] < Y[num_axis_pts-1]) ? Y[0] : Y[num_axis_pts-1];
290 PLFLT yu_ = (Y[0] < Y[num_axis_pts-1]) ? Y[num_axis_pts-1] : Y[0];
291
292 // Now generate the 3D PLPLOT window
293 plvsta(); // set to standard device independent viewport
294 plwind(-1.0, 1.0, -.80, 1.4); // sets the world coordinates of the view port
295 plcol(1); // set the current pen color
296 // 45 deg. altitude, 45 deg azimuth
297 plw3d(1.0, 1.0, 1.0, xl_, xu_, yl_, yu_, zlow, zhigh, 45, 45);
298 plbox3("bnstu", "x axis", 0.0, 0, "bnstu", "y axis", 0.0, 0,
299 "bcdmnstuv", "z axis", 0.0, 0); // draw the axes, labels, etc.
300 plcol(1);
301 static const char *t = "#frApproximation Surface";
302 plmtex("t", -1.0, 0.5, 0.5, t); // add a title to the plot
303 //plmtex("t", -2.5, 0.5, 0.5, t); // add a title to the plot
304 plcol(2);
305 plot3d(x, y, vis_mat, num_axis_pts, num_axis_pts, 3, 0); // make the 3D plot
306 pladv(0); // advance to the next page, overwriting existing plot
307
308 // clean up
309 for (i=0; i<num_axis_pts; ++i)
310 delete [] vis_mat[i];
311 delete [] vis_mat;
312 delete [] x;
313 delete [] y;
314 #endif // HAVE_X_GRAPHICS
315 }
316 */
317
318
close()319 void Graphics::close()
320 {
321 #ifdef HAVE_X_GRAPHICS
322 //if (win3dOn)
323 // plend(); // 3D
324 if (win2dOn) {
325 // hold for user action so graphics don't close too quickly
326 Cout << "Exit graphics window to terminate DAKOTA." << std::endl;
327 graphics2D->thread_wait(); // wait for user to exit the graphics thread
328
329 delete graphics2D; // 2D
330 // reset member data since this Graphics is currently global
331 win2dOn = false;
332 }
333 #endif // HAVE_X_GRAPHICS
334 }
335
336
set_x_labels2d(const char * x_label)337 void Graphics::set_x_labels2d(const char* x_label)
338 {
339 #ifdef HAVE_X_GRAPHICS
340 if (win2dOn) {
341 int num_2d_plots = graphics2D->num_2d_plots();
342 for (int i=0; i<num_2d_plots; ++i)
343 graphics2D->set_x_label2d(i, x_label);
344 }
345 #endif // HAVE_X_GRAPHICS
346 }
347
348
set_y_labels2d(const char * y_label)349 void Graphics::set_y_labels2d(const char* y_label)
350 {
351 #ifdef HAVE_X_GRAPHICS
352 if (win2dOn) {
353 int num_2d_plots = graphics2D->num_2d_plots();
354 for (int i=0; i<num_2d_plots; ++i)
355 graphics2D->set_y_label2d(i, y_label);
356 }
357 #endif // HAVE_X_GRAPHICS
358 }
359
360
set_x_label2d(int i,const char * x_label)361 void Graphics::set_x_label2d(int i, const char* x_label)
362 {
363 #ifdef HAVE_X_GRAPHICS
364 if (win2dOn)
365 graphics2D->set_x_label2d(i, x_label);
366 #endif // HAVE_X_GRAPHICS
367 }
368
369
set_y_label2d(int i,const char * y_label)370 void Graphics::set_y_label2d(int i, const char* y_label)
371 {
372 #ifdef HAVE_X_GRAPHICS
373 if (win2dOn)
374 graphics2D->set_y_label2d(i, y_label);
375 #endif // HAVE_X_GRAPHICS
376 }
377
378 } // namespace Dakota
379