All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.barrybecker4.simulation.liquid.compute.ParticleAdvector Maven / Gradle / Ivy

There is a newer version: 1.6.2
Show newest version
/** Copyright by Barry G. Becker, 2000-2011. Licensed under MIT License: http://www.opensource.org/licenses/MIT  */
package com.barrybecker4.simulation.liquid.compute;

import com.barrybecker4.simulation.liquid.Logger;
import com.barrybecker4.simulation.liquid.model.Cell;
import com.barrybecker4.simulation.liquid.model.CellDimensions;
import com.barrybecker4.simulation.liquid.model.CellStatus;
import com.barrybecker4.simulation.liquid.model.Grid;
import com.barrybecker4.simulation.liquid.model.Particle;
import com.barrybecker4.simulation.liquid.model.Particles;

import javax.vecmath.Vector2d;

/**
 *  Updates positions of the particle markers in the grid.
 *
 *  @author Barry Becker
 */
public class ParticleAdvector {

    /** the grid of cells that make up the environment */
    private Grid grid ;

    private static final double EPSILON = 0.0000001;

    VelocityInterpolator interpolator;

    /**
     * Constructor to use if you want the environment based on a config file.
     */
    public ParticleAdvector(Grid grid) {

        this.grid = grid;
        interpolator = new VelocityInterpolator(grid);
    }

    /**
     * move particles according to vector field.
     * updates the timeStep if the cfl condition is not met.
     * RISK: 3
     * @return the current timeStep (it was possible adjusted)
     */
    public double advectParticles(double timeStep, Particles particles) {

        // keep track of the biggest velocity magnitude so we can adjust the timestep if needed.
        double maxLength = Double.MIN_VALUE;

        for (Particle particle : particles)  {

            double length = advectParticle(timeStep, particle);

            if (length > maxLength)
                maxLength = length;
        }

        adjustTimeStep(timeStep, maxLength);
        return timeStep;
    }

    /**
     * The velocity of a particle is determined using area weighting interpolation.
     * @return advect a particle if it is completely under water.
     */
    private double advectParticle(double timeStep, Particle particle) {

        int i = (int) Math.floor(particle.x);
        int j = (int) Math.floor(particle.y);
        CellStatus status = grid.getCell(i, j).getStatus();
        double magnitude = 0;

        if ( status == CellStatus.FULL || status == CellStatus.SURFACE || status == CellStatus.ISOLATED ) {

            magnitude = advectWaterParticle(timeStep, particle, i, j);
        }
        return magnitude;
    }

    /**
     * Advect a single water particle.
     * @return  the magnitude of its velocity vector.
     */
    private double advectWaterParticle(double timeStep, Particle particle, int i, int j) {

        Vector2d vel = interpolator.findVelocity(particle);

        // scale the velocity by the cell size so we can assume the cells have unit dims
        vel.scale(CellDimensions.INVERSE_CELL_SIZE);

        double xChange = timeStep * vel.x;
        double yChange = timeStep * vel.y;
        particle.set( particle.x + xChange, particle.y + yChange );
        particle.incAge( timeStep );

        Cell newHomeCell = findNewHomeCell(particle, i, j);

        assert ( particle.x >= 1 && particle.y >= 1
                && particle.x < grid.getXDimension() - 1
                && particle.y < grid.getYDimension() - 1) :
                "particle.x=" + particle.x + "particle.y=" + particle.y ;

        // adjust # particles as they cross cell boundaries
        newHomeCell.incParticles(); // increment new cell
        grid.getCell(i, j).decParticles();  // decrement last cell
        particle.setCell( newHomeCell );

        assert ( grid.getCell(i, j).getNumParticles() >= 0):
                "The number of particles in grid[" + i + "][" + j + "] is " + grid.getCell(i, j).getNumParticles();
        assert ( newHomeCell.getNumParticles() >= 0 );
        return vel.length();
    }

    /**
     * Determine the cell that the particle is in now after moving it. May be same as it was.
     * Ensure the liquid does not enter an OBSTACLE.
     * @return new Cell that the particle is in. Not necessarily different than original.
     */
    private Cell findNewHomeCell(Particle particle, int i, int j) {

        int ii = (int) Math.floor(particle.x);
        int jj = (int) Math.floor(particle.y);

        if (ii < 0) {
            particle.x = 0.0;
            ii = 0;
        }
        if (jj < 0) {
            particle.y = 0.0;
            jj = 0;
        }
        if (ii >= grid.getXDimension()) {
            ii = grid.getXDimension() - 1;
            particle.x = ii;

        }
        if (jj >= grid.getYDimension()) {
            jj = grid.getYDimension() - 1;
            particle.y = jj;
        }


        // move outside the obstacle if we find ourselves in one
        if ( grid.getCell(ii, jj).getStatus() == CellStatus.OBSTACLE ) {
            if ( i < ii ) {
                particle.set( ii - EPSILON, particle.y );
            }
            else if (i > ii ) {
                particle.set( ii + 1.0 + EPSILON, particle.y );
            }
            if ( j < jj ) {
                particle.set( particle.x, jj - EPSILON );
            }
            else if ( j > jj ) {
                particle.set( particle.x, jj + 1.0 + EPSILON );
            }
        }

        ii = (int) particle.x;
        jj = (int) particle.y;
        return grid.getCell(ii, jj);
    }

    /**
     * Adaptively change the time step if needed.
     * @return new timeStep to use. May be unchanged.
     */
    private double adjustTimeStep(double timeStep, double maxLength) {

        // max distance to go in one step. Beyond this, we apply the governor.
        double maxDistance = CellDimensions.CELL_SIZE / 10.0;
        double minDistance = CellDimensions.CELL_SIZE / 10000.0;

        // adjust the timestep if needed.
        double increment = (timeStep * maxLength);
        double newTimeStep = timeStep;
        if (increment > maxDistance) {
            newTimeStep /= 2.0;
            Logger.log(1, "advectParticles: HALVED dt=" + timeStep +" increment="+increment );
        }
        else if (increment < minDistance) {
            newTimeStep *= 2.0;
            Logger.log(1, "advectParticles: DOUBLED dt=" + timeStep
                    +" increment="+increment +" maxLength=" + maxLength);
        }
        return newTimeStep;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy