org.geppetto.solver.sph.SPHSolverService.java Source code

Java tutorial

Introduction

Here is the source code for org.geppetto.solver.sph.SPHSolverService.java

Source

/*******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2011 - 2015 OpenWorm.
 * http://openworm.org
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the MIT License
 * which accompanies this distribution, and is available at
 * http://opensource.org/licenses/MIT
 *
 * Contributors:
 *        OpenWorm - http://openworm.org/people.html
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 *******************************************************************************/

package org.geppetto.solver.sph;

import static java.lang.System.out;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bridj.Pointer;
import org.geppetto.core.common.GeppettoExecutionException;
import org.geppetto.core.common.GeppettoInitializationException;
import org.geppetto.core.data.model.AVariable;
import org.geppetto.core.data.model.ArrayVariable;
import org.geppetto.core.data.model.SimpleType;
import org.geppetto.core.data.model.SimpleType.Type;
import org.geppetto.core.data.model.SimpleVariable;
import org.geppetto.core.data.model.StructuredType;
import org.geppetto.core.data.model.VariableList;
import org.geppetto.core.model.IModel;
import org.geppetto.core.model.quantities.PhysicalQuantity;
import org.geppetto.core.model.runtime.ACompositeNode;
import org.geppetto.core.model.runtime.ANode;
import org.geppetto.core.model.runtime.AspectNode;
import org.geppetto.core.model.runtime.AspectSubTreeNode;
import org.geppetto.core.model.runtime.AspectSubTreeNode.AspectTreeType;
import org.geppetto.core.model.runtime.CompositeNode;
import org.geppetto.core.model.runtime.EntityNode;
import org.geppetto.core.model.runtime.ParticleNode;
import org.geppetto.core.model.runtime.VariableNode;
import org.geppetto.core.model.values.FloatValue;
import org.geppetto.core.model.values.ValuesFactory;
import org.geppetto.core.simulation.IRunConfiguration;
import org.geppetto.core.solver.ISolver;
import org.geppetto.core.utilities.VariablePathSerializer;
import org.geppetto.core.visualisation.model.Point;
import org.geppetto.model.sph.Connection;
import org.geppetto.model.sph.common.SPHConstants;
import org.geppetto.model.sph.services.SPHModelInterpreterService;
import org.geppetto.model.sph.x.SPHModelX;
import org.geppetto.model.sph.x.Vector3DX;
import org.springframework.stereotype.Service;

import com.nativelibs4java.opencl.CLBuffer;
import com.nativelibs4java.opencl.CLContext;
import com.nativelibs4java.opencl.CLDevice;
import com.nativelibs4java.opencl.CLEvent;
import com.nativelibs4java.opencl.CLKernel;
import com.nativelibs4java.opencl.CLMem;
import com.nativelibs4java.opencl.CLPlatform.DeviceFeature;
import com.nativelibs4java.opencl.CLProgram;
import com.nativelibs4java.opencl.CLQueue;
import com.nativelibs4java.opencl.JavaCL;
import com.nativelibs4java.util.IOUtils;

@Service
public class SPHSolverService implements ISolver {

    private static Log logger = LogFactory.getLog(SPHSolverService.class);

    private VariableList watchableVariables = new VariableList();
    private VariableList forceableVariables = new VariableList();

    List<String> watchListVarNames = new ArrayList<String>();
    boolean watching = false;

    private CLContext _context;
    public CLQueue _queue;
    private CLProgram _program;
    private CLDevice _device;
    private CLBuffer<Float> _acceleration;
    private CLBuffer<Integer> _gridCellIndex;
    private CLBuffer<Integer> _gridCellIndexFixedUp;
    private CLBuffer<Float> _neighborMap;
    private CLBuffer<Integer> _particleIndex;
    private CLBuffer<Integer> _particleIndexBack;
    private CLBuffer<Float> _position;
    private CLBuffer<Float> _pressure;
    private CLBuffer<Float> _rho;
    private CLBuffer<Float> _sortedPosition;
    private CLBuffer<Float> _sortedVelocity;
    private CLBuffer<Float> _velocity;
    private CLBuffer<Float> _elasticConnectionsData;
    private CLBuffer<Float> _activationSignal;

    private Pointer<Float> _accelerationPtr;
    private Pointer<Integer> _gridCellIndexPtr;
    private Pointer<Integer> _gridCellIndexFixedUpPtr;
    private Pointer<Float> _neighborMapPtr;
    private Pointer<Integer> _particleIndexPtr;
    private Pointer<Integer> _particleIndexBackPtr;
    private Pointer<Float> _positionPtr;
    private Pointer<Float> _pressurePtr;
    private Pointer<Float> _rhoPtr;
    private Pointer<Float> _sortedPositionPtr;
    private Pointer<Float> _sortedVelocityPtr;
    private Pointer<Float> _velocityPtr;
    private Pointer<Float> _elasticConnectionsDataPtr;
    private Pointer<Float> _activationSignalPtr;

    /*
     * Kernel declarations
     */
    private CLKernel _clearBuffers;
    private CLKernel _findNeighbors;
    private CLKernel _hashParticles;
    private CLKernel _indexx;
    private CLKernel _sortPostPass;

    // additional kernels for PCISPH
    private CLKernel _pcisph_computeDensity;
    private CLKernel _pcisph_computeForcesAndInitPressure;
    private CLKernel _pcisph_integrate;
    private CLKernel _pcisph_predictPositions;
    private CLKernel _pcisph_predictDensity;
    private CLKernel _pcisph_correctPressure;
    private CLKernel _pcisph_computePressureForceAcceleration;
    private CLKernel _pcisph_computeElasticForces;

    public float _xMax;
    public float _xMin;
    public float _yMax;
    public float _yMin;
    public float _zMax;
    public float _zMin;
    public int _elasticBundlesCount = 0;

    public int _gridCellsX;
    public int _gridCellsY;
    public int _gridCellsZ;
    public int _gridCellCount;
    public int _particleCount;
    public int _numOfLiquidP;
    public int _numOfElasticP;
    public int _numOfBoundaryP;

    private SPHModelX _model;

    private boolean _recordCheckPoints = false;

    /*
     * Checkpoints for the last computed step NOTE: stores all buffer values
     * after each kernel execution for troubleshooting purposes
     */
    private Map<KernelsEnum, PCISPHCheckPoint> _checkpointsMap = new LinkedHashMap<KernelsEnum, PCISPHCheckPoint>();

    public Map<KernelsEnum, PCISPHCheckPoint> getCheckpointsMap() {
        return _checkpointsMap;
    }

    /*
     * A map of buffer sizes NOTE: these values are used in multiple places so
     * storing them here reduces potential for error
     */
    private Map<BuffersEnum, Integer> _buffersSizeMap = new LinkedHashMap<BuffersEnum, Integer>();

    public Map<BuffersEnum, Integer> getBuffersSizeMap() {
        return _buffersSizeMap;
    }

    public static Random RandomGenerator = new Random();

    public SPHSolverService(HardwareProfileEnum hardwareProfile) throws Exception {
        this.onceOffInit(hardwareProfile);
    }

    public SPHSolverService() throws Exception {
        this(HardwareProfileEnum.GPU);
    }

    public SPHSolverService(boolean recordCheckpoints) throws Exception {
        this();

        _recordCheckPoints = recordCheckpoints;
    }

    private void onceOffInit(HardwareProfileEnum hwProfile) throws IOException {
        // TODO: check if the selected profile is actually available
        DeviceFeature feature = (hwProfile == HardwareProfileEnum.CPU) ? DeviceFeature.CPU : DeviceFeature.GPU;

        _context = JavaCL.createBestContext(feature);

        out.println("created " + _context);
        // an array with available devices
        CLDevice[] devices = _context.getDevices();

        for (int i = 0; i < devices.length; i++) {
            out.println("device - " + i + ": " + devices[i]);
        }

        // have a look at the output and select a device
        _device = devices[0];
        out.println("Version " + _device.getOpenCLVersion());
        out.println("Version " + _device.getDriverVersion());
        out.println("using " + _device);
        out.println("max workgroup size: " + _device.getMaxWorkGroupSize());
        out.println("max workitems size: " + _device.getMaxWorkItemSizes()[0]);

        // create command queue on selected device.
        _queue = _context.createDefaultQueue();// device.createCommandQueue();

        // load sources, create and build program
        String src = IOUtils.readText(SPHSolverService.class.getResourceAsStream("/resource/sphFluid.cl"));
        _program = _context.createProgram(src);

        // kernels
        _clearBuffers = _program.createKernel(KernelsEnum.CLEAR_BUFFERS.toString());
        _findNeighbors = _program.createKernel(KernelsEnum.FIND_NEIGHBORS.toString());
        _hashParticles = _program.createKernel(KernelsEnum.HASH_PARTICLES.toString());
        _indexx = _program.createKernel(KernelsEnum.INDEX.toString());
        _sortPostPass = _program.createKernel(KernelsEnum.SORT_POST_PASS.toString());

        // PCI-SPH specific
        _pcisph_computeForcesAndInitPressure = _program
                .createKernel(KernelsEnum.COMPUTE_FORCES_INIT_PRESSURE.toString());
        _pcisph_integrate = _program.createKernel(KernelsEnum.INTEGRATE.toString());
        _pcisph_predictPositions = _program.createKernel(KernelsEnum.PREDICT_POSITION.toString());
        _pcisph_predictDensity = _program.createKernel(KernelsEnum.PREDICT_DENSITY.toString());
        _pcisph_correctPressure = _program.createKernel(KernelsEnum.CORRECT_PRESSURE.toString());
        _pcisph_computePressureForceAcceleration = _program
                .createKernel(KernelsEnum.COMPUTE_PRESSURE_FORCE_ACCELERATION.toString());
        _pcisph_computeDensity = _program.createKernel(KernelsEnum.COMPUTE_DENSITY.toString());
        _pcisph_computeElasticForces = _program.createKernel(KernelsEnum.COMPUTE_ELASTIC_FORCES.toString());
    }

    private void allocateBuffers() {
        // init buffer size map
        _buffersSizeMap.put(BuffersEnum.ACCELERATION, _particleCount * 4 * 2);
        _buffersSizeMap.put(BuffersEnum.GRID_CELL_INDEX, _gridCellCount + 1);
        _buffersSizeMap.put(BuffersEnum.GRID_CELL_INDEX_FIXED, _gridCellCount + 1);
        _buffersSizeMap.put(BuffersEnum.NEIGHBOR_MAP, _particleCount * SPHConstants.NEIGHBOR_COUNT * 2);
        _buffersSizeMap.put(BuffersEnum.PARTICLE_INDEX, _particleCount * 2);
        _buffersSizeMap.put(BuffersEnum.PARTICLE_INDEX_BACK, _particleCount);
        _buffersSizeMap.put(BuffersEnum.POSITION, _particleCount * 4);
        _buffersSizeMap.put(BuffersEnum.PRESSURE, _particleCount * 4);
        _buffersSizeMap.put(BuffersEnum.RHO, _particleCount * 2);
        _buffersSizeMap.put(BuffersEnum.SORTED_POSITION, _particleCount * 4 * 2);
        _buffersSizeMap.put(BuffersEnum.SORTED_VELOCITY, _particleCount * 4);
        _buffersSizeMap.put(BuffersEnum.VELOCITY, _particleCount * 4);
        _buffersSizeMap.put(BuffersEnum.ELASTIC_BUNDLES, _elasticBundlesCount);

        // allocate native device memory for all buffers
        _acceleration = _context.createFloatBuffer(CLMem.Usage.InputOutput,
                _buffersSizeMap.get(BuffersEnum.ACCELERATION));
        _gridCellIndex = _context.createIntBuffer(CLMem.Usage.InputOutput,
                _buffersSizeMap.get(BuffersEnum.GRID_CELL_INDEX));
        _gridCellIndexFixedUp = _context.createIntBuffer(
                _recordCheckPoints ? CLMem.Usage.InputOutput : CLMem.Usage.Input,
                _buffersSizeMap.get(BuffersEnum.GRID_CELL_INDEX_FIXED));
        _neighborMap = _context.createFloatBuffer(_recordCheckPoints ? CLMem.Usage.InputOutput : CLMem.Usage.Input,
                _buffersSizeMap.get(BuffersEnum.NEIGHBOR_MAP));
        _particleIndex = _context.createIntBuffer(CLMem.Usage.InputOutput,
                _buffersSizeMap.get(BuffersEnum.PARTICLE_INDEX));
        _particleIndexBack = _context.createIntBuffer(
                _recordCheckPoints ? CLMem.Usage.InputOutput : CLMem.Usage.Input,
                _buffersSizeMap.get(BuffersEnum.PARTICLE_INDEX_BACK));
        _position = _context.createFloatBuffer(CLMem.Usage.InputOutput, _buffersSizeMap.get(BuffersEnum.POSITION));
        _pressure = _context.createFloatBuffer(_recordCheckPoints ? CLMem.Usage.InputOutput : CLMem.Usage.Input,
                _buffersSizeMap.get(BuffersEnum.PRESSURE));
        _rho = _context.createFloatBuffer(_recordCheckPoints ? CLMem.Usage.InputOutput : CLMem.Usage.Input,
                _buffersSizeMap.get(BuffersEnum.RHO));
        _sortedPosition = _context.createFloatBuffer(
                _recordCheckPoints ? CLMem.Usage.InputOutput : CLMem.Usage.Input,
                _buffersSizeMap.get(BuffersEnum.SORTED_POSITION));
        _sortedVelocity = _context.createFloatBuffer(
                _recordCheckPoints ? CLMem.Usage.InputOutput : CLMem.Usage.Input,
                _buffersSizeMap.get(BuffersEnum.SORTED_VELOCITY));
        _velocity = _context.createFloatBuffer(CLMem.Usage.InputOutput, _buffersSizeMap.get(BuffersEnum.VELOCITY));
    }

    private void setBuffersFromModel() {
        // set dimensions
        _xMax = _model.getXMax();
        _xMin = _model.getXMin();
        _yMax = _model.getYMax();
        _yMin = _model.getYMin();
        _zMax = _model.getZMax();
        _zMin = _model.getZMin();
        _elasticBundlesCount = (_model.getElasticBundles() == null) ? 0 : _model.getElasticBundles().intValue();

        _particleCount = _model.getNumberOfParticles();
        _numOfElasticP = 0;
        _numOfLiquidP = 0;
        _numOfBoundaryP = 0;

        _gridCellsX = (int) ((_model.getXMax() - _model.getXMin()) / SPHConstants.H) + 1;
        _gridCellsY = (int) ((_model.getYMax() - _model.getYMin()) / SPHConstants.H) + 1;
        _gridCellsZ = (int) ((_model.getZMax() - _model.getZMin()) / SPHConstants.H) + 1;

        // set grid dimensions
        _gridCellCount = _gridCellsX * _gridCellsY * _gridCellsZ;

        // allocate buffers - requires global dimensions of the grid
        this.allocateBuffers();

        int index = 0;

        for (int i = 0; i < _particleCount; i++) {
            if (i != 0) {
                index = index + 4;
            }

            Vector3DX positionVector = (Vector3DX) _model.getParticles().get(i).getPositionVector();
            Vector3DX velocityVector = (Vector3DX) _model.getParticles().get(i).getVelocityVector();

            // map for writing
            _positionPtr = _position.map(_queue, CLMem.MapFlags.Write);
            _velocityPtr = _velocity.map(_queue, CLMem.MapFlags.Write);

            // buffer population
            _positionPtr.set(index, positionVector.getX());
            _positionPtr.set(index + 1, positionVector.getY());
            _positionPtr.set(index + 2, positionVector.getZ());
            _positionPtr.set(index + 3, positionVector.getP());
            _velocityPtr.set(index, velocityVector.getX());
            _velocityPtr.set(index + 1, velocityVector.getY());
            _velocityPtr.set(index + 2, velocityVector.getZ());
            _velocityPtr.set(index + 3, velocityVector.getP());

            // unmap after writing
            _position.unmap(_queue, _positionPtr);
            _velocity.unmap(_queue, _velocityPtr);

            // particle counts
            if (positionVector.getP() == SPHConstants.BOUNDARY_TYPE) {
                _numOfBoundaryP++;
            } else if (positionVector.getP() == SPHConstants.ELASTIC_TYPE) {
                _numOfElasticP++;
            } else if (positionVector.getP() == SPHConstants.LIQUID_TYPE) {
                _numOfLiquidP++;
            }
        }

        // populate elastic connection buffers if we have any
        if (_numOfElasticP > 0 && _model.getConnections().size() > 0) {
            // init elastic connections buffers
            // TODO: move this back with the other buffers init stuff
            _buffersSizeMap.put(BuffersEnum.ELASTIC_CONNECTIONS, _numOfElasticP * SPHConstants.NEIGHBOR_COUNT * 4);
            _elasticConnectionsData = _context.createFloatBuffer(CLMem.Usage.InputOutput,
                    _buffersSizeMap.get(BuffersEnum.ELASTIC_CONNECTIONS));
            _elasticConnectionsDataPtr = _elasticConnectionsData.map(_queue, CLMem.MapFlags.Write);

            int connIndex = 0;
            for (Connection conn : _model.getConnections()) {
                _elasticConnectionsDataPtr.set(connIndex, conn.getP1());
                _elasticConnectionsDataPtr.set(connIndex + 1, conn.getDistance());
                _elasticConnectionsDataPtr.set(connIndex + 2, conn.getMysteryValue());
                _elasticConnectionsDataPtr.set(connIndex + 3, 0f); // padding
                connIndex += 4;
            }

            // we copied the stuff down to the device and we won't touch it
            // again so we can unmap
            _elasticConnectionsData.unmap(_queue, _elasticConnectionsDataPtr);

            // allocate activation signal buffers
            if (_buffersSizeMap.get(BuffersEnum.ELASTIC_BUNDLES) > 0) {
                _activationSignal = _context.createFloatBuffer(CLMem.Usage.Input,
                        _buffersSizeMap.get(BuffersEnum.ELASTIC_BUNDLES));
            } else {
                // allocate a buffer with 1 single element
                // NOTE: this is a HACK to avoid exceptions in case of having
                // elastic particles but no contractible bundles
                _activationSignal = _context.createFloatBuffer(CLMem.Usage.Input, 1);
            }
        }

        // check that counts are fine
        if (_particleCount != (_numOfBoundaryP + _numOfElasticP + _numOfLiquidP)) {
            throw new IllegalArgumentException("SPHSolverService:setModels - particle counts do not add up");
        }
    }

    public void cleanContext() {
        _context.release();
    }

    private int runClearBuffers() {
        _clearBuffers.setArg(0, _neighborMap);
        _clearBuffers.setArg(1, _particleCount);
        _clearBuffers.enqueueNDRange(_queue, new int[] { getParticleCountRoundedUp() });
        return 0;
    }

    private int runFindNeighbors() {
        _findNeighbors.setArg(0, _gridCellIndexFixedUp);
        _findNeighbors.setArg(1, _sortedPosition);
        _gridCellCount = ((_gridCellsX) * (_gridCellsY)) * (_gridCellsZ);
        _findNeighbors.setArg(2, _gridCellCount);
        _findNeighbors.setArg(3, _gridCellsX);
        _findNeighbors.setArg(4, _gridCellsY);
        _findNeighbors.setArg(5, _gridCellsZ);
        _findNeighbors.setArg(6, SPHConstants.H);
        _findNeighbors.setArg(7, SPHConstants.HASH_GRID_CELL_SIZE);
        _findNeighbors.setArg(8, SPHConstants.HASH_GRID_CELL_SIZE_INV);
        _findNeighbors.setArg(9, SPHConstants.SIMULATION_SCALE);
        _findNeighbors.setArg(10, _xMin);
        _findNeighbors.setArg(11, _yMin);
        _findNeighbors.setArg(12, _zMin);
        _findNeighbors.setArg(13, _neighborMap);
        _findNeighbors.setArg(14, _particleCount);
        _findNeighbors.enqueueNDRange(_queue, new int[] { getParticleCountRoundedUp() });
        return 0;
    }

    private CLEvent runHashParticles() {
        // Stage HashParticles
        _hashParticles.setArg(0, _position);
        _hashParticles.setArg(1, _gridCellsX);
        _hashParticles.setArg(2, _gridCellsY);
        _hashParticles.setArg(3, _gridCellsZ);
        _hashParticles.setArg(4, SPHConstants.HASH_GRID_CELL_SIZE_INV);
        _hashParticles.setArg(5, _xMin);
        _hashParticles.setArg(6, _yMin);
        _hashParticles.setArg(7, _zMin);
        _hashParticles.setArg(8, _particleIndex);
        _hashParticles.setArg(9, _particleCount);
        CLEvent event = _hashParticles.enqueueNDRange(_queue, new int[] { getParticleCountRoundedUp() });

        return event;
    }

    private int runIndexPostPass() {
        // get values out of buffer
        _gridCellIndexPtr = _gridCellIndex.map(_queue, CLMem.MapFlags.Read);
        int[] gridNextNonEmptyCellBuffer = _gridCellIndexPtr.getInts();
        _gridCellIndex.unmap(_queue, _gridCellIndexPtr);

        int recentNonEmptyCell = _gridCellCount;
        for (int i = _gridCellCount; i >= 0; i--) {
            if (gridNextNonEmptyCellBuffer[i] == SPHConstants.NO_CELL_ID) {
                gridNextNonEmptyCellBuffer[i] = recentNonEmptyCell;
            } else {
                recentNonEmptyCell = gridNextNonEmptyCellBuffer[i];
            }
        }

        // put results back
        _gridCellIndexFixedUpPtr = _gridCellIndexFixedUp.map(_queue, CLMem.MapFlags.Write);
        _gridCellIndexFixedUpPtr.setInts(gridNextNonEmptyCellBuffer);
        _gridCellIndexFixedUp.unmap(_queue, _gridCellIndexFixedUpPtr);

        return 0;
    }

    private CLEvent runIndexx() {
        // Stage Indexx
        _indexx.setArg(0, _particleIndex);
        _gridCellCount = ((_gridCellsX) * (_gridCellsY)) * (_gridCellsZ);
        _indexx.setArg(1, _gridCellCount);
        _indexx.setArg(2, _gridCellIndex);
        _indexx.setArg(3, _particleCount);
        int gridCellCountRoundedUp = (((_gridCellCount - 1) / 256) + 1) * 256;
        CLEvent event = _indexx.enqueueNDRange(_queue, new int[] { gridCellCountRoundedUp });

        return event;
    }

    private int runSortPostPass() {
        // Stage SortPostPass
        _sortPostPass.setArg(0, _particleIndex);
        _sortPostPass.setArg(1, _particleIndexBack);
        _sortPostPass.setArg(2, _position);
        _sortPostPass.setArg(3, _velocity);
        _sortPostPass.setArg(4, _sortedPosition);
        _sortPostPass.setArg(5, _sortedVelocity);
        _sortPostPass.setArg(6, _particleCount);
        _sortPostPass.enqueueNDRange(_queue, new int[] { getParticleCountRoundedUp() });
        return 0;
    }

    private int run_pcisph_computeDensity() {
        // Stage ComputeDensityPressure
        _pcisph_computeDensity.setArg(0, _neighborMap);
        _pcisph_computeDensity.setArg(1, SPHConstants.W_POLY_6_COEFFICIENT);
        _pcisph_computeDensity.setArg(2, SPHConstants.GRAD_W_SPIKY_COEFFICIENT);
        _pcisph_computeDensity.setArg(3, SPHConstants.H);
        _pcisph_computeDensity.setArg(4, SPHConstants.MASS);
        _pcisph_computeDensity.setArg(5, SPHConstants.RHO0);
        _pcisph_computeDensity.setArg(6, SPHConstants.SIMULATION_SCALE);
        _pcisph_computeDensity.setArg(7, SPHConstants.STIFFNESS);
        _pcisph_computeDensity.setArg(8, _sortedPosition);
        _pcisph_computeDensity.setArg(9, _pressure);
        _pcisph_computeDensity.setArg(10, _rho);
        _pcisph_computeDensity.setArg(11, _particleIndexBack);
        _pcisph_computeDensity.setArg(12, SPHConstants.DELTA); // calculated
        // from
        // constants
        _pcisph_computeDensity.setArg(13, _particleCount);
        _pcisph_computeDensity.enqueueNDRange(_queue, new int[] { getParticleCountRoundedUp() });

        return 0;
    }

    private int run_pcisph_computeForcesAndInitPressure() {
        _pcisph_computeForcesAndInitPressure.setArg(0, _neighborMap);
        _pcisph_computeForcesAndInitPressure.setArg(1, _rho);
        _pcisph_computeForcesAndInitPressure.setArg(2, _pressure);
        _pcisph_computeForcesAndInitPressure.setArg(3, _sortedPosition);
        _pcisph_computeForcesAndInitPressure.setArg(4, _sortedVelocity);
        _pcisph_computeForcesAndInitPressure.setArg(5, _acceleration);
        _pcisph_computeForcesAndInitPressure.setArg(6, _particleIndexBack);
        _pcisph_computeForcesAndInitPressure.setArg(7, SPHConstants.W_POLY_6_COEFFICIENT);
        _pcisph_computeForcesAndInitPressure.setArg(8, SPHConstants.DEL_2_W_VISCOSITY_COEFFICIENT);
        _pcisph_computeForcesAndInitPressure.setArg(9, SPHConstants.H);
        _pcisph_computeForcesAndInitPressure.setArg(10, SPHConstants.MASS);
        _pcisph_computeForcesAndInitPressure.setArg(11, SPHConstants.MU);
        _pcisph_computeForcesAndInitPressure.setArg(12, SPHConstants.SIMULATION_SCALE);
        _pcisph_computeForcesAndInitPressure.setArg(13, SPHConstants.GRAVITY_X);
        _pcisph_computeForcesAndInitPressure.setArg(14, SPHConstants.GRAVITY_Y);
        _pcisph_computeForcesAndInitPressure.setArg(15, SPHConstants.GRAVITY_Z);
        _pcisph_computeForcesAndInitPressure.setArg(16, _position);
        _pcisph_computeForcesAndInitPressure.setArg(17, _particleIndex);
        _pcisph_computeForcesAndInitPressure.setArg(18, _particleCount);
        _pcisph_computeForcesAndInitPressure.enqueueNDRange(_queue, new int[] { getParticleCountRoundedUp() });

        return 0;
    }

    private int run_pcisph_computeElasticForces() {
        _pcisph_computeElasticForces.setArg(0, _neighborMap);
        _pcisph_computeElasticForces.setArg(1, _sortedPosition);
        _pcisph_computeElasticForces.setArg(2, _sortedVelocity);
        _pcisph_computeElasticForces.setArg(3, _acceleration);
        _pcisph_computeElasticForces.setArg(4, _particleIndexBack);
        _pcisph_computeElasticForces.setArg(5, _velocity);
        _pcisph_computeElasticForces.setArg(6, SPHConstants.H);
        _pcisph_computeElasticForces.setArg(7, SPHConstants.MASS);
        _pcisph_computeElasticForces.setArg(8, SPHConstants.SIMULATION_SCALE);
        _pcisph_computeElasticForces.setArg(9, _numOfElasticP);
        _pcisph_computeElasticForces.setArg(10, _elasticConnectionsData);
        _pcisph_computeElasticForces.setArg(11, 0);
        _pcisph_computeElasticForces.setArg(12, _activationSignal);
        _pcisph_computeElasticForces.setArg(13, _elasticBundlesCount);
        _pcisph_computeElasticForces.setArg(14, _particleCount);

        int numOfElasticPRoundedUp = (((_numOfElasticP - 1) / 256) + 1) * 256;

        _pcisph_computeElasticForces.enqueueNDRange(_queue, new int[] { numOfElasticPRoundedUp });

        return 0;
    }

    private int run_pcisph_predictPositions() {
        _pcisph_predictPositions.setArg(0, _acceleration);
        _pcisph_predictPositions.setArg(1, _sortedPosition);
        _pcisph_predictPositions.setArg(2, _sortedVelocity);
        _pcisph_predictPositions.setArg(3, _particleIndex);
        _pcisph_predictPositions.setArg(4, _particleIndexBack);
        _pcisph_predictPositions.setArg(5, SPHConstants.GRAVITY_X);
        _pcisph_predictPositions.setArg(6, SPHConstants.GRAVITY_Y);
        _pcisph_predictPositions.setArg(7, SPHConstants.GRAVITY_Z);
        _pcisph_predictPositions.setArg(8, SPHConstants.SIMULATION_SCALE_INV);
        _pcisph_predictPositions.setArg(9, SPHConstants.TIME_STEP);
        _pcisph_predictPositions.setArg(10, _xMin);
        _pcisph_predictPositions.setArg(11, _xMax);
        _pcisph_predictPositions.setArg(12, _yMin);
        _pcisph_predictPositions.setArg(13, _yMax);
        _pcisph_predictPositions.setArg(14, _zMin);
        _pcisph_predictPositions.setArg(15, _zMax);
        _pcisph_predictPositions.setArg(16, SPHConstants.DAMPING);
        _pcisph_predictPositions.setArg(17, _position);
        _pcisph_predictPositions.setArg(18, _velocity);
        _pcisph_predictPositions.setArg(19, SPHConstants.R0);
        _pcisph_predictPositions.setArg(20, _neighborMap);
        _pcisph_predictPositions.setArg(21, _particleCount);
        _pcisph_predictPositions.enqueueNDRange(_queue, new int[] { getParticleCountRoundedUp() });

        return 0;
    }

    private int run_pcisph_predictDensity() {
        // Stage predict density
        _pcisph_predictDensity.setArg(0, _neighborMap);
        _pcisph_predictDensity.setArg(1, _particleIndexBack);
        _pcisph_predictDensity.setArg(2, SPHConstants.W_POLY_6_COEFFICIENT);
        _pcisph_predictDensity.setArg(3, SPHConstants.GRAD_W_SPIKY_COEFFICIENT);
        _pcisph_predictDensity.setArg(4, SPHConstants.H);
        _pcisph_predictDensity.setArg(5, SPHConstants.MASS);
        _pcisph_predictDensity.setArg(6, SPHConstants.RHO0);
        _pcisph_predictDensity.setArg(7, SPHConstants.SIMULATION_SCALE);
        _pcisph_predictDensity.setArg(8, SPHConstants.STIFFNESS);
        _pcisph_predictDensity.setArg(9, _sortedPosition);
        _pcisph_predictDensity.setArg(10, _pressure);
        _pcisph_predictDensity.setArg(11, _rho);
        _pcisph_predictDensity.setArg(12, SPHConstants.DELTA);
        _pcisph_predictDensity.setArg(13, _particleCount);
        _pcisph_predictDensity.enqueueNDRange(_queue, new int[] { getParticleCountRoundedUp() });

        return 0;
    }

    private int run_pcisph_correctPressure() {
        // Stage correct pressure
        _pcisph_correctPressure.setArg(0, _neighborMap);
        _pcisph_correctPressure.setArg(1, _particleIndexBack);
        _pcisph_correctPressure.setArg(2, SPHConstants.W_POLY_6_COEFFICIENT);
        _pcisph_correctPressure.setArg(3, SPHConstants.GRAD_W_SPIKY_COEFFICIENT);
        _pcisph_correctPressure.setArg(4, SPHConstants.H);
        _pcisph_correctPressure.setArg(5, SPHConstants.MASS);
        _pcisph_correctPressure.setArg(6, SPHConstants.RHO0);
        _pcisph_correctPressure.setArg(7, SPHConstants.SIMULATION_SCALE);
        _pcisph_correctPressure.setArg(8, SPHConstants.STIFFNESS);
        _pcisph_correctPressure.setArg(9, _sortedPosition);
        _pcisph_correctPressure.setArg(10, _pressure);
        _pcisph_correctPressure.setArg(11, _rho);
        _pcisph_correctPressure.setArg(12, SPHConstants.DELTA);
        _pcisph_correctPressure.setArg(13, _position);
        _pcisph_correctPressure.setArg(14, _particleIndex);
        _pcisph_correctPressure.setArg(15, _particleCount);
        _pcisph_correctPressure.enqueueNDRange(_queue, new int[] { getParticleCountRoundedUp() });

        return 0;
    }

    private int run_pcisph_computePressureForceAcceleration() {
        // Stage ComputeAcceleration
        _pcisph_computePressureForceAcceleration.setArg(0, _neighborMap);
        _pcisph_computePressureForceAcceleration.setArg(1, _pressure);
        _pcisph_computePressureForceAcceleration.setArg(2, _rho);
        _pcisph_computePressureForceAcceleration.setArg(3, _sortedPosition);
        _pcisph_computePressureForceAcceleration.setArg(4, _sortedVelocity);
        _pcisph_computePressureForceAcceleration.setArg(5, _particleIndexBack);
        _pcisph_computePressureForceAcceleration.setArg(6, SPHConstants.CFLLimit);
        _pcisph_computePressureForceAcceleration.setArg(7, SPHConstants.DEL_2_W_VISCOSITY_COEFFICIENT);
        _pcisph_computePressureForceAcceleration.setArg(8, SPHConstants.GRAD_W_SPIKY_COEFFICIENT);
        _pcisph_computePressureForceAcceleration.setArg(9, SPHConstants.H);
        _pcisph_computePressureForceAcceleration.setArg(10, SPHConstants.MASS);
        _pcisph_computePressureForceAcceleration.setArg(11, SPHConstants.MU);
        _pcisph_computePressureForceAcceleration.setArg(12, SPHConstants.SIMULATION_SCALE);
        _pcisph_computePressureForceAcceleration.setArg(13, _acceleration);
        _pcisph_computePressureForceAcceleration.setArg(14, SPHConstants.RHO0);
        _pcisph_computePressureForceAcceleration.setArg(15, _position);
        _pcisph_computePressureForceAcceleration.setArg(16, _particleIndex);
        _pcisph_computePressureForceAcceleration.setArg(17, _particleCount);
        _pcisph_computePressureForceAcceleration.enqueueNDRange(_queue, new int[] { getParticleCountRoundedUp() });

        return 0;
    }

    private CLEvent run_pcisph_integrate() {
        // Stage Integrate
        _pcisph_integrate.setArg(0, _acceleration);
        _pcisph_integrate.setArg(1, _sortedPosition);
        _pcisph_integrate.setArg(2, _sortedVelocity);
        _pcisph_integrate.setArg(3, _particleIndex);
        _pcisph_integrate.setArg(4, _particleIndexBack);
        _pcisph_integrate.setArg(5, SPHConstants.GRAVITY_X);
        _pcisph_integrate.setArg(6, SPHConstants.GRAVITY_Y);
        _pcisph_integrate.setArg(7, SPHConstants.GRAVITY_Z);
        _pcisph_integrate.setArg(8, SPHConstants.SIMULATION_SCALE_INV);
        _pcisph_integrate.setArg(9, SPHConstants.TIME_STEP);
        _pcisph_integrate.setArg(10, _xMin);
        _pcisph_integrate.setArg(11, _xMax);
        _pcisph_integrate.setArg(12, _yMin);
        _pcisph_integrate.setArg(13, _yMax);
        _pcisph_integrate.setArg(14, _zMin);
        _pcisph_integrate.setArg(15, _zMax);
        _pcisph_integrate.setArg(16, SPHConstants.DAMPING);
        _pcisph_integrate.setArg(17, _position);
        _pcisph_integrate.setArg(18, _velocity);
        _pcisph_integrate.setArg(19, _rho);
        _pcisph_integrate.setArg(20, SPHConstants.R0);
        _pcisph_integrate.setArg(21, _neighborMap);
        _pcisph_integrate.setArg(22, _particleCount);
        CLEvent event = _pcisph_integrate.enqueueNDRange(_queue, new int[] { getParticleCountRoundedUp() });

        return event;
    }

    private int runSort() {
        // this version work with qsort
        int index = 0;
        List<int[]> particleIndex = new ArrayList<int[]>();

        // get values out of buffer
        _particleIndexPtr = _particleIndex.map(_queue, CLMem.MapFlags.ReadWrite);
        int[] particleInd = _particleIndexPtr.getInts();

        for (int i = 0; i < _particleCount * 2; i += 2) {
            int[] element = { particleInd[i], particleInd[i + 1] };
            particleIndex.add(element);
        }
        Collections.sort(particleIndex, new MyCompare());
        for (int i = 0; i < particleIndex.size(); i++) {
            for (int j = 0; j < 2; j++) {
                particleInd[index] = particleIndex.get(i)[j];
                index++;
            }
        }

        // put results back
        _particleIndexPtr.setInts(particleInd);
        _particleIndex.unmap(_queue, _particleIndexPtr);

        return 0;
    }

    class MyCompare implements Comparator<int[]> {
        public int compare(int[] o1, int[] o2) {
            if (o1[0] < o2[0])
                return -1;
            if (o1[0] > o2[0])
                return +1;
            return 0;
        }
    }

    private void step() {
        long endStep = 0;
        long startStep = System.currentTimeMillis();
        long end = 0;
        long start = System.currentTimeMillis();

        logger.info("SPH clear buffer");
        runClearBuffers();
        if (_recordCheckPoints) {
            recordCheckpoints(KernelsEnum.CLEAR_BUFFERS);
        }
        end = System.currentTimeMillis();
        logger.info("SPH clear buffer end, took " + (end - start) + "ms");
        start = end;

        logger.info("SPH hash particles");
        CLEvent hashParticles = runHashParticles();
        if (_recordCheckPoints) {
            recordCheckpoints(KernelsEnum.HASH_PARTICLES);
        }
        end = System.currentTimeMillis();
        logger.info("SPH hash particles end, took " + (end - start) + "ms");
        start = end;

        // host needs to wait as the next operation requires values from buffers
        hashParticles.waitFor();

        logger.info("SPH sort");
        runSort();
        if (_recordCheckPoints) {
            recordCheckpoints(KernelsEnum.SORT);
        }
        end = System.currentTimeMillis();
        logger.info("SPH sort end, took " + (end - start) + "ms");
        start = end;

        logger.info("SPH sort post pass");
        runSortPostPass();
        if (_recordCheckPoints) {
            recordCheckpoints(KernelsEnum.SORT_POST_PASS);
        }
        end = System.currentTimeMillis();
        logger.info("SPH sort post pass end, took " + (end - start) + "ms");
        start = end;

        logger.info("SPH index");
        CLEvent runIndexx = runIndexx();
        if (_recordCheckPoints) {
            recordCheckpoints(KernelsEnum.INDEX);
        }
        end = System.currentTimeMillis();
        logger.info("SPH index end, took " + (end - start) + "ms");
        start = end;

        // host needs to wait as the next operation requires values from buffers
        runIndexx.waitFor();

        logger.info("SPH index post pass");
        runIndexPostPass();
        if (_recordCheckPoints) {
            recordCheckpoints(KernelsEnum.INDEX_POST_PASS);
        }
        end = System.currentTimeMillis();
        logger.info("SPH index post pass end, took " + (end - start) + "ms");
        start = end;

        logger.info("SPH find neighbors");
        runFindNeighbors();
        if (_recordCheckPoints) {
            recordCheckpoints(KernelsEnum.FIND_NEIGHBORS);
        }
        end = System.currentTimeMillis();
        logger.info("SPH find neighbors end, took " + (end - start) + "ms");
        start = end;

        // PCISPH stuff starts here
        logger.info("PCI-SPH compute density");
        run_pcisph_computeDensity();
        if (_recordCheckPoints) {
            recordCheckpoints(KernelsEnum.COMPUTE_DENSITY);
        }
        end = System.currentTimeMillis();
        logger.info("PCI-SPH compute density end, took " + (end - start) + "ms");
        start = end;

        logger.info("PCI-SPH compute forces and init pressure");
        run_pcisph_computeForcesAndInitPressure();
        if (_recordCheckPoints) {
            recordCheckpoints(KernelsEnum.COMPUTE_FORCES_INIT_PRESSURE);
        }
        end = System.currentTimeMillis();
        logger.info("PCI-SPH compute forces and init pressure end, took " + (end - start) + "ms");
        start = end;

        // Do elastic stuff only if we have elastic particles
        if (_numOfElasticP > 0) {
            logger.info("PCI-SPH compute elastic forces");
            run_pcisph_computeElasticForces();
            if (_recordCheckPoints) {
                recordCheckpoints(KernelsEnum.COMPUTE_ELASTIC_FORCES);
            }
            end = System.currentTimeMillis();
            logger.info("PCI-SPH compute elastic forces end, took " + (end - start) + "ms");
            start = end;
        }

        logger.info("PCI-SPH predict/correct loop");
        // LOOP: 3 times or until "error" becomes less than 2%
        int iter = 0;
        int maxIterations = 3;
        do {
            run_pcisph_predictPositions();
            run_pcisph_predictDensity();
            run_pcisph_correctPressure();
            run_pcisph_computePressureForceAcceleration();

            iter++;
        } while ((iter < maxIterations));
        if (_recordCheckPoints) {
            recordCheckpoints(KernelsEnum.PREDICTIVE_LOOP);
        }
        end = System.currentTimeMillis();
        logger.info("PCI-SPH predict/correct loop end, took " + (end - start) + "ms");
        start = end;

        logger.info("PCI-SPH integrate");
        CLEvent event = run_pcisph_integrate();
        if (_recordCheckPoints) {
            recordCheckpoints(KernelsEnum.INTEGRATE);
        }
        end = System.currentTimeMillis();
        logger.info("PCI-SPH integrate end, took " + (end - start) + "ms");
        start = end;

        // wait for the end of the run_pcisph_integrate on device
        event.waitFor();

        logger.info("SPH finish queue");
        // TODO: figure out if we need to actually call this
        _queue.finish();
        end = System.currentTimeMillis();
        logger.info("SPH finish queue end, took " + (end - start) + "ms");
        start = end;

        endStep = System.currentTimeMillis();
        logger.info("SPH computation step done, took " + (endStep - startStep) + "ms");
    }

    public void finishQueue() {
        _queue.finish();
    }

    private int getParticleCountRoundedUp() {
        return (((_particleCount - 1) / 256) + 1) * 256;
    }

    @Override
    public void solve(IRunConfiguration timeConfiguration, AspectNode aspect) throws GeppettoExecutionException {
        // TODO: extend this to use time configuration to do multiple steps in
        // one go
        long time = System.currentTimeMillis();
        logger.info("SPH solver start");

        for (int i = 0; i < timeConfiguration.getTimeSteps(); i++) {
            // TODO: setActivationSignal

            long end = 0;
            long start = System.currentTimeMillis();
            logger.info("SPH STEP START");
            step();
            updateStateTree(aspect);

            end = System.currentTimeMillis();
            logger.info("SPH STEP END, took " + (end - start) + "ms");
        }

        logger.info("SPH solver end, took: " + (System.currentTimeMillis() - time) + "ms");
    }

    private void updateStateTree(AspectNode aspect) {
        AspectSubTreeNode visualTree = (AspectSubTreeNode) aspect.getSubTree(AspectTreeType.VISUALIZATION_TREE);
        AspectSubTreeNode simulationTree = (AspectSubTreeNode) aspect.getSubTree(AspectTreeType.WATCH_TREE);

        _positionPtr = _position.map(_queue, CLMem.MapFlags.Read);

        // ASSUMPTION: The solver will never create new states after the first
        // time step
        // we can call it principle of conservation of the states; if there is a
        // good
        // reason to revoke this assumption we need to add code that at every
        // cycle checks
        // if some new states exist to eventually add them to the stateTree
        UpdateSPHVisualizationTreeVisitor updateSPHStateTreeVisitor = new UpdateSPHVisualizationTreeVisitor(
                _positionPtr);
        visualTree.apply(updateSPHStateTreeVisitor);

        if (watching) {
            updateSimulationTree(simulationTree);
        }

        _position.unmap(_queue, _positionPtr);

        visualTree.setModified(true);
        simulationTree.setModified(true);
        AspectNode aspectNode = (AspectNode) simulationTree.getParent();
        aspectNode.setModified(true);
        if (aspectNode.getParent() != null) {
            ((EntityNode) aspectNode.getParentEntity()).updateParentEntitiesFlags(true);
        }

    }

    /**
     * Updates nodes for simulation tree, if tree is empty call method to
     * populate it.
     * 
     * @param simulationTree
     */
    private void updateSimulationTree(AspectSubTreeNode simulationTree) {
        // map watchable buffers that are not already mapped
        // NOTE: position is mapped for scene generation - improving performance
        // by not mapping it again
        _velocityPtr = _velocity.map(_queue, CLMem.MapFlags.Read);

        if (simulationTree.getChildren().isEmpty()) {
            simulationTree.setId(AspectTreeType.WATCH_TREE.toString());
            populateSimulationTree(simulationTree);
        } else {
            // watch tree not empty populate new values
            UpdateSPHSimulationTreeVisitor visitor = new UpdateSPHSimulationTreeVisitor(_positionPtr);
            simulationTree.apply(visitor);
        }

        // unmap watchable buffers
        _velocity.unmap(_queue, _positionPtr);
    }

    private boolean containsNode(ACompositeNode node, String name) {
        List<ANode> children = node.getChildren();

        boolean addNewNode = true;
        for (ANode child : children) {
            if (child.getId().equals(name)) {
                addNewNode = false;
                return addNewNode;
            }
            if (child instanceof ACompositeNode) {
                if (((ACompositeNode) child).getChildren() != null) {
                    addNewNode = containsNode((ACompositeNode) child, name);
                }
            }

        }

        return addNewNode;
    }

    private ACompositeNode getNode(ACompositeNode node, String name) {
        ACompositeNode newNode = null;

        List<ANode> children = node.getChildren();

        boolean addNewNode = true;
        for (ANode child : children) {
            if (child.getId().equals(name)) {
                newNode = (ACompositeNode) child;
                return newNode;
            }
            if (child instanceof ACompositeNode) {
                if (((ACompositeNode) child).getChildren() != null) {
                    newNode = getNode((ACompositeNode) child, name);
                }
            }

        }

        return newNode;
    }

    @Override
    public void initialize(IModel model) {
        _model = (SPHModelX) model;

        setBuffersFromModel();

        setWatchableVariables();
        setForceableVariables();

    }

    @Override
    public void dispose() {
        // close the context and "buonanotte al secchio" (good night to the
        // bucket)
        cleanContext();
    }

    private void setActivationSignal(float[] activation) {
        // put results back
        _activationSignalPtr = _activationSignal.map(_queue, CLMem.MapFlags.Write);
        _activationSignalPtr.setFloats(activation);
        _activationSignal.unmap(_queue, _activationSignalPtr);
    }

    private void recordCheckpoints(KernelsEnum kernelCheckpoint) {
        PCISPHCheckPoint check = new PCISPHCheckPoint();

        // read buffers into lists and populate checkpoint object
        check.acceleration = this.<Float>getBufferValues(_accelerationPtr, _acceleration,
                this._buffersSizeMap.get(BuffersEnum.ACCELERATION));
        check.gridCellIndex = this.<Integer>getBufferValues(_gridCellIndexPtr, _gridCellIndex,
                this._buffersSizeMap.get(BuffersEnum.GRID_CELL_INDEX));
        check.gridCellIndexFixedUp = this.<Integer>getBufferValues(_gridCellIndexFixedUpPtr, _gridCellIndexFixedUp,
                this._buffersSizeMap.get(BuffersEnum.GRID_CELL_INDEX_FIXED));
        check.neighborMap = this.<Float>getBufferValues(_neighborMapPtr, _neighborMap,
                this._buffersSizeMap.get(BuffersEnum.NEIGHBOR_MAP));
        check.particleIndex = this.<Integer>getBufferValues(_particleIndexPtr, _particleIndex,
                this._buffersSizeMap.get(BuffersEnum.PARTICLE_INDEX));
        check.particleIndexBack = this.<Integer>getBufferValues(_particleIndexBackPtr, _particleIndexBack,
                this._buffersSizeMap.get(BuffersEnum.PARTICLE_INDEX_BACK));
        check.position = this.<Float>getBufferValues(_positionPtr, _position,
                this._buffersSizeMap.get(BuffersEnum.POSITION));
        check.pressure = this.<Float>getBufferValues(_pressurePtr, _pressure,
                this._buffersSizeMap.get(BuffersEnum.PRESSURE));
        check.rho = this.<Float>getBufferValues(_rhoPtr, _rho, this._buffersSizeMap.get(BuffersEnum.RHO));
        check.sortedPosition = this.<Float>getBufferValues(_sortedPositionPtr, _sortedPosition,
                this._buffersSizeMap.get(BuffersEnum.SORTED_POSITION));
        check.sortedVelocity = this.<Float>getBufferValues(_sortedVelocityPtr, _sortedVelocity,
                this._buffersSizeMap.get(BuffersEnum.SORTED_VELOCITY));
        check.velocity = this.<Float>getBufferValues(_velocityPtr, _velocity,
                this._buffersSizeMap.get(BuffersEnum.VELOCITY));
        if (_numOfElasticP > 0) {
            check.elasticConnections = this.<Float>getBufferValues(_elasticConnectionsDataPtr,
                    _elasticConnectionsData, this._buffersSizeMap.get(BuffersEnum.ELASTIC_CONNECTIONS));
        }

        _checkpointsMap.put(kernelCheckpoint, check);
    }

    /*
     * A method to retrieve buffer values into simple lists
     */
    private <T> List<T> getBufferValues(Pointer<T> pointer, CLBuffer<T> buffer, int size) {
        List<T> list = new ArrayList<T>();

        pointer = buffer.map(_queue, CLMem.MapFlags.Read);

        for (int i = 0; i < size; i++) {
            list.add(pointer.get(i));
        }

        buffer.unmap(_queue, pointer);

        return list;
    }

    @Override
    public VariableList getForceableVariables() {
        return forceableVariables;
    }

    @Override
    public VariableList getWatchableVariables() {
        return watchableVariables;
    }

    /**
     * Populates state variables that can be watched
     * 
     * */
    private void setWatchableVariables() {

        SimpleType floatType = new SimpleType();
        floatType.setType(Type.FLOAT);

        // structure type vector
        StructuredType vector = new StructuredType();
        List<AVariable> vectorVars = new ArrayList<AVariable>();
        SimpleVariable x = new SimpleVariable();
        SimpleVariable y = new SimpleVariable();
        SimpleVariable z = new SimpleVariable();
        x.setName("x");
        x.setType(floatType);
        y.setName("y");
        y.setType(floatType);
        z.setName("z");
        z.setType(floatType);
        vectorVars.addAll(Arrays.asList(x, y, z));
        vector.setVariables(vectorVars);

        // structure type particle
        StructuredType particle = new StructuredType();
        List<AVariable> particleVars = new ArrayList<AVariable>();
        SimpleVariable position = new SimpleVariable();
        SimpleVariable velocity = new SimpleVariable();
        position.setName("position");
        position.setType(vector);
        velocity.setName("velocity");
        velocity.setType(vector);
        particleVars.addAll(Arrays.asList(position, velocity));
        particle.setVariables(particleVars);

        List<AVariable> vars = new ArrayList<AVariable>();

        // array of particles
        ArrayVariable particles = new ArrayVariable();
        particles.setName("particle");
        particles.setType(particle);
        particles.setSize(_particleCount);

        vars.add(particles);

        this.watchableVariables.setVariables(vars);
    }

    /**
     * Populates state variables that can be watched
     * 
     * */
    private void setForceableVariables() {
        List<AVariable> vars = new ArrayList<AVariable>();

        // a float type
        SimpleType floatType = new SimpleType();
        floatType.setType(Type.FLOAT);

        // activation signals - array of floats
        ArrayVariable activationSignals = new ArrayVariable();
        activationSignals.setName("activation");
        activationSignals.setType(floatType);
        activationSignals.setSize(_buffersSizeMap.get(BuffersEnum.ELASTIC_BUNDLES));

        vars.add(activationSignals);

        this.forceableVariables.setVariables(vars);
    }

    @Override
    public void addWatchVariables(List<String> variableNames) {
        watchListVarNames.addAll(variableNames);
    }

    @Override
    public void startWatch() {
        watching = true;
    }

    @Override
    public void stopWatch() {
        watching = false;
    }

    @Override
    public void clearWatchVariables() {
        watchListVarNames.clear();
    }

    @Override
    public void populateVisualTree(IModel model, AspectSubTreeNode visualTree)
            throws GeppettoInitializationException {
        _positionPtr = _position.map(_queue, CLMem.MapFlags.Read);

        CompositeNode _liquidModel = new CompositeNode("LIQUID_" + model.getId());
        CompositeNode _boundaryModel = new CompositeNode("BOUNDARY_" + model.getId());
        CompositeNode _elasticModel = new CompositeNode("ELASTIC_" + model.getId());

        // the state tree is empty, let's create it
        for (int i = 0, index = 0; i < _particleCount; i++, index = index + 4) {
            String particleId = SPHModelInterpreterService.getParticleId(i);
            FloatValue xV = ValuesFactory.getFloatValue(_positionPtr.get(index));
            FloatValue yV = ValuesFactory.getFloatValue(_positionPtr.get(index + 1));
            FloatValue zV = ValuesFactory.getFloatValue(_positionPtr.get(index + 2));
            FloatValue pV = ValuesFactory.getFloatValue(_positionPtr.get(index + 3));

            if (pV.getAsFloat() != SPHConstants.BOUNDARY_TYPE) {
                // don't need to create a state for the boundary particles,
                // they don't move.
                ParticleNode particle = new ParticleNode(particleId);
                Point pos = new Point();
                pos.setX(xV.getAsDouble());
                pos.setY(yV.getAsDouble());
                pos.setZ(zV.getAsDouble());
                particle.setPosition(pos);
                particle.setParticleKind(pV.getAsFloat());
                particle.setId(particleId);

                if (pV.getAsFloat() == (SPHConstants.LIQUID_TYPE)) {
                    _liquidModel.addChild(particle);
                } else if (pV.getAsFloat() == (SPHConstants.ELASTIC_TYPE)) {
                    _elasticModel.addChild(particle);
                } else if (pV.getAsFloat() == (SPHConstants.BOUNDARY_TYPE)) {
                    _boundaryModel.addChild(particle);
                }
            }
        }

        visualTree.addChild(_liquidModel);
        visualTree.addChild(_elasticModel);
        visualTree.addChild(_boundaryModel);

        _position.unmap(_queue, _positionPtr);
    }

    @Override
    public void populateSimulationTree(AspectSubTreeNode watchTree) {
        // map watchable buffers that are not already mapped
        // NOTE: position is mapped for scene generation - improving performance
        // by not mapping it again
        _velocityPtr = _velocity.map(_queue, CLMem.MapFlags.Read);

        // check which watchable variables are being watched
        for (AVariable var : getWatchableVariables().getVariables()) {
            for (String varName : watchListVarNames) {
                // get watchable variables path
                List<String> watchableVarsPaths = new ArrayList<String>();
                VariablePathSerializer.GetFullVariablePath(var, "", watchableVarsPaths);

                varName = varName.replace(watchTree.getInstancePath() + ".", "");
                // remove array bracket arguments from variable paths
                String varNameNoBrackets = varName;
                String particleID = null;
                if (varName.indexOf("[") != -1) {
                    varNameNoBrackets = varName.substring(0, varName.indexOf("["))
                            + varName.substring(varName.indexOf("]") + 1, varName.length());
                    particleID = varName.substring(varName.indexOf("[") + 1, varName.indexOf("]"));
                }

                // loop through paths and look for matching paths
                for (String s : watchableVarsPaths) {
                    if (s.equals(varNameNoBrackets)) {
                        // we have a match

                        Integer ID = null;
                        if (particleID != null) {
                            // check that paticleID is valid
                            ID = Integer.parseInt(particleID);
                            if (!(ID < _particleCount)) {
                                throw new IllegalArgumentException(
                                        "SPHSolverService:updateStateTreeForWatch - particle index is out of boundaries");
                            }
                        }

                        // tokenize variable path in watch list via dot
                        // separator (handle array brackets)
                        StringTokenizer tokenizer = new StringTokenizer(s, ".");
                        ACompositeNode node = watchTree;
                        while (tokenizer.hasMoreElements()) {
                            // loop through tokens and build tree
                            String current = tokenizer.nextToken();
                            boolean found = false;
                            for (ANode child : node.getChildren()) {
                                if (child.getId().equals(current)) {
                                    if (child instanceof ACompositeNode) {
                                        node = (ACompositeNode) child;
                                    }
                                    found = true;
                                    break;
                                }
                            }
                            if (found) {
                                continue;
                            } else {
                                if (tokenizer.hasMoreElements()) {
                                    // not a leaf, create a composite statenode
                                    String nodeName = current;
                                    if (current.equals("particle")) {
                                        nodeName = current + "[" + particleID + "]";
                                    }

                                    CompositeNode newNode = new CompositeNode(nodeName);
                                    newNode.setId(nodeName);

                                    boolean addNewNode = containsNode(node, newNode.getId());

                                    if (addNewNode) {
                                        node.addChild(newNode);
                                        node = newNode;
                                    } else {
                                        node = getNode(node, newNode.getId());
                                    }
                                } else {
                                    // it's a leaf node
                                    VariableNode newNode = new VariableNode(current);
                                    newNode.setId(current);

                                    FloatValue val = null;

                                    // get value
                                    switch (current) {
                                    case "x":
                                        val = ValuesFactory.getFloatValue(_positionPtr.get(ID));
                                        break;
                                    case "y":
                                        val = ValuesFactory.getFloatValue(_positionPtr.get(ID + 1));
                                        break;
                                    case "z":
                                        val = ValuesFactory.getFloatValue(_positionPtr.get(ID + 2));
                                        break;
                                    }

                                    PhysicalQuantity q = new PhysicalQuantity();
                                    q.setValue(val);
                                    newNode.addPhysicalQuantity(q);

                                    node.addChild(newNode);
                                }
                            }
                        }
                    }
                }
            }
        }

        watchTree.setModified(true);
        // unmap watchable buffers
        _velocity.unmap(_queue, _positionPtr);
    }
};