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