Actual source code: matlab.c

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

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

 12: PetscClassId MATLABENGINE_CLASSID = -1;

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

 17:   Not Collective

 19:   Input Parameters:
 20: + comm - a separate MATLAB engine is started for each process in the communicator
 21: - host - name of machine where MATLAB engine is to be run (usually NULL)

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

 26:   Options Database Keys:
 27: + -matlab_engine_graphics - allow the MATLAB engine to display graphics
 28: . -matlab_engine_host     - hostname, machine to run the MATLAB engine on
 29: - -info                   - print out all requests to MATLAB and all if its responses (for debugging)

 31:   Level: advanced

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

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

 40: .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`,
 41:           `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`,
 42:           `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`
 43: @*/
 44: PetscErrorCode PetscMatlabEngineCreate(MPI_Comm comm, const char host[], PetscMatlabEngine *mengine)
 45: {
 46:   PetscMPIInt       rank, size;
 47:   char              buffer[256];
 48:   PetscMatlabEngine e;
 49:   PetscBool         flg = PETSC_FALSE;
 50:   char              lhost[64];

 52:   PetscFunctionBegin;
 53:   PetscAssertPointer(mengine, 3);
 54:   if (MATLABENGINE_CLASSID == -1) PetscCall(PetscClassIdRegister("MATLAB Engine", &MATLABENGINE_CLASSID));

 56:   PetscCall(PetscHeaderCreate(e, MATLABENGINE_CLASSID, "MatlabEngine", "MATLAB Engine", "Sys", comm, PetscMatlabEngineDestroy, NULL));
 57:   if (!host) {
 58:     PetscCall(PetscOptionsGetString(NULL, NULL, "-matlab_engine_host", lhost, sizeof(lhost), &flg));
 59:     if (flg) host = lhost;
 60:   }
 61:   flg = PETSC_FALSE;
 62:   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"));
 83:   PetscCallMPI(MPI_Comm_rank(comm, &rank));
 84:   PetscCallMPI(MPI_Comm_size(comm, &size));
 85:   PetscCall(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:   PetscCall(PetscMatlabEngineEvaluate(e, "settings"));
 88:   *mengine = e;
 89:   PetscFunctionReturn(PETSC_SUCCESS);
 90: }

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

 95:   Collective

 97:   Input Parameter:
 98: . v - 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:   PetscFunctionBegin;
111:   if (!*v) PetscFunctionReturn(PETSC_SUCCESS);
113:   if (--((PetscObject)*v)->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
114:   PetscCall(PetscInfo(0, "Stopping MATLAB engine\n"));
115:   err = engClose((*v)->ep);
116:   PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error closing MATLAB engine");
117:   PetscCall(PetscInfo(0, "MATLAB engine stopped\n"));
118:   PetscCall(PetscHeaderDestroy(v));
119:   PetscFunctionReturn(PETSC_SUCCESS);
120: }

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

125:   Not Collective

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

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

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

136:   Level: advanced

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

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

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

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

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

169:   Not Collective

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

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

177:   Level: advanced

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

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

194:   Collective

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

200:   Level: advanced

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

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

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

222:   Collective

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

228:   Level: advanced

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

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

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

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

255:   Collective

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

261:   Level: advanced

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

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

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

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

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

295:    Not Collective

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

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

303:    Level: developer

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

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

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

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

360:   Collective

362:   Input Parameters:
363: + mengine - the MATLAB engine
364: . m       - the x dimension of the array
365: . n       - the y dimension of the array
366: . array   - the array (represented in one dimension)
367: - name    - the name of the array

369:   Level: advanced

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

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

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

394: /*@
395:   PetscMatlabEngineGetArray - Gets a variable from MATLAB into an array

397:   Not Collective

399:   Input Parameters:
400: + mengine - the MATLAB engine
401: . m       - the x dimension of the array
402: . n       - the y dimension of the array
403: . array   - the array (represented in one dimension), much be large enough to hold all the data
404: - name    - the name of the array

406:   Level: advanced

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

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