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

 36:    Level: advanced

 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: {
 44:   PetscMPIInt       rank, size;
 45:   char              buffer[256];
 46:   PetscMatlabEngine e;
 47:   PetscBool         flg = PETSC_FALSE;
 48:   char              lhost[64];
 49:   if (MATLABENGINE_CLASSID == -1) PetscClassIdRegister("MATLAB Engine", &MATLABENGINE_CLASSID);
 50:   PetscHeaderCreate(e, MATLABENGINE_CLASSID, "MatlabEngine", "MATLAB Engine", "Sys", comm, PetscMatlabEngineDestroy, NULL);

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

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

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

 89: /*@
 90:    PetscMatlabEngineDestroy - Shuts down a MATLAB engine.

 92:    Collective on v

 94:    Input Parameters:
 95: .  e  - the engine

 97:    Level: advanced

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

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

118: /*@C
119:     PetscMatlabEngineEvaluate - Evaluates a string in MATLAB

121:     Not Collective

123:     Input Parameters:
124: +   mengine - the MATLAB engine
125: -   string - format as in a printf()

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

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

132:    Level: advanced

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

144:   va_start(Argp, string);
145:   PetscVSNPrintf(buffer, sizeof(buffer) - 9 - 5, string, &fullLength, Argp);
146:   va_end(Argp);

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

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

160: /*@C
161:     PetscMatlabEngineGetOutput - Gets a string buffer where the MATLAB output is
162:           printed

164:     Not Collective

166:     Input Parameter:
167: .   mengine - the MATLAB engine

169:     Output Parameter:
170: .   string - buffer where MATLAB output is printed

172:    Level: advanced

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

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

188:     Collective on mengine

190:     Input Parameters:
191: +    mengine - the MATLAB engine
192: -    fd - the file

194:    Level: advanced

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

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

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

215:     Collective on mengine

217:     Input Parameters:
218: +    mengine - the MATLAB engine
219: -    object - the PETSc object, for example Vec

221:    Level: advanced

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

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

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

244: /*@
245:     PetscMatlabEngineGet - Gets a variable from MATLAB into a PETSc object.

247:     Collective on mengine

249:     Input Parameters:
250: +    mengine - the MATLAB engine
251: -    object - the PETSc object, for example a `Vec`

253:    Level: advanced

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

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

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

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

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

286:    Not Collective

288:    Input Parameter:
289: .  comm - the MPI communicator to share the engine

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

294:    Level: developer

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

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

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

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

348:     Collective on mengine

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

356:    Level: advanced

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

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

376:   PetscInfo(0, "Put MATLAB array %s\n", name);
377:   return 0;
378: }

380: /*@C
381:     PetscMatlabEngineGetArray - Gets a variable from MATLAB into an array

383:     Not Collective

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

391:    Level: advanced

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

402:   PetscInfo(0, "Getting MATLAB array %s\n", name);
403:   mat = engGetVariable(mengine->ep, name);
407:   PetscArraycpy(array, mxGetPr(mat), m * n);
408:   PetscInfo(0, "Got MATLAB array %s\n", name);
409:   return 0;
410: }