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