xref: /petsc/src/sys/classes/matlabengine/matlab.c (revision f4d061e980d13bc62f06124c58b76593bdf99e72)
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 Keys:
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    Note:
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 v
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    If the string utilizes a MATLAB script that needs to run in the engine, the script must be available via MATLABPATH on that machine.
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   va_list Argp;
138   char    buffer[1024];
139   size_t  fullLength;
140 
141   PetscFunctionBegin;
142   va_start(Argp, string);
143   PetscCall(PetscVSNPrintf(buffer, sizeof(buffer) - 9 - 5, string, &fullLength, Argp));
144   va_end(Argp);
145 
146   PetscCall(PetscInfo(0, "Evaluating MATLAB string: %s\n", buffer));
147   engEvalString(mengine->ep, buffer);
148   PetscCall(PetscInfo(0, "Done evaluating MATLAB string: %s\n", buffer));
149   PetscCall(PetscInfo(0, "  MATLAB output message: %s\n", mengine->buffer));
150 
151   /*
152      Check for error in MATLAB: indicated by ? as first character in engine->buffer
153   */
154   PetscCheck(mengine->buffer[4] != '?', PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in evaluating MATLAB command:%s\n%s", string, mengine->buffer);
155   PetscFunctionReturn(0);
156 }
157 
158 /*@C
159     PetscMatlabEngineGetOutput - Gets a string buffer where the MATLAB output is
160           printed
161 
162     Not Collective
163 
164     Input Parameter:
165 .   mengine - the MATLAB engine
166 
167     Output Parameter:
168 .   string - buffer where MATLAB output is printed
169 
170    Level: advanced
171 
172 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
173           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineCreate()`, `PetscMatlabEnginePrintOutput()`,
174           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
175 @*/
176 PetscErrorCode PetscMatlabEngineGetOutput(PetscMatlabEngine mengine, char **string) {
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 to an ASCII file
185 
186     Collective on mengine
187 
188     Input Parameters:
189 +    mengine - the MATLAB engine
190 -    fd - the file
191 
192    Level: advanced
193 
194 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
195           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEngineCreate()`,
196           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
197 @*/
198 PetscErrorCode PetscMatlabEnginePrintOutput(PetscMatlabEngine mengine, FILE *fd) {
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, such as a `Mat` or `Vec` into the MATLAB space. For parallel objects,
211       each processor's part is put in a separate  MATLAB process.
212 
213     Collective on mengine
214 
215     Input Parameters:
216 +    mengine - the MATLAB engine
217 -    object - the PETSc object, for example Vec
218 
219    Level: advanced
220 
221    Note:
222    `Mat`s transferred between PETSc and MATLAB and vis versa are transposed in the other space
223    (this is because MATLAB uses compressed column format and PETSc uses compressed row format)
224 
225 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEngineCreate()`, `PetscMatlabEngineGet()`,
226           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
227           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
228 @*/
229 PetscErrorCode PetscMatlabEnginePut(PetscMatlabEngine mengine, PetscObject obj) {
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 mengine
246 
247     Input Parameters:
248 +    mengine - the MATLAB engine
249 -    object - the PETSc object, for example a `Vec`
250 
251    Level: advanced
252 
253    Note:
254    `Mat`s transferred between PETSc and MATLAB and vis versa are transposed in the other space
255    (this is because MATLAB uses compressed column format and PETSc uses compressed row format)
256 
257 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineCreate()`,
258           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
259           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
260 @*/
261 PetscErrorCode PetscMatlabEngineGet(PetscMatlabEngine mengine, PetscObject obj) {
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 Key:
290 .  -matlab_engine_host - hostname on which to run MATLAB, one must be able to ssh to this host
291 
292    Level: developer
293 
294    Note:
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 PetscMatlabEngine PETSC_MATLAB_ENGINE_(MPI_Comm comm) {
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) {
313       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
314       PetscFunctionReturn(NULL);
315     }
316   }
317   ierr = MPI_Comm_get_attr(comm, Petsc_Matlab_Engine_keyval, (void **)&mengine, (int *)&flg);
318   if (ierr) {
319     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
320     PetscFunctionReturn(NULL);
321   }
322   if (!flg) { /* viewer not yet created */
323     ierr = PetscMatlabEngineCreate(comm, NULL, &mengine);
324     if (ierr) {
325       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
326       PetscFunctionReturn(NULL);
327     }
328     ierr = PetscObjectRegisterDestroy((PetscObject)mengine);
329     if (ierr) {
330       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
331       PetscFunctionReturn(NULL);
332     }
333     ierr = MPI_Comm_set_attr(comm, Petsc_Matlab_Engine_keyval, mengine);
334     if (ierr) {
335       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
336       PetscFunctionReturn(NULL);
337     }
338   }
339   PetscFunctionReturn(mengine);
340 }
341 
342 /*@C
343     PetscMatlabEnginePutArray - Puts an array into the MATLAB space, treating it as a Fortran style (column major ordering) array. For parallel objects,
344       each processors part is put in a separate  MATLAB process.
345 
346     Collective on mengine
347 
348     Input Parameters:
349 +    mengine - the MATLAB engine
350 .    m,n - the dimensions of the array
351 .    array - the array (represented in one dimension)
352 -    name - the name of the array
353 
354    Level: advanced
355 
356 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEngineCreate()`, `PetscMatlabEngineGet()`,
357           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
358           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
359 @*/
360 PetscErrorCode PetscMatlabEnginePutArray(PetscMatlabEngine mengine, int m, int n, const PetscScalar *array, const char name[]) {
361   mxArray *mat;
362 
363   PetscFunctionBegin;
364   PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed");
365   PetscCall(PetscInfo(0, "Putting MATLAB array %s\n", name));
366 #if !defined(PETSC_USE_COMPLEX)
367   mat = mxCreateDoubleMatrix(m, n, mxREAL);
368 #else
369   mat = mxCreateDoubleMatrix(m, n, mxCOMPLEX);
370 #endif
371   PetscCall(PetscArraycpy(mxGetPr(mat), array, m * n));
372   engPutVariable(mengine->ep, name, mat);
373 
374   PetscCall(PetscInfo(0, "Put MATLAB array %s\n", name));
375   PetscFunctionReturn(0);
376 }
377 
378 /*@C
379     PetscMatlabEngineGetArray - Gets a variable from MATLAB into an array
380 
381     Not Collective
382 
383     Input Parameters:
384 +    mengine - the MATLAB engine
385 .    m,n - the dimensions of the array
386 .    array - the array (represented in one dimension)
387 -    name - the name of the array
388 
389    Level: advanced
390 
391 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineCreate()`,
392           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
393           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGet()`, `PetscMatlabEngine`
394 @*/
395 PetscErrorCode PetscMatlabEngineGetArray(PetscMatlabEngine mengine, int m, int n, PetscScalar *array, const char name[]) {
396   mxArray *mat;
397 
398   PetscFunctionBegin;
399   PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed");
400   PetscCall(PetscInfo(0, "Getting MATLAB array %s\n", name));
401   mat = engGetVariable(mengine->ep, name);
402   PetscCheck(mat, PETSC_COMM_SELF, PETSC_ERR_LIB, "Unable to get array %s from matlab", name);
403   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);
404   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);
405   PetscCall(PetscArraycpy(array, mxGetPr(mat), m * n));
406   PetscCall(PetscInfo(0, "Got MATLAB array %s\n", name));
407   PetscFunctionReturn(0);
408 }
409