1cc4c1da9SBarry Smith #include <petsc/private/viewerimpl.h> /*I "petscviewer.h" I*/
2cc4c1da9SBarry Smith #include <mat.h> /*I "petscmat.h" I*/
35c6c1daeSBarry Smith
45c6c1daeSBarry Smith typedef struct {
55c6c1daeSBarry Smith MATFile *ep;
65c6c1daeSBarry Smith PetscMPIInt rank;
75c6c1daeSBarry Smith PetscFileMode btype;
85c6c1daeSBarry Smith } PetscViewer_Matlab;
95c6c1daeSBarry Smith
105d83a8b1SBarry Smith /*@
11811af0c4SBarry Smith PetscViewerMatlabPutArray - Puts an array into the `PETSCVIEWERMATLAB` viewer.
125c6c1daeSBarry Smith
1310450e9eSJacob Faibussowitsch Not Collective, only processor zero saves `array`
145c6c1daeSBarry Smith
155c6c1daeSBarry Smith Input Parameters:
165c6c1daeSBarry Smith + mfile - the viewer
1720f4b53cSBarry Smith . m - the first dimensions of `array`
1820f4b53cSBarry Smith . n - the second dimensions of `array`
195c6c1daeSBarry Smith . array - the array (represented in one dimension)
203f423023SBarry Smith - name - the MATLAB name of `array`
215c6c1daeSBarry Smith
225c6c1daeSBarry Smith Level: advanced
235c6c1daeSBarry Smith
24811af0c4SBarry Smith Note:
253f423023SBarry Smith Only writes `array` values on processor 0.
265c6c1daeSBarry Smith
27811af0c4SBarry Smith .seealso: `PETSCVIEWERMATLAB`, `PetscViewerMatlabGetArray()`
285c6c1daeSBarry Smith @*/
PetscViewerMatlabPutArray(PetscViewer mfile,int m,int n,const PetscScalar * array,const char * name)29d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscViewerMatlabPutArray(PetscViewer mfile, int m, int n, const PetscScalar *array, const char *name)
30d71ae5a4SJacob Faibussowitsch {
312cb5e1ccSBarry Smith PetscViewer_Matlab *ml;
325c6c1daeSBarry Smith mxArray *mat;
335c6c1daeSBarry Smith
345c6c1daeSBarry Smith PetscFunctionBegin;
3528b400f6SJacob Faibussowitsch PetscCheck(mfile, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_VIEWER_MATLAB_() failed");
362cb5e1ccSBarry Smith ml = (PetscViewer_Matlab *)mfile->data;
375c6c1daeSBarry Smith if (!ml->rank) {
389566063dSJacob Faibussowitsch PetscCall(PetscInfo(mfile, "Putting MATLAB array %s\n", name));
395c6c1daeSBarry Smith #if !defined(PETSC_USE_COMPLEX)
405c6c1daeSBarry Smith mat = mxCreateDoubleMatrix(m, n, mxREAL);
415c6c1daeSBarry Smith #else
425c6c1daeSBarry Smith mat = mxCreateDoubleMatrix(m, n, mxCOMPLEX);
435c6c1daeSBarry Smith #endif
449566063dSJacob Faibussowitsch PetscCall(PetscArraycpy(mxGetPr(mat), array, m * n));
455c6c1daeSBarry Smith matPutVariable(ml->ep, name, mat);
465c6c1daeSBarry Smith
479566063dSJacob Faibussowitsch PetscCall(PetscInfo(mfile, "Put MATLAB array %s\n", name));
485c6c1daeSBarry Smith }
493ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
505c6c1daeSBarry Smith }
515c6c1daeSBarry Smith
PetscViewerMatlabPutVariable(PetscViewer viewer,const char * name,void * mat)52d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscViewerMatlabPutVariable(PetscViewer viewer, const char *name, void *mat)
53d71ae5a4SJacob Faibussowitsch {
54a297a907SKarl Rupp PetscViewer_Matlab *ml = (PetscViewer_Matlab *)viewer->data;
555c6c1daeSBarry Smith
565c6c1daeSBarry Smith PetscFunctionBegin;
575c6c1daeSBarry Smith matPutVariable(ml->ep, name, (mxArray *)mat);
583ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
595c6c1daeSBarry Smith }
605c6c1daeSBarry Smith
615d83a8b1SBarry Smith /*@
62811af0c4SBarry Smith PetscViewerMatlabGetArray - Gets a variable from a `PETSCVIEWERMATLAB` viewer into an array
635c6c1daeSBarry Smith
645c6c1daeSBarry Smith Not Collective; only processor zero reads in the array
655c6c1daeSBarry Smith
665c6c1daeSBarry Smith Input Parameters:
675c6c1daeSBarry Smith + mfile - the MATLAB file viewer
6820f4b53cSBarry Smith . m - the first dimensions of `array`
6920f4b53cSBarry Smith . n - the second dimensions of `array`
70f13dfd9eSBarry Smith . array - the array (represented in one dimension), must of be length `m` * `n`
713f423023SBarry Smith - name - the MATLAB name of `array`
725c6c1daeSBarry Smith
735c6c1daeSBarry Smith Level: advanced
745c6c1daeSBarry Smith
75811af0c4SBarry Smith Note:
763f423023SBarry Smith Only reads in `array` values on processor 0.
775c6c1daeSBarry Smith
78811af0c4SBarry Smith .seealso: `PETSCVIEWERMATLAB`, `PetscViewerMatlabPutArray()`
795c6c1daeSBarry Smith @*/
PetscViewerMatlabGetArray(PetscViewer mfile,int m,int n,PetscScalar array[],const char * name)805d83a8b1SBarry Smith PetscErrorCode PetscViewerMatlabGetArray(PetscViewer mfile, int m, int n, PetscScalar array[], const char *name)
81d71ae5a4SJacob Faibussowitsch {
822cb5e1ccSBarry Smith PetscViewer_Matlab *ml;
835c6c1daeSBarry Smith mxArray *mat;
845c6c1daeSBarry Smith
855c6c1daeSBarry Smith PetscFunctionBegin;
8628b400f6SJacob Faibussowitsch PetscCheck(mfile, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null argument: probably PETSC_VIEWER_MATLAB_() failed");
872cb5e1ccSBarry Smith ml = (PetscViewer_Matlab *)mfile->data;
885c6c1daeSBarry Smith if (!ml->rank) {
899566063dSJacob Faibussowitsch PetscCall(PetscInfo(mfile, "Getting MATLAB array %s\n", name));
905c6c1daeSBarry Smith mat = matGetVariable(ml->ep, name);
9128b400f6SJacob Faibussowitsch PetscCheck(mat, PETSC_COMM_SELF, PETSC_ERR_LIB, "Unable to get array %s from matlab", name);
929566063dSJacob Faibussowitsch PetscCall(PetscArraycpy(array, mxGetPr(mat), m * n));
939566063dSJacob Faibussowitsch PetscCall(PetscInfo(mfile, "Got MATLAB array %s\n", name));
945c6c1daeSBarry Smith }
953ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
965c6c1daeSBarry Smith }
975c6c1daeSBarry Smith
PetscViewerFileSetMode_Matlab(PetscViewer viewer,PetscFileMode type)9866976f2fSJacob Faibussowitsch static PetscErrorCode PetscViewerFileSetMode_Matlab(PetscViewer viewer, PetscFileMode type)
99d71ae5a4SJacob Faibussowitsch {
1005c6c1daeSBarry Smith PetscViewer_Matlab *vmatlab = (PetscViewer_Matlab *)viewer->data;
1015c6c1daeSBarry Smith
1025c6c1daeSBarry Smith PetscFunctionBegin;
1035c6c1daeSBarry Smith vmatlab->btype = type;
1043ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
1055c6c1daeSBarry Smith }
1065c6c1daeSBarry Smith
1075c6c1daeSBarry Smith /*
1085c6c1daeSBarry Smith Actually opens the file
1095c6c1daeSBarry Smith */
PetscViewerFileSetName_Matlab(PetscViewer viewer,const char name[])11066976f2fSJacob Faibussowitsch static PetscErrorCode PetscViewerFileSetName_Matlab(PetscViewer viewer, const char name[])
111d71ae5a4SJacob Faibussowitsch {
1125c6c1daeSBarry Smith PetscViewer_Matlab *vmatlab = (PetscViewer_Matlab *)viewer->data;
1135c6c1daeSBarry Smith PetscFileMode type = vmatlab->btype;
1145c6c1daeSBarry Smith
1155c6c1daeSBarry Smith PetscFunctionBegin;
116cc73adaaSBarry Smith PetscCheck(type != (PetscFileMode)-1, PETSC_COMM_SELF, PETSC_ERR_ORDER, "Must call PetscViewerFileSetMode() before PetscViewerFileSetName()");
1175c6c1daeSBarry Smith if (vmatlab->ep) matClose(vmatlab->ep);
1185c6c1daeSBarry Smith
1195c6c1daeSBarry Smith /* only first processor opens file */
1205c6c1daeSBarry Smith if (!vmatlab->rank) {
121a297a907SKarl Rupp if (type == FILE_MODE_READ) vmatlab->ep = matOpen(name, "r");
1227e4fd573SVaclav Hapla else if (type == FILE_MODE_WRITE) vmatlab->ep = matOpen(name, "w");
123f7d195e4SLawrence Mitchell else {
124f7d195e4SLawrence Mitchell PetscCheck(type != FILE_MODE_UNDEFINED, PetscObjectComm((PetscObject)viewer), PETSC_ERR_ORDER, "Must call PetscViewerFileSetMode() before PetscViewerFileSetName()");
125f7d195e4SLawrence Mitchell SETERRQ(PetscObjectComm((PetscObject)viewer), PETSC_ERR_SUP, "Unsupported file mode %s", PetscFileModes[type]);
126f7d195e4SLawrence Mitchell }
1275c6c1daeSBarry Smith }
1283ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
1295c6c1daeSBarry Smith }
1305c6c1daeSBarry Smith
PetscViewerDestroy_Matlab(PetscViewer v)13166976f2fSJacob Faibussowitsch static PetscErrorCode PetscViewerDestroy_Matlab(PetscViewer v)
132d71ae5a4SJacob Faibussowitsch {
1335c6c1daeSBarry Smith PetscViewer_Matlab *vf = (PetscViewer_Matlab *)v->data;
1345c6c1daeSBarry Smith
1355c6c1daeSBarry Smith PetscFunctionBegin;
1365c6c1daeSBarry Smith if (vf->ep) matClose(vf->ep);
1379566063dSJacob Faibussowitsch PetscCall(PetscFree(vf));
1382e956fe4SStefano Zampini PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetName_C", NULL));
1392e956fe4SStefano Zampini PetscCall(PetscObjectComposeFunction((PetscObject)v, "PetscViewerFileSetMode_C", NULL));
1403ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
1415c6c1daeSBarry Smith }
1425c6c1daeSBarry Smith
1438556b5ebSBarry Smith /*MC
1448556b5ebSBarry Smith PETSCVIEWERMATLAB - A viewer that saves the variables into a MATLAB .mat file that may be read into MATLAB
1458556b5ebSBarry Smith with load('filename').
1468556b5ebSBarry Smith
1478556b5ebSBarry Smith Level: intermediate
1488556b5ebSBarry Smith
149b0f901b8SJose E. Roman Notes:
150b0f901b8SJose E. Roman Currently can only save PETSc vectors to .mat files, not matrices (use the `PETSCVIEWERBINARY` and
1518556b5ebSBarry Smith ${PETSC_DIR}/share/petsc/matlab/PetscBinaryRead.m to read matrices into MATLAB).
1528556b5ebSBarry Smith
153811af0c4SBarry Smith For parallel vectors obtained with `DMCreateGlobalVector()` or `DMGetGlobalVector()` the vectors are saved to
154811af0c4SBarry Smith the .mat file in natural ordering. You can use DMView() to save the `DMDA` information to the .mat file
155b0f901b8SJose E. Roman the fields in the MATLAB loaded da variable give the array dimensions so you can reshape the MATLAB
1568556b5ebSBarry Smith vector to the same multidimensional shape as it had in PETSc for plotting etc. For example,
1578556b5ebSBarry Smith
158b0f901b8SJose E. Roman In your PETSc C/C++ code (assuming a two dimensional `DMDA` with one degree of freedom per node)
159b0f901b8SJose E. Roman .vb
160b0f901b8SJose E. Roman PetscObjectSetName((PetscObject)x,"x");
161b0f901b8SJose E. Roman VecView(x,PETSC_VIEWER_MATLAB_WORLD);
162b0f901b8SJose E. Roman PetscObjectSetName((PetscObject)da,"da");
163b0f901b8SJose E. Roman DMView(x,PETSC_VIEWER_MATLAB_WORLD);
164b0f901b8SJose E. Roman .ve
165b0f901b8SJose E. Roman Then from MATLAB
166b0f901b8SJose E. Roman .vb
167b0f901b8SJose E. Roman load('matlaboutput.mat') % matlaboutput.mat is the default filename
168b0f901b8SJose E. Roman xnew = zeros(da.n,da.m);
169b0f901b8SJose E. Roman xnew(:) = x; % reshape one dimensional vector back to two dimensions
170b0f901b8SJose E. Roman .ve
1718556b5ebSBarry Smith
1728556b5ebSBarry Smith If you wish to put the same variable into the .mat file several times you need to give it a new
1738556b5ebSBarry Smith name before each call to view.
1748556b5ebSBarry Smith
175811af0c4SBarry Smith Use `PetscViewerMatlabPutArray()` to just put an array of doubles into the .mat file
1768556b5ebSBarry Smith
177c2e3fba1SPatrick Sanan .seealso: `PETSC_VIEWER_MATLAB_()`, `PETSC_VIEWER_MATLAB_SELF`, `PETSC_VIEWER_MATLAB_WORLD`, `PetscViewerCreate()`,
178db781477SPatrick Sanan `PetscViewerMatlabOpen()`, `VecView()`, `DMView()`, `PetscViewerMatlabPutArray()`, `PETSCVIEWERBINARY`, `PETSCVIEWERASCII`, `PETSCVIEWERDRAW`,
179811af0c4SBarry Smith `PETSC_VIEWER_STDOUT_()`, `PetscViewerFileSetName()`, `PetscViewerFileSetMode()`, `PetscViewerFormat`, `PetscMatlabEngine`
1808556b5ebSBarry Smith M*/
PetscViewerCreate_Matlab(PetscViewer viewer)181d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode PetscViewerCreate_Matlab(PetscViewer viewer)
182d71ae5a4SJacob Faibussowitsch {
1835c6c1daeSBarry Smith PetscViewer_Matlab *e;
1845c6c1daeSBarry Smith
1855c6c1daeSBarry Smith PetscFunctionBegin;
1864dfa11a4SJacob Faibussowitsch PetscCall(PetscNew(&e));
1879566063dSJacob Faibussowitsch PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &e->rank));
1887e4fd573SVaclav Hapla e->btype = FILE_MODE_UNDEFINED;
1895c6c1daeSBarry Smith viewer->data = (void *)e;
190a297a907SKarl Rupp
1919566063dSJacob Faibussowitsch PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetName_C", PetscViewerFileSetName_Matlab));
1929566063dSJacob Faibussowitsch PetscCall(PetscObjectComposeFunction((PetscObject)viewer, "PetscViewerFileSetMode_C", PetscViewerFileSetMode_Matlab));
193a297a907SKarl Rupp
1945c6c1daeSBarry Smith viewer->ops->destroy = PetscViewerDestroy_Matlab;
1953ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
1965c6c1daeSBarry Smith }
1975c6c1daeSBarry Smith
198cc4c1da9SBarry Smith /*@
19921afe8ebSBarry Smith PetscViewerMatlabOpen - Opens a MATLAB .mat file for output
2005c6c1daeSBarry Smith
201d083f849SBarry Smith Collective
2025c6c1daeSBarry Smith
2035c6c1daeSBarry Smith Input Parameters:
2045c6c1daeSBarry Smith + comm - MPI communicator
2055c6c1daeSBarry Smith . name - name of file
2065c6c1daeSBarry Smith - type - type of file
2073f423023SBarry Smith .vb
2083f423023SBarry Smith FILE_MODE_WRITE - create new file for MATLAB output
2093f423023SBarry Smith FILE_MODE_READ - open existing file for MATLAB input
2103f423023SBarry Smith FILE_MODE_WRITE - open existing file for MATLAB output
2113f423023SBarry Smith .ve
2125c6c1daeSBarry Smith
2135c6c1daeSBarry Smith Output Parameter:
2145c6c1daeSBarry Smith . binv - PetscViewer for MATLAB output to use with the specified file
2155c6c1daeSBarry Smith
2165c6c1daeSBarry Smith Level: beginner
2175c6c1daeSBarry Smith
218811af0c4SBarry Smith Notes:
219811af0c4SBarry Smith This `PetscViewer` should be destroyed with `PetscViewerDestroy()`.
2205c6c1daeSBarry Smith
2215c6c1daeSBarry Smith For writing files it only opens the file on processor 0 in the communicator.
2225c6c1daeSBarry Smith
223811af0c4SBarry Smith This only saves `Vec`s it cannot be used to save `Mat`s. We recommend using the `PETSCVIEWERBINARY` to save objects to be loaded into MATLAB
2245c6c1daeSBarry Smith instead of this routine.
2255c6c1daeSBarry Smith
2263f423023SBarry Smith PETSc must be configured with the option `--with-matlab` for this functionality
227750b007cSBarry Smith
228811af0c4SBarry Smith .seealso: `PETSCVIEWERMATLAB`, `PetscViewerASCIIOpen()`, `PetscViewerPushFormat()`, `PetscViewerDestroy()`, `PETSCVIEWERBINARY`, `PetscViewerBinaryOpen()`
229db781477SPatrick Sanan `VecView()`, `MatView()`, `VecLoad()`, `MatLoad()`
2305c6c1daeSBarry Smith @*/
PetscViewerMatlabOpen(MPI_Comm comm,const char name[],PetscFileMode type,PetscViewer * binv)231d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscViewerMatlabOpen(MPI_Comm comm, const char name[], PetscFileMode type, PetscViewer *binv)
232d71ae5a4SJacob Faibussowitsch {
2335c6c1daeSBarry Smith PetscFunctionBegin;
2349566063dSJacob Faibussowitsch PetscCall(PetscViewerCreate(comm, binv));
2359566063dSJacob Faibussowitsch PetscCall(PetscViewerSetType(*binv, PETSCVIEWERMATLAB));
2369566063dSJacob Faibussowitsch PetscCall(PetscViewerFileSetMode(*binv, type));
2379566063dSJacob Faibussowitsch PetscCall(PetscViewerFileSetName(*binv, name));
2383ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS);
2395c6c1daeSBarry Smith }
2405c6c1daeSBarry Smith
2415c6c1daeSBarry Smith static PetscMPIInt Petsc_Viewer_Matlab_keyval = MPI_KEYVAL_INVALID;
2425c6c1daeSBarry Smith
2435c6c1daeSBarry Smith /*@C
244811af0c4SBarry Smith PETSC_VIEWER_MATLAB_ - Creates a `PETSCVIEWERMATLAB` `PetscViewer` shared by all processors
2455c6c1daeSBarry Smith in a communicator.
2465c6c1daeSBarry Smith
247d083f849SBarry Smith Collective
2485c6c1daeSBarry Smith
2495c6c1daeSBarry Smith Input Parameter:
25021afe8ebSBarry Smith . comm - the MPI communicator to share the MATLAB `PetscViewer`
251811af0c4SBarry Smith
252811af0c4SBarry Smith Options Database Key:
25321afe8ebSBarry Smith . -viewer_matlab_filename <name> - name of the MATLAB file
254811af0c4SBarry Smith
255811af0c4SBarry Smith Environmental variable:
25621afe8ebSBarry Smith . `PETSC_VIEWER_MATLAB_FILENAME` - name of the MATLAB file
2575c6c1daeSBarry Smith
2585c6c1daeSBarry Smith Level: intermediate
2595c6c1daeSBarry Smith
26034fa283eSBarry Smith Notes:
26134fa283eSBarry Smith This object is destroyed in `PetscFinalize()`, `PetscViewerDestroy()` should never be called on it
26234fa283eSBarry Smith
263811af0c4SBarry Smith Unlike almost all other PETSc routines, `PETSC_VIEWER_MATLAB_()` does not return
264a3b724e8SBarry Smith an error code. The MATLAB `PetscViewer` is usually used in the form `XXXView(XXX object, PETSC_VIEWER_MATLAB_(comm))`
2655c6c1daeSBarry Smith
266811af0c4SBarry Smith Use `PETSC_VIEWER_SOCKET_()` or `PetscViewerSocketOpen()` to communicator with an interactive MATLAB session.
2675c6c1daeSBarry Smith
268db781477SPatrick Sanan .seealso: `PETSC_VIEWER_MATLAB_WORLD`, `PETSC_VIEWER_MATLAB_SELF`, `PetscViewerMatlabOpen()`, `PetscViewerCreate()`,
269db781477SPatrick Sanan `PetscViewerDestroy()`
2705c6c1daeSBarry Smith @*/
PETSC_VIEWER_MATLAB_(MPI_Comm comm)271d71ae5a4SJacob Faibussowitsch PetscViewer PETSC_VIEWER_MATLAB_(MPI_Comm comm)
272d71ae5a4SJacob Faibussowitsch {
2735c6c1daeSBarry Smith PetscBool flg;
274*b8b5be36SMartin Diehl PetscMPIInt iflg;
2755c6c1daeSBarry Smith PetscViewer viewer;
2765c6c1daeSBarry Smith char fname[PETSC_MAX_PATH_LEN];
2775c6c1daeSBarry Smith MPI_Comm ncomm;
2785c6c1daeSBarry Smith
2795c6c1daeSBarry Smith PetscFunctionBegin;
280648c30bcSBarry Smith PetscCallNull(PetscCommDuplicate(comm, &ncomm, NULL));
2813a7d0413SPierre Jolivet if (Petsc_Viewer_Matlab_keyval == MPI_KEYVAL_INVALID) PetscCallMPINull(MPI_Comm_create_keyval(MPI_COMM_NULL_COPY_FN, MPI_COMM_NULL_DELETE_FN, &Petsc_Viewer_Matlab_keyval, 0));
282*b8b5be36SMartin Diehl PetscCallMPINull(MPI_Comm_get_attr(ncomm, Petsc_Viewer_Matlab_keyval, (void **)&viewer, &iflg));
283*b8b5be36SMartin Diehl if (!iflg) { /* PetscViewer not yet created */
284648c30bcSBarry Smith PetscCallNull(PetscOptionsGetenv(ncomm, "PETSC_VIEWER_MATLAB_FILENAME", fname, PETSC_MAX_PATH_LEN, &flg));
2853a7d0413SPierre Jolivet if (!flg) PetscCallNull(PetscStrncpy(fname, "matlaboutput.mat", sizeof(fname)));
286648c30bcSBarry Smith PetscCallNull(PetscViewerMatlabOpen(ncomm, fname, FILE_MODE_WRITE, &viewer));
287648c30bcSBarry Smith PetscCallNull(PetscObjectRegisterDestroy((PetscObject)viewer));
288648c30bcSBarry Smith PetscCallMPINull(MPI_Comm_set_attr(ncomm, Petsc_Viewer_Matlab_keyval, (void *)viewer));
2899371c9d4SSatish Balay }
290648c30bcSBarry Smith PetscCallNull(PetscCommDestroy(&ncomm));
2915c6c1daeSBarry Smith PetscFunctionReturn(viewer);
2925c6c1daeSBarry Smith }
293