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 //- Description: An API for launching DAKOTA from a DLL.
10 //- Owner: Bill Hart
11 //- Checked by:
12 //- Version: $Id$
13
14 /** \file dakota_dll_api.cpp
15 \brief This file contains a DakotaRunner class, which launches DAKOTA. */
16
17 #include "dakota_windows.h"
18 #include "dakota_system_defs.hpp"
19 #include "ProgramOptions.hpp"
20 #include "LibraryEnvironment.hpp"
21 #include "ProblemDescDB.hpp"
22 #include "PRPMultiIndex.hpp"
23 #include "DakotaModel.hpp"
24 #include "DakotaInterface.hpp"
25 #include "PluginSerialDirectApplicInterface.hpp"
26 #include "dakota_global_defs.hpp"
27 #include "dakota_dll_api.h"
28 #include <string>
29
30 #if defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
31 #define BUILDING_DAKOTA_DLL
32 #endif
33
34
35 namespace Dakota {
36 extern PRPCache data_pairs;
37 }
38
39 using namespace Dakota;
40
41 namespace {
42
43 /// initialize signal handlers (not using Dakota's helper function
44 /// since DLL may need different behavior.)
signal_init()45 void signal_init()
46 {
47 #if defined(__MINGW32__) || defined(_MSC_VER)
48 std::signal(SIGBREAK, abort_handler);
49 #else
50 std::signal(SIGKILL, abort_handler);
51 #endif
52 std::signal(SIGTERM, abort_handler);
53 std::signal(SIGINT, abort_handler);
54 }
55
56 /// Class to manage an instance of Dakota's library interface for
57 /// presentation to the Dakota DLL interface.
58 class DakotaRunner
59 {
60 public:
61
62 /// Construct a runner object, setting output/error file names with logname
DakotaRunner(std::string logname)63 DakotaRunner(std::string logname)
64 : dakotaEnv(NULL), numVars(0), varNames(NULL), numResp(0), respNames(NULL)
65 {
66 signal_init();
67
68 // Add -output and -error arguments to the command line with
69 // filename.log and filename.err, respectively.
70 progOpts.output_file(logname + ".log");
71 progOpts.error_file(logname + ".err");
72 }
73
74 /// Destroy the runner object, freeing any allocated memory
~DakotaRunner()75 ~DakotaRunner()
76 {
77 if (respNames) {
78 for (size_t i=0; i<numResp; i++) {
79 // memory allocated with strdup requires free, not delete
80 std::free(respNames[i]);
81 }
82 delete[] respNames;
83 }
84
85 if (varNames) {
86 for (size_t i=0; i<numVars; i++) {
87 // memory allocated with strdup requires free, not delete
88 std::free(varNames[i]);
89 }
90 delete[] varNames;
91 }
92
93 if (dakotaEnv) {
94 delete dakotaEnv;
95 dakotaEnv = NULL;
96 }
97 }
98
99 /// Set the input file and parse it, creating a Dakota
100 /// LibraryEnvironment instance
read_input(const char * dakota_input)101 void read_input(const char* dakota_input)
102 {
103 progOpts.input_file(dakota_input);
104
105 /// this shouldn't happen, but was a safeguard in historical code
106 if (dakotaEnv)
107 delete dakotaEnv;
108 dakotaEnv = new LibraryEnvironment(progOpts);
109
110 if (!dakotaEnv)
111 throw std::logic_error("DakotaRunner: could not instantiate LibraryEnvironment");
112
113 // initialize variable and response names
114 initialize_names();
115 }
116
initialize_names()117 void initialize_names()
118 {
119
120 ProblemDescDB& problem_db = dakotaEnv->problem_description_db();
121
122 // set the variable names
123 const VariablesList& vlist = problem_db.variables_list();
124 VariablesList::const_iterator vlist_it;
125 VariablesList::const_iterator vlist_end = vlist.end();
126
127 // calculate total number of vars by iterating over each set
128 // overly cautious check for non-empty labels (shouldn't they have
129 // defaults?)
130 numVars = 0;
131 for (vlist_it = vlist.begin(); vlist_it != vlist_end; ++vlist_it)
132 numVars += vlist_it->all_continuous_variable_labels().size() +
133 vlist_it->all_discrete_int_variable_labels().size() +
134 vlist_it->all_discrete_real_variable_labels().size();
135 // if appropriate, populate name array
136 if (numVars > 0) {
137 varNames = new char* [numVars];
138 size_t j, idx = 0;
139 for (vlist_it = vlist.begin(); vlist_it != vlist_end; ++vlist_it) {
140 StringMultiArrayConstView acv_labels
141 = vlist_it->all_continuous_variable_labels();
142 StringMultiArrayConstView adiv_labels
143 = vlist_it->all_discrete_int_variable_labels();
144 StringMultiArrayConstView adrv_labels
145 = vlist_it->all_discrete_real_variable_labels();
146 for (j=0; j<acv_labels.size(); ++j, ++idx)
147 varNames[idx] = strdup(acv_labels[j].c_str());
148 for (j=0; j<adiv_labels.size(); ++j, ++idx)
149 varNames[idx] = strdup(adiv_labels[j].c_str());
150 for (j=0; j<adrv_labels.size(); ++j, ++idx)
151 varNames[idx] = strdup(adrv_labels[j].c_str());
152 }
153 }
154
155 // set the response names
156 const ResponseList& rlist = problem_db.response_list();
157 ResponseList::const_iterator rlist_it;
158 ResponseList::const_iterator rlist_end = rlist.end();
159
160 // calculate total number of responses by iterating over each set
161 numResp = 0;
162 for (rlist_it = rlist.begin(); rlist_it != rlist_end; ++rlist_it)
163 numResp += rlist_it->function_labels().size();
164 // if appropriate, populate name array
165 if (numResp > 0) {
166 respNames = new char* [numResp];
167 size_t j, idx = 0;
168 for (rlist_it = rlist.begin(); rlist_it != rlist_end; ++rlist_it) {
169 const StringArray& fn_labels = rlist_it->function_labels();
170 for (j=0; j<fn_labels.size(); ++j, ++idx)
171 respNames[idx] = strdup(fn_labels[j].c_str());
172 }
173 }
174
175 }
176
177 /// Plugin interfaces and execute strategy
178 void start();
179
180 // for tracking variable and response names
181 int numVars; ///< number of variables active in DAKOTA
182 char** varNames; ///< array of strings of variable names
183 int numResp; ///< number of responses active in DAKOTA
184 char** respNames; ///< array of strings of response names
185
186 static int id_ctr; ///< counter for next instance ID to return
187
188 private:
189
190 /// don't allow default construction due to memory management concerns
191 DakotaRunner();
192 // TOOD: disallow copy/assign as well
193
194 /// Options to control the behavior of the Dakota instance
195 ProgramOptions progOpts;
196 /// Pointer to the Dakota instance
197 LibraryEnvironment* dakotaEnv;
198
199 };
200
201 int DakotaRunner::id_ctr = 0;
202
start()203 void DakotaRunner::start()
204 {
205 // Any library mode plug-ins would go here.
206 // Refer to the library mode documentation in the Developers Manual.
207 ProblemDescDB& problem_db = dakotaEnv->problem_description_db();
208 ModelList& models = problem_db.model_list();
209 size_t model_index = problem_db.get_db_model_node(); // for restoration
210 for (ModelLIter ml_iter = models.begin(); ml_iter != models.end(); ml_iter++){
211 Interface& model_interface = ml_iter->derived_interface();
212 if ( (model_interface.interface_type() & DIRECT_INTERFACE_BIT) &&
213 contains(model_interface.analysis_drivers(), "plugin_rosenbrock") ) {
214 // set the DB nodes to that of the existing Model specification
215 problem_db.set_db_model_nodes(ml_iter->model_id());
216 // plug in the new derived Interface object
217 model_interface.assign_rep(std::make_shared<SIM::SerialDirectApplicInterface>
218 (problem_db));
219 }
220 }
221 problem_db.set_db_model_nodes(model_index); // restore
222
223 // Execute the Dakota environment assume proceeding beyond help/version/check
224 if (!dakotaEnv->check()) {
225
226 // In case we're running a sequence of DAKOTA problems, make sure
227 // the global evaluation cache is cleared in between runs.
228 // Ideally, we'd manage this with interface IDs from the caller
229 // instead of this aggressive clear.
230 data_pairs.clear();
231
232 dakotaEnv->execute();
233
234 }
235 }
236
237 /// map from DakotaRunner id to instance
238 std::map<int ,DakotaRunner*> runners;
239
240 } // end global namespace
241
dakota_create(int * dakota_ptr_int,const char * logname)242 extern "C" void DAKOTA_DLL_FN dakota_create(int* dakota_ptr_int, const char* logname)
243 {
244 // logname is the base filename for output and error to .log and .err
245 std::string str_logname = logname ? logname : "dakota_dll";
246 DakotaRunner* pDakota = new DakotaRunner(str_logname);
247 // increment the runner id and return to the caller
248 int id = DakotaRunner::id_ctr++;
249 runners[id] = pDakota;
250 *dakota_ptr_int = id;
251 }
252
dakota_readInput(int id,const char * dakotaInput)253 extern "C" int DAKOTA_DLL_FN dakota_readInput(int id, const char* dakotaInput)
254 {
255 try {
256 runners[id]->read_input(dakotaInput);
257 }
258 catch (std::logic_error le) {
259 Cout << "Dakota::dll_api readInput caught " << le.what() << std::endl;
260 return(-2);
261 }
262 return(0);
263 }
264
265 extern "C" void DAKOTA_DLL_FN
dakota_get_variable_info(int id,char *** pVarNames,int * pNumVarNames,char *** pRespNames,int * pNumRespNames)266 dakota_get_variable_info(int id,
267 char*** pVarNames, int* pNumVarNames,
268 char*** pRespNames, int* pNumRespNames)
269 {
270 *pNumVarNames = runners[id]->numVars;
271 *pVarNames = runners[id]->varNames;
272 *pNumRespNames = runners[id]->numResp;
273 *pRespNames = runners[id]->respNames;
274 }
275
276
dakota_start(int id)277 extern "C" int DAKOTA_DLL_FN dakota_start(int id)
278 {
279 try {
280 runners[id]->start();
281 }
282 catch (std::logic_error le) {
283 Cout << "Dakota::dll_api start caught " << le.what() << std::endl;
284 return(-1);
285 }
286 return(0);
287 }
288
dakota_destroy(int id)289 extern "C" void DAKOTA_DLL_FN dakota_destroy (int id)
290 {
291 delete runners[id];
292 runners.erase(id);
293 }
294
dakota_stop(int * id)295 extern "C" void DAKOTA_DLL_FN dakota_stop(int* id)
296 {
297 /** TODO: trick application to quit through the syscall interface or
298 throw exception. **/
299 }
300
dakota_getStatus(int id)301 extern "C" const char* DAKOTA_DLL_FN dakota_getStatus(int id)
302 {
303 static std::string tmp;
304 tmp = "<DakotaOutput>None</DakotaOutput>";
305 return tmp.c_str();
306 }
307
get_mc_ptr_int()308 extern "C" int get_mc_ptr_int()
309 {
310 #ifdef DAKOTA_MODELCENTER
311 return Dakota::mc_ptr_int;
312 #else
313 return 0;
314 #endif
315 }
316
set_mc_ptr_int(int ptr_int)317 extern "C" void set_mc_ptr_int(int ptr_int)
318 {
319 #ifdef DAKOTA_MODELCENTER
320 Dakota::mc_ptr_int = ptr_int;
321 #endif
322 }
323
get_dc_ptr_int()324 extern "C" int get_dc_ptr_int()
325 {
326 #ifdef DAKOTA_MODELCENTER
327 return Dakota::dc_ptr_int;
328 #else
329 return 0;
330 #endif
331 }
332
set_dc_ptr_int(int ptr_int)333 extern "C" void set_dc_ptr_int(int ptr_int)
334 {
335 #ifdef DAKOTA_MODELCENTER
336 Dakota::dc_ptr_int = ptr_int;
337 #endif
338 }
339