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 /*@C 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 PetscFunctionBegin; 52 if (MATLABENGINE_CLASSID == -1) PetscCall(PetscClassIdRegister("MATLAB Engine", &MATLABENGINE_CLASSID)); 53 PetscCall(PetscHeaderCreate(e, MATLABENGINE_CLASSID, "MatlabEngine", "MATLAB Engine", "Sys", comm, PetscMatlabEngineDestroy, NULL)); 54 55 if (!host) { 56 PetscCall(PetscOptionsGetString(NULL, NULL, "-matlab_engine_host", lhost, sizeof(lhost), &flg)); 57 if (flg) host = lhost; 58 } 59 flg = PETSC_FALSE; 60 PetscCall(PetscOptionsGetBool(NULL, NULL, "-matlab_engine_graphics", &flg, NULL)); 61 62 if (host) { 63 PetscCall(PetscInfo(0, "Starting MATLAB engine on %s\n", host)); 64 PetscCall(PetscStrncpy(buffer, "ssh ", sizeof(buffer))); 65 PetscCall(PetscStrlcat(buffer, host, sizeof(buffer))); 66 PetscCall(PetscStrlcat(buffer, " \"", sizeof(buffer))); 67 PetscCall(PetscStrlcat(buffer, PETSC_MATLAB_COMMAND, sizeof(buffer))); 68 if (!flg) PetscCall(PetscStrlcat(buffer, " -nodisplay ", sizeof(buffer))); 69 PetscCall(PetscStrlcat(buffer, " -nosplash ", sizeof(buffer))); 70 PetscCall(PetscStrlcat(buffer, "\"", sizeof(buffer))); 71 } else { 72 PetscCall(PetscStrncpy(buffer, PETSC_MATLAB_COMMAND, sizeof(buffer))); 73 if (!flg) PetscCall(PetscStrlcat(buffer, " -nodisplay ", sizeof(buffer))); 74 PetscCall(PetscStrlcat(buffer, " -nosplash ", sizeof(buffer))); 75 } 76 PetscCall(PetscInfo(0, "Starting MATLAB engine with command %s\n", buffer)); 77 e->ep = engOpen(buffer); 78 PetscCheck(e->ep, PETSC_COMM_SELF, PETSC_ERR_LIB, "Unable to start MATLAB engine with %s", buffer); 79 engOutputBuffer(e->ep, e->buffer, sizeof(e->buffer)); 80 if (host) PetscCall(PetscInfo(0, "Started MATLAB engine on %s\n", host)); 81 else PetscCall(PetscInfo(0, "Started MATLAB engine\n")); 82 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\n%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, 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 $ PetscMatlabEngineYYY(XXX object, PETSC_MATLAB_ENGINE_(comm)); 309 310 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`, 311 `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`, 312 `PetscMatlabEngineCreate()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`, 313 `PETSC_MATLAB_ENGINE_WORLD`, `PETSC_MATLAB_ENGINE_SELF` 314 @*/ 315 PetscMatlabEngine PETSC_MATLAB_ENGINE_(MPI_Comm comm) 316 { 317 PetscErrorCode ierr; 318 PetscBool flg; 319 PetscMatlabEngine mengine; 320 321 PetscFunctionBegin; 322 if (Petsc_Matlab_Engine_keyval == MPI_KEYVAL_INVALID) { 323 ierr = MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, MPI_COMM_NULL_DELETE_FN, &Petsc_Matlab_Engine_keyval, 0); 324 if (ierr) { 325 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 326 PetscFunctionReturn(NULL); 327 } 328 } 329 ierr = MPI_Comm_get_attr(comm, Petsc_Matlab_Engine_keyval, (void **)&mengine, (int *)&flg); 330 if (ierr) { 331 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 332 PetscFunctionReturn(NULL); 333 } 334 if (!flg) { /* viewer not yet created */ 335 ierr = PetscMatlabEngineCreate(comm, NULL, &mengine); 336 if (ierr) { 337 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " "); 338 PetscFunctionReturn(NULL); 339 } 340 ierr = PetscObjectRegisterDestroy((PetscObject)mengine); 341 if (ierr) { 342 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " "); 343 PetscFunctionReturn(NULL); 344 } 345 ierr = MPI_Comm_set_attr(comm, Petsc_Matlab_Engine_keyval, mengine); 346 if (ierr) { 347 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 348 PetscFunctionReturn(NULL); 349 } 350 } 351 PetscFunctionReturn(mengine); 352 } 353 354 /*@C 355 PetscMatlabEnginePutArray - Puts an array into the MATLAB space, treating it as a Fortran style (column major ordering) array. For parallel objects, 356 each processors part is put in a separate MATLAB process. 357 358 Collective 359 360 Input Parameters: 361 + mengine - the MATLAB engine 362 . m - the x dimension of the array 363 . n - the y dimension of the array 364 . array - the array (represented in one dimension) 365 - name - the name of the array 366 367 Level: advanced 368 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; 376 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); 387 388 PetscCall(PetscInfo(0, "Put MATLAB array %s\n", name)); 389 PetscFunctionReturn(PETSC_SUCCESS); 390 } 391 392 /*@C 393 PetscMatlabEngineGetArray - Gets a variable from MATLAB into an array 394 395 Not Collective 396 397 Input Parameters: 398 + mengine - the MATLAB engine 399 . m - the x dimension of the array 400 . n - the y dimension of the array 401 . array - the array (represented in one dimension) 402 - name - the name of the array 403 404 Level: advanced 405 406 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineCreate()`, 407 `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`, 408 `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGet()`, `PetscMatlabEngine` 409 @*/ 410 PetscErrorCode PetscMatlabEngineGetArray(PetscMatlabEngine mengine, int m, int n, PetscScalar *array, const char name[]) 411 { 412 mxArray *mat; 413 414 PetscFunctionBegin; 415 PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed"); 416 PetscCall(PetscInfo(0, "Getting MATLAB array %s\n", name)); 417 mat = engGetVariable(mengine->ep, name); 418 PetscCheck(mat, PETSC_COMM_SELF, PETSC_ERR_LIB, "Unable to get array %s from matlab", name); 419 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); 420 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); 421 PetscCall(PetscArraycpy(array, mxGetPr(mat), m * n)); 422 PetscCall(PetscInfo(0, "Got MATLAB array %s\n", name)); 423 PetscFunctionReturn(PETSC_SUCCESS); 424 } 425