1 2 #include <engine.h> /* MATLAB include file */ 3 #include <petscsys.h> 4 #include <petscmatlab.h> /*I "petscmatlab.h" I*/ 5 #include <petsc/private/petscimpl.h> 6 7 struct _p_PetscMatlabEngine { 8 PETSCHEADER(int); 9 Engine *ep; 10 char buffer[1024]; 11 }; 12 13 PetscClassId MATLABENGINE_CLASSID = -1; 14 15 /*@C 16 PetscMatlabEngineCreate - Creates a MATLAB engine object 17 18 Not Collective 19 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) 23 24 Output Parameter: 25 . mengine - the resulting object 26 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) 31 32 Note: 33 If a host string is passed in, any MATLAB scripts that need to run in the 34 engine must be available via MATLABPATH on that machine. 35 36 Level: advanced 37 38 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`, 39 `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`, 40 `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine` 41 @*/ 42 PetscErrorCode PetscMatlabEngineCreate(MPI_Comm comm, const char host[], PetscMatlabEngine *mengine) 43 { 44 PetscMPIInt rank, size; 45 char buffer[256]; 46 PetscMatlabEngine e; 47 PetscBool flg = PETSC_FALSE; 48 char lhost[64]; 49 PetscFunctionBegin; 50 if (MATLABENGINE_CLASSID == -1) PetscCall(PetscClassIdRegister("MATLAB Engine", &MATLABENGINE_CLASSID)); 51 PetscCall(PetscHeaderCreate(e, MATLABENGINE_CLASSID, "MatlabEngine", "MATLAB Engine", "Sys", comm, PetscMatlabEngineDestroy, NULL)); 52 53 if (!host) { 54 PetscCall(PetscOptionsGetString(NULL, NULL, "-matlab_engine_host", lhost, sizeof(lhost), &flg)); 55 if (flg) host = lhost; 56 } 57 flg = PETSC_FALSE; 58 PetscCall(PetscOptionsGetBool(NULL, NULL, "-matlab_engine_graphics", &flg, NULL)); 59 60 if (host) { 61 PetscCall(PetscInfo(0, "Starting MATLAB engine on %s\n", host)); 62 PetscCall(PetscStrcpy(buffer, "ssh ")); 63 PetscCall(PetscStrcat(buffer, host)); 64 PetscCall(PetscStrcat(buffer, " \"")); 65 PetscCall(PetscStrlcat(buffer, PETSC_MATLAB_COMMAND, sizeof(buffer))); 66 if (!flg) PetscCall(PetscStrlcat(buffer, " -nodisplay ", sizeof(buffer))); 67 PetscCall(PetscStrlcat(buffer, " -nosplash ", sizeof(buffer))); 68 PetscCall(PetscStrcat(buffer, "\"")); 69 } else { 70 PetscCall(PetscStrncpy(buffer, PETSC_MATLAB_COMMAND, sizeof(buffer))); 71 if (!flg) PetscCall(PetscStrlcat(buffer, " -nodisplay ", sizeof(buffer))); 72 PetscCall(PetscStrlcat(buffer, " -nosplash ", sizeof(buffer))); 73 } 74 PetscCall(PetscInfo(0, "Starting MATLAB engine with command %s\n", buffer)); 75 e->ep = engOpen(buffer); 76 PetscCheck(e->ep, PETSC_COMM_SELF, PETSC_ERR_LIB, "Unable to start MATLAB engine with %s", buffer); 77 engOutputBuffer(e->ep, e->buffer, sizeof(e->buffer)); 78 if (host) PetscCall(PetscInfo(0, "Started MATLAB engine on %s\n", host)); 79 else PetscCall(PetscInfo(0, "Started MATLAB engine\n")); 80 81 PetscCallMPI(MPI_Comm_rank(comm, &rank)); 82 PetscCallMPI(MPI_Comm_size(comm, &size)); 83 PetscCall(PetscMatlabEngineEvaluate(e, "MPI_Comm_rank = %d; MPI_Comm_size = %d;\n", rank, size)); 84 /* work around bug in MATLAB R2021b https://www.mathworks.com/matlabcentral/answers/1566246-got-error-using-exit-in-nodesktop-mode */ 85 PetscCall(PetscMatlabEngineEvaluate(e, "settings")); 86 *mengine = e; 87 PetscFunctionReturn(0); 88 } 89 90 /*@ 91 PetscMatlabEngineDestroy - Shuts down a MATLAB engine. 92 93 Collective on v 94 95 Input Parameters: 96 . e - the engine 97 98 Level: advanced 99 100 .seealso: `PetscMatlabEngineCreate()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`, 101 `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`, 102 `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine` 103 @*/ 104 PetscErrorCode PetscMatlabEngineDestroy(PetscMatlabEngine *v) 105 { 106 int err; 107 108 PetscFunctionBegin; 109 if (!*v) PetscFunctionReturn(0); 110 PetscValidHeaderSpecific(*v, MATLABENGINE_CLASSID, 1); 111 if (--((PetscObject)(*v))->refct > 0) PetscFunctionReturn(0); 112 PetscCall(PetscInfo(0, "Stopping MATLAB engine\n")); 113 err = engClose((*v)->ep); 114 PetscCheck(!err, PETSC_COMM_SELF, PETSC_ERR_LIB, "Error closing Matlab engine"); 115 PetscCall(PetscInfo(0, "MATLAB engine stopped\n")); 116 PetscCall(PetscHeaderDestroy(v)); 117 PetscFunctionReturn(0); 118 } 119 120 /*@C 121 PetscMatlabEngineEvaluate - Evaluates a string in MATLAB 122 123 Not Collective 124 125 Input Parameters: 126 + mengine - the MATLAB engine 127 - string - format as in a printf() 128 129 Notes: 130 Run the PETSc program with -info to always have printed back MATLAB's response to the string evaluation 131 132 If the string utilizes a MATLAB script that needs to run in the engine, the script must be available via MATLABPATH on that machine. 133 134 Level: advanced 135 136 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`, 137 `PetscMatlabEngineCreate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`, 138 `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine` 139 @*/ 140 PetscErrorCode PetscMatlabEngineEvaluate(PetscMatlabEngine mengine, const char string[], ...) 141 { 142 va_list Argp; 143 char buffer[1024]; 144 size_t fullLength; 145 146 PetscFunctionBegin; 147 va_start(Argp, string); 148 PetscCall(PetscVSNPrintf(buffer, sizeof(buffer) - 9 - 5, string, &fullLength, Argp)); 149 va_end(Argp); 150 151 PetscCall(PetscInfo(0, "Evaluating MATLAB string: %s\n", buffer)); 152 engEvalString(mengine->ep, buffer); 153 PetscCall(PetscInfo(0, "Done evaluating MATLAB string: %s\n", buffer)); 154 PetscCall(PetscInfo(0, " MATLAB output message: %s\n", mengine->buffer)); 155 156 /* 157 Check for error in MATLAB: indicated by ? as first character in engine->buffer 158 */ 159 PetscCheck(mengine->buffer[4] != '?', PETSC_COMM_SELF, PETSC_ERR_LIB, "Error in evaluating MATLAB command:%s\n%s", string, mengine->buffer); 160 PetscFunctionReturn(0); 161 } 162 163 /*@C 164 PetscMatlabEngineGetOutput - Gets a string buffer where the MATLAB output is 165 printed 166 167 Not Collective 168 169 Input Parameter: 170 . mengine - the MATLAB engine 171 172 Output Parameter: 173 . string - buffer where MATLAB output is printed 174 175 Level: advanced 176 177 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`, 178 `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineCreate()`, `PetscMatlabEnginePrintOutput()`, 179 `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine` 180 @*/ 181 PetscErrorCode PetscMatlabEngineGetOutput(PetscMatlabEngine mengine, char **string) 182 { 183 PetscFunctionBegin; 184 PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed"); 185 *string = mengine->buffer; 186 PetscFunctionReturn(0); 187 } 188 189 /*@C 190 PetscMatlabEnginePrintOutput - prints the output from MATLAB to an ASCII file 191 192 Collective on mengine 193 194 Input Parameters: 195 + mengine - the MATLAB engine 196 - fd - the file 197 198 Level: advanced 199 200 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`, 201 `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEngineCreate()`, 202 `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine` 203 @*/ 204 PetscErrorCode PetscMatlabEnginePrintOutput(PetscMatlabEngine mengine, FILE *fd) 205 { 206 PetscMPIInt rank; 207 208 PetscFunctionBegin; 209 PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed"); 210 PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)mengine), &rank)); 211 PetscCall(PetscSynchronizedFPrintf(PetscObjectComm((PetscObject)mengine), fd, "[%d]%s", rank, mengine->buffer)); 212 PetscCall(PetscSynchronizedFlush(PetscObjectComm((PetscObject)mengine), fd)); 213 PetscFunctionReturn(0); 214 } 215 216 /*@ 217 PetscMatlabEnginePut - Puts a Petsc object, such as a `Mat` or `Vec` into the MATLAB space. For parallel objects, 218 each processor's part is put in a separate MATLAB process. 219 220 Collective on mengine 221 222 Input Parameters: 223 + mengine - the MATLAB engine 224 - object - the PETSc object, for example Vec 225 226 Level: advanced 227 228 Note: 229 `Mat`s transferred between PETSc and MATLAB and vis versa are transposed in the other space 230 (this is because MATLAB uses compressed column format and PETSc uses compressed row format) 231 232 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEngineCreate()`, `PetscMatlabEngineGet()`, 233 `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`, 234 `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine` 235 @*/ 236 PetscErrorCode PetscMatlabEnginePut(PetscMatlabEngine mengine, PetscObject obj) 237 { 238 PetscErrorCode (*put)(PetscObject, void *); 239 240 PetscFunctionBegin; 241 PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed"); 242 PetscCall(PetscObjectQueryFunction(obj, "PetscMatlabEnginePut_C", &put)); 243 PetscCheck(put, PETSC_COMM_SELF, PETSC_ERR_SUP, "Object %s cannot be put into MATLAB engine", obj->class_name); 244 PetscCall(PetscInfo(0, "Putting MATLAB object\n")); 245 PetscCall((*put)(obj, mengine->ep)); 246 PetscCall(PetscInfo(0, "Put MATLAB object: %s\n", obj->name)); 247 PetscFunctionReturn(0); 248 } 249 250 /*@ 251 PetscMatlabEngineGet - Gets a variable from MATLAB into a PETSc object. 252 253 Collective on mengine 254 255 Input Parameters: 256 + mengine - the MATLAB engine 257 - object - the PETSc object, for example a `Vec` 258 259 Level: advanced 260 261 Note: 262 `Mat`s transferred between PETSc and MATLAB and vis versa are transposed in the other space 263 (this is because MATLAB uses compressed column format and PETSc uses compressed row format) 264 265 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineCreate()`, 266 `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`, 267 `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine` 268 @*/ 269 PetscErrorCode PetscMatlabEngineGet(PetscMatlabEngine mengine, PetscObject obj) 270 { 271 PetscErrorCode (*get)(PetscObject, void *); 272 273 PetscFunctionBegin; 274 PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed"); 275 PetscCheck(obj->name, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Cannot get object that has no name"); 276 PetscCall(PetscObjectQueryFunction(obj, "PetscMatlabEngineGet_C", &get)); 277 PetscCheck(get, PETSC_COMM_SELF, PETSC_ERR_SUP, "Object %s cannot be gotten from MATLAB engine", obj->class_name); 278 PetscCall(PetscInfo(0, "Getting MATLAB object\n")); 279 PetscCall((*get)(obj, mengine->ep)); 280 PetscCall(PetscInfo(0, "Got MATLAB object: %s\n", obj->name)); 281 PetscFunctionReturn(0); 282 } 283 284 /* 285 The variable Petsc_Matlab_Engine_keyval is used to indicate an MPI attribute that 286 is attached to a communicator, in this case the attribute is a PetscMatlabEngine 287 */ 288 static PetscMPIInt Petsc_Matlab_Engine_keyval = MPI_KEYVAL_INVALID; 289 290 /*@C 291 PETSC_MATLAB_ENGINE_ - Creates a MATLAB engine on each process in a communicator. 292 293 Not Collective 294 295 Input Parameter: 296 . comm - the MPI communicator to share the engine 297 298 Options Database Key: 299 . -matlab_engine_host - hostname on which to run MATLAB, one must be able to ssh to this host 300 301 Level: developer 302 303 Note: 304 Unlike almost all other PETSc routines, this does not return 305 an error code. Usually used in the form 306 $ PetscMatlabEngineYYY(XXX object,PETSC_MATLAB_ENGINE_(comm)); 307 308 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGet()`, 309 `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`, 310 `PetscMatlabEngineCreate()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine`, 311 `PETSC_MATLAB_ENGINE_WORLD`, `PETSC_MATLAB_ENGINE_SELF` 312 @*/ 313 PetscMatlabEngine PETSC_MATLAB_ENGINE_(MPI_Comm comm) 314 { 315 PetscErrorCode ierr; 316 PetscBool flg; 317 PetscMatlabEngine mengine; 318 319 PetscFunctionBegin; 320 if (Petsc_Matlab_Engine_keyval == MPI_KEYVAL_INVALID) { 321 ierr = MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, MPI_COMM_NULL_DELETE_FN, &Petsc_Matlab_Engine_keyval, 0); 322 if (ierr) { 323 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 324 PetscFunctionReturn(NULL); 325 } 326 } 327 ierr = MPI_Comm_get_attr(comm, Petsc_Matlab_Engine_keyval, (void **)&mengine, (int *)&flg); 328 if (ierr) { 329 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 330 PetscFunctionReturn(NULL); 331 } 332 if (!flg) { /* viewer not yet created */ 333 ierr = PetscMatlabEngineCreate(comm, NULL, &mengine); 334 if (ierr) { 335 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " "); 336 PetscFunctionReturn(NULL); 337 } 338 ierr = PetscObjectRegisterDestroy((PetscObject)mengine); 339 if (ierr) { 340 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_REPEAT, " "); 341 PetscFunctionReturn(NULL); 342 } 343 ierr = MPI_Comm_set_attr(comm, Petsc_Matlab_Engine_keyval, mengine); 344 if (ierr) { 345 PetscError(PETSC_COMM_SELF, __LINE__, "PETSC_MATLAB_ENGINE_", __FILE__, PETSC_ERR_PLIB, PETSC_ERROR_INITIAL, " "); 346 PetscFunctionReturn(NULL); 347 } 348 } 349 PetscFunctionReturn(mengine); 350 } 351 352 /*@C 353 PetscMatlabEnginePutArray - Puts an array into the MATLAB space, treating it as a Fortran style (column major ordering) array. For parallel objects, 354 each processors part is put in a separate MATLAB process. 355 356 Collective on mengine 357 358 Input Parameters: 359 + mengine - the MATLAB engine 360 . m,n - the dimensions of the array 361 . array - the array (represented in one dimension) 362 - name - the name of the array 363 364 Level: advanced 365 366 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEngineCreate()`, `PetscMatlabEngineGet()`, 367 `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`, 368 `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineGetArray()`, `PetscMatlabEngine` 369 @*/ 370 PetscErrorCode PetscMatlabEnginePutArray(PetscMatlabEngine mengine, int m, int n, const PetscScalar *array, const char name[]) 371 { 372 mxArray *mat; 373 374 PetscFunctionBegin; 375 PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed"); 376 PetscCall(PetscInfo(0, "Putting MATLAB array %s\n", name)); 377 #if !defined(PETSC_USE_COMPLEX) 378 mat = mxCreateDoubleMatrix(m, n, mxREAL); 379 #else 380 mat = mxCreateDoubleMatrix(m, n, mxCOMPLEX); 381 #endif 382 PetscCall(PetscArraycpy(mxGetPr(mat), array, m * n)); 383 engPutVariable(mengine->ep, name, mat); 384 385 PetscCall(PetscInfo(0, "Put MATLAB array %s\n", name)); 386 PetscFunctionReturn(0); 387 } 388 389 /*@C 390 PetscMatlabEngineGetArray - Gets a variable from MATLAB into an array 391 392 Not Collective 393 394 Input Parameters: 395 + mengine - the MATLAB engine 396 . m,n - the dimensions of the array 397 . array - the array (represented in one dimension) 398 - name - the name of the array 399 400 Level: advanced 401 402 .seealso: `PetscMatlabEngineDestroy()`, `PetscMatlabEnginePut()`, `PetscMatlabEngineCreate()`, 403 `PetscMatlabEngineEvaluate()`, `PetscMatlabEngineGetOutput()`, `PetscMatlabEnginePrintOutput()`, 404 `PETSC_MATLAB_ENGINE_()`, `PetscMatlabEnginePutArray()`, `PetscMatlabEngineGet()`, `PetscMatlabEngine` 405 @*/ 406 PetscErrorCode PetscMatlabEngineGetArray(PetscMatlabEngine mengine, int m, int n, PetscScalar *array, const char name[]) 407 { 408 mxArray *mat; 409 410 PetscFunctionBegin; 411 PetscCheck(mengine, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_MATLAB_ENGINE_() failed"); 412 PetscCall(PetscInfo(0, "Getting MATLAB array %s\n", name)); 413 mat = engGetVariable(mengine->ep, name); 414 PetscCheck(mat, PETSC_COMM_SELF, PETSC_ERR_LIB, "Unable to get array %s from matlab", name); 415 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); 416 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); 417 PetscCall(PetscArraycpy(array, mxGetPr(mat), m * n)); 418 PetscCall(PetscInfo(0, "Got MATLAB array %s\n", name)); 419 PetscFunctionReturn(0); 420 } 421