xref: /petsc/src/sys/classes/matlabengine/matlab.c (revision 1baa6e3354bfe224b33a0c158f482508792a8a6e)
1 
2 #include <engine.h>   /* MATLAB include file */
3 #include <petscsys.h>
4 #include <petscmatlab.h>               /*I   "petscmatlab.h"  I*/
5 #include <petsc/private/petscimpl.h>
6 
7 struct  _p_PetscMatlabEngine {
8   PETSCHEADER(int);
9   Engine *ep;
10   char   buffer[1024];
11 };
12 
13 PetscClassId MATLABENGINE_CLASSID = -1;
14 
15 /*@C
16     PetscMatlabEngineCreate - Creates a MATLAB engine object
17 
18     Not Collective
19 
20     Input Parameters:
21 +   comm - a separate MATLAB engine is started for each process in the communicator
22 -   host - name of machine where MATLAB engine is to be run (usually NULL)
23 
24     Output Parameter:
25 .   mengine - the resulting object
26 
27    Options Database:
28 +    -matlab_engine_graphics - allow the MATLAB engine to display graphics
29 .    -matlab_engine_host - hostname, machine to run the MATLAB engine on
30 -    -info - print out all requests to MATLAB and all if its responses (for debugging)
31 
32    Notes:
33    If a host string is passed in, any Matlab scripts that need to run in the
34    engine must be available via MATLABPATH on that machine.
35 
36    Level: advanced
37 
38 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
39           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
40           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
41 @*/
42 PetscErrorCode  PetscMatlabEngineCreate(MPI_Comm comm,const char host[],PetscMatlabEngine *mengine)
43 {
44   PetscMPIInt       rank,size;
45   char              buffer[256];
46   PetscMatlabEngine e;
47   PetscBool         flg = PETSC_FALSE;
48   char              lhost[64];
49   PetscFunctionBegin;
50   if (MATLABENGINE_CLASSID == -1) PetscCall(PetscClassIdRegister("MATLAB Engine",&MATLABENGINE_CLASSID));
51   PetscCall(PetscHeaderCreate(e,MATLABENGINE_CLASSID,"MatlabEngine","MATLAB Engine","Sys",comm,PetscMatlabEngineDestroy,NULL));
52 
53   if (!host) {
54     PetscCall(PetscOptionsGetString(NULL,NULL,"-matlab_engine_host",lhost,sizeof(lhost),&flg));
55     if (flg) host = lhost;
56   }
57   flg = PETSC_FALSE;
58   PetscCall(PetscOptionsGetBool(NULL,NULL,"-matlab_engine_graphics",&flg,NULL));
59 
60   if (host) {
61     PetscCall(PetscInfo(0,"Starting MATLAB engine on %s\n",host));
62     PetscCall(PetscStrcpy(buffer,"ssh "));
63     PetscCall(PetscStrcat(buffer,host));
64     PetscCall(PetscStrcat(buffer," \""));
65     PetscCall(PetscStrlcat(buffer,PETSC_MATLAB_COMMAND,sizeof(buffer)));
66     if (!flg) PetscCall(PetscStrlcat(buffer," -nodisplay ",sizeof(buffer)));
67     PetscCall(PetscStrlcat(buffer," -nosplash ",sizeof(buffer)));
68     PetscCall(PetscStrcat(buffer,"\""));
69   } else {
70     PetscCall(PetscStrncpy(buffer,PETSC_MATLAB_COMMAND,sizeof(buffer)));
71     if (!flg) PetscCall(PetscStrlcat(buffer," -nodisplay ",sizeof(buffer)));
72     PetscCall(PetscStrlcat(buffer," -nosplash ",sizeof(buffer)));
73   }
74   PetscCall(PetscInfo(0,"Starting MATLAB engine with command %s\n",buffer));
75   e->ep = engOpen(buffer);
76   PetscCheck(e->ep,PETSC_COMM_SELF,PETSC_ERR_LIB,"Unable to start MATLAB engine with %s",buffer);
77   engOutputBuffer(e->ep,e->buffer,sizeof(e->buffer));
78   if (host) PetscCall(PetscInfo(0,"Started MATLAB engine on %s\n",host));
79   else PetscCall(PetscInfo(0,"Started MATLAB engine\n"));
80 
81   PetscCallMPI(MPI_Comm_rank(comm,&rank));
82   PetscCallMPI(MPI_Comm_size(comm,&size));
83   PetscCall(PetscMatlabEngineEvaluate(e,"MPI_Comm_rank = %d; MPI_Comm_size = %d;\n",rank,size));
84   *mengine = e;
85   PetscFunctionReturn(0);
86 }
87 
88 /*@
89    PetscMatlabEngineDestroy - Shuts down a MATLAB engine.
90 
91    Collective on PetscMatlabEngine
92 
93    Input Parameters:
94 .  e  - the engine
95 
96    Level: advanced
97 
98 .seealso: `PetscMatlabEngineCreate()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
99           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
100           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
101 @*/
102 PetscErrorCode  PetscMatlabEngineDestroy(PetscMatlabEngine *v)
103 {
104   int err;
105 
106   PetscFunctionBegin;
107   if (!*v) PetscFunctionReturn(0);
108   PetscValidHeaderSpecific(*v,MATLABENGINE_CLASSID,1);
109   if (--((PetscObject)(*v))->refct > 0) PetscFunctionReturn(0);
110   PetscCall(PetscInfo(0,"Stopping MATLAB engine\n"));
111   err = engClose((*v)->ep);
112   PetscCheck(!err,PETSC_COMM_SELF,PETSC_ERR_LIB,"Error closing Matlab engine");
113   PetscCall(PetscInfo(0,"MATLAB engine stopped\n"));
114   PetscCall(PetscHeaderDestroy(v));
115   PetscFunctionReturn(0);
116 }
117 
118 /*@C
119     PetscMatlabEngineEvaluate - Evaluates a string in MATLAB
120 
121     Not Collective
122 
123     Input Parameters:
124 +   mengine - the MATLAB engine
125 -   string - format as in a printf()
126 
127    Notes:
128      Run the PETSc program with -info to always have printed back MATLAB's response to the string evaluation
129 
130    Level: advanced
131 
132 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
133           `PetscMatlabEngineCreate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
134           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
135 @*/
136 PetscErrorCode  PetscMatlabEngineEvaluate(PetscMatlabEngine mengine,const char string[],...)
137 {
138   va_list        Argp;
139   char           buffer[1024];
140   size_t         fullLength;
141 
142   PetscFunctionBegin;
143   va_start(Argp,string);
144   PetscCall(PetscVSNPrintf(buffer,sizeof(buffer)-9-5,string,&fullLength,Argp));
145   va_end(Argp);
146 
147   PetscCall(PetscInfo(0,"Evaluating MATLAB string: %s\n",buffer));
148   engEvalString(mengine->ep, buffer);
149   PetscCall(PetscInfo(0,"Done evaluating MATLAB string: %s\n",buffer));
150   PetscCall(PetscInfo(0,"  MATLAB output message: %s\n",mengine->buffer));
151 
152   /*
153      Check for error in MATLAB: indicated by ? as first character in engine->buffer
154   */
155   PetscCheck(mengine->buffer[4] != '?',PETSC_COMM_SELF,PETSC_ERR_LIB,"Error in evaluating MATLAB command:%s\n%s",string,mengine->buffer);
156   PetscFunctionReturn(0);
157 }
158 
159 /*@C
160     PetscMatlabEngineGetOutput - Gets a string buffer where the MATLAB output is
161           printed
162 
163     Not Collective
164 
165     Input Parameter:
166 .   mengine - the MATLAB engine
167 
168     Output Parameter:
169 .   string - buffer where MATLAB output is printed
170 
171    Level: advanced
172 
173 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
174           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineCreate()`, `PetscMatlabEnginePrintOutput()`,
175           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
176 @*/
177 PetscErrorCode  PetscMatlabEngineGetOutput(PetscMatlabEngine mengine,char **string)
178 {
179   PetscFunctionBegin;
180   PetscCheck(mengine,PETSC_COMM_SELF,PETSC_ERR_ARG_NULL,"Null argument: probably PETSC_MATLAB_ENGINE_() failed");
181   *string = mengine->buffer;
182   PetscFunctionReturn(0);
183 }
184 
185 /*@C
186     PetscMatlabEnginePrintOutput - prints the output from MATLAB
187 
188     Collective on PetscMatlabEngine
189 
190     Input Parameters:
191 .    mengine - the Matlab engine
192 
193    Level: advanced
194 
195 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
196           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEngineCreate()`,
197           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
198 @*/
199 PetscErrorCode  PetscMatlabEnginePrintOutput(PetscMatlabEngine mengine,FILE *fd)
200 {
201   PetscMPIInt    rank;
202 
203   PetscFunctionBegin;
204   PetscCheck(mengine,PETSC_COMM_SELF,PETSC_ERR_ARG_NULL,"Null argument: probably PETSC_MATLAB_ENGINE_() failed");
205   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)mengine),&rank));
206   PetscCall(PetscSynchronizedFPrintf(PetscObjectComm((PetscObject)mengine),fd,"[%d]%s",rank,mengine->buffer));
207   PetscCall(PetscSynchronizedFlush(PetscObjectComm((PetscObject)mengine),fd));
208   PetscFunctionReturn(0);
209 }
210 
211 /*@
212     PetscMatlabEnginePut - Puts a Petsc object into the MATLAB space. For parallel objects,
213       each processors part is put in a separate  MATLAB process.
214 
215     Collective on PetscObject
216 
217     Input Parameters:
218 +    mengine - the MATLAB engine
219 -    object - the PETSc object, for example Vec
220 
221    Level: advanced
222 
223    Note: Mats transferred between PETSc and MATLAB and vis versa are transposed in the other space
224          (this is because MATLAB uses compressed column format and PETSc uses compressed row format)
225 
226 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEngineCreate()`, `PetscMatlabEngineGet()`,
227           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
228           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
229 @*/
230 PetscErrorCode  PetscMatlabEnginePut(PetscMatlabEngine mengine,PetscObject obj)
231 {
232   PetscErrorCode (*put)(PetscObject,void*);
233 
234   PetscFunctionBegin;
235   PetscCheck(mengine,PETSC_COMM_SELF,PETSC_ERR_ARG_NULL,"Null argument: probably PETSC_MATLAB_ENGINE_() failed");
236   PetscCall(PetscObjectQueryFunction(obj,"PetscMatlabEnginePut_C",&put));
237   PetscCheck(put,PETSC_COMM_SELF,PETSC_ERR_SUP,"Object %s cannot be put into MATLAB engine",obj->class_name);
238   PetscCall(PetscInfo(0,"Putting MATLAB object\n"));
239   PetscCall((*put)(obj,mengine->ep));
240   PetscCall(PetscInfo(0,"Put MATLAB object: %s\n",obj->name));
241   PetscFunctionReturn(0);
242 }
243 
244 /*@
245     PetscMatlabEngineGet - Gets a variable from MATLAB into a PETSc object.
246 
247     Collective on PetscObject
248 
249     Input Parameters:
250 +    mengine - the MATLAB engine
251 -    object - the PETSc object, for example Vec
252 
253    Level: advanced
254 
255    Note: Mats transferred between PETSc and MATLAB and vis versa are transposed in the other space
256          (this is because MATLAB uses compressed column format and PETSc uses compressed row format)
257 
258 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineCreate()`,
259           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
260           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
261 @*/
262 PetscErrorCode  PetscMatlabEngineGet(PetscMatlabEngine mengine,PetscObject obj)
263 {
264   PetscErrorCode (*get)(PetscObject,void*);
265 
266   PetscFunctionBegin;
267   PetscCheck(mengine,PETSC_COMM_SELF,PETSC_ERR_ARG_NULL,"Null argument: probably PETSC_MATLAB_ENGINE_() failed");
268   PetscCheck(obj->name,PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"Cannot get object that has no name");
269   PetscCall(PetscObjectQueryFunction(obj,"PetscMatlabEngineGet_C",&get));
270   PetscCheck(get,PETSC_COMM_SELF,PETSC_ERR_SUP,"Object %s cannot be gotten from MATLAB engine",obj->class_name);
271   PetscCall(PetscInfo(0,"Getting MATLAB object\n"));
272   PetscCall((*get)(obj,mengine->ep));
273   PetscCall(PetscInfo(0,"Got MATLAB object: %s\n",obj->name));
274   PetscFunctionReturn(0);
275 }
276 
277 /*
278     The variable Petsc_Matlab_Engine_keyval is used to indicate an MPI attribute that
279   is attached to a communicator, in this case the attribute is a PetscMatlabEngine
280 */
281 static PetscMPIInt Petsc_Matlab_Engine_keyval = MPI_KEYVAL_INVALID;
282 
283 /*@C
284    PETSC_MATLAB_ENGINE_ - Creates a MATLAB engine on each process in a communicator.
285 
286    Not Collective
287 
288    Input Parameter:
289 .  comm - the MPI communicator to share the engine
290 
291    Options Database:
292 .  -matlab_engine_host - hostname
293 
294    Level: developer
295 
296    Notes:
297    Unlike almost all other PETSc routines, this does not return
298    an error code. Usually used in the form
299 $      PetscMatlabEngineYYY(XXX object,PETSC_MATLAB_ENGINE_(comm));
300 
301 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
302           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
303           `PetscMatlabEngineCreate()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`,
304           `PETSC_MATLAB_ENGINE_WORLD`, `PETSC_MATLAB_ENGINE_SELF`
305 
306 @*/
307 PetscMatlabEngine  PETSC_MATLAB_ENGINE_(MPI_Comm comm)
308 {
309   PetscErrorCode    ierr;
310   PetscBool         flg;
311   PetscMatlabEngine mengine;
312 
313   PetscFunctionBegin;
314   if (Petsc_Matlab_Engine_keyval == MPI_KEYVAL_INVALID) {
315     ierr = MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN,MPI_COMM_NULL_DELETE_FN,&Petsc_Matlab_Engine_keyval,0);
316     if (ierr) {PetscError(PETSC_COMM_SELF,__LINE__,"PETSC_MATLAB_ENGINE_",__FILE__,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL," ");PetscFunctionReturn(NULL);}
317   }
318   ierr = MPI_Comm_get_attr(comm,Petsc_Matlab_Engine_keyval,(void**)&mengine,(int*)&flg);
319   if (ierr) {PetscError(PETSC_COMM_SELF,__LINE__,"PETSC_MATLAB_ENGINE_",__FILE__,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL," ");PetscFunctionReturn(NULL);}
320   if (!flg) { /* viewer not yet created */
321     ierr = PetscMatlabEngineCreate(comm,NULL,&mengine);
322     if (ierr) {PetscError(PETSC_COMM_SELF,__LINE__,"PETSC_MATLAB_ENGINE_",__FILE__,PETSC_ERR_PLIB,PETSC_ERROR_REPEAT," ");PetscFunctionReturn(NULL);}
323     ierr = PetscObjectRegisterDestroy((PetscObject)mengine);
324     if (ierr) {PetscError(PETSC_COMM_SELF,__LINE__,"PETSC_MATLAB_ENGINE_",__FILE__,PETSC_ERR_PLIB,PETSC_ERROR_REPEAT," ");PetscFunctionReturn(NULL);}
325     ierr = MPI_Comm_set_attr(comm,Petsc_Matlab_Engine_keyval,mengine);
326     if (ierr) {PetscError(PETSC_COMM_SELF,__LINE__,"PETSC_MATLAB_ENGINE_",__FILE__,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL," ");PetscFunctionReturn(NULL);}
327   }
328   PetscFunctionReturn(mengine);
329 }
330 
331 /*@C
332     PetscMatlabEnginePutArray - Puts an array into the MATLAB space, treating it as a Fortran style (column major ordering) array. For parallel objects,
333       each processors part is put in a separate  MATLAB process.
334 
335     Collective on PetscObject
336 
337     Input Parameters:
338 +    mengine - the MATLAB engine
339 .    m,n - the dimensions of the array
340 .    array - the array (represented in one dimension)
341 -    name - the name of the array
342 
343    Level: advanced
344 
345 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEngineCreate()`, `PetscMatlabEngineGet()`,
346           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
347           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
348 @*/
349 PetscErrorCode  PetscMatlabEnginePutArray(PetscMatlabEngine mengine,int m,int n,const PetscScalar *array,const char name[])
350 {
351   mxArray *mat;
352 
353   PetscFunctionBegin;
354   PetscCheck(mengine,PETSC_COMM_SELF,PETSC_ERR_ARG_NULL,"Null argument: probably PETSC_MATLAB_ENGINE_() failed");
355   PetscCall(PetscInfo(0,"Putting MATLAB array %s\n",name));
356 #if !defined(PETSC_USE_COMPLEX)
357   mat = mxCreateDoubleMatrix(m,n,mxREAL);
358 #else
359   mat = mxCreateDoubleMatrix(m,n,mxCOMPLEX);
360 #endif
361   PetscCall(PetscArraycpy(mxGetPr(mat),array,m*n));
362   engPutVariable(mengine->ep,name,mat);
363 
364   PetscCall(PetscInfo(0,"Put MATLAB array %s\n",name));
365   PetscFunctionReturn(0);
366 }
367 
368 /*@C
369     PetscMatlabEngineGetArray - Gets a variable from MATLAB into an array
370 
371     Not Collective
372 
373     Input Parameters:
374 +    mengine - the MATLAB engine
375 .    m,n - the dimensions of the array
376 .    array - the array (represented in one dimension)
377 -    name - the name of the array
378 
379    Level: advanced
380 
381 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineCreate()`,
382           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
383           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGet()`, `PetscMatlabEngine`
384 @*/
385 PetscErrorCode  PetscMatlabEngineGetArray(PetscMatlabEngine mengine,int m,int n,PetscScalar *array,const char name[])
386 {
387   mxArray        *mat;
388 
389   PetscFunctionBegin;
390   PetscCheck(mengine,PETSC_COMM_SELF,PETSC_ERR_ARG_NULL,"Null argument: probably PETSC_MATLAB_ENGINE_() failed");
391   PetscCall(PetscInfo(0,"Getting MATLAB array %s\n",name));
392   mat  = engGetVariable(mengine->ep,name);
393   PetscCheck(mat,PETSC_COMM_SELF,PETSC_ERR_LIB,"Unable to get array %s from matlab",name);
394   PetscCheck(mxGetM(mat) == (size_t) m,PETSC_COMM_SELF,PETSC_ERR_LIB,"Array %s in MATLAB first dimension %d does not match requested size %d",name,(int)mxGetM(mat),m);
395   PetscCheck(mxGetN(mat) == (size_t) n,PETSC_COMM_SELF,PETSC_ERR_LIB,"Array %s in MATLAB second dimension %d does not match requested size %d",name,(int)mxGetN(mat),m);
396   PetscCall(PetscArraycpy(array,mxGetPr(mat),m*n));
397   PetscCall(PetscInfo(0,"Got MATLAB array %s\n",name));
398   PetscFunctionReturn(0);
399 }
400