r97: Converted Scanner and Projections to C++
[ctsim.git] / src / phm2pj.cpp
diff --git a/src/phm2pj.cpp b/src/phm2pj.cpp
new file mode 100644 (file)
index 0000000..fe83154
--- /dev/null
@@ -0,0 +1,342 @@
+/*****************************************************************************
+** FILE IDENTIFICATION
+**
+**   Name:          phm2pj.cpp
+**   Purpose:       Take projections of a phantom object
+**   Programmer:    Kevin Rosenberg
+**   Date Started:  1984
+**
+**  This is part of the CTSim program
+**  Copyright (C) 1983-2000 Kevin Rosenberg
+**
+**  $Id: phm2pj.cpp,v 1.1 2000/06/17 20:12:15 kevin Exp $
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License (version 2) as
+**  published by the Free Software Foundation.
+**
+**  This program is distributed in the hope that it will be useful,
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+**
+**  You should have received a copy of the GNU General Public License
+**  along with this program; if not, write to the Free Software
+**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+******************************************************************************/
+
+#include "ct.h"
+#include "timer.h"
+
+
+enum { O_PHANTOM, O_DESC, O_NRAY, O_ROTANGLE, O_PHMFILE,
+       O_TRACE, O_VERBOSE, O_HELP, O_DEBUG, O_VERSION };
+
+static struct option phm2pj_options[] = 
+{
+  {"phantom", 1, 0, O_PHANTOM},
+  {"phmfile", 1, 0, O_PHMFILE},
+  {"desc", 1, 0, O_DESC},
+  {"nray", 1, 0, O_NRAY},
+  {"rotangle", 1, 0, O_ROTANGLE},
+  {"trace", 1, 0, O_TRACE},
+  {"verbose", 0, 0, O_VERBOSE},
+  {"help", 0, 0, O_HELP},
+  {"debug", 0, 0, O_DEBUG},
+  {"version", 0, 0, O_VERSION},
+  {0, 0, 0, 0}
+};
+
+
+void 
+phm2pj_usage (const char *program)
+{
+  cout << "usage: " << fileBasename(program) << " outfile ndet nview [--phantom phantom-name] [--phmfile filename] [OPTIONS]" << endl;
+  cout << "Calculate (projections) through phantom object, either a predefined --phantom or a --phmfile" << endl;
+  cout << "" << endl;
+  cout << "     outfile      Name of output file for raysums" << endl;
+  cout << "     ndet         Number of detectors" << endl;
+  cout << "     nview        Number of rotated views" << endl;
+  cout << "     --phantom    Phantom to use for projection" << endl;
+  cout << "        herman    Herman head phantom" << endl;
+  cout << "        rowland   Rowland head phantom" << endl;
+  cout << "        browland  Bordered Rowland head phantom" << endl;
+  cout << "        unitpulse Unit pulse phantom" << endl;
+  cout << "     --phmfile    Get Phantom from phantom file" << endl;
+  cout << "     --desc       Description of raysum" << endl;
+  cout << "     --nray       Number of rays per detector (default = 1)" << endl;
+  cout << "     --rotangle   Degrees to rotate view through, multiple of PI (default = 1)" << endl;
+  cout << "     --trace      Trace level to use" << endl;
+  cout << "        none      No tracing (default)" << endl;
+  cout << "        text      Trace text level" << endl;
+  cout << "        phm       Trace phantom image" << endl;
+  cout << "        rays      Trace rays" << endl;
+  cout << "        plot      Trace plot" << endl;
+  cout << "        clipping  Trace clipping" << endl;
+  cout << "     --verbose    Verbose mode" << endl;
+  cout << "     --debug      Debug mode" << endl;
+  cout << "     --version    Print version" << endl;
+  cout << "     --help       Print this help message" << endl;
+}
+
+#ifdef HAVE_MPI
+void GatherProjectionsMPI (MPIWorld& mpiWorld, Projections& pjGlobal, Projections& pjLocal, const int opt_debug);
+#endif
+
+int 
+phm2pj_main (int argc, char* argv[])
+{
+  Phantom phm;
+  ScannerGeometry opt_geometry = DETECTOR_PARALLEL;
+  char *opt_outfile = NULL;
+  string opt_desc;
+  string opt_phmfilename;
+  int opt_ndet, opt_nview;
+  int opt_nray = 1;
+  int opt_trace = 0;
+  int opt_phmnum = -1;
+  int opt_verbose = 0;
+  int opt_debug = 0;
+  double opt_rotangle = 1;
+  char* endptr = NULL;
+  char* endstr;
+
+#ifdef HAVE_MPI
+  MPIWorld mpiWorld (argc, argv);
+#endif
+
+  Timer timerProgram;
+
+#ifdef HAVE_MPI
+  if (mpiWorld.getRank() == 0) {
+#endif
+    while (1) {
+      int c = getopt_long(argc, argv, "", phm2pj_options, NULL);
+
+      if (c == -1)
+       break;
+      
+      switch (c) {
+      case O_PHANTOM:
+       if ((opt_phmnum = opt_set_phantom (optarg)) < 0) {
+         phm2pj_usage(argv[0]);
+         return (1);
+       }
+       phm.create (opt_phmnum);
+       break;
+      case O_PHMFILE:
+#ifdef HAVE_MPI
+       if (mpiWorld.getRank() == 0)
+         cerr << "Can not read phantom from file in MPI mode" << endl;
+       return (1);
+#endif
+       opt_phmfilename = optarg;
+       phm.createFromFile (opt_phmfilename.c_str());
+       break;
+      case O_VERBOSE:
+       opt_verbose = 1;
+       break;
+      case O_DEBUG:
+       opt_debug = 1;
+       break;
+       break;
+      case O_TRACE:
+       if ((opt_trace = opt_set_trace(optarg)) < 0) {
+         phm2pj_usage(argv[0]);
+         return (1);
+       }
+       break;
+      case O_DESC:
+       opt_desc = optarg;
+       break;
+      case O_ROTANGLE:
+       opt_rotangle = strtod(optarg, &endptr);
+       endstr = optarg + strlen(optarg);
+       if (endptr != endstr) {
+         cerr << "Error setting --rotangle to " << optarg << endl;
+         phm2pj_usage(argv[0]);
+         return (1);
+       }
+       break;
+      case O_NRAY:
+       opt_nray = strtol(optarg, &endptr, 10);
+       endstr = optarg + strlen(optarg);
+       if (endptr != endstr) {
+         cerr << "Error setting --nray to %s" << optarg << endl;
+         phm2pj_usage(argv[0]);
+         return (1);
+       }
+       break;
+        case O_VERSION:
+#ifdef VERSION
+       cout << "Version: " << VERSION << endl;
+#else
+        cout << "Unknown version number" << endl;
+#endif
+         exit(0);
+      case O_HELP:
+      case '?':
+       phm2pj_usage(argv[0]);
+       return (0);
+      default:
+       phm2pj_usage(argv[0]);
+       return (1);
+      }
+    }
+  
+    if (phm.nPElem() == 0) {
+      cerr << "No phantom defined" << endl;
+      phm2pj_usage(argv[0]);
+      return (1);
+    }
+    if (optind + 3 != argc) {
+      phm2pj_usage(argv[0]);
+      return (1);
+    }
+
+    opt_outfile = argv[optind];
+    opt_ndet = strtol(argv[optind+1], &endptr, 10);
+    endstr = argv[optind+1] + strlen(argv[optind+1]);
+    if (endptr != endstr) {
+      cerr << "Error setting --ndet to " << argv[optind+1] << endl;
+      phm2pj_usage(argv[0]);
+      return (1);
+    }
+    opt_nview = strtol(argv[optind+2], &endptr, 10);
+    endstr = argv[optind+2] + strlen(argv[optind+2]);
+    if (endptr != endstr) {
+      cerr << "Error setting --nview to " << argv[optind+2] << endl;
+      phm2pj_usage(argv[0]);
+      return (1);
+    }
+
+    ostringstream desc;
+    desc << "Raysum_Collect: NDet=" << opt_ndet << ", Nview=" << opt_nview << ", NRay=" << opt_nray << ", RotAngle=" << opt_rotangle << ", ";
+    if (opt_phmfilename.length()) {
+      desc << "PhantomFile=" << opt_phmfilename;
+    } else if (opt_phmnum != -1) {
+      desc << "Phantom=" << name_of_phantom(opt_phmnum);
+    }
+    if (opt_desc.length()) {
+      desc << ": " << opt_desc;
+    }
+    opt_desc = desc.str();
+#ifdef HAVE_MPI
+  }
+#endif
+
+#ifdef HAVE_MPI
+  mpiWorld.getComm().Bcast (&opt_rotangle, 1, MPI::DOUBLE, 0);
+  mpiWorld.getComm().Bcast (&opt_nview, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&opt_ndet, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&opt_nray, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&opt_phmnum, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&opt_verbose, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&opt_debug, 1, MPI::INT, 0);
+  mpiWorld.getComm().Bcast (&opt_trace, 1, MPI::INT, 0);
+
+  if (mpiWorld.getRank() > 0 && opt_phmnum >= 0)
+    phm.create (opt_phmnum);
+#endif
+
+  opt_rotangle *= PI;
+  Scanner scanner (phm, opt_geometry, opt_ndet, opt_nview, opt_nray, opt_rotangle);
+
+#ifdef HAVE_MPI
+  mpiWorld.setTotalWorkUnits (opt_nview);
+
+  Projections pjGlobal;
+  if (mpiWorld.getRank() == 0) {
+    pjGlobal = Projections (scanner);
+  }
+  
+  Scanner localScanner (phm, opt_geometry, opt_ndet, mpiWorld.getMyLocalWorkUnits(), opt_nray, opt_rotangle);
+  Projections pjLocal (localScanner);
+  if (opt_debug)
+    cout << "pjLocal->nview = " << pjLocal.nView() << " (process " << mpiWorld.getRank() << ")" << endl;;
+
+  TimerMPI timerProject (mpiWorld.getComm());
+  localScanner.collectProjections (pjLocal, phm, mpiWorld.getMyStartWorkUnit(), opt_trace);
+  if (opt_verbose)
+    timerProject.timerEndAndReport ("Time to collect projections");
+
+  TimerMPI timerGather (mpiWorld.getComm());
+  GatherProjectionsMPI (mpiWorld, pjGlobal, pjLocal, opt_debug);
+  if (opt_verbose)
+    timerGather.timerEndAndReport ("Time to gather projections");
+#else
+  Projections pjGlobal (scanner);
+  scanner.collectProjections (pjGlobal, phm, 0, opt_trace);
+#endif
+  
+#ifdef HAVE_MPI
+  if (mpiWorld.getRank() == 0) 
+#endif
+    {
+      pjGlobal.setCalcTime (timerProgram.timerEnd());
+      pjGlobal.setRemark (opt_desc);
+      pjGlobal.write (opt_outfile);
+      if (opt_verbose) {
+       phm.print();
+       cout << endl;
+       pjGlobal.printScanInfo();
+       cout << endl;
+       cout << "Remark: " << pjGlobal.remark() << endl;
+       cout << "Run time: " << pjGlobal.calcTime() << " seconds" << endl;
+      }
+    }
+
+  return (0);
+}
+
+
+/* FUNCTION
+ *    GatherProjectionsMPI
+ *
+ * SYNOPSIS
+ *    Gather's raysums from all processes in pjLocal in pjGlobal in process 0
+ */
+
+#ifdef HAVE_MPI
+void GatherProjectionsMPI (MPIWorld& mpiWorld, Projections& pjGlobal, Projections& pjLocal, const int opt_debug)
+{
+  for (int iw = 0; iw < mpiWorld.getMyLocalWorkUnits(); iw++) {
+    DetectorArray& detArray = pjLocal.getDetectorArray(iw);
+    double viewAngle = detArray.viewAngle();
+    int nDet = detArray.nDet();
+    DetectorValue* detval = detArray.detValues();
+
+    mpiWorld.getComm().Send(&viewAngle, 1, MPI::DOUBLE, 0, 0);
+    mpiWorld.getComm().Send(&nDet, 1, MPI::INT, 0, 0);
+    mpiWorld.getComm().Send(detval, nDet, MPI::FLOAT, 0, 0);
+  }
+
+  if (mpiWorld.getRank() == 0) {
+    for (int iProc = 0; iProc < mpiWorld.getNumProcessors(); iProc++) {
+      for (int iw = mpiWorld.getStartWorkUnit(iProc); iw <= mpiWorld.getEndWorkUnit(iProc); iw++) {
+       MPI::Status status;
+       double viewAngle;
+       int nDet;
+       DetectorArray detArray = pjGlobal.getDetectorArray(iw);
+       DetectorValue* detval = detArray.detValues();
+
+       mpiWorld.getComm().Recv(&viewAngle, 1, MPI::DOUBLE, iProc, 0, status);
+       mpiWorld.getComm().Recv(&nDet, 1, MPI::INT, iProc, 0, status);
+       mpiWorld.getComm().Recv(detval, nDet, MPI::FLOAT, iProc, 0, status);
+       detArray.setViewAngle (viewAngle);
+      }
+    }
+  }
+
+}
+#endif
+
+
+#ifndef NO_MAIN
+int 
+main (int argc, char* argv[])
+{
+  return (phm2pj_main(argc, argv));
+}
+#endif
+