82cad2f5ab8d92b69e97280a04336c233ed909ac
[ctsim.git] / tools / phm2pj.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          phm2pj.cpp
5 **   Purpose:       Take projections of a phantom object
6 **   Programmer:    Kevin Rosenberg
7 **   Date Started:  1984
8 **
9 **  This is part of the CTSim program
10 **  Copyright (C) 1983-2000 Kevin Rosenberg
11 **
12 **  $Id: phm2pj.cpp,v 1.9 2000/08/03 09:57:29 kevin Exp $
13 **
14 **  This program is free software; you can redistribute it and/or modify
15 **  it under the terms of the GNU General Public License (version 2) as
16 **  published by the Free Software Foundation.
17 **
18 **  This program is distributed in the hope that it will be useful,
19 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 **  GNU General Public License for more details.
22 **
23 **  You should have received a copy of the GNU General Public License
24 **  along with this program; if not, write to the Free Software
25 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 ******************************************************************************/
27
28 #include "ct.h"
29 #include "timer.h"
30
31
32 enum { O_PHANTOM, O_DESC, O_NRAY, O_ROTANGLE, O_PHMFILE,
33        O_TRACE, O_VERBOSE, O_HELP, O_DEBUG, O_VERSION };
34
35 static struct option phm2pj_options[] = 
36 {
37   {"phantom", 1, 0, O_PHANTOM},
38   {"phmfile", 1, 0, O_PHMFILE},
39   {"desc", 1, 0, O_DESC},
40   {"nray", 1, 0, O_NRAY},
41   {"rotangle", 1, 0, O_ROTANGLE},
42   {"trace", 1, 0, O_TRACE},
43   {"verbose", 0, 0, O_VERBOSE},
44   {"help", 0, 0, O_HELP},
45   {"debug", 0, 0, O_DEBUG},
46   {"version", 0, 0, O_VERSION},
47   {0, 0, 0, 0}
48 };
49
50 static const char* g_szIdStr = "$Id: phm2pj.cpp,v 1.9 2000/08/03 09:57:29 kevin Exp $";
51
52
53 void 
54 phm2pj_usage (const char *program)
55 {
56   cout << "usage: " << fileBasename(program) << " outfile ndet nview [--phantom phantom-name] [--phmfile filename] [OPTIONS]" << endl;
57   cout << "Calculate (projections) through phantom object, either a predefined --phantom or a --phmfile" << endl;
58   cout << "" << endl;
59   cout << "     outfile      Name of output file for raysums" << endl;
60   cout << "     ndet         Number of detectors" << endl;
61   cout << "     nview        Number of rotated views" << endl;
62   cout << "     --phantom    Phantom to use for projection" << endl;
63   cout << "        herman    Herman head phantom" << endl;
64   cout << "        bherman   Bordered herman head phantom" << endl;
65   cout << "        rowland   Rowland head phantom" << endl;
66   cout << "        browland  Bordered Rowland head phantom" << endl;
67   cout << "        unitpulse Unit pulse phantom" << endl;
68   cout << "     --phmfile    Get Phantom from phantom file" << endl;
69   cout << "     --desc       Description of raysum" << endl;
70   cout << "     --nray       Number of rays per detector (default = 1)" << endl;
71   cout << "     --rotangle   Degrees to rotate view through, multiple of PI (default = 1)" << endl;
72   cout << "     --trace      Trace level to use" << endl;
73   cout << "        none      No tracing (default)" << endl;
74   cout << "        text      Trace text level" << endl;
75   cout << "        phm       Trace phantom image" << endl;
76   cout << "        rays      Trace rays" << endl;
77   cout << "        plot      Trace plot" << endl;
78   cout << "        clipping  Trace clipping" << endl;
79   cout << "     --verbose    Verbose mode" << endl;
80   cout << "     --debug      Debug mode" << endl;
81   cout << "     --version    Print version" << endl;
82   cout << "     --help       Print this help message" << endl;
83 }
84
85 #ifdef HAVE_MPI
86 void GatherProjectionsMPI (MPIWorld& mpiWorld, Projections& pjGlobal, Projections& pjLocal, const int opt_debug);
87 #endif
88
89 int 
90 phm2pj_main (int argc, char* argv[])
91 {
92   Phantom phm;
93   string optGeometryName = Scanner::convertGeometryIDToName(Scanner::GEOMETRY_PARALLEL);
94   char *opt_outfile = NULL;
95   string opt_desc;
96   string optPhmFileName;
97   int opt_ndet;
98   int opt_nview;
99   int opt_nray = 1;
100   int opt_trace = 0;
101   string optPhmName (Phantom::convertPhantomIDToName(Phantom::PHM_HERMAN));
102   int opt_verbose = 0;
103   int opt_debug = 0;
104   double opt_rotangle = 1;
105   char* endptr = NULL;
106   char* endstr;
107
108 #ifdef HAVE_MPI
109   MPIWorld mpiWorld (argc, argv);
110 #endif
111
112   Timer timerProgram;
113
114 #ifdef HAVE_MPI
115   if (mpiWorld.getRank() == 0) {
116 #endif
117     while (1) {
118       int c = getopt_long(argc, argv, "", phm2pj_options, NULL);
119
120       if (c == -1)
121         break;
122       
123       switch (c) {
124       case O_PHANTOM:
125         optPhmName = optarg;
126         break;
127       case O_PHMFILE:
128         optPhmFileName = optarg;
129         break;
130       case O_VERBOSE:
131         opt_verbose = 1;
132         break;
133       case O_DEBUG:
134         opt_debug = 1;
135         break;
136         break;
137       case O_TRACE:
138         if ((opt_trace = TraceLevel::convertTraceNameToID(optarg)) == TRACE_INVALID) {
139           phm2pj_usage(argv[0]);
140           return (1);
141         }
142         break;
143       case O_DESC:
144         opt_desc = optarg;
145         break;
146       case O_ROTANGLE:
147         opt_rotangle = strtod(optarg, &endptr);
148         endstr = optarg + strlen(optarg);
149         if (endptr != endstr) {
150           cerr << "Error setting --rotangle to " << optarg << endl;
151           phm2pj_usage(argv[0]);
152           return (1);
153         }
154         break;
155       case O_NRAY:
156         opt_nray = strtol(optarg, &endptr, 10);
157         endstr = optarg + strlen(optarg);
158         if (endptr != endstr) {
159           cerr << "Error setting --nray to %s" << optarg << endl;
160           phm2pj_usage(argv[0]);
161           return (1);
162         }
163         break;
164         case O_VERSION:
165 #ifdef VERSION
166           cout << "Version: " << VERSION << endl << g_szIdStr << endl;
167 #else
168           cout << "Unknown version number" << endl;
169 #endif
170           return (0);
171       case O_HELP:
172       case '?':
173         phm2pj_usage(argv[0]);
174         return (0);
175       default:
176         phm2pj_usage(argv[0]);
177         return (1);
178       }
179     }
180   
181     if (optPhmName == "" && optPhmFileName == "") {
182       cerr << "No phantom defined" << endl << endl;
183       phm2pj_usage(argv[0]);
184       return (1);
185     }
186     if (optind + 3 != argc) {
187       phm2pj_usage(argv[0]);
188       return (1);
189     }
190
191     opt_outfile = argv[optind];
192     opt_ndet = strtol(argv[optind+1], &endptr, 10);
193     endstr = argv[optind+1] + strlen(argv[optind+1]);
194     if (endptr != endstr) {
195       cerr << "Error setting --ndet to " << argv[optind+1] << endl;
196       phm2pj_usage(argv[0]);
197       return (1);
198     }
199     opt_nview = strtol(argv[optind+2], &endptr, 10);
200     endstr = argv[optind+2] + strlen(argv[optind+2]);
201     if (endptr != endstr) {
202       cerr << "Error setting --nview to " << argv[optind+2] << endl;
203       phm2pj_usage(argv[0]);
204       return (1);
205     }
206
207     ostringstream desc;
208     desc << "phm2pj: NDet=" << opt_ndet << ", Nview=" << opt_nview << ", NRay=" << opt_nray << ", RotAngle=" << opt_rotangle << ", ";
209     if (optPhmFileName.length()) {
210       desc << "PhantomFile=" << optPhmFileName;
211     } else if (optPhmName != "") {
212       desc << "Phantom=" << optPhmName;
213     }
214     if (opt_desc.length()) {
215       desc << ": " << opt_desc;
216     }
217     opt_desc = desc.str();
218
219     if (optPhmName != "") {
220       phm.createFromPhantom (optPhmName.c_str());
221       if (phm.fail()) {
222         cout << phm.failMessage() << endl << endl;
223         phm2pj_usage(argv[0]);
224         return (1);
225       }
226     }
227
228     if (optPhmFileName != "") {
229 #ifdef HAVE_MPI
230       cerr << "Can not read phantom from file in MPI mode" << endl;
231       return (1);
232 #endif
233       phm.createFromFile (optPhmFileName.c_str());
234     }
235
236 #ifdef HAVE_MPI
237   }
238 #endif
239
240 #ifdef HAVE_MPI
241   TimerCollectiveMPI timerBcast(mpiWorld.getComm());
242   mpiWorld.BcastString (optPhmName);
243   mpiWorld.getComm().Bcast (&opt_rotangle, 1, MPI::DOUBLE, 0);
244   mpiWorld.getComm().Bcast (&opt_nview, 1, MPI::INT, 0);
245   mpiWorld.getComm().Bcast (&opt_ndet, 1, MPI::INT, 0);
246   mpiWorld.getComm().Bcast (&opt_nray, 1, MPI::INT, 0);
247   mpiWorld.getComm().Bcast (&opt_verbose, 1, MPI::INT, 0);
248   mpiWorld.getComm().Bcast (&opt_debug, 1, MPI::INT, 0);
249   mpiWorld.getComm().Bcast (&opt_trace, 1, MPI::INT, 0);
250   timerBcast.timerEndAndReport ("Time to broadcast variables");
251
252   if (mpiWorld.getRank() > 0 && optPhmName != "")
253     phm.createFromPhantom (optPhmName.c_str());
254 #endif
255
256   opt_rotangle *= PI;
257   Scanner scanner (phm, optGeometryName.c_str(), opt_ndet, opt_nview, opt_nray, opt_rotangle);
258   if (scanner.fail()) {
259     cout << "Scanner Creation Error: " << scanner.failMessage() << endl;
260     return (1);
261   }
262 #ifdef HAVE_MPI
263   mpiWorld.setTotalWorkUnits (opt_nview);
264
265   Projections pjGlobal;
266   if (mpiWorld.getRank() == 0)
267     pjGlobal.initFromScanner (scanner);
268   
269   if (opt_verbose)
270     pjGlobal.printScanInfo();
271
272   Projections pjLocal (scanner);
273   pjLocal.setNView (mpiWorld.getMyLocalWorkUnits());
274
275   if (opt_debug)
276     cout << "pjLocal->nview = " << pjLocal.nView() << " (process " << mpiWorld.getRank() << ")" << endl;;
277
278   TimerCollectiveMPI timerProject (mpiWorld.getComm());
279   scanner.collectProjections (pjLocal, phm, mpiWorld.getMyStartWorkUnit(), opt_trace);
280   if (opt_verbose)
281     timerProject.timerEndAndReport ("Time to collect projections");
282
283   TimerCollectiveMPI timerGather (mpiWorld.getComm());
284   GatherProjectionsMPI (mpiWorld, pjGlobal, pjLocal, opt_debug);
285   if (opt_verbose) 
286      timerGather.timerEndAndReport ("Time to gather projections");
287
288 #else
289   Projections pjGlobal (scanner);
290 #if HAVE_SGP
291   SGPDriver* pSGPDriver = NULL;
292   SGP* pSGP = NULL;
293   if (opt_trace >= TRACE_PHM) {
294     pSGPDriver = new SGPDriver ("phm2pj", 600, 600);
295     pSGP = new SGP (*pSGPDriver);
296   }
297   scanner.collectProjections (pjGlobal, phm, 0, opt_trace, pSGP);
298   if (opt_trace >= TRACE_PHM) {
299     cout << "Press enter to continue\n";
300     cio_kb_getc();
301     delete pSGP;  pSGP = NULL;
302     delete pSGPDriver;  pSGPDriver = NULL;
303   }
304
305 #else
306   scanner.collectProjections (pjGlobal, phm, 0, opt_trace);
307 #endif
308
309 #endif
310   
311 #ifdef HAVE_MPI
312   if (mpiWorld.getRank() == 0) 
313 #endif
314     {
315       pjGlobal.setCalcTime (timerProgram.timerEnd());
316       pjGlobal.setRemark (opt_desc);
317       pjGlobal.write (opt_outfile);
318       if (opt_verbose) {
319         phm.print();
320         cout << endl;
321         pjGlobal.printScanInfo();
322         cout << endl;
323         cout << "  Remark: " << pjGlobal.remark() << endl;
324         cout << "Run time: " << pjGlobal.calcTime() << " seconds" << endl;
325       }
326     }
327
328   return (0);
329 }
330
331
332 /* FUNCTION
333  *    GatherProjectionsMPI
334  *
335  * SYNOPSIS
336  *    Gather's raysums from all processes in pjLocal in pjGlobal in process 0
337  */
338
339 #ifdef HAVE_MPI
340 void GatherProjectionsMPI (MPIWorld& mpiWorld, Projections& pjGlobal, Projections& pjLocal, const int opt_debug)
341 {
342   for (int iw = 0; iw < mpiWorld.getMyLocalWorkUnits(); iw++) {
343     DetectorArray& detArray = pjLocal.getDetectorArray(iw);
344     double viewAngle = detArray.viewAngle();
345     int nDet = detArray.nDet();
346     DetectorValue* detval = detArray.detValues();
347
348     mpiWorld.getComm().Send(&viewAngle, 1, MPI::DOUBLE, 0, 0);
349     mpiWorld.getComm().Send(&nDet, 1, MPI::INT, 0, 0);
350     mpiWorld.getComm().Send(detval, nDet, MPI::FLOAT, 0, 0);
351   }
352
353   if (mpiWorld.getRank() == 0) {
354     for (int iProc = 0; iProc < mpiWorld.getNumProcessors(); iProc++) {
355       for (int iw = mpiWorld.getStartWorkUnit(iProc); iw <= mpiWorld.getEndWorkUnit(iProc); iw++) {
356         MPI::Status status;
357         double viewAngle;
358         int nDet;
359         DetectorArray& detArray = pjGlobal.getDetectorArray(iw);
360         DetectorValue* detval = detArray.detValues();
361
362         mpiWorld.getComm().Recv(&viewAngle, 1, MPI::DOUBLE, iProc, 0, status);
363         mpiWorld.getComm().Recv(&nDet, 1, MPI::INT, iProc, 0, status);
364         mpiWorld.getComm().Recv(detval, nDet, MPI::FLOAT, iProc, 0, status);
365         detArray.setViewAngle (viewAngle);
366       }
367     }
368   }
369 }
370 #endif
371
372
373 #ifndef NO_MAIN
374 int 
375 main (int argc, char* argv[])
376 {
377   int retval = 1;
378
379   try {
380     retval = phm2pj_main(argc, argv);
381 #if HAVE_DMALLOC
382     //    dmalloc_shutdown();
383 #endif
384   } catch (exception e) {
385     cerr << "Exception: " << e.what() << endl;
386   } catch (...) {
387     cerr << "Unknown exception" << endl;
388   }
389
390   return (retval);
391 }
392 #endif
393