#

API Docs for: 0.6.1
Show: Inherited Protected Private Deprecated

# File: src/objects/SPHSystem.js

    module.exports = SPHSystem;

    var Shape = require('../shapes/Shape');
    var Vec3 = require('../math/Vec3');
    var Quaternion = require('../math/Quaternion');
    var Particle = require('../shapes/Particle');
    var Body = require('../objects/Body');
    var Material = require('../material/Material');

    /**
     * Smoothed-particle hydrodynamics system
     * @class SPHSystem
     * @constructor
     */
    function SPHSystem(){
        this.particles = [];

        /**
         * Density of the system (kg/m3).
         * @property {number} density
         */
        this.density = 1;

        /**
         * Distance below which two particles are considered to be neighbors.
         * It should be adjusted so there are about 15-20 neighbor particles within this radius.
         * @property {number} smoothingRadius
         */
        this.smoothingRadius = 1;
        this.speedOfSound = 1;

        /**
         * Viscosity of the system.
         * @property {number} viscosity
         */
        this.viscosity = 0.01;
        this.eps = 0.000001;

        // Stuff Computed per particle
        this.pressures = [];
        this.densities = [];
        this.neighbors = [];
    }

    /**
     * Add a particle to the system.
     * @method add
     * @param {Body} particle
     */
    SPHSystem.prototype.add = function(particle){
        this.particles.push(particle);
        if(this.neighbors.length < this.particles.length){
            this.neighbors.push([]);
        }
    };

    /**
     * Remove a particle from the system.
     * @method remove
     * @param {Body} particle
     */
    SPHSystem.prototype.remove = function(particle){
        var idx = this.particles.indexOf(particle);
        if(idx !== -1){
            this.particles.splice(idx,1);
            if(this.neighbors.length > this.particles.length){
                this.neighbors.pop();
            }
        }
    };

    /**
     * Get neighbors within smoothing volume, save in the array neighbors
     * @method getNeighbors
     * @param {Body} particle
     * @param {Array} neighbors
     */
    var SPHSystem_getNeighbors_dist = new Vec3();
    SPHSystem.prototype.getNeighbors = function(particle,neighbors){
        var N = this.particles.length,
            id = particle.id,
            R2 = this.smoothingRadius * this.smoothingRadius,
            dist = SPHSystem_getNeighbors_dist;
        for(var i=0; i!==N; i++){
            var p = this.particles[i];
            p.position.vsub(particle.position,dist);
            if(id!==p.id && dist.norm2() < R2){
                neighbors.push(p);
            }
        }
    };

    // Temp vectors for calculation
    var SPHSystem_update_dist = new Vec3(),
        SPHSystem_update_a_pressure = new Vec3(),
        SPHSystem_update_a_visc = new Vec3(),
        SPHSystem_update_gradW = new Vec3(),
        SPHSystem_update_r_vec = new Vec3(),
        SPHSystem_update_u = new Vec3(); // Relative velocity
    SPHSystem.prototype.update = function(){
        var N = this.particles.length,
            dist = SPHSystem_update_dist,
            cs = this.speedOfSound,
            eps = this.eps;

        for(var i=0; i!==N; i++){
            var p = this.particles[i]; // Current particle
            var neighbors = this.neighbors[i];

            // Get neighbors
            neighbors.length = 0;
            this.getNeighbors(p,neighbors);
            neighbors.push(this.particles[i]); // Add current too
            var numNeighbors = neighbors.length;

            // Accumulate density for the particle
            var sum = 0.0;
            for(var j=0; j!==numNeighbors; j++){

                //printf("Current particle has position %f %f %f\n",objects[id].pos.x(),objects[id].pos.y(),objects[id].pos.z());
                p.position.vsub(neighbors[j].position, dist);
                var len = dist.norm();

                var weight = this.w(len);
                sum += neighbors[j].mass * weight;
            }

            // Save
            this.densities[i] = sum;
            this.pressures[i] = cs * cs * (this.densities[i] - this.density);
        }

        // Add forces

        // Sum to these accelerations
        var a_pressure= SPHSystem_update_a_pressure;
        var a_visc =    SPHSystem_update_a_visc;
        var gradW =     SPHSystem_update_gradW;
        var r_vec =     SPHSystem_update_r_vec;
        var u =         SPHSystem_update_u;

        for(var i=0; i!==N; i++){

            var particle = this.particles[i];

            a_pressure.set(0,0,0);
            a_visc.set(0,0,0);

            // Init vars
            var Pij;
            var nabla;
            var Vij;

            // Sum up for all other neighbors
            var neighbors = this.neighbors[i];
            var numNeighbors = neighbors.length;

            //printf("Neighbors: ");
            for(var j=0; j!==numNeighbors; j++){

                var neighbor = neighbors[j];
                //printf("%d ",nj);

                // Get r once for all..
                particle.position.vsub(neighbor.position,r_vec);
                var r = r_vec.norm();

                // Pressure contribution
                Pij = -neighbor.mass * (this.pressures[i] / (this.densities[i]*this.densities[i] + eps) + this.pressures[j] / (this.densities[j]*this.densities[j] + eps));
                this.gradw(r_vec, gradW);
                // Add to pressure acceleration
                gradW.mult(Pij , gradW);
                a_pressure.vadd(gradW, a_pressure);

                // Viscosity contribution
                neighbor.velocity.vsub(particle.velocity, u);
                u.mult( 1.0 / (0.0001+this.densities[i] * this.densities[j]) * this.viscosity * neighbor.mass , u );
                nabla = this.nablaw(r);
                u.mult(nabla,u);
                // Add to viscosity acceleration
                a_visc.vadd( u, a_visc );
            }

            // Calculate force
            a_visc.mult(particle.mass, a_visc);
            a_pressure.mult(particle.mass, a_pressure);

            // Add force to particles
            particle.force.vadd(a_visc, particle.force);
            particle.force.vadd(a_pressure, particle.force);
        }
    };

    // Calculate the weight using the W(r) weightfunction
    SPHSystem.prototype.w = function(r){
        // 315
        var h = this.smoothingRadius;
        return 315.0/(64.0*Math.PI*Math.pow(h,9)) * Math.pow(h*h-r*r,3);
    };

    // calculate gradient of the weight function
    SPHSystem.prototype.gradw = function(rVec,resultVec){
        var r = rVec.norm(),
            h = this.smoothingRadius;
        rVec.mult(945.0/(32.0*Math.PI*Math.pow(h,9)) * Math.pow((h*h-r*r),2) , resultVec);
    };

    // Calculate nabla(W)
    SPHSystem.prototype.nablaw = function(r){
        var h = this.smoothingRadius;
        var nabla = 945.0/(32.0*Math.PI*Math.pow(h,9)) * (h*h-r*r)*(7*r*r - 3*h*h);
        return nabla;
    };