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:        ProcessHandleApplicInterface
10 //- Description:  Derived class for the case when analysis code simulators use
11 //-               fork\exec\wait to provide the function evaluations
12 //- Owner:        Mike Eldred
13 //- Version: $Id: ProcessHandleApplicInterface.hpp 6492 2009-12-19 00:04:28Z briadam $
14 
15 #ifndef PROCESS_HANDLE_APPLIC_INTERFACE_H
16 #define PROCESS_HANDLE_APPLIC_INTERFACE_H
17 
18 #include "ProcessApplicInterface.hpp"
19 #include <boost/shared_array.hpp>
20 
21 namespace Dakota {
22 
23 /// Derived application interface class that spawns a simulation code
24 /// using a separate process, receives a process identifier, and
25 /// communicates with the spawned process through files.
26 
27 /** ProcessHandleApplicInterface is subclassed for fork/execvp/waitpid
28     (Unix) and spawnvp (Windows). */
29 
30 
31 class ProcessHandleApplicInterface: public ProcessApplicInterface
32 {
33 public:
34 
35   //
36   //- Heading: Constructors and destructor
37   //
38 
39   /// constructor
40   ProcessHandleApplicInterface(const ProblemDescDB& problem_db);
41   /// destructor
42   ~ProcessHandleApplicInterface();
43 
44 protected:
45 
46   //
47   //- Heading: Virtual function redefinitions
48   //
49 
50   int synchronous_local_analysis(int analysis_id);
51 
52   void init_communicators_checks(int max_eval_concurrency);
53   void set_communicators_checks(int max_eval_concurrency);
54 
55   void map_bookkeeping(pid_t pid, int fn_eval_id);
56 
57   pid_t create_evaluation_process(bool block_flag);
58 
59   //
60   //- Heading: New virtual functions
61   //
62 
63   /// spawn a child process for an analysis component within an evaluation
64   virtual pid_t create_analysis_process(bool block_flag, bool new_group) = 0;
65 
66   /// wait for asynchronous analyses on the local processor, completing
67   /// at least one job
68   virtual size_t wait_local_analyses() = 0;
69 
70   /// test for asynchronous analysis completions on the local processor
71   /// and return results for any completions by sending messages
72   virtual size_t test_local_analyses_send(int analysis_id) = 0;
73 
74   /// create (if new_group) and join the process group for asynch evaluations
75   virtual void join_evaluation_process_group(bool new_group);
76   /// create (if new_group) and join the process group for asynch analyses
77   virtual void join_analysis_process_group(bool new_group);
78 
79   /// set evalProcGroupId
80   virtual void evaluation_process_group_id(pid_t pgid);
81   /// return evalProcGroupId
82   virtual pid_t evaluation_process_group_id() const;
83   /// set analysisProcGroupId
84   virtual void analysis_process_group_id(pid_t pgid);
85   /// return analysisProcGroupId
86   virtual pid_t analysis_process_group_id() const;
87 
88   //
89   //- Heading: Methods
90   //
91 
92   /// Common processing code used by {wait,test}_local_evaluations
93   void process_local_evaluation(PRPQueue& prp_queue, const pid_t pid);
94 
95   //void clear_bookkeeping(); // virtual fn redefinition: clear processIdMap
96 
97   /// check the exit status of a forked process and abort if an error code
98   /// was returned
99   void check_wait(pid_t pid, int status);
100 
101   /// execute analyses asynchronously on the local processor
102   void asynchronous_local_analyses(int start, int end, int step);
103 
104   /// serve the analysis scheduler and execute analysis jobs asynchronously
105   void serve_analyses_asynch();
106 
107   /// set argList for execution of the input filter
108   void ifilter_argument_list();
109   /// set argList for execution of the output filter
110   void ofilter_argument_list();
111   /// set argList for execution of the specified analysis driver
112   void driver_argument_list(int analysis_id);
113 
114   /// parse argList into argument array av suitable for passing to
115   /// execvp, appending parameters and results filenames if requested
116   /// by commandLineArgs
117   void create_command_arguments(boost::shared_array<const char*>& av,
118 				StringArray& driver_and_args);
119 
120 
121   //
122   //- Heading: Data
123   //
124 
125   /// map of fork process id's to function evaluation id's for asynchronous
126   /// evaluations
127   std::map<pid_t, int> evalProcessIdMap;
128   /// map of fork process id's to analysis job id's for asynchronous analyses
129   std::map<pid_t, int> analysisProcessIdMap;
130 
131   /// an array of strings for use with execvp(const char *, char * const *).
132   /// These are converted to an array of const char*'s in fork_program().
133   std::vector<std::string> argList;
134 
135 private:
136 
137   //
138   //- Heading: Data
139   //
140 
141   // Used for substitution of parameter input files and result output files
142   // for analysis drivers.
143 };
144 
145 
146 /** argList sized 3 for [driver name, input file, output file] */
147 inline ProcessHandleApplicInterface::
ProcessHandleApplicInterface(const ProblemDescDB & problem_db)148 ProcessHandleApplicInterface(const ProblemDescDB& problem_db):
149   ProcessApplicInterface(problem_db), argList(3)
150 { }
151 
152 
~ProcessHandleApplicInterface()153 inline ProcessHandleApplicInterface::~ProcessHandleApplicInterface()
154 { }
155 
156 
157 /** This code provides the derived function used by ApplicationInterface::
158     serve_analyses_synch() as well as a convenience function for
159     ProcessHandleApplicInterface::synchronous_local_analyses() below. */
160 inline int ProcessHandleApplicInterface::
synchronous_local_analysis(int analysis_id)161 synchronous_local_analysis(int analysis_id)
162 {
163 #ifdef MPI_DEBUG
164   Cout << "Blocking fork to analysis " << analysis_id << std::endl; // flush buf
165 #endif // MPI_DEBUG
166   driver_argument_list(analysis_id);
167   create_analysis_process(BLOCK, false);
168   return 0; // used for failure codes in DirectFn case
169 }
170 
171 
172 /** No derived interface plug-ins, so perform construct-time checks.
173     However, process init issues as warnings since some contexts (e.g.,
174     HierarchSurrModel) initialize more configurations than will be used. */
175 inline void ProcessHandleApplicInterface::
init_communicators_checks(int max_eval_concurrency)176 init_communicators_checks(int max_eval_concurrency)
177 {
178   bool warn = true;
179   check_multiprocessor_analysis(warn);
180   check_multiprocessor_asynchronous(warn, max_eval_concurrency);
181 }
182 
183 
184 /** Process run-time issues as hard errors. */
185 inline void ProcessHandleApplicInterface::
set_communicators_checks(int max_eval_concurrency)186 set_communicators_checks(int max_eval_concurrency)
187 {
188   bool warn = false, mp1 = check_multiprocessor_analysis(warn),
189     mp2 = check_multiprocessor_asynchronous(warn, max_eval_concurrency);
190   if (mp1 || mp2)
191     abort_handler(-1);
192 }
193 
194 
195 //inline void ProcessHandleApplicInterface::clear_bookkeeping()
196 //{ evalProcessIdMap.clear(); }
197 
198 
ifilter_argument_list()199 inline void ProcessHandleApplicInterface::ifilter_argument_list()
200 {
201   argList[0] = iFilterName;
202   argList[1] = paramsFileName;
203   argList[2] = resultsFileName;
204 }
205 
206 
ofilter_argument_list()207 inline void ProcessHandleApplicInterface::ofilter_argument_list()
208 {
209   argList[0] = oFilterName;
210   argList[1] = paramsFileName;
211   argList[2] = resultsFileName;
212 }
213 
214 
driver_argument_list(int analysis_id)215 inline void ProcessHandleApplicInterface::driver_argument_list(int analysis_id)
216 {
217   std::string tag_str = "." + std::to_string(analysis_id);
218   argList[0] = programNames[analysis_id-1];
219   argList[1] = (multipleParamsFiles) ? paramsFileName+tag_str : paramsFileName;
220   argList[2] = (programNames.size() > 1) ? resultsFileName+tag_str :
221     resultsFileName;
222 
223 #ifdef DEBUG
224   Cout << "argList: " << argList[0] << ' ' << argList[1] << ' ' << argList[2]
225        << std::endl;
226 #endif
227 }
228 
229 } // namespace Dakota
230 
231 #endif
232