fe831548437ad183353f8882e7d3f3479df79443
[ctsim.git] / src / 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.1 2000/06/17 20:12:15 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
51 void 
52 phm2pj_usage (const char *program)
53 {
54   cout << "usage: " << fileBasename(program) << " outfile ndet nview [--phantom phantom-name] [--phmfile filename] [OPTIONS]" << endl;
55   cout << "Calculate (projections) through phantom object, either a predefined --phantom or a --phmfile" << endl;
56   cout << "" << endl;
57   cout << "     outfile      Name of output file for raysums" << endl;
58   cout << "     ndet         Number of detectors" << endl;
59   cout << "     nview        Number of rotated views" << endl;
60   cout << "     --phantom    Phantom to use for projection" << endl;
61   cout << "        herman    Herman head phantom" << endl;
62   cout << "        rowland   Rowland head phantom" << endl;
63   cout << "        browland  Bordered Rowland head phantom" << endl;
64   cout << "        unitpulse Unit pulse phantom" << endl;
65   cout << "     --phmfile    Get Phantom from phantom file" << endl;
66   cout << "     --desc       Description of raysum" << endl;
67   cout << "     --nray       Number of rays per detector (default = 1)" << endl;
68   cout << "     --rotangle   Degrees to rotate view through, multiple of PI (default = 1)" << endl;
69   cout << "     --trace      Trace level to use" << endl;
70   cout << "        none      No tracing (default)" << endl;
71   cout << "        text      Trace text level" << endl;
72   cout << "        phm       Trace phantom image" << endl;
73   cout << "        rays      Trace rays" << endl;
74   cout << "        plot      Trace plot" << endl;
75   cout << "        clipping  Trace clipping" << endl;
76   cout << "     --verbose    Verbose mode" << endl;
77   cout << "     --debug      Debug mode" << endl;
78   cout << "     --version    Print version" << endl;
79   cout << "     --help       Print this help message" << endl;
80 }
81
82 #ifdef HAVE_MPI
83 void GatherProjectionsMPI (MPIWorld& mpiWorld, Projections& pjGlobal, Projections& pjLocal, const int opt_debug);
84 #endif
85
86 int 
87 phm2pj_main (int argc, char* argv[])
88 {
89   Phantom phm;
90   ScannerGeometry opt_geometry = DETECTOR_PARALLEL;
91   char *opt_outfile = NULL;
92   string opt_desc;
93   string opt_phmfilename;
94   int opt_ndet, opt_nview;
95   int opt_nray = 1;
96   int opt_trace = 0;
97   int opt_phmnum = -1;
98   int opt_verbose = 0;
99   int opt_debug = 0;
100   double opt_rotangle = 1;
101   char* endptr = NULL;
102   char* endstr;
103
104 #ifdef HAVE_MPI
105   MPIWorld mpiWorld (argc, argv);
106 #endif
107
108   Timer timerProgram;
109
110 #ifdef HAVE_MPI
111   if (mpiWorld.getRank() == 0) {
112 #endif
113     while (1) {
114       int c = getopt_long(argc, argv, "", phm2pj_options, NULL);
115
116       if (c == -1)
117         break;
118       
119       switch (c) {
120       case O_PHANTOM:
121         if ((opt_phmnum = opt_set_phantom (optarg)) < 0) {
122           phm2pj_usage(argv[0]);
123           return (1);
124         }
125         phm.create (opt_phmnum);
126         break;
127       case O_PHMFILE:
128 #ifdef HAVE_MPI
129         if (mpiWorld.getRank() == 0)
130           cerr << "Can not read phantom from file in MPI mode" << endl;
131         return (1);
132 #endif
133         opt_phmfilename = optarg;
134         phm.createFromFile (opt_phmfilename.c_str());
135         break;
136       case O_VERBOSE:
137         opt_verbose = 1;
138         break;
139       case O_DEBUG:
140         opt_debug = 1;
141         break;
142         break;
143       case O_TRACE:
144         if ((opt_trace = opt_set_trace(optarg)) < 0) {
145           phm2pj_usage(argv[0]);
146           return (1);
147         }
148         break;
149       case O_DESC:
150         opt_desc = optarg;
151         break;
152       case O_ROTANGLE:
153         opt_rotangle = strtod(optarg, &endptr);
154         endstr = optarg + strlen(optarg);
155         if (endptr != endstr) {
156           cerr << "Error setting --rotangle to " << optarg << endl;
157           phm2pj_usage(argv[0]);
158           return (1);
159         }
160         break;
161       case O_NRAY:
162         opt_nray = strtol(optarg, &endptr, 10);
163         endstr = optarg + strlen(optarg);
164         if (endptr != endstr) {
165           cerr << "Error setting --nray to %s" << optarg << endl;
166           phm2pj_usage(argv[0]);
167           return (1);
168         }
169         break;
170         case O_VERSION:
171 #ifdef VERSION
172         cout << "Version: " << VERSION << endl;
173 #else
174         cout << "Unknown version number" << endl;
175 #endif
176           exit(0);
177       case O_HELP:
178       case '?':
179         phm2pj_usage(argv[0]);
180         return (0);
181       default:
182         phm2pj_usage(argv[0]);
183         return (1);
184       }
185     }
186   
187     if (phm.nPElem() == 0) {
188       cerr << "No phantom defined" << endl;
189       phm2pj_usage(argv[0]);
190       return (1);
191     }
192     if (optind + 3 != argc) {
193       phm2pj_usage(argv[0]);
194       return (1);
195     }
196
197     opt_outfile = argv[optind];
198     opt_ndet = strtol(argv[optind+1], &endptr, 10);
199     endstr = argv[optind+1] + strlen(argv[optind+1]);
200     if (endptr != endstr) {
201       cerr << "Error setting --ndet to " << argv[optind+1] << endl;
202       phm2pj_usage(argv[0]);
203       return (1);
204     }
205     opt_nview = strtol(argv[optind+2], &endptr, 10);
206     endstr = argv[optind+2] + strlen(argv[optind+2]);
207     if (endptr != endstr) {
208       cerr << "Error setting --nview to " << argv[optind+2] << endl;
209       phm2pj_usage(argv[0]);
210       return (1);
211     }
212
213     ostringstream desc;
214     desc << "Raysum_Collect: NDet=" << opt_ndet << ", Nview=" << opt_nview << ", NRay=" << opt_nray << ", RotAngle=" << opt_rotangle << ", ";
215     if (opt_phmfilename.length()) {
216       desc << "PhantomFile=" << opt_phmfilename;
217     } else if (opt_phmnum != -1) {
218       desc << "Phantom=" << name_of_phantom(opt_phmnum);
219     }
220     if (opt_desc.length()) {
221       desc << ": " << opt_desc;
222     }
223     opt_desc = desc.str();
224 #ifdef HAVE_MPI
225   }
226 #endif
227
228 #ifdef HAVE_MPI
229   mpiWorld.getComm().Bcast (&opt_rotangle, 1, MPI::DOUBLE, 0);
230   mpiWorld.getComm().Bcast (&opt_nview, 1, MPI::INT, 0);
231   mpiWorld.getComm().Bcast (&opt_ndet, 1, MPI::INT, 0);
232   mpiWorld.getComm().Bcast (&opt_nray, 1, MPI::INT, 0);
233   mpiWorld.getComm().Bcast (&opt_phmnum, 1, MPI::INT, 0);
234   mpiWorld.getComm().Bcast (&opt_verbose, 1, MPI::INT, 0);
235   mpiWorld.getComm().Bcast (&opt_debug, 1, MPI::INT, 0);
236   mpiWorld.getComm().Bcast (&opt_trace, 1, MPI::INT, 0);
237
238   if (mpiWorld.getRank() > 0 && opt_phmnum >= 0)
239     phm.create (opt_phmnum);
240 #endif
241
242   opt_rotangle *= PI;
243   Scanner scanner (phm, opt_geometry, opt_ndet, opt_nview, opt_nray, opt_rotangle);
244
245 #ifdef HAVE_MPI
246   mpiWorld.setTotalWorkUnits (opt_nview);
247
248   Projections pjGlobal;
249   if (mpiWorld.getRank() == 0) {
250     pjGlobal = Projections (scanner);
251   }
252   
253   Scanner localScanner (phm, opt_geometry, opt_ndet, mpiWorld.getMyLocalWorkUnits(), opt_nray, opt_rotangle);
254   Projections pjLocal (localScanner);
255   if (opt_debug)
256     cout << "pjLocal->nview = " << pjLocal.nView() << " (process " << mpiWorld.getRank() << ")" << endl;;
257
258   TimerMPI timerProject (mpiWorld.getComm());
259   localScanner.collectProjections (pjLocal, phm, mpiWorld.getMyStartWorkUnit(), opt_trace);
260   if (opt_verbose)
261     timerProject.timerEndAndReport ("Time to collect projections");
262
263   TimerMPI timerGather (mpiWorld.getComm());
264   GatherProjectionsMPI (mpiWorld, pjGlobal, pjLocal, opt_debug);
265   if (opt_verbose)
266     timerGather.timerEndAndReport ("Time to gather projections");
267 #else
268   Projections pjGlobal (scanner);
269   scanner.collectProjections (pjGlobal, phm, 0, opt_trace);
270 #endif
271   
272 #ifdef HAVE_MPI
273   if (mpiWorld.getRank() == 0) 
274 #endif
275     {
276       pjGlobal.setCalcTime (timerProgram.timerEnd());
277       pjGlobal.setRemark (opt_desc);
278       pjGlobal.write (opt_outfile);
279       if (opt_verbose) {
280         phm.print();
281         cout << endl;
282         pjGlobal.printScanInfo();
283         cout << endl;
284         cout << "Remark: " << pjGlobal.remark() << endl;
285         cout << "Run time: " << pjGlobal.calcTime() << " seconds" << endl;
286       }
287     }
288
289   return (0);
290 }
291
292
293 /* FUNCTION
294  *    GatherProjectionsMPI
295  *
296  * SYNOPSIS
297  *    Gather's raysums from all processes in pjLocal in pjGlobal in process 0
298  */
299
300 #ifdef HAVE_MPI
301 void GatherProjectionsMPI (MPIWorld& mpiWorld, Projections& pjGlobal, Projections& pjLocal, const int opt_debug)
302 {
303   for (int iw = 0; iw < mpiWorld.getMyLocalWorkUnits(); iw++) {
304     DetectorArray& detArray = pjLocal.getDetectorArray(iw);
305     double viewAngle = detArray.viewAngle();
306     int nDet = detArray.nDet();
307     DetectorValue* detval = detArray.detValues();
308
309     mpiWorld.getComm().Send(&viewAngle, 1, MPI::DOUBLE, 0, 0);
310     mpiWorld.getComm().Send(&nDet, 1, MPI::INT, 0, 0);
311     mpiWorld.getComm().Send(detval, nDet, MPI::FLOAT, 0, 0);
312   }
313
314   if (mpiWorld.getRank() == 0) {
315     for (int iProc = 0; iProc < mpiWorld.getNumProcessors(); iProc++) {
316       for (int iw = mpiWorld.getStartWorkUnit(iProc); iw <= mpiWorld.getEndWorkUnit(iProc); iw++) {
317         MPI::Status status;
318         double viewAngle;
319         int nDet;
320         DetectorArray detArray = pjGlobal.getDetectorArray(iw);
321         DetectorValue* detval = detArray.detValues();
322
323         mpiWorld.getComm().Recv(&viewAngle, 1, MPI::DOUBLE, iProc, 0, status);
324         mpiWorld.getComm().Recv(&nDet, 1, MPI::INT, iProc, 0, status);
325         mpiWorld.getComm().Recv(detval, nDet, MPI::FLOAT, iProc, 0, status);
326         detArray.setViewAngle (viewAngle);
327       }
328     }
329   }
330
331 }
332 #endif
333
334
335 #ifndef NO_MAIN
336 int 
337 main (int argc, char* argv[])
338 {
339   return (phm2pj_main(argc, argv));
340 }
341 #endif
342