//******************************************************************************
//
// File:    SpinRelaxometryClu.java
// Package: edu.rit.mri
// Unit:    Class edu.rit.mri.SpinRelaxometryClu
//
// This Java source file is copyright (C) 2008 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ 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.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************

package edu.rit.mri;

import edu.rit.io.Files;

import edu.rit.mp.ObjectBuf;

import edu.rit.mp.buf.ObjectItemBuf;

import edu.rit.numeric.ArraySeries;
import edu.rit.numeric.Series;

import edu.rit.pj.Comm;
import edu.rit.pj.CommStatus;
import edu.rit.pj.IntegerSchedule;
import edu.rit.pj.ParallelRegion;
import edu.rit.pj.ParallelSection;
import edu.rit.pj.ParallelTeam;

import edu.rit.util.Range;

import java.io.File;
import java.io.IOException;

import java.util.ArrayList;

/**
 * Class SpinRelaxometryClu is a cluster parallel program that does a spin
 * relaxometry analysis of one or more magnetic resonance images. Each MR
 * image's spin signal data set is stored in a file as defined in class
 * {@linkplain SignalDataSetWriter}. The program does the following for each
 * spin signal data set file. Using an instance of class {@linkplain
 * SignalDataSetReader}, the program reads the spin signal data for each pixel
 * in the image. Using class {@linkplain PixelAnalysis}, the program does a spin
 * relaxometry analysis on each pixel and computes the tissues for each pixel.
 * Using an instance of class {@linkplain TissuesDataSetWriter}, the program
 * writes the tissues data into another file. For example, if the input spin
 * signal data set files are named image1.dat, image2.dat, and
 * so on, the output tissues data set files are named
 * tissues_image1.dat, tissues_image2.dat, and so on.
 * 

* The program uses the master-worker pattern for load balancing. The program * uses the parallel input files pattern for reduced message passing. The master * partitions each image into chunks of 100 pixels and sends the chunks to the * workers. In parallel, the workers read the input spin signal data set file, * do the spin relaxometry analysis calculations for each pixel in the chunk, * and send the calculated tissues data back to the master. The master writes * the tissues data into the output tissues data set file. *

* Usage: java -Dpj.np=K edu.rit.mri.SpinRelaxometryClu R1_lower * R1_upper N signalfile [ signalfile . . . ] *
K = Number of parallel processes *
R1_lower = Lower R1 spin-lattice relaxation rate (1/sec) *
R1_upper = Upper R1 spin-lattice relaxation rate (1/sec) *
N = Number of R1 intervals *
signalfile = Input spin signal data set file * * @author Alan Kaminsky * @version 25-Jun-2008 */ public class SpinRelaxometryClu { // Global variables. // World communicator. static Comm world; static int size; static int rank; // Command line arguments. static double R1_lower; static double R1_upper; static int N; static String[] signalfilename; // Prevent construction. private SpinRelaxometryClu() { } // Main program. /** * Main program. */ public static void main (String[] args) throws Exception { // Start timing. long t1 = System.currentTimeMillis(); // Initialize world communicator. Comm.init (args); world = Comm.world(); size = world.size(); rank = world.rank(); // Parse command line arguments. if (args.length < 4) usage(); R1_lower = Double.parseDouble (args[0]); R1_upper = Double.parseDouble (args[1]); N = Integer.parseInt (args[2]); signalfilename = new String [args.length-3]; System.arraycopy (args, 3, signalfilename, 0, args.length-3); // Process 0 executes the master section and the worker section in // parallel threads. if (rank == 0) { new ParallelTeam(2).execute (new ParallelRegion() { public void run() throws Exception { execute (new ParallelSection() { public void run() throws Exception { masterSection(); } }, new ParallelSection() { public void run() throws Exception { workerSection(); } }); } }); } // Processes 1 and up execute just the worker section. else { workerSection(); } // Stop timing. long t2 = System.currentTimeMillis(); System.out.println ((t2-t1)+" msec "+rank); } // Hidden operations. /** * Execute the master section. * * @exception IOException * Thrown if an I/O error occurred. */ private static void masterSection() throws IOException { int worker; PixelChunk chunk; // Set up array of writers for output tissues data sets. TissuesDataSetWriter[] writer = new TissuesDataSetWriter [signalfilename.length]; for (int f = 0; f < writer.length; ++ f) { SignalDataSetReader reader = new SignalDataSetReader (new File (signalfilename[f])); int H = reader.getHeight(); int W = reader.getWidth(); reader.close(); writer[f] = new TissuesDataSetWriter (new File (Files.fileNamePrepend (signalfilename[f], "tissues_")), H, W); } // Set up schedule to analyze pixels in chunks of 100. PixelSchedule schedule = new PixelSchedule (100, signalfilename); // Send initial chunk to each worker. If null, no more work for that // worker. Keep count of active workers. int activeWorkers = size; for (worker = 0; worker < size; ++ worker) { chunk = schedule.next(); world.send (worker, ObjectBuf.buffer (chunk)); if (chunk == null) -- activeWorkers; } // Repeat until all workers have finished. while (activeWorkers > 0) { // Receive a chunk of pixel tissues from any worker. ObjectItemBuf buf = ObjectBuf.buffer(); CommStatus status = world.receive (null, buf); worker = status.fromRank; // Send next chunk to that specific worker. If null, no more work. chunk = schedule.next(); world.send (worker, ObjectBuf.buffer (chunk)); if (chunk == null) -- activeWorkers; // Record pixel tissues in output tissues data set. for (PixelTissues tissues : buf.item) { if (tissues != null) { writer[tissues.fileIndex()].addPixelTissues (tissues); } } } // All done. for (int f = 0; f < writer.length; ++ f) { writer[f].close(); } } /** * Execute the worker section. * * @exception IOException * Thrown if an I/O error occurred. */ private static void workerSection() throws IOException { int fileIndex = -1; SignalDataSetReader reader = null; Series t_series = null; int M = 0; double[][] A = null; // Compute spin relaxation rates. double[] R1 = new double [N+1]; double log_R1_lower = Math.log (R1_lower); double log_R1_upper = Math.log (R1_upper); double interval = (log_R1_upper - log_R1_lower)/N; for (int j = 0; j <= N; ++ j) { R1[j] = Math.exp (log_R1_lower + j*interval); } ArraySeries R1_series = new ArraySeries (R1); // Set up lists to receive analysis results. ArrayList rho_list = new ArrayList(); ArrayList R1_list = new ArrayList(); // Repeat until no more work. workerloop: for (;;) { // Receive a chunk of pixel indexes from the master. If null, no // more work. ObjectItemBuf buf = ObjectBuf.buffer(); world.receive (0, buf); PixelChunk chunk = buf.item; if (chunk == null) break workerloop; int f = chunk.fileIndex(); int lb = chunk.pixelIndex(); int len = chunk.pixelCount(); // If we are now working on a different file: if (f != fileIndex) { // Close old file. if (reader != null) reader.close(); // Open new file. fileIndex = f; reader = new SignalDataSetReader (new File (signalfilename[f])); // Get time series. t_series = reader.getTimeSeries(); M = t_series.length(); // Compute design matrix. A = new double [M] [N+1]; for (int i = 0; i < M; ++ i) { double[] A_i = A[i]; double t_i = t_series.x(i); for (int j = 0; j <= N; ++ j) { A_i[j] = SpinSignal.S (R1[j], t_i); } } } // Set up array of pixel tissues to hold analysis results. PixelTissues[] tissues = new PixelTissues [len]; // Process all pixels in chunk. for (int i = 0; i < len; ++ i) { int index = lb + i; PixelSignal signal_i = reader.getPixelSignal (index); if (signal_i != null) { // Get measured spin signal. Series S_series = signal_i.S_measured(); // Do the spin relaxometry analysis. PixelAnalysis.analyze (t_series, S_series, R1_series, A, rho_list, R1_list); // Record analysis results. tissues[i] = new PixelTissues (f, index, rho_list, R1_list); } } // Send chunk of pixel tissues to the master. world.send (0, ObjectBuf.objectBuffer (tissues)); } // All done. if (reader != null) reader.close(); } /** * Print a usage message and exit. */ private static void usage() { System.err.println ("Usage: java -Dpj.np= edu.rit.mri.SpinRelaxometryClu [ ...]"); System.err.println (" = Number of parallel processes"); System.err.println (" = Lower R1 spin-lattice relaxation rate (1/sec)"); System.err.println (" = Upper R1 spin-lattice relaxation rate (1/sec)"); System.err.println (" = Number of R1 intervals"); System.err.println (" = Input spin signal data set file"); System.exit (1); } }

Ads help maintain this website.