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:    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.

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

 39:    Level: advanced

 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:   if (MATLABENGINE_CLASSID == -1) PetscClassIdRegister("MATLAB Engine", &MATLABENGINE_CLASSID);
 53:   PetscHeaderCreate(e, MATLABENGINE_CLASSID, "MatlabEngine", "MATLAB Engine", "Sys", comm, PetscMatlabEngineDestroy, NULL);

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

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

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

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

 95:    Collective

 97:    Input Parameters:
 98: .  e  - the engine

100:    Level: advanced

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

110:   if (!*v) return 0;
112:   if (--((PetscObject)(*v))->refct > 0) return 0;
113:   PetscInfo(0, "Stopping MATLAB engine\n");
114:   err = engClose((*v)->ep);
116:   PetscInfo(0, "MATLAB engine stopped\n");
117:   PetscHeaderDestroy(v);
118:   return 0;
119: }

121: /*@C
122:     PetscMatlabEngineEvaluate - Evaluates a string in MATLAB

124:     Not Collective

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

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

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

135:    Level: advanced

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

147:   va_start(Argp, string);
148:   PetscVSNPrintf(buffer, sizeof(buffer) - 9 - 5, string, &fullLength, Argp);
149:   va_end(Argp);

151:   PetscInfo(0, "Evaluating MATLAB string: %s\n", buffer);
152:   engEvalString(mengine->ep, buffer);
153:   PetscInfo(0, "Done evaluating MATLAB string: %s\n", buffer);
154:   PetscInfo(0, "  MATLAB output message: %s\n", mengine->buffer);

156:   /*
157:      Check for error in MATLAB: indicated by ? as first character in engine->buffer
158:   */
160:   return 0;
161: }

163: /*@C
164:     PetscMatlabEngineGetOutput - Gets a string buffer where the MATLAB output is
165:           printed

167:     Not Collective

169:     Input Parameter:
170: .   mengine - the MATLAB engine

172:     Output Parameter:
173: .   string - buffer where MATLAB output is printed

175:    Level: advanced

177: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
178:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineCreate()`, `PetscMatlabEnginePrintOutput()`,
179:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
180: @*/
181: PetscErrorCode PetscMatlabEngineGetOutput(PetscMatlabEngine mengine, char **string)
182: {
184:   *string = mengine->buffer;
185:   return 0;
186: }

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

191:     Collective

193:     Input Parameters:
194: +    mengine - the MATLAB engine
195: -    fd - the file

197:    Level: advanced

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

208:   MPI_Comm_rank(PetscObjectComm((PetscObject)mengine), &rank);
209:   PetscSynchronizedFPrintf(PetscObjectComm((PetscObject)mengine), fd, "[%d]%s", rank, mengine->buffer);
210:   PetscSynchronizedFlush(PetscObjectComm((PetscObject)mengine), fd);
211:   return 0;
212: }

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

218:     Collective

220:     Input Parameters:
221: +    mengine - the MATLAB engine
222: -    object - the PETSc object, for example Vec

224:    Level: advanced

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

230: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEngineCreate()`, `PetscMatlabEngineGet()`,
231:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
232:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
233: @*/
234: PetscErrorCode PetscMatlabEnginePut(PetscMatlabEngine mengine, PetscObject obj)
235: {
236:   PetscErrorCode (*put)(PetscObject, void *);

239:   PetscObjectQueryFunction(obj, "PetscMatlabEnginePut_C", &put);
241:   PetscInfo(0, "Putting MATLAB object\n");
242:   (*put)(obj, mengine->ep);
243:   PetscInfo(0, "Put MATLAB object: %s\n", obj->name);
244:   return 0;
245: }

247: /*@
248:     PetscMatlabEngineGet - Gets a variable from MATLAB into a PETSc object.

250:     Collective

252:     Input Parameters:
253: +    mengine - the MATLAB engine
254: -    object - the PETSc object, for example a `Vec`

256:    Level: advanced

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

262: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineCreate()`,
263:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
264:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
265: @*/
266: PetscErrorCode PetscMatlabEngineGet(PetscMatlabEngine mengine, PetscObject obj)
267: {
268:   PetscErrorCode (*get)(PetscObject, void *);

272:   PetscObjectQueryFunction(obj, "PetscMatlabEngineGet_C", &get);
274:   PetscInfo(0, "Getting MATLAB object\n");
275:   (*get)(obj, mengine->ep);
276:   PetscInfo(0, "Got MATLAB object: %s\n", obj->name);
277:   return 0;
278: }

280: /*
281:     The variable Petsc_Matlab_Engine_keyval is used to indicate an MPI attribute that
282:   is attached to a communicator, in this case the attribute is a PetscMatlabEngine
283: */
284: static PetscMPIInt Petsc_Matlab_Engine_keyval = MPI_KEYVAL_INVALID;

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

289:    Not Collective

291:    Input Parameter:
292: .  comm - the MPI communicator to share the engine

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

297:    Level: developer

299:    Note:
300:    Unlike almost all other PETSc routines, this does not return
301:    an error code. Usually used in the form
302: $      PetscMatlabEngineYYY(XXX object,PETSC_MATLAB_ENGINE_(comm));

304: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
305:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
306:           `PetscMatlabEngineCreate()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`,
307:           `PETSC_MATLAB_ENGINE_WORLD`, `PETSC_MATLAB_ENGINE_SELF`
308: @*/
309: PetscMatlabEngine PETSC_MATLAB_ENGINE_(MPI_Comm comm)
310: {
311:   PetscErrorCode    ierr;
312:   PetscBool         flg;
313:   PetscMatlabEngine mengine;

315:   if (Petsc_Matlab_Engine_keyval == MPI_KEYVAL_INVALID) {
316:     MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, MPI_COMM_NULL_DELETE_FN, &Petsc_Matlab_Engine_keyval, 0);
317:     if (ierr) {
318:       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
319:       return NULL;
320:     }
321:   }
322:   MPI_Comm_get_attr(comm, Petsc_Matlab_Engine_keyval, (void **)&mengine, (int *)&flg);
323:   if (ierr) {
324:     PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
325:     return NULL;
326:   }
327:   if (!flg) { /* viewer not yet created */
328:     PetscMatlabEngineCreate(comm, NULL, &mengine);
329:     if (ierr) {
330:       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
331:       return NULL;
332:     }
333:     PetscObjectRegisterDestroy((PetscObject)mengine);
334:     if (ierr) {
335:       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " ");
336:       return NULL;
337:     }
338:     MPI_Comm_set_attr(comm, Petsc_Matlab_Engine_keyval, mengine);
339:     if (ierr) {
340:       PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " ");
341:       return NULL;
342:     }
343:   }
344:   return mengine;
345: }

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

351:     Collective

353:     Input Parameters:
354: +    mengine - the MATLAB engine
355: .    m,n - the dimensions of the array
356: .    array - the array (represented in one dimension)
357: -    name - the name of the array

359:    Level: advanced

361: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEngineCreate()`, `PetscMatlabEngineGet()`,
362:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
363:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
364: @*/
365: PetscErrorCode PetscMatlabEnginePutArray(PetscMatlabEngine mengine, int m, int n, const PetscScalar *array, const char name[])
366: {
367:   mxArray *mat;

370:   PetscInfo(0, "Putting MATLAB array %s\n", name);
371: #if !defined(PETSC_USE_COMPLEX)
372:   mat = mxCreateDoubleMatrix(m, n, mxREAL);
373: #else
374:   mat = mxCreateDoubleMatrix(m, n, mxCOMPLEX);
375: #endif
376:   PetscArraycpy(mxGetPr(mat), array, m * n);
377:   engPutVariable(mengine->ep, name, mat);

379:   PetscInfo(0, "Put MATLAB array %s\n", name);
380:   return 0;
381: }

383: /*@C
384:     PetscMatlabEngineGetArray - Gets a variable from MATLAB into an array

386:     Not Collective

388:     Input Parameters:
389: +    mengine - the MATLAB engine
390: .    m,n - the dimensions of the array
391: .    array - the array (represented in one dimension)
392: -    name - the name of the array

394:    Level: advanced

396: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineCreate()`,
397:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
398:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGet()`, `PetscMatlabEngine`
399: @*/
400: PetscErrorCode PetscMatlabEngineGetArray(PetscMatlabEngine mengine, int m, int n, PetscScalar *array, const char name[])
401: {
402:   mxArray *mat;

405:   PetscInfo(0, "Getting MATLAB array %s\n", name);
406:   mat = engGetVariable(mengine->ep, name);
410:   PetscArraycpy(array, mxGetPr(mat), m * n);
411:   PetscInfo(0, "Got MATLAB array %s\n", name);
412:   return 0;
413: }