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