Format if history label output
[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-2009 Kevin Rosenberg
11 **
12 **  This program is free software; you can redistribute it and/or modify
13 **  it under the terms of the GNU General Public License (version 2) as
14 **  published by the Free Software Foundation.
15 **
16 **  This program is distributed in the hope that it will be useful,
17 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 **  GNU General Public License for more details.
20 **
21 **  You should have received a copy of the GNU General Public License
22 **  along with this program; if not, write to the Free Software
23 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 ******************************************************************************/
25
26 #include "ct.h"
27 #include "timer.h"
28
29
30 enum { O_PHANTOM, O_DESC, O_NRAY, O_ROTANGLE, O_PHMFILE, O_GEOMETRY, O_FOCAL_LENGTH, O_CENTER_DETECTOR_LENGTH,
31 O_VIEW_RATIO, O_SCAN_RATIO, O_OFFSETVIEW, O_TRACE, O_VERBOSE, O_HELP, O_DEBUG, O_VERSION };
32
33 static struct option phm2pj_options[] =
34 {
35   {"phantom", 1, 0, O_PHANTOM},
36   {"phmfile", 1, 0, O_PHMFILE},
37   {"desc", 1, 0, O_DESC},
38   {"nray", 1, 0, O_NRAY},
39   {"rotangle", 1, 0, O_ROTANGLE},
40   {"geometry", 1, 0, O_GEOMETRY},
41   {"focal-length", 1, 0, O_FOCAL_LENGTH},
42   {"center-detector-length", 1, 0, O_CENTER_DETECTOR_LENGTH},
43   {"offsetview", 1, 0, O_OFFSETVIEW},
44   {"view-ratio", 1, 0, O_VIEW_RATIO},
45   {"scan-ratio", 1, 0, O_SCAN_RATIO},
46   {"trace", 1, 0, O_TRACE},
47   {"verbose", 0, 0, O_VERBOSE},
48   {"help", 0, 0, O_HELP},
49   {"debug", 0, 0, O_DEBUG},
50   {"version", 0, 0, O_VERSION},
51   {0, 0, 0, 0}
52 };
53
54 static const char* g_szIdStr = "$Id$";
55
56
57 void
58 phm2pj_usage (const char *program)
59 {
60   std::cout << "usage: " << fileBasename(program) << " outfile ndet nview [--phantom phantom-name] [--phmfile filename] [OPTIONS]\n";
61   std::cout << "Calculate (projections) through phantom object, either a predefined --phantom or a --phmfile\n\n";
62   std::cout << "     outfile          Name of output file for projections\n";
63   std::cout << "     ndet             Number of detectors\n";
64   std::cout << "     nview            Number of rotated views\n";
65   std::cout << "     --phantom        Phantom to use for projection\n";
66   std::cout << "        herman        Herman head phantom\n";
67   std::cout << "        shepp-logan   Shepp-Logan head phantom\n";
68   std::cout << "        unit-pulse     Unit pulse phantom\n";
69   std::cout << "     --phmfile        Get Phantom from phantom file\n";
70   std::cout << "     --desc           Description of raysum\n";
71   std::cout << "     --nray           Number of rays per detector (default = 1)\n";
72   std::cout << "     --rotangle       Angle to rotate view through (fraction of a circle)\n";
73   std::cout << "                      (default = select appropriate for geometry)\n";
74   std::cout << "     --geometry       Geometry of scanning\n";
75   std::cout << "        parallel      Parallel scan beams (default)\n";
76   std::cout << "        equilinear    Equilinear divergent scan beams\n";
77   std::cout << "        equiangular   Equiangular divergent scan beams\n";
78   std::cout << "     --focal-length   Focal length ratio (ratio to radius of view area)\n";
79   std::cout << "                      (default = 2)\n";
80   std::cout << "     --center-detector-length  Distance from center of phantom to detector array\n";
81   std::cout << "                      (ratio to radius of view area) (default = 2)\n";
82   std::cout << "     --view-ratio     Length to view (view diameter to phantom diameter)\n";
83   std::cout << "                      (default = 1)\n";
84   std::cout << "     --scan-ratio     Length to scan (scan diameter to view diameter)\n";
85   std::cout << "                      (default = 1)\n";
86   std::cout << "     --offsetview     Initial gantry offset in 'views' (default = 0)\n";
87   std::cout << "     --trace          Trace level to use\n";
88   std::cout << "        none          No tracing (default)\n";
89   std::cout << "        console       Trace text level\n";
90   std::cout << "     --verbose        Verbose mode\n";
91   std::cout << "     --debug          Debug mode\n";
92   std::cout << "     --version        Print version\n";
93   std::cout << "     --help           Print this help message\n";
94 }
95
96 #ifdef HAVE_MPI
97 void GatherProjectionsMPI (MPIWorld& mpiWorld, Projections& pjGlobal, Projections& pjLocal, const int opt_debug);
98 #endif
99
100 int
101 phm2pj_main (int argc, char* const argv[])
102 {
103   Phantom phm;
104   std::string optGeometryName = Scanner::convertGeometryIDToName(Scanner::GEOMETRY_PARALLEL);
105   char *opt_outfile = NULL;
106   std::string opt_desc;
107   std::string optPhmName;
108   std::string optPhmFileName;
109   int opt_ndet;
110   int opt_nview;
111   int opt_offsetview = 0;
112   int opt_nray = 1;
113   double dOptFocalLength = 2.;
114   double dOptCenterDetectorLength = 2.;
115   double dOptViewRatio = 1.;
116   double dOptScanRatio = 1.;
117   int opt_trace = Trace::TRACE_NONE;
118   int opt_verbose = 0;
119   int opt_debug = 0;
120   double opt_rotangle = -1;
121   char* endptr = NULL;
122   char* endstr;
123
124 #ifdef HAVE_MPI
125   MPIWorld mpiWorld (argc, argv);
126 #endif
127
128   Timer timerProgram;
129
130 #ifdef HAVE_MPI
131   if (mpiWorld.getRank() == 0) {
132 #endif
133     while (1) {
134       int c = getopt_long(argc, argv, "", phm2pj_options, NULL);
135
136       if (c == -1)
137         break;
138
139       switch (c) {
140       case O_PHANTOM:
141         optPhmName = optarg;
142         break;
143       case O_PHMFILE:
144         optPhmFileName = optarg;
145         break;
146       case O_VERBOSE:
147         opt_verbose = 1;
148         break;
149       case O_DEBUG:
150         opt_debug = 1;
151         break;
152         break;
153       case O_TRACE:
154         if ((opt_trace = Trace::convertTraceNameToID(optarg)) == Trace::TRACE_INVALID) {
155           phm2pj_usage(argv[0]);
156           return (1);
157         }
158         break;
159       case O_DESC:
160         opt_desc = optarg;
161         break;
162       case O_ROTANGLE:
163         opt_rotangle = strtod(optarg, &endptr);
164         endstr = optarg + strlen(optarg);
165         if (endptr != endstr) {
166           std::cerr << "Error setting --rotangle to " << optarg << std::endl;
167           phm2pj_usage(argv[0]);
168           return (1);
169         }
170         break;
171       case O_GEOMETRY:
172         optGeometryName = optarg;
173         break;
174       case O_FOCAL_LENGTH:
175         dOptFocalLength = strtod(optarg, &endptr);
176         endstr = optarg + strlen(optarg);
177         if (endptr != endstr) {
178           std::cerr << "Error setting --focal-length to " << optarg << std::endl;
179           phm2pj_usage(argv[0]);
180           return (1);
181         }
182         break;
183       case O_CENTER_DETECTOR_LENGTH:
184         dOptCenterDetectorLength = strtod(optarg, &endptr);
185         endstr = optarg + strlen(optarg);
186         if (endptr != endstr) {
187           std::cerr << "Error setting --center-detector-length to " << optarg << std::endl;
188           phm2pj_usage(argv[0]);
189           return (1);
190         }
191         break;
192       case O_VIEW_RATIO:
193         dOptViewRatio = strtod(optarg, &endptr);
194         endstr = optarg + strlen(optarg);
195         if (endptr != endstr) {
196           std::cerr << "Error setting --view-ratio to " << optarg << std::endl;
197           phm2pj_usage(argv[0]);
198           return (1);
199         }
200         break;
201       case O_SCAN_RATIO:
202         dOptScanRatio = strtod(optarg, &endptr);
203         endstr = optarg + strlen(optarg);
204         if (endptr != endstr) {
205           std::cerr << "Error setting --scan-ratio to " << optarg << std::endl;
206           phm2pj_usage(argv[0]);
207           return (1);
208         }
209         break;
210       case O_NRAY:
211         opt_nray = strtol(optarg, &endptr, 10);
212         endstr = optarg + strlen(optarg);
213         if (endptr != endstr) {
214           std::cerr << "Error setting --nray to %s" << optarg << std::endl;
215           phm2pj_usage(argv[0]);
216           return (1);
217         }
218         break;
219           case O_OFFSETVIEW:
220                 opt_offsetview = strtol(optarg, &endptr, 10);
221                 endstr = optarg + strlen(optarg);
222                 if (endptr != endstr) {
223                   std::cerr << "Error setting --offsetview to %s" << optarg << std::endl;
224                   phm2pj_usage(argv[0]);
225                   return (1);
226                 }
227                 break;
228
229       case O_VERSION:
230 #ifdef VERSION
231         std::cout << "Version: " << VERSION << std::endl << g_szIdStr << std::endl;
232 #else
233         std::cout << "Unknown version number\n";
234 #endif
235         return (0);
236       case O_HELP:
237       case '?':
238         phm2pj_usage(argv[0]);
239         return (0);
240       default:
241         phm2pj_usage(argv[0]);
242         return (1);
243       }
244     }
245
246     if (optPhmName == "" && optPhmFileName == "") {
247       std::cerr << "No phantom defined\n" << std::endl;
248       phm2pj_usage(argv[0]);
249       return (1);
250     }
251     if (optind + 3 != argc) {
252       phm2pj_usage(argv[0]);
253       return (1);
254     }
255
256     opt_outfile = argv[optind];
257     opt_ndet = strtol(argv[optind+1], &endptr, 10);
258     endstr = argv[optind+1] + strlen(argv[optind+1]);
259     if (endptr != endstr) {
260       std::cerr << "Error setting --ndet to " << argv[optind+1] << std::endl;
261       phm2pj_usage(argv[0]);
262       return (1);
263     }
264     opt_nview = strtol(argv[optind+2], &endptr, 10);
265     endstr = argv[optind+2] + strlen(argv[optind+2]);
266     if (endptr != endstr) {
267       std::cerr << "Error setting --nview to " << argv[optind+2] << std::endl;
268       phm2pj_usage(argv[0]);
269       return (1);
270     }
271
272     if (opt_rotangle < 0) {
273       if (optGeometryName.compare ("parallel") == 0)
274         opt_rotangle = 0.5;
275       else
276         opt_rotangle = 1.0;
277     }
278
279     std::ostringstream desc;
280     desc << "phm2pj: NDet=" << opt_ndet << ", Nview=" << opt_nview << ", NRay=" << opt_nray << ", RotAngle=" << opt_rotangle << ", OffsetView =" << opt_offsetview << ", Geometry=" << optGeometryName << ", ";
281     if (optPhmFileName.length()) {
282       desc << "PhantomFile=" << optPhmFileName;
283     } else if (optPhmName != "") {
284       desc << "Phantom=" << optPhmName;
285     }
286     if (opt_desc.length()) {
287       desc << ": " << opt_desc;
288     }
289     opt_desc = desc.str();
290
291     if (optPhmName != "") {
292       phm.createFromPhantom (optPhmName.c_str());
293       if (phm.fail()) {
294         std::cout << phm.failMessage() << std::endl << std::endl;
295         phm2pj_usage(argv[0]);
296         return (1);
297       }
298     }
299
300     if (optPhmFileName != "") {
301 #ifdef HAVE_MPI
302       std::cerr << "Can not read phantom from file in MPI mode\n";
303       return (1);
304 #endif
305       phm.createFromFile (optPhmFileName.c_str());
306     }
307
308 #ifdef HAVE_MPI
309   }
310 #endif
311
312 #ifdef HAVE_MPI
313   TimerCollectiveMPI timerBcast(mpiWorld.getComm());
314   mpiWorld.BcastString (optPhmName);
315   mpiWorld.getComm().Bcast (&opt_rotangle, 1, MPI::DOUBLE, 0);
316   mpiWorld.getComm().Bcast (&dOptFocalLength, 1, MPI::DOUBLE, 0);
317   mpiWorld.getComm().Bcast (&dOptCenterDetectorLength, 1, MPI::DOUBLE, 0);
318   mpiWorld.getComm().Bcast (&dOptViewRatio, 1, MPI::DOUBLE, 0);
319   mpiWorld.getComm().Bcast (&dOptScanRatio, 1, MPI::DOUBLE, 0);
320   mpiWorld.getComm().Bcast (&opt_nview, 1, MPI::INT, 0);
321   mpiWorld.getComm().Bcast (&opt_ndet, 1, MPI::INT, 0);
322   mpiWorld.getComm().Bcast (&opt_nray, 1, MPI::INT, 0);
323   mpiWorld.getComm().Bcast (&opt_verbose, 1, MPI::INT, 0);
324   mpiWorld.getComm().Bcast (&opt_debug, 1, MPI::INT, 0);
325   mpiWorld.getComm().Bcast (&opt_trace, 1, MPI::INT, 0);
326   if (opt_verbose)
327     timerBcast.timerEndAndReport ("Time to broadcast variables");
328
329   if (mpiWorld.getRank() > 0 && optPhmName != "")
330     phm.createFromPhantom (optPhmName.c_str());
331 #endif
332
333   opt_rotangle *= TWOPI;
334   Scanner scanner (phm, optGeometryName.c_str(), opt_ndet, opt_nview, opt_offsetview, opt_nray,
335                 opt_rotangle, dOptFocalLength, dOptCenterDetectorLength, dOptViewRatio, dOptScanRatio);
336   if (scanner.fail()) {
337     std::cout << "Scanner Creation Error: " << scanner.failMessage() << std::endl;
338     return (1);
339   }
340 #ifdef HAVE_MPI
341   mpiWorld.setTotalWorkUnits (opt_nview);
342
343   Projections pjGlobal;
344   if (mpiWorld.getRank() == 0)
345     pjGlobal.initFromScanner (scanner);
346
347   if (opt_verbose) {
348     std::ostringstream os;
349     pjGlobal.printScanInfo(os);
350     std::cout << os.str();
351   }
352
353   Projections pjLocal (scanner);
354   pjLocal.setNView (mpiWorld.getMyLocalWorkUnits());
355
356   if (opt_debug)
357     std::cout << "pjLocal->nview = " << pjLocal.nView() << " (process " << mpiWorld.getRank() << ")\n";;
358
359   TimerCollectiveMPI timerProject (mpiWorld.getComm());
360   scanner.collectProjections (pjLocal, phm, mpiWorld.getMyStartWorkUnit(), mpiWorld.getMyLocalWorkUnits(), false, opt_trace);
361   if (opt_verbose)
362     timerProject.timerEndAndReport ("Time to collect projections");
363
364   TimerCollectiveMPI timerGather (mpiWorld.getComm());
365   GatherProjectionsMPI (mpiWorld, pjGlobal, pjLocal, opt_debug);
366   if (opt_verbose)
367     timerGather.timerEndAndReport ("Time to gather projections");
368
369 #else
370   Projections pjGlobal (scanner);
371   scanner.collectProjections (pjGlobal, phm, 0, opt_nview, opt_offsetview, true, opt_trace);
372 #endif
373
374 #ifdef HAVE_MPI
375   if (mpiWorld.getRank() == 0)
376 #endif
377   {
378     pjGlobal.setCalcTime (timerProgram.timerEnd());
379     pjGlobal.setRemark (opt_desc);
380     pjGlobal.write (opt_outfile);
381     if (opt_verbose) {
382       phm.print (std::cout);
383       std::cout << std::endl;
384       std::ostringstream os;
385       pjGlobal.printScanInfo (os);
386       std::cout << os.str() << std::endl;
387       std::cout << "  Remark: " << pjGlobal.remark() << std::endl;
388       std::cout << "Run time: " << pjGlobal.calcTime() << " seconds\n";
389     }
390   }
391
392   return (0);
393 }
394
395
396 /* FUNCTION
397 *    GatherProjectionsMPI
398 *
399 * SYNOPSIS
400 *    Gather's raysums from all processes in pjLocal in pjGlobal in process 0
401 */
402
403 #ifdef HAVE_MPI
404 void GatherProjectionsMPI (MPIWorld& mpiWorld, Projections& pjGlobal, Projections& pjLocal, const int opt_debug)
405 {
406   for (int iw = 0; iw < mpiWorld.getMyLocalWorkUnits(); iw++) {
407     DetectorArray& detArray = pjLocal.getDetectorArray(iw);
408     double viewAngle = detArray.viewAngle();
409     int nDet = detArray.nDet();
410     DetectorValue* detval = detArray.detValues();
411
412     mpiWorld.getComm().Send(&viewAngle, 1, MPI::DOUBLE, 0, 0);
413     mpiWorld.getComm().Send(&nDet, 1, MPI::INT, 0, 0);
414     mpiWorld.getComm().Send(detval, nDet, MPI::FLOAT, 0, 0);
415   }
416
417   if (mpiWorld.getRank() == 0) {
418     for (int iProc = 0; iProc < mpiWorld.getNumProcessors(); iProc++) {
419       for (int iw = mpiWorld.getStartWorkUnit(iProc); iw <= mpiWorld.getEndWorkUnit(iProc); iw++) {
420         MPI::Status status;
421         double viewAngle;
422         int nDet;
423         DetectorArray& detArray = pjGlobal.getDetectorArray(iw);
424         DetectorValue* detval = detArray.detValues();
425
426         mpiWorld.getComm().Recv(&viewAngle, 1, MPI::DOUBLE, iProc, 0, status);
427         mpiWorld.getComm().Recv(&nDet, 1, MPI::INT, iProc, 0, status);
428         mpiWorld.getComm().Recv(detval, nDet, MPI::FLOAT, iProc, 0, status);
429         detArray.setViewAngle (viewAngle);
430       }
431     }
432   }
433 }
434 #endif
435
436
437 #ifndef NO_MAIN
438 int
439 main (int argc, char* argv[])
440 {
441   int retval = 1;
442
443   try {
444     retval = phm2pj_main(argc, argv);
445 #if HAVE_DMALLOC
446     //    dmalloc_shutdown();
447 #endif
448   } catch (exception e) {
449     std::cerr << "Exception: " << e.what() << std::endl;
450   } catch (...) {
451     std::cerr << "Unknown exception\n";
452   }
453
454   return (retval);
455 }
456 #endif
457