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 Notes: 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 One must `./configure` PETSc with `--with-matlab [-with-matlab-dir=matlab_root_directory]` to 37 use this capability 38 39 Level: advanced 40 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)); 55 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)); 62 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 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 } 92 93 /*@ 94 PetscMatlabEngineDestroy - Shuts down a MATLAB engine. 95 96 Collective 97 98 Input Parameters: 99 . e - the engine 100 101 Level: advanced 102 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; 110 111 PetscFunctionBegin; 112 if (!*v) PetscFunctionReturn(PETSC_SUCCESS); 113 PetscValidHeaderSpecific(*v, MATLABENGINE_CLASSID, 1); 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 } 122 123 /*@C 124 PetscMatlabEngineEvaluate - Evaluates a string in MATLAB 125 126 Not Collective 127 128 Input Parameters: 129 + mengine - the MATLAB engine 130 - string - format as in a printf() 131 132 Notes: 133 Run the PETSc program with -info to always have printed back MATLAB's response to the string evaluation 134 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. 136 137 Level: advanced 138 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; 148 149 PetscFunctionBegin; 150 va_start(Argp, string); 151 PetscCall(PetscVSNPrintf(buffer, sizeof(buffer) - 9 - 5, string, &fullLength, Argp)); 152 va_end(Argp); 153 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)); 158 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 } 165 166 /*@C 167 PetscMatlabEngineGetOutput - Gets a string buffer where the MATLAB output is 168 printed 169 170 Not Collective 171 172 Input Parameter: 173 . mengine - the MATLAB engine 174 175 Output Parameter: 176 . string - buffer where MATLAB output is printed 177 178 Level: advanced 179 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 } 191 192 /*@C 193 PetscMatlabEnginePrintOutput - prints the output from MATLAB to an ASCII file 194 195 Collective 196 197 Input Parameters: 198 + mengine - the MATLAB engine 199 - fd - the file 200 201 Level: advanced 202 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; 210 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 } 218 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. 222 223 Collective 224 225 Input Parameters: 226 + mengine - the MATLAB engine 227 - object - the PETSc object, for example Vec 228 229 Level: advanced 230 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) 234 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 *); 242 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 } 252 253 /*@ 254 PetscMatlabEngineGet - Gets a variable from MATLAB into a PETSc object. 255 256 Collective 257 258 Input Parameters: 259 + mengine - the MATLAB engine 260 - object - the PETSc object, for example a `Vec` 261 262 Level: advanced 263 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) 267 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 *); 275 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 } 286 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; 292 293 /*@C 294 PETSC_MATLAB_ENGINE_ - Creates a MATLAB engine on each process in a communicator. 295 296 Not Collective 297 298 Input Parameter: 299 . comm - the MPI communicator to share the engine 300 301 Options Database Key: 302 . -matlab_engine_host - hostname on which to run MATLAB, one must be able to ssh to this host 303 304 Level: developer 305 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)); 310 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; 321 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 } 354 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. 358 359 Collective 360 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 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,n - the dimensions of the array 400 . array - the array (represented in one dimension) 401 - name - the name of the array 402 403 Level: advanced 404 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; 412 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 } 424