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