Actual source code: matlab.c


  2: #include <engine.h> /* MATLAB include file */
  3: #include <petscsys.h>
  4: #include <petscmatlab.h>
  5: #include <petsc/private/petscimpl.h>

  7: struct _p_PetscMatlabEngine {
  8:   PETSCHEADER(int);
  9:   Engine *ep;
 10:   char    buffer[1024];
 11: };

 13: PetscClassId MATLABENGINE_CLASSID = -1;

 15: /*@C
 16:     PetscMatlabEngineCreate - Creates a MATLAB engine object

 18:     Not Collective

 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)

 24:     Output Parameter:
 25: .   mengine - the resulting object

 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)

 32:    Level: advanced

 34:    Notes:
 35:    If a host string is passed in, any MATLAB scripts that need to run in the
 36:    engine must be available via MATLABPATH on that machine.

 38:    One must `./configure` PETSc with  `--with-matlab [-with-matlab-dir=matlab_root_directory]` to
 39:    use this capability

 41: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
 42:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
 43:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
 44: @*/
 45: PetscErrorCode PetscMatlabEngineCreate(MPI_Comm comm, const char host[], PetscMatlabEngine *mengine)
 46: {
 47:   PetscMPIInt       rank, size;
 48:   char              buffer[256];
 49:   PetscMatlabEngine e;
 50:   PetscBool         flg = PETSC_FALSE;
 51:   char              lhost[64];
 52:   PetscFunctionBegin;
 53:   if (MATLABENGINE_CLASSID == -1) PetscCall(PetscClassIdRegister("MATLAB Engine", &MATLABENGINE_CLASSID));
 54:   PetscCall(PetscHeaderCreate(e, MATLABENGINE_CLASSID, "MatlabEngine", "MATLAB Engine", "Sys", comm, PetscMatlabEngineDestroy, NULL));

 56:   if (!host) {
 57:     PetscCall(PetscOptionsGetString(NULL, NULL, "-matlab_engine_host", lhost, sizeof(lhost), &flg));
 58:     if (flg) host = lhost;
 59:   }
 60:   flg = PETSC_FALSE;
 61:   PetscCall(PetscOptionsGetBool(NULL, NULL, "-matlab_engine_graphics", &flg, NULL));

 63:   if (host) {
 64:     PetscCall(PetscInfo(0, "Starting MATLAB engine on %s\n", host));
 65:     PetscCall(PetscStrncpy(buffer, "ssh ", sizeof(buffer)));
 66:     PetscCall(PetscStrlcat(buffer, host, sizeof(buffer)));
 67:     PetscCall(PetscStrlcat(buffer, " \"", sizeof(buffer)));
 68:     PetscCall(PetscStrlcat(buffer, PETSC_MATLAB_COMMAND, sizeof(buffer)));
 69:     if (!flg) PetscCall(PetscStrlcat(buffer, " -nodisplay ", sizeof(buffer)));
 70:     PetscCall(PetscStrlcat(buffer, " -nosplash ", sizeof(buffer)));
 71:     PetscCall(PetscStrlcat(buffer, "\"", sizeof(buffer)));
 72:   } else {
 73:     PetscCall(PetscStrncpy(buffer, PETSC_MATLAB_COMMAND, sizeof(buffer)));
 74:     if (!flg) PetscCall(PetscStrlcat(buffer, " -nodisplay ", sizeof(buffer)));
 75:     PetscCall(PetscStrlcat(buffer, " -nosplash ", sizeof(buffer)));
 76:   }
 77:   PetscCall(PetscInfo(0, "Starting MATLAB engine with command %s\n", buffer));
 78:   e->ep = engOpen(buffer);
 79:   PetscCheck(e->ep, PETSC_COMM_SELF, PETSC_ERR_LIB, "Unable to start MATLAB engine with %s", buffer);
 80:   engOutputBuffer(e->ep, e->buffer, sizeof(e->buffer));
 81:   if (host) PetscCall(PetscInfo(0, "Started MATLAB engine on %s\n", host));
 82:   else PetscCall(PetscInfo(0, "Started MATLAB engine\n"));

 84:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
 85:   PetscCallMPI(MPI_Comm_size(comm, &size));
 86:   PetscCall(PetscMatlabEngineEvaluate(e, "MPI_Comm_rank = %d; MPI_Comm_size = %d;\n", rank, size));
 87:   /* work around bug in MATLAB R2021b https://www.mathworks.com/matlabcentral/answers/1566246-got-error-using-exit-in-nodesktop-mode */
 88:   PetscCall(PetscMatlabEngineEvaluate(e, "settings"));
 89:   *mengine = e;
 90:   PetscFunctionReturn(PETSC_SUCCESS);
 91: }

 93: /*@
 94:    PetscMatlabEngineDestroy - Shuts down a MATLAB engine.

 96:    Collective

 98:    Input Parameter:
 99: .  e  - the engine

101:    Level: advanced

103: .seealso: `PetscMatlabEngineCreate()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
104:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
105:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
106: @*/
107: PetscErrorCode PetscMatlabEngineDestroy(PetscMatlabEngine *v)
108: {
109:   int err;

111:   PetscFunctionBegin;
112:   if (!*v) PetscFunctionReturn(PETSC_SUCCESS);
114:   if (--((PetscObject)(*v))->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
115:   PetscCall(PetscInfo(0, "Stopping MATLAB engine\n"));
116:   err = engClose((*v)->ep);
117:   PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error closing Matlab engine");
118:   PetscCall(PetscInfo(0, "MATLAB engine stopped\n"));
119:   PetscCall(PetscHeaderDestroy(v));
120:   PetscFunctionReturn(PETSC_SUCCESS);
121: }

123: /*@C
124:     PetscMatlabEngineEvaluate - Evaluates a string in MATLAB

126:     Not Collective

128:     Input Parameters:
129: +   mengine - the MATLAB engine
130: -   string - format as in a printf()

132:    Notes:
133:    Run the PETSc program with -info to always have printed back MATLAB's response to the string evaluation

135:    If the string utilizes a MATLAB script that needs to run in the engine, the script must be available via MATLABPATH on that machine.

137:    Level: advanced

139: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
140:           `PetscMatlabEngineCreate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
141:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
142: @*/
143: PetscErrorCode PetscMatlabEngineEvaluate(PetscMatlabEngine mengine, const char string[], ...)
144: {
145:   va_list Argp;
146:   char    buffer[1024];
147:   size_t  fullLength;

149:   PetscFunctionBegin;
150:   va_start(Argp, string);
151:   PetscCall(PetscVSNPrintf(buffer, sizeof(buffer) - 9 - 5, string, &fullLength, Argp));
152:   va_end(Argp);

154:   PetscCall(PetscInfo(0, "Evaluating MATLAB string: %s\n", buffer));
155:   engEvalString(mengine->ep, buffer);
156:   PetscCall(PetscInfo(0, "Done evaluating MATLAB string: %s\n", buffer));
157:   PetscCall(PetscInfo(0, "  MATLAB output message: %s\n", mengine->buffer));

159:   /*
160:      Check for error in MATLAB: indicated by ? as first character in engine->buffer
161:   */
162:   PetscCheck(mengine->buffer[4] != '?', PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in evaluating MATLAB command:%s\n%s", string, mengine->buffer);
163:   PetscFunctionReturn(PETSC_SUCCESS);
164: }

166: /*@C
167:     PetscMatlabEngineGetOutput - Gets a string buffer where the MATLAB output is
168:           printed

170:     Not Collective

172:     Input Parameter:
173: .   mengine - the MATLAB engine

175:     Output Parameter:
176: .   string - buffer where MATLAB output is printed

178:    Level: advanced

180: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
181:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineCreate()`, `PetscMatlabEnginePrintOutput()`,
182:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
183: @*/
184: PetscErrorCode PetscMatlabEngineGetOutput(PetscMatlabEngine mengine, char **string)
185: {
186:   PetscFunctionBegin;
187:   PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed");
188:   *string = mengine->buffer;
189:   PetscFunctionReturn(PETSC_SUCCESS);
190: }

192: /*@C
193:     PetscMatlabEnginePrintOutput - prints the output from MATLAB to an ASCII file

195:     Collective

197:     Input Parameters:
198: +    mengine - the MATLAB engine
199: -    fd - the file

201:    Level: advanced

203: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
204:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEngineCreate()`,
205:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
206: @*/
207: PetscErrorCode PetscMatlabEnginePrintOutput(PetscMatlabEngine mengine, FILE *fd)
208: {
209:   PetscMPIInt rank;

211:   PetscFunctionBegin;
212:   PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed");
213:   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)mengine), &rank));
214:   PetscCall(PetscSynchronizedFPrintf(PetscObjectComm((PetscObject)mengine), fd, "[%d]%s", rank, mengine->buffer));
215:   PetscCall(PetscSynchronizedFlush(PetscObjectComm((PetscObject)mengine), fd));
216:   PetscFunctionReturn(PETSC_SUCCESS);
217: }

219: /*@
220:     PetscMatlabEnginePut - Puts a Petsc object, such as a `Mat` or `Vec` into the MATLAB space. For parallel objects,
221:       each processor's part is put in a separate  MATLAB process.

223:     Collective

225:     Input Parameters:
226: +    mengine - the MATLAB engine
227: -    object - the PETSc object, for example Vec

229:    Level: advanced

231:    Note:
232:    `Mat`s transferred between PETSc and MATLAB and vis versa are transposed in the other space
233:    (this is because MATLAB uses compressed column format and PETSc uses compressed row format)

235: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEngineCreate()`, `PetscMatlabEngineGet()`,
236:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
237:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
238: @*/
239: PetscErrorCode PetscMatlabEnginePut(PetscMatlabEngine mengine, PetscObject obj)
240: {
241:   PetscErrorCode (*put)(PetscObject, void *);

243:   PetscFunctionBegin;
244:   PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed");
245:   PetscCall(PetscObjectQueryFunction(obj, "PetscMatlabEnginePut_C", &put));
246:   PetscCheck(put, PETSC_COMM_SELF, PETSC_ERR_SUP, "Object %s cannot be put into MATLAB engine", obj->class_name);
247:   PetscCall(PetscInfo(0, "Putting MATLAB object\n"));
248:   PetscCall((*put)(obj, mengine->ep));
249:   PetscCall(PetscInfo(0, "Put MATLAB object: %s\n", obj->name));
250:   PetscFunctionReturn(PETSC_SUCCESS);
251: }

253: /*@
254:     PetscMatlabEngineGet - Gets a variable from MATLAB into a PETSc object.

256:     Collective

258:     Input Parameters:
259: +    mengine - the MATLAB engine
260: -    object - the PETSc object, for example a `Vec`

262:    Level: advanced

264:    Note:
265:    `Mat`s transferred between PETSc and MATLAB and vis versa are transposed in the other space
266:    (this is because MATLAB uses compressed column format and PETSc uses compressed row format)

268: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineCreate()`,
269:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
270:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
271: @*/
272: PetscErrorCode PetscMatlabEngineGet(PetscMatlabEngine mengine, PetscObject obj)
273: {
274:   PetscErrorCode (*get)(PetscObject, void *);

276:   PetscFunctionBegin;
277:   PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed");
278:   PetscCheck(obj->name, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Cannot get object that has no name");
279:   PetscCall(PetscObjectQueryFunction(obj, "PetscMatlabEngineGet_C", &get));
280:   PetscCheck(get, PETSC_COMM_SELF, PETSC_ERR_SUP, "Object %s cannot be gotten from MATLAB engine", obj->class_name);
281:   PetscCall(PetscInfo(0, "Getting MATLAB object\n"));
282:   PetscCall((*get)(obj, mengine->ep));
283:   PetscCall(PetscInfo(0, "Got MATLAB object: %s\n", obj->name));
284:   PetscFunctionReturn(PETSC_SUCCESS);
285: }

287: /*
288:     The variable Petsc_Matlab_Engine_keyval is used to indicate an MPI attribute that
289:   is attached to a communicator, in this case the attribute is a PetscMatlabEngine
290: */
291: static PetscMPIInt Petsc_Matlab_Engine_keyval = MPI_KEYVAL_INVALID;

293: /*@C
294:    PETSC_MATLAB_ENGINE_ - Creates a MATLAB engine on each process in a communicator.

296:    Not Collective

298:    Input Parameter:
299: .  comm - the MPI communicator to share the engine

301:    Options Database Key:
302: .  -matlab_engine_host - hostname on which to run MATLAB, one must be able to ssh to this host

304:    Level: developer

306:    Note:
307:    Unlike almost all other PETSc routines, this does not return
308:    an error code. Usually used in the form
309: $      PetscMatlabEngineYYY(XXX object, PETSC_MATLAB_ENGINE_(comm));

311: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
312:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
313:           `PetscMatlabEngineCreate()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`,
314:           `PETSC_MATLAB_ENGINE_WORLD`, `PETSC_MATLAB_ENGINE_SELF`
315: @*/
316: PetscMatlabEngine PETSC_MATLAB_ENGINE_(MPI_Comm comm)
317: {
318:   PetscErrorCode    ierr;
319:   PetscBool         flg;
320:   PetscMatlabEngine mengine;

322:   PetscFunctionBegin;
323:   if (Petsc_Matlab_Engine_keyval == MPI_KEYVAL_INVALID) {
324:     ierr = MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, MPI_COMM_NULL_DELETE_FN, &Petsc_Matlab_Engine_keyval, 0);
325:     if (ierr) {
326:       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
327:       PetscFunctionReturn(NULL);
328:     }
329:   }
330:   ierr = MPI_Comm_get_attr(comm, Petsc_Matlab_Engine_keyval, (void **)&mengine, (int *)&flg);
331:   if (ierr) {
332:     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
333:     PetscFunctionReturn(NULL);
334:   }
335:   if (!flg) { /* viewer not yet created */
336:     ierr = PetscMatlabEngineCreate(comm, NULL, &mengine);
337:     if (ierr) {
338:       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
339:       PetscFunctionReturn(NULL);
340:     }
341:     ierr = PetscObjectRegisterDestroy((PetscObject)mengine);
342:     if (ierr) {
343:       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
344:       PetscFunctionReturn(NULL);
345:     }
346:     ierr = MPI_Comm_set_attr(comm, Petsc_Matlab_Engine_keyval, mengine);
347:     if (ierr) {
348:       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
349:       PetscFunctionReturn(NULL);
350:     }
351:   }
352:   PetscFunctionReturn(mengine);
353: }

355: /*@C
356:     PetscMatlabEnginePutArray - Puts an array into the MATLAB space, treating it as a Fortran style (column major ordering) array. For parallel objects,
357:       each processors part is put in a separate  MATLAB process.

359:     Collective

361:     Input Parameters:
362: +    mengine - the MATLAB engine
363: .    m,n - the dimensions of the array
364: .    array - the array (represented in one dimension)
365: -    name - the name of the array

367:    Level: advanced

369: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEngineCreate()`, `PetscMatlabEngineGet()`,
370:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
371:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
372: @*/
373: PetscErrorCode PetscMatlabEnginePutArray(PetscMatlabEngine mengine, int m, int n, const PetscScalar *array, const char name[])
374: {
375:   mxArray *mat;

377:   PetscFunctionBegin;
378:   PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed");
379:   PetscCall(PetscInfo(0, "Putting MATLAB array %s\n", name));
380: #if !defined(PETSC_USE_COMPLEX)
381:   mat = mxCreateDoubleMatrix(m, n, mxREAL);
382: #else
383:   mat = mxCreateDoubleMatrix(m, n, mxCOMPLEX);
384: #endif
385:   PetscCall(PetscArraycpy(mxGetPr(mat), array, m * n));
386:   engPutVariable(mengine->ep, name, mat);

388:   PetscCall(PetscInfo(0, "Put MATLAB array %s\n", name));
389:   PetscFunctionReturn(PETSC_SUCCESS);
390: }

392: /*@C
393:     PetscMatlabEngineGetArray - Gets a variable from MATLAB into an array

395:     Not Collective

397:     Input Parameters:
398: +    mengine - the MATLAB engine
399: .    m,n - the dimensions of the array
400: .    array - the array (represented in one dimension)
401: -    name - the name of the array

403:    Level: advanced

405: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineCreate()`,
406:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
407:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGet()`, `PetscMatlabEngine`
408: @*/
409: PetscErrorCode PetscMatlabEngineGetArray(PetscMatlabEngine mengine, int m, int n, PetscScalar *array, const char name[])
410: {
411:   mxArray *mat;

413:   PetscFunctionBegin;
414:   PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed");
415:   PetscCall(PetscInfo(0, "Getting MATLAB array %s\n", name));
416:   mat = engGetVariable(mengine->ep, name);
417:   PetscCheck(mat, PETSC_COMM_SELF, PETSC_ERR_LIB, "Unable to get array %s from matlab", name);
418:   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);
419:   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);
420:   PetscCall(PetscArraycpy(array, mxGetPr(mat), m * n));
421:   PetscCall(PetscInfo(0, "Got MATLAB array %s\n", name));
422:   PetscFunctionReturn(PETSC_SUCCESS);
423: }