/** * Transforms the specified array of times and marks. Known samples are those for which times are * zero, and times and marks for these known samples are used to compute times and marks for * unknown samples. * * @param times input/output array of times. * @param marks input/output array of marks. */ public void apply(float[][][] times, int[][][] marks) { // Measure elapsed time in seconds. Stopwatch sw = new Stopwatch(); sw.start(); log.fine("TimeMarker3.apply: begin time=" + (int) sw.time()); // Initialize all unknown times to infinity. for (int i3 = 0; i3 < _n3; ++i3) { for (int i2 = 0; i2 < _n2; ++i2) { for (int i1 = 0; i1 < _n1; ++i1) { if (times[i3][i2][i1] != 0.0f) times[i3][i2][i1] = INFINITY; } } } // Indices of known samples in random order. short[][] kk = indexKnownSamples(times); short[] k1 = kk[0]; short[] k2 = kk[1]; short[] k3 = kk[2]; shuffle(k1, k2, k3); int nk = k1.length; // Array for the eikonal solution times. float[][][] t = new float[_n3][_n2][_n1]; // Active list of samples used to compute times. ActiveList al = new ActiveList(); // For all known samples, ... for (int ik = 0; ik < nk; ++ik) { if (ik % (1 + (nk - 1) / 100) == 0) log.fine(" apply: ik/nk=" + ik + "/" + nk + " time=" + (int) sw.time()); int i1 = k1[ik]; int i2 = k2[ik]; int i3 = k3[ik]; // Clear activated flags so we can tell which samples become activated. clearActivated(); // Put the known sample with time zero into the active list. t[i3][i2][i1] = 0.0f; al.append(_s[i3][i2][i1]); // The mark for the known sample. int m = marks[i3][i2][i1]; // Process the active list until empty. solve(al, t, m, times, marks); } // Log elapsed time. sw.stop(); log.fine("TimeMarker3.apply: end time=" + (int) sw.time()); }
/* * Solves for times by processing samples in the active list in parallel. */ private void solveParallel( final ActiveList al, final float[][][] t, final int m, final float[][][] times, final int[][][] marks) { int mbmin = 64; // target minimum number of samples per block int nbmax = 256; // maximum number of blocks final float[][] dtask = new float[nbmax][]; final ActiveList[] bltask = new ActiveList[nbmax]; while (!al.isEmpty()) { final int n = al.size(); // number of samples in active (A) list final int mbmax = max(mbmin, 1 + (n - 1) / nbmax); // max samples per block final int nb = 1 + (n - 1) / mbmax; // number of blocks <= nbmax final int mb = 1 + (n - 1) / nb; // evenly distribute samples per block Parallel.loop( nb, new Parallel.LoopInt() { // for all blocks, ... public void compute(int ib) { if (bltask[ib] == null) { // if necessary for this block, make ... dtask[ib] = new float[6]; // work array for tensor coefficients bltask[ib] = new ActiveList(); // and an empty active list } int i = ib * mb; // beginning of block int j = min(i + mb, n); // beginning of next block (or end) for (int k = i; k < j; ++k) { // for each sample in block, ... Sample s = al.get(k); // get k'th sample from A list solveOne(t, m, times, marks, s, bltask[ib], dtask[ib]); // do sample } bltask[ib].setAllAbsent(); // needed when merging B lists below } }); // Merge samples from all B lists to a new A list. All samples // in B lists are currently marked as absent in the A list. As // samples in B lists are appended to the A list, their absent // flags are set to false, so that no sample is appended more // than once to the new A list. al.clear(); for (int ib = 0; ib < nb; ++ib) { if (bltask[ib] != null) { al.appendIfAbsent(bltask[ib]); bltask[ib].clear(); } } } }
void appendIfAbsent(ActiveList al) { if (_n + al._n > _a.length) growTo(2 * (_n + al._n)); int n = al._n; for (int i = 0; i < n; ++i) { Sample s = al.get(i); if (s.absent) { _a[_n++] = s; s.absent = false; } } }
/* * Solves for times by sequentially processing each sample in active list. */ private void solveSerial( ActiveList al, float[][][] t, int m, float[][][] times, int[][][] marks) { float[] d = new float[6]; ActiveList bl = new ActiveList(); int ntotal = 0; while (!al.isEmpty()) { // al.shuffle(); // demonstrate that solution depends on order int n = al.size(); ntotal += n; for (int i = 0; i < n; ++i) { Sample s = al.get(i); solveOne(t, m, times, marks, s, bl, d); } bl.setAllAbsent(); al.clear(); al.appendIfAbsent(bl); bl.clear(); } trace("solveSerial: ntotal=" + ntotal); trace(" nratio=" + (float) ntotal / (float) (_n1 * _n2 * _n3)); }
/* * Processes one sample from the A list. * Appends samples not yet converged to the B list. */ private void solveOne( float[][][] t, int m, float[][][] times, int[][][] marks, Sample s, ActiveList bl, float[] d) { // Sample indices. int i1 = s.i1; int i2 = s.i2; int i3 = s.i3; // Current time and new time computed from all four neighbors. float ti = currentTime(t, i1, i2, i3); float ci = computeTime(t, i1, i2, i3, K1S[6], K2S[6], K3S[6], d); t[i3][i2][i1] = ci; // If new and current times are close enough (converged), then ... if (ci >= ti * ONE_MINUS_EPSILON) { // Neighbors may need to be activated if computed time is small // relative to the minimum time computed so far. The factor 1.5 // improves accuracy for large anisotropy. Cost increases as the // square of this factor, so we do not want it to be too large. boolean checkNabors = ci <= 1.5f * times[i3][i2][i1]; // If computed time less than minimum time, mark this sample. if (ci < times[i3][i2][i1]) { times[i3][i2][i1] = ci; marks[i3][i2][i1] = m; } // If necessary, check the neighbors. if (checkNabors) { // For all six neighbors, ... for (int k = 0; k < 6; ++k) { // Neighbor sample indices; skip if out of bounds. int j1 = i1 + K1[k]; if (j1 < 0 || j1 >= _n1) continue; int j2 = i2 + K2[k]; if (j2 < 0 || j2 >= _n2) continue; int j3 = i3 + K3[k]; if (j3 < 0 || j3 >= _n3) continue; // Skip neighbor sample if computed time would be too big. // if (!doComputeTime(t,times,j1,j2)) continue; // Current and computed times for the neighbor. float tj = currentTime(t, j1, j2, j3); float cj = computeTime(t, j1, j2, j3, K1S[k], K2S[k], K3S[k], d); // If computed time is significantly less than current time, ... if (cj < tj * ONE_MINUS_EPSILON) { // Replace the current time. t[j3][j2][j1] = cj; // Append neighbor to the B list, thereby activating it. bl.append(_s[j3][j2][j1]); } } } } // Else, if not converged, append this sample to the B list. else { bl.append(s); } }
/* * Solves for times by processing samples in the active list in parallel. */ private void solveParallelX( final ActiveList al, final float[][][] t, final int m, final float[][][] times, final int[][][] marks) { int nthread = Runtime.getRuntime().availableProcessors(); ExecutorService es = Executors.newFixedThreadPool(nthread); CompletionService<Void> cs = new ExecutorCompletionService<Void>(es); ActiveList[] bl = new ActiveList[nthread]; float[][] d = new float[nthread][]; for (int ithread = 0; ithread < nthread; ++ithread) { bl[ithread] = new ActiveList(); d[ithread] = new float[6]; } final AtomicInteger ai = new AtomicInteger(); int ntotal = 0; // int niter = 0; while (!al.isEmpty()) { ai.set(0); // initialize the shared block index to zero final int n = al.size(); // number of samples in active (A) list ntotal += n; final int mb = 32; // size of blocks of samples final int nb = 1 + (n - 1) / mb; // number of blocks of samples int ntask = min(nb, nthread); // number of tasks (threads to be used) for (int itask = 0; itask < ntask; ++itask) { // for each task, ... final ActiveList bltask = bl[itask]; // task-specific B list final float[] dtask = d[itask]; // task-specific work array cs.submit( new Callable<Void>() { // submit new task public Void call() { for (int ib = ai.getAndIncrement(); ib < nb; ib = ai.getAndIncrement()) { int i = ib * mb; // beginning of block int j = min(i + mb, n); // beginning of next block (or end) for (int k = i; k < j; ++k) { // for each sample in block, ... Sample s = al.get(k); // get k'th sample from A list solveOne(t, m, times, marks, s, bltask, dtask); // process sample } } bltask.setAllAbsent(); // needed when merging B lists below return null; } }); } try { for (int itask = 0; itask < ntask; ++itask) cs.take(); } catch (InterruptedException e) { throw new RuntimeException(e); } // Merge samples from all B lists to a new A list. As samples // are appended, their absent flags are set to false, so that // each sample is appended no more than once to the new A list. al.clear(); for (int itask = 0; itask < ntask; ++itask) { al.appendIfAbsent(bl[itask]); bl[itask].clear(); } // ++niter; } es.shutdown(); // trace("solveParallel: ntotal="+ntotal); // trace(" nratio="+(float)ntotal/(float)(_n1*_n2*_n3)); }