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