1 #include <engine.h> /* MATLAB include file */ 2 #include <petscsys.h> 3 #include <petscmatlab.h> /*I "petscmatlab.h" I*/ 4 #include <petsc/private/petscimpl.h> 5 6 struct _p_PetscMatlabEngine { 7 PETSCHEADER(int); 8 Engine *ep; 9 char buffer[1024]; 10 }; 11 12 PetscClassId MATLABENGINE_CLASSID = -1; 13 14 /*@ 15 PetscMatlabEngineCreate - Creates a MATLAB engine object 16 17 Not Collective 18 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) 22 23 Output Parameter: 24 . mengine - the resulting object 25 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) 30 31 Level: advanced 32 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. 36 37 One must `./configure` PETSc with `--with-matlab [-with-matlab-dir=matlab_root_directory]` to 38 use this capability 39 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]; 51 52 PetscFunctionBegin; 53 PetscAssertPointer(mengine, 3); 54 if (MATLABENGINE_CLASSID == -1) PetscCall(PetscClassIdRegister("MATLAB Engine", &MATLABENGINE_CLASSID)); 55 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 } 91 92 /*@ 93 PetscMatlabEngineDestroy - Shuts down a MATLAB engine. 94 95 Collective 96 97 Input Parameter: 98 . v - the engine 99 100 Level: advanced 101 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; 109 110 PetscFunctionBegin; 111 if (!*v) PetscFunctionReturn(PETSC_SUCCESS); 112 PetscValidHeaderSpecific(*v, MATLABENGINE_CLASSID, 1); 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 } 121 122 /*@C 123 PetscMatlabEngineEvaluate - Evaluates a string in MATLAB 124 125 Not Collective 126 127 Input Parameters: 128 + mengine - the MATLAB engine 129 - string - format as in a printf() 130 131 Notes: 132 Run the PETSc program with -info to always have printed back MATLAB's response to the string evaluation 133 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. 135 136 Level: advanced 137 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; 147 148 PetscFunctionBegin; 149 va_start(Argp, string); 150 PetscCall(PetscVSNPrintf(buffer, sizeof(buffer) - 9 - 5, string, &fullLength, Argp)); 151 va_end(Argp); 152 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)); 157 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 } 164 165 /*@C 166 PetscMatlabEngineGetOutput - Gets a string buffer where the MATLAB output is 167 printed 168 169 Not Collective 170 171 Input Parameter: 172 . mengine - the MATLAB engine 173 174 Output Parameter: 175 . string - buffer where MATLAB output is printed 176 177 Level: advanced 178 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 } 190 191 /*@C 192 PetscMatlabEnginePrintOutput - prints the output from MATLAB to an ASCII file 193 194 Collective 195 196 Input Parameters: 197 + mengine - the MATLAB engine 198 - fd - the file 199 200 Level: advanced 201 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; 209 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 } 217 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. 221 222 Collective 223 224 Input Parameters: 225 + mengine - the MATLAB engine 226 - obj - the PETSc object, for example Vec 227 228 Level: advanced 229 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) 233 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 *); 241 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 } 251 252 /*@ 253 PetscMatlabEngineGet - Gets a variable from MATLAB into a PETSc object. 254 255 Collective 256 257 Input Parameters: 258 + mengine - the MATLAB engine 259 - obj - the PETSc object, for example a `Vec` 260 261 Level: advanced 262 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) 266 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 *); 274 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 } 285 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; 291 292 /*@C 293 PETSC_MATLAB_ENGINE_ - Creates a MATLAB engine on each process in a communicator. 294 295 Not Collective 296 297 Input Parameter: 298 . comm - the MPI communicator to share the engine 299 300 Options Database Key: 301 . -matlab_engine_host - hostname on which to run MATLAB, one must be able to ssh to this host 302 303 Level: developer 304 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 311 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 PetscMPIInt iflg; 321 PetscMatlabEngine mengine; 322 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, &iflg); 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 (!iflg) { /* 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 } 355 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. 359 360 Collective 361 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 368 369 Level: advanced 370 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; 378 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); 389 390 PetscCall(PetscInfo(0, "Put MATLAB array %s\n", name)); 391 PetscFunctionReturn(PETSC_SUCCESS); 392 } 393 394 /*@ 395 PetscMatlabEngineGetArray - Gets a variable from MATLAB into an array 396 397 Not Collective 398 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 405 406 Level: advanced 407 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; 415 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 } 427