private synchronized void initProjectionMatrix(int projectionNumber) {
    // load projection Matrix for current Projection.
    SimpleMatrix pMat = getGeometry().getProjectionMatrix(projectionNumber).computeP();
    float[] pMatFloat = new float[pMat.getCols() * pMat.getRows()];
    for (int j = 0; j < pMat.getRows(); j++) {
      for (int i = 0; i < pMat.getCols(); i++) {

        pMatFloat[(j * pMat.getCols()) + i] = (float) pMat.getElement(j, i);
      }
    }

    // Obtain the global pointer to the view matrix from
    // the module
    if (projectionMatrix == null)
      projectionMatrix = context.createFloatBuffer(pMatFloat.length, Mem.READ_ONLY);

    projectionMatrix.getBuffer().put(pMatFloat);
    projectionMatrix.getBuffer().rewind();
    commandQueue.putWriteBuffer(projectionMatrix, true).finish();
  }
  public void OpenCLRun(double[] motionfield) {
    try {
      while (projectionsAvailable.size() > 0) {
        Thread.sleep(CONRAD.INVERSE_SPEEDUP);
        if (showStatus) {
          float status = (float) (1.0 / projections.size());
          if (largeVolumeMode) {
            IJ.showStatus("Streaming Projections to OpenCL Buffer");
          } else {
            IJ.showStatus("Backprojecting with OpenCL");
          }
          IJ.showProgress(status);
        }
        if (!largeVolumeMode) {
          workOnProjectionData(motionfield);
        } else {
          checkProjectionData();
        }
      }
      //			System.out.println("large Volume " + largeVolumeMode);
      if (largeVolumeMode) {
        // we have collected all projections.
        // now we can reconstruct subvolumes and stich them together.
        int reconDimensionZ = getGeometry().getReconDimensionZ();
        double voxelSpacingX = getGeometry().getVoxelSpacingX();
        double voxelSpacingY = getGeometry().getVoxelSpacingY();
        double voxelSpacingZ = getGeometry().getVoxelSpacingZ();
        useVOImap = false;
        initialize(projections.get(0));
        double originalOffsetZ = offsetZ;
        double originalReconDimZ = reconDimensionZ;
        reconDimensionZ = subVolumeZ;
        int maxProjectionNumber = projections.size();
        float all = nSteps * maxProjectionNumber * 2;
        for (int n = 0; n < nSteps; n++) { // For each subvolume
          // set all to 0;
          Arrays.fill(h_volume, 0);

          volumePointer.getBuffer().rewind();
          volumePointer.getBuffer().put(h_volume);
          volumePointer.getBuffer().rewind();
          commandQueue.putWriteBuffer(volumePointer, true).finish();

          offsetZ = originalOffsetZ - (reconDimensionZ * voxelSpacingZ * n);
          for (int p = 0; p < maxProjectionNumber; p++) { // For all projections
            float currentStep = (n * maxProjectionNumber * 2) + p;
            if (showStatus) {
              IJ.showStatus("Backprojecting with OpenCL");
              IJ.showProgress(currentStep / all);
            }
            // System.out.println("Current: " + p);
            float respoffset = (float) Math.round(motionfield[p] / voxelSpacingZ);
            try {
              projectSingleProjection(p, reconDimensionZ, respoffset);
            } catch (Exception e) {
              System.out.println("Backprojection of projection " + p + " was not successful.");
              e.printStackTrace();
            }
          }
          // Gather volume
          commandQueue.putReadBuffer(volumePointer, true).finish();
          volumePointer.getBuffer().rewind();
          volumePointer.getBuffer().get(h_volume);
          volumePointer.getBuffer().rewind();

          // move data to ImagePlus;
          if (projectionVolume != null) {
            for (int k = 0; k < reconDimensionZ; k++) {
              int index = (n * subVolumeZ) + k;
              if (showStatus) {
                float currentStep = (n * maxProjectionNumber * 2) + maxProjectionNumber + k;
                IJ.showStatus("Fetching Volume from OpenCL");
                IJ.showProgress(currentStep / all);
              }
              if (index < originalReconDimZ) {
                for (int j = 0; j < projectionVolume.getSize()[1]; j++) {
                  for (int i = 0; i < projectionVolume.getSize()[0]; i++) {
                    float value =
                        h_volume[
                            (((projectionVolume.getSize()[1] * k) + j)
                                    * projectionVolume.getSize()[0])
                                + i];
                    double[][] voxel = new double[4][1];
                    voxel[0][0] = (voxelSpacingX * i) - offsetX;
                    voxel[1][0] = (voxelSpacingY * j) - offsetY;
                    voxel[2][0] = (voxelSpacingZ * index) - originalOffsetZ;

                    // exception for the case "interestedInVolume == null" and largeVolume is
                    // enabled
                    if (interestedInVolume == null) {
                      projectionVolume.setAtIndex(i, j, index, value);
                    } else {
                      if (interestedInVolume.contains(voxel[0][0], voxel[1][0], voxel[2][0])) {
                        projectionVolume.setAtIndex(i, j, index, value);
                      } else {
                        projectionVolume.setAtIndex(i, j, index, 0);
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }

    } catch (InterruptedException e) {

      e.printStackTrace();
    }
    if (showStatus) IJ.showProgress(1.0);
    unload();
    if (debug) System.out.println("Unloaded");
  }
  protected void init() {
    if (!initialized) {
      largeVolumeMode = false;

      int reconDimensionX = getGeometry().getReconDimensionX();
      int reconDimensionY = getGeometry().getReconDimensionY();
      int reconDimensionZ = getGeometry().getReconDimensionZ();
      projectionsAvailable = new ArrayList<Integer>();
      projectionsDone = new ArrayList<Integer>();

      // Initialize JOCL.
      context = OpenCLUtil.createContext();

      try {
        // get the fastest device
        device = context.getMaxFlopsDevice();
        // create the command queue
        commandQueue = device.createCommandQueue();

        // initialize the program
        if (program == null || !program.getContext().equals(this.context)) {
          program =
              context
                  .createProgram(
                      OpenCLCompensatedBackProjector.class.getResourceAsStream(
                          "compensatedBackprojectCL.cl"))
                  .build();
        }

      } catch (Exception e) {
        if (commandQueue != null) commandQueue.release();
        if (kernelFunction != null) kernelFunction.release();
        if (program != null) program.release();
        // destory context
        if (context != null) context.release();
        // TODO: handle exception
        e.printStackTrace();
      }

      // check space on device:
      long memory = device.getMaxMemAllocSize();
      long availableMemory = (memory);
      long requiredMemory =
          (long)
              (((((double) reconDimensionX) * reconDimensionY * ((double) reconDimensionZ) * 4)
                  + (((double)
                          Configuration.getGlobalConfiguration().getGeometry().getDetectorHeight())
                      * Configuration.getGlobalConfiguration().getGeometry().getDetectorWidth()
                      * 4)));
      if (debug) {
        System.out.println("Total available Memory on OpenCL card:" + availableMemory);
        System.out.println("Required Memory on OpenCL card:" + requiredMemory);
      }
      if (requiredMemory > availableMemory) {
        nSteps = (int) OpenCLUtil.iDivUp(requiredMemory, availableMemory);
        if (debug) System.out.println("Switching to large volume mode with nSteps = " + nSteps);
        largeVolumeMode = true;
      }
      if (debug) {
        // TODO replace
        /*
        CUdevprop prop = new CUdevprop();
        JCudaDriver.cuDeviceGetProperties(prop, dev);
        System.out.println(prop.toFormattedString());
        */
      }

      // create the computing kernel
      kernelFunction = program.createCLKernel("backprojectKernel");

      // create the reconstruction volume;
      int memorysize = reconDimensionX * reconDimensionY * reconDimensionZ * 4;
      if (largeVolumeMode) {
        subVolumeZ = OpenCLUtil.iDivUp(reconDimensionZ, nSteps);
        if (debug) System.out.println("SubVolumeZ: " + subVolumeZ);
        h_volume = new float[reconDimensionX * reconDimensionY * subVolumeZ];
        memorysize = reconDimensionX * reconDimensionY * subVolumeZ * 4;
        if (debug) System.out.println("Memory: " + memorysize);
      } else {
        h_volume = new float[reconDimensionX * reconDimensionY * reconDimensionZ];
      }

      // compute adapted volume size
      //    volume size in x = multiple of bpBlockSize[0]
      //    volume size in y = multiple of bpBlockSize[1]

      int adaptedVolSize[] = new int[3];
      if ((reconDimensionX % bpBlockSize[0]) == 0) {
        adaptedVolSize[0] = reconDimensionX;
      } else {
        adaptedVolSize[0] = ((reconDimensionX / bpBlockSize[0]) + 1) * bpBlockSize[0];
      }
      if ((reconDimensionY % bpBlockSize[1]) == 0) {
        adaptedVolSize[1] = reconDimensionY;
      } else {
        adaptedVolSize[1] = ((reconDimensionY / bpBlockSize[1]) + 1) * bpBlockSize[1];
      }
      adaptedVolSize[2] = reconDimensionZ;
      int volStrideHost[] = new int[2];
      // compute volstride and copy it to constant memory
      volStrideHost[0] = adaptedVolSize[0];
      volStrideHost[1] = adaptedVolSize[0] * adaptedVolSize[1];

      // copy volume to device
      volumePointer = context.createFloatBuffer(h_volume.length, Mem.WRITE_ONLY);
      volumePointer.getBuffer().put(h_volume);
      volumePointer.getBuffer().rewind();

      // copy volume stride to device
      volStride = context.createIntBuffer(volStrideHost.length, Mem.READ_ONLY);
      volStride.getBuffer().put(volStrideHost);
      volStride.getBuffer().rewind();

      commandQueue.putWriteBuffer(volumePointer, true).putWriteBuffer(volStride, true).finish();

      initialized = true;
    }
  }