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