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