Untitled

mail@pastecode.io avatar
unknown
c_cpp
19 days ago
19 kB
2
Indexable
Never
#include <omp.h>

#include <algorithm>
#include <array>
#include <cassert>
#include <cmath>
#include <fstream>
#include <iostream>
#include <queue>
#include <vector>

#include "collision.h"
#include "io.h"
#include "sim_validator.h"

/*
 * ObjectId consists of
 * bit 30 - 31: Home bit
 * bit 26 - 29: Control bits
 * bit 0 - 25: Particle ID
 */
class ObjectId {
   private:
    unsigned int objectId;

    static constexpr unsigned int homeBitMask = 0b11 << 30;
    static constexpr unsigned int controlBitMask = 0b1111 << 26;
    static constexpr unsigned int particleIdBitMask = (1 << 26) - 1;

   public:
    ObjectId() : objectId(0) {}

    unsigned int getHomeBits() const { return (objectId & homeBitMask) >> 30; }
    unsigned int getControlBits() const { return (objectId & controlBitMask) >> 26; }
    unsigned int getParticleId() const { return objectId & particleIdBitMask; }

    void setHomeBits(unsigned int homeBit) { objectId = (objectId & ~homeBitMask) | (homeBit << 30); }
    void setControlBits(unsigned int controlBits) { objectId = (objectId & ~controlBitMask) | (controlBits << 26); }
    void setParticleId(int particleId) { objectId = (objectId & ~particleIdBitMask) | particleId; }

    void print() { printf("objectId: %d | %d | %d\n", getHomeBits(), getControlBits(), getParticleId()); }
};

struct CellCollision {
    int cellId;
    int numHomeCells;
    int numPhantomCells;
    ObjectId* homeCellPtr;
    ObjectId* phantomCellPtr;

    void print() {
        std::cout << "====Cell Collision====" << "\n";
        std::cout << "Cell ID: " << cellId << "\n";
        std::cout << "Num Home Cells: " << numHomeCells << "\n";
        for (int i = 0; i < numHomeCells; i++) {
            std::cout << homeCellPtr[i].getParticleId() << " ";
        }
        std::cout << "\n";
        std::cout << "Num Phantom Cells: " << numPhantomCells << "\n";
        for (int i = 0; i < numPhantomCells; i++) {
            std::cout << phantomCellPtr[i].getParticleId() << " ";
        }
        std::cout << "\n";
    }
};

inline int determineCellType(int xCoord, int yCoord) {
    int xMod = xCoord % 2;
    int yMod = yCoord % 2;
    return yMod << 1 | xMod;
}

inline int determineCellTypeFromIndex(int cellIndex, int cellsPerSide) {
    int xCoord = cellIndex % cellsPerSide;
    int yCoord = cellIndex / cellsPerSide;
    return determineCellType(xCoord, yCoord);
}

inline bool isSideInBounds(int coord, int cellsPerSide) { return (coord >= 0 && coord < cellsPerSide); }

inline int getCellFromCoords(int xCoord, int yCoord, int cellsPerSide) { return (yCoord * cellsPerSide) + xCoord; }

int main(int argc, char* argv[]) {
    // Read arguments and input file
    Params params{};
    std::vector<Particle> particles;
    read_args(argc, argv, params, particles);

    // Set number of threads
    omp_set_num_threads(params.param_threads);
#if CHECK == 1
    // Initialize collision checker
    SimulationValidator validator(params.param_particles, params.square_size, params.param_radius);
    // Initialize with starting positions
    validator.initialize(particles);
    // Uncomment the line below to enable visualization (makes program much
    // slower) validator.enable_viz_output("test.out");
#endif

    // code start here
    int cellLength = params.param_radius * 2 * 1.5;
    int cellsPerSide = params.square_size / cellLength;

    for (int i = 0; i < params.param_steps; i++) {
        std::vector<int> cellIdArray(params.param_particles * 4, -1);

        std::vector<ObjectId> objectIdArray(params.param_particles * 4, ObjectId());

        // update all particle's position, and add to cells
#pragma omp parallel for
        for (int j = 0; j < params.param_particles; j++) {
            Particle* particle = &(particles[j]);
            particle->loc.x += particle->vel.x;
            particle->loc.y += particle->vel.y;

            double xPos = particle->loc.x / cellLength;
            double yPos = particle->loc.y / cellLength;

            int xCoord = std::max(0, std::min(static_cast<int>(xPos), cellsPerSide - 1));
            int yCoord = std::max(0, std::min(static_cast<int>(yPos), cellsPerSide - 1));

            int cellIndex = getCellFromCoords(xCoord, yCoord, cellsPerSide);

            // update cellIdArray and objectIdArray
            cellIdArray[j * 4] = cellIndex;                              // h-cell
            cellIdArray[(j * 4) + 1] = std::numeric_limits<int>::max();  // p-cell for left-right overlap
            cellIdArray[(j * 4) + 2] = std::numeric_limits<int>::max();  // p-cell for top-bottom overlap
            cellIdArray[(j * 4) + 3] = std::numeric_limits<int>::max();  // p-cell for diagonal overlap

            int controlBits = 0;

            int homeCellType = determineCellType(xCoord, yCoord);
            controlBits |= 1 << (homeCellType);

            double xDisplacementWithinCell = particle->loc.x - (xCoord * cellLength);
            double yDisplacementWithinCell = particle->loc.y - (yCoord * cellLength);

            int xShift = 0;
            int yShift = 0;

            if (xDisplacementWithinCell < params.param_radius) {
                xShift = -1;
            } else if (xDisplacementWithinCell > cellLength - params.param_radius) {
                xShift = 1;
            }

            if (yDisplacementWithinCell < params.param_radius) {
                yShift = -1;
            } else if (yDisplacementWithinCell > cellLength - params.param_radius) {
                yShift = 1;
            }

            // check for left-right overlap
            if (xShift) {
                if (isSideInBounds(xCoord + xShift, cellsPerSide)) {
                    int xCellIndex = (cellIndex + xShift);
                    cellIdArray[(j * 4) + 1] = xCellIndex;
                    int xCellType = determineCellType(xCoord + xShift, yCoord);
                    // int xCellType = determineCellTypeFromIndex(xCellIndex, cellsPerSide);
                    controlBits |= (1 << xCellType);
                }
            }

            // check for top-bottom overlap
            if (yShift) {
                if (isSideInBounds(yCoord + yShift, cellsPerSide)) {
                    int yCellIndex = cellIndex + (cellsPerSide * yShift);
                    cellIdArray[(j * 4) + 2] = yCellIndex;
                    int yCellType = determineCellType(xCoord, yCoord + yShift);
                    // int yCellType = determineCellType(xCoord, yCoord + yShift); // Corrected line
                    // int yCellType = determineCellType(xCoord, yCoord - 1);
                    controlBits |= (1 << yCellType);
                }
            }

            // check for diagonal overlap
            if (xShift && yShift) {
                if (isSideInBounds(xCoord + xShift, cellsPerSide) && isSideInBounds(yCoord + yShift, cellsPerSide)) {
                    int xyCellIndex = cellIndex + (cellsPerSide * yShift) + xShift;
                    cellIdArray[(j * 4) + 3] = xyCellIndex;
                    int xyCellType = determineCellType(xCoord + xShift, yCoord + yShift);
                    // int xyCellType = determineCellTypeFromIndex(xyCellIndex, cellsPerSide);
                    controlBits |= (1 << xyCellType);
                }
            }
            if (j == 439 || j == 3602) {
                std::cout << "home cell type is: " << homeCellType << "control bits are: " << controlBits << " particle index is:" << j << "\n";
            }
            ObjectId objectId = ObjectId();
            objectId.setParticleId(j);
            objectId.setControlBits(controlBits);
            objectId.setHomeBits(homeCellType);
            for (int k = 0; k < 4; k++) {
                objectIdArray[j * 4 + k] = objectId;
            }

            if (i == 10) {
                printf("in building arrays: particleId: %d\n", j);
                for (int k = 0; k < 4; k++) {
                    printf("cellId: %d\n", cellIdArray[j * 4 + k]);
                    objectIdArray[j * 4 + k].print();
                }
                printf("----\n");
            }
        }

        // for every 4 elements, (e.g. idx 0, 4, 8, ...)
        // move first element into a new H array
        // move the next 3 elements into a new P array
        std::vector<int> hCellIdArray;
        hCellIdArray.reserve(params.param_particles);
        std::vector<ObjectId> hObjectIdArray;
        hObjectIdArray.reserve(params.param_particles);

        std::vector<int> pCellIdArray;
        pCellIdArray.reserve(params.param_particles * 3);
        std::vector<ObjectId> pObjectIdArray;
        pObjectIdArray.reserve(params.param_particles * 3);

        // Separate the home and phantom cells
        for (int j = 0; j < params.param_particles; j++) {
            int hIdx = j * 4;  // Home cell index (first element in each 4-element block)

            // Home cell
            int cellId = cellIdArray[hIdx];
            assert(cellIdArray[hIdx] != std::numeric_limits<int>::max());
            assert(objectIdArray[hIdx].getHomeBits() ==
                   static_cast<unsigned int>(determineCellTypeFromIndex(cellId, cellsPerSide)));

            hCellIdArray.push_back(cellIdArray[hIdx]);
            hObjectIdArray.push_back(objectIdArray[hIdx]);

            // Phantom cells (next 3 elements)
            for (int k = 1; k < 4; ++k) {
                int pIdx = hIdx + k;  // Phantom cell index
                if (cellIdArray[pIdx] != std::numeric_limits<int>::max()) {
                    pCellIdArray.push_back(cellIdArray[pIdx]);
                    pObjectIdArray.push_back(objectIdArray[pIdx]);
                }
            }
        }

        // Concatenate the H array first, then the P array
        cellIdArray.clear();
        cellIdArray.insert(cellIdArray.end(), hCellIdArray.begin(), hCellIdArray.end());
        cellIdArray.insert(cellIdArray.end(), pCellIdArray.begin(), pCellIdArray.end());

        objectIdArray.clear();
        objectIdArray.insert(objectIdArray.end(), hObjectIdArray.begin(), hObjectIdArray.end());
        objectIdArray.insert(objectIdArray.end(), pObjectIdArray.begin(), pObjectIdArray.end());

        // Perform the radix sort to sort by cellId
        constexpr int bitsPerPass = 8;
        constexpr int numBuckets = 1 << bitsPerPass;                // 256 buckets for 8 bits
        constexpr int numPasses = (sizeof(int) * 8) / bitsPerPass;  // Number of passes (4 passes for 32-bit int)

        int numElements = cellIdArray.size();
        std::vector<int> sortedCellIdArray(numElements);
        std::vector<ObjectId> sortedObjectIdArray(numElements);

        // Iterate over each bit pass
        for (int pass = 0; pass < numPasses; ++pass) {
            int shift = pass * bitsPerPass;  // Determine the current bit shift

            // Initialize count array for the current pass
            std::vector<int> count(numBuckets, 0);

// Count occurrences of each bucket
#pragma omp parallel
            {
                std::vector<int> localCount(numBuckets, 0);

#pragma omp for nowait
                for (int i = 0; i < numElements; ++i) {
                    int bucket = (cellIdArray[i] >> shift) & (numBuckets - 1);
                    localCount[bucket]++;
                }

#pragma omp critical
                {
                    for (int i = 0; i < numBuckets; i++) {
                        count[i] += localCount[i];
                    }
                }
            }

            // Compute prefix sum to get positions
            for (int i = 1; i < numBuckets; ++i) {
                count[i] += count[i - 1];
            }

            // Sort based on current digit
            for (int i = numElements - 1; i >= 0; --i) {
                int bucket = (cellIdArray[i] >> shift) & (numBuckets - 1);
                sortedCellIdArray[--count[bucket]] = cellIdArray[i];
                sortedObjectIdArray[count[bucket]] = objectIdArray[i];
            }

            // Copy sorted arrays back for the next pass
            std::swap(cellIdArray, sortedCellIdArray);
            std::swap(objectIdArray, sortedObjectIdArray);
        }

        if (i == 10) {
            printf("-----sorted arrays-----\n");
            for (int t = 0; t < std::ssize(cellIdArray); t++) {
                printf("cellId: %d\n", cellIdArray[t]);
                objectIdArray[t].print();
                bool isHome = objectIdArray[t].getHomeBits() ==
                              static_cast<unsigned int>(determineCellTypeFromIndex(cellIdArray[t], cellsPerSide));
                printf("isHome: %d\n", isHome);
            }
            printf("-----sorted arrays end-----\n");
        }

        std::vector<CellCollision> cellCollisionList;

        // check cellIdArray
        // if new cellId -> if it is a home cell -> add to collision cell list
        for (int j = 0; j < std::ssize(cellIdArray); j++) {
            int cellId = cellIdArray[j];
            if (cellId == std::numeric_limits<int>::max()) break;

            int cellType = determineCellTypeFromIndex(cellId, cellsPerSide);
            ObjectId objectId = objectIdArray[j];

            if (i == 10) {
                printf("creating collisionlist: cellId: %d\n", cellId);
                objectId.print();
            }

            if (objectId.getHomeBits() == static_cast<unsigned int>(cellType)) {
                int numH = 0;
                int numP = 0;
                ObjectId* hPtr = &(objectIdArray[j]);
                ObjectId* pPtr = nullptr;
                while (cellIdArray[j] == cellId) {
                    if (objectIdArray[j].getHomeBits() == static_cast<unsigned int>(cellType)) {
                        numH++;
                    } else {
                        numP++;
                        if (pPtr == nullptr) {
                            pPtr = &(objectIdArray[j]);
                        }
                    }
                    j++;
                }
                j--;  // wacky, replace this
                cellCollisionList.emplace_back(cellId, numH, numP, hPtr, pPtr);
            } else {
                continue;
            }
        }

        bool collisionsOccurred = true;
        while (collisionsOccurred) {
            collisionsOccurred = false;

#pragma omp parallel for
            for (int j = 0; j < std::ssize(cellCollisionList); j++) {
                CellCollision cc = cellCollisionList[j];

                // debug
                if (i == 10) {
                    cc.print();
                }

                // process home cells wall collision
                for (int k = 0; k < cc.numHomeCells; k++) {
                    auto objectId = cc.homeCellPtr[k];

                    int particleIndex = objectId.getParticleId();
                    Particle* particle = &(particles[particleIndex]);

                    if (is_wall_collision(particle->loc, particle->vel, params.square_size, params.param_radius)) {
                        resolve_wall_collision(particle->loc, particle->vel, params.square_size, params.param_radius);
                        collisionsOccurred = true;
                    }
                }

                // process home cell - home cell collision and home cell - phantom cell collision
                for (int k = 0; k < cc.numHomeCells; k++) {
                    auto objectId = cc.homeCellPtr[k];
                    
                    int curParticleIndex = objectId.getParticleId();
                    Particle* curParticle = &(particles[curParticleIndex]);

                    // home cell - home cell collision
                    for (int l = k + 1; l < cc.numHomeCells; l++) {
                        auto otherObjectId = cc.homeCellPtr[l];

                        int otherParticleIndex = otherObjectId.getParticleId();
                        if ((curParticleIndex == 439 && otherParticleIndex == 3602) || (otherParticleIndex == 439 && curParticleIndex == 3602)) { 
                            std::cout << "home home here" << "\n";
                        }
                        Particle* otherParticle = &(particles[otherParticleIndex]);

                        if (is_particle_collision(curParticle->loc, curParticle->vel, otherParticle->loc,
                                                  otherParticle->vel, params.param_radius)) {
                            resolve_particle_collision(curParticle->loc, curParticle->vel, otherParticle->loc,
                                                       otherParticle->vel);
                            collisionsOccurred = true;
                        }
                    }

                    // home cell - phantom cell collision
                    for (int l = 0; l < cc.numPhantomCells; l++) {
                        auto otherObjectId = cc.phantomCellPtr[l];

                        // int particleHomeCell = objectId.getHomeBits();
                        // int otherHomeCell = otherObjectId.getHomeBits();
                        // int otherControlBits = otherObjectId.getControlBits();

                        // // bool otherHasParticle = (otherControlBits & (1 << particleHomeCell)); // not necessary
                        // bool particleHasOther = (objectId.getControlBits() & (1 << otherHomeCell));

                        // if ((otherHomeCell > particleHomeCell) && particleHasOther) {
                        //     if (i == 10) {
                        //         printf("skipping: particle: %d, other: %d, pHome: %d, oHome: %d, oControl: %d\n",
                        //                curParticleIndex, otherObjectId.getParticleId(), otherHomeCell,
                        //                particleHomeCell, otherControlBits);
                        //     }
                        //     continue;
                        // }

                        int otherParticleIndex = otherObjectId.getParticleId();
                        Particle* otherParticle = &(particles[otherParticleIndex]);
                        if ((curParticleIndex == 439 && otherParticleIndex == 3602) || (otherParticleIndex == 439 && curParticleIndex == 3602)) { 
                            std::cout << "home phantom here" << "\n";
                        }
                        if (is_particle_collision(curParticle->loc, curParticle->vel, otherParticle->loc,
                                                  otherParticle->vel, params.param_radius)) {
                            resolve_particle_collision(curParticle->loc, curParticle->vel, otherParticle->loc,
                                                       otherParticle->vel);
                            collisionsOccurred = true;
                        }
                    }
                }
            }
        }
#if CHECK == 1
        validator.validate_step(particles);
#endif
    }

// code end here
#if CHECK == 1
// Check final positions
// validator.validate_step(particles);
#endif
}
Leave a Comment