生成一组随机 x,y,z 数字,它们之间的差异最小,在定义的限制之间

计算科学 并行计算 分子动力学 随机数生成
2021-12-11 17:35:51

所以我正在尝试分子动力学模拟,并尝试用随机粒子填充一个 3D 矩形体积,这些随机粒子均匀分布在整个体积中。每个分子都有一个固定的半径 r(sphere),其中每个分子的 r 可以不同。所以我希望在体积中生成球体,然后在距该点的 ( 2(r+tolerance) ) 距离内不应存在其他球体(公差会非常小,如 10^-6)。

此外,通道将具有指定的长度、宽度和宽度,因此随机化应在单个方向上的这些限制内进行,具有上述类似条件,即如果分子比其半径更接近壁,则不应生成分子+宽容。

我最初是在尝试结构化晶格,但这意味着我的分子数量将与维度相关,这对我不起作用。所以我写了下面的算法(我想粘贴我的代码,但它不起作用,现在它是一个很大的、夸张的混乱,并且几乎让我的电脑崩溃了一次。

所以逻辑是

1.) 对于每个粒子,使 max_X=X-(radius+tol)。对 Y 和 Z 执行相同的操作。

2.) 对于每个粒子,生成 0 到 max_X 之间的随机数。

3.)计算每个粒子之间的距离并创建一个违反上述任何条件的粒子列表。

4.) 遍历列表并重新生成这些粒子。

5.) 创建另一个违规粒子对列表。

6.) 过去。冲洗并重复,直到列表的大小为零。

所以,它不起作用。而且我需要这段代码最终为大量粒子执行,所以我需要它尽可能高效和 OpenMP 可并行化。我在 for 循环前面使用标准的陈词滥调的#pragma omp parallel for 方法来并行化计算,但是在运行时,在超强风扇噪音大约 5 到 10 分钟后,无论有没有编译指示,它都不起作用。

我是一个非编码背景的人,最近开始学习用 C++ 编写复杂的代码,所以我现在不能做一些花哨的事情,比如结构、类或指针。我正在使用 std::vectors。如果你们能告诉我一条出路,我会很高兴的,如果你在罗利及其周边地区,我会亲自开车过来给你一杯冰镇啤酒。几天来一直在尝试这样做,并且是我的代码中唯一主要的非功能性东西。

帮助?!!!

PS:要明确一点,这不是作业,否则我现在已经问教授了。它是一个独立项目的一部分,我将在完成后自由分发。

1个回答

因此,您可以做的一件事是将结构化区域(例如 3D 矩形体积)隐式表示为一组切碎的子体积,其中每个子体积会自动执行您需要的空间约束。您将隐含地执行此操作,只需存储将每个维度单独切片的数量,并使用唯一的索引元组引用每个子卷。

然后,您可以使用粒子半径和容差的值来计算每个维度应该有多少切片,以便满足您的空间约束。

然后从这里你可以迭代地蒙特卡罗下一个子卷以插入​​一个粒子。为了有效地检查你是否蒙特卡罗一个你已经使用过的子卷,你可以计算一个唯一的哈希,给定一个子卷的索引元组, 并将其存储在有效的数据结构中,例如C++std::mapstd::unordered_mapC++,以便能够快速检查是否存在。

我为此用 C++ 编写的示例代码如下:

粒子云.hpp

#ifndef _particle_cloud_
#define _particle_cloud_

#include <vector>
#include <cstdint>

namespace particle {

    class cloud {
    public:

        // useful typedefs
        typedef std::vector<double>     position;
        typedef std::vector<position>   positions;

        // ctors / initializers
        cloud();
        cloud(int num_particles, double xrng[2], double yrng[2], double zrng[2], double radius, double tolerance);
        void init(int num_particles, double xrng[2], double yrng[2], double zrng[2], double radius, double tolerance);

        // getter for particle position
        const position & getParticlePositionAt(int idx) const;

        // get number of particles in particle cloud
        int numParticles() const;

    private:
        typedef uint64_t h_int;
        enum Bounds { Low = 0, High = 1};
        positions ps;

        // unique hash function
        static h_int hash(h_int ix, h_int iy, h_int iz, h_int Nx, h_int Ny);
    };

}


#endif

粒子云.cpp

#include "particle_cloud.hpp"
#include <map>
#include <random>
#include <cmath>

namespace particle {


    cloud::cloud()
    {
    }

    cloud::cloud(int num_particles, double xrng[2], double yrng[2], double zrng[2], double radius, double tolerance)
    {
        init(num_particles, xrng, yrng, zrng, radius, tolerance);
    }

    void cloud::init(int num_particles, double xrng[2], double yrng[2], double zrng[2], double radius, double tolerance)
    {
        // init constant quantities
        const double r = radius;
        const double e = tolerance;
        const double lb[3]      = { xrng[Low], yrng[Low], zrng[Low] };
        const double ub[3]      = { xrng[High], yrng[High], zrng[High] };
        const double span[3]    = { ub[0]- lb[0], ub[1]- lb[1], ub[2]- lb[2] };
        const h_int N[3]        = { std::floor(0.5*span[0]/(r+e)), std::floor(0.5*span[1] / (r + e)), std::floor(0.5*span[2] / (r + e)) };
        const double del[3]     = { span[0] / static_cast<double>(N[0]), span[1] / static_cast<double>(N[1]) , span[2] / static_cast<double>(N[2]) };

        // init array of temp map indices
        h_int idx[3]            = { 0 };

        // init temp position vector
        position tmp(3, 0.0);

        // size up the positions container correctly
        ps.resize(num_particles, tmp);

        // init the uniform distribution random number generator
        std::default_random_engine generator;
        std::uniform_real_distribution<double> U(0, 1);

        // create a map container to check if a given hash
        // has already been created
        std::map<h_int, bool> exist_map;

        // init variable that will say if the new position generated
        // is valid or not
        bool noGoodPos = false;

        // loop and generate the particle positions
        for (unsigned int i = 0; i < num_particles; ++i) {
            noGoodPos = true;

            // try to generate a valid position via Monte Carlo sampling until one has been made
            while (noGoodPos) {
                for (int d = 0; d < 3; ++d) { idx[d] = U(generator)*N[d]; }
                h_int h_ = hash(idx[0], idx[1], idx[2], N[0], N[1]);

                if (exist_map.count(h_) != 0) { }
                else { 
                    exist_map[h_] = true;
                    noGoodPos = false; 
                }
            }

            // compute each component of position
            // based on indices and dimensions of container
            for (int d = 0; d < 3; ++d) {
                tmp[d] = lb[d] + (del[d] * idx[d]) + (del[d] * 0.5);
            }

            // copy temp position to ith position in positions container
            ps[i] = tmp;
        }

    }

    const typename cloud::position & cloud::getParticlePositionAt(int idx) const
    {
        return ps[idx];
    }

    int cloud::numParticles() const
    {
        return (int)ps.size();
    }

    typename cloud::h_int cloud::hash(h_int ix, h_int iy, h_int iz, h_int Nx, h_int Ny)
    {
        return ix + Nx*(iy + Ny*iz); // unique hash based on spatial indices
    }


}

主文件

#include <stdio.h>
#include "particle_cloud.hpp"

int main(int argc, char** argv) {

    particle::cloud pc;
    double v[2] = { 0, 1 };
    pc.init(100000, v, v, v, 1e-4, 1e-8);

    FILE * file_ = fopen("points.csv", "w");
    if (file_) {
        for (int i = 0; i < pc.numParticles(); ++i) {
            const particle::cloud::position & p = pc.getParticlePositionAt(i);
            fprintf(file_, "%0.8e, %0.8e, %0.8e\n", p[0], p[1], p[2]);
        }

        fclose(file_); file_ = nullptr;
    }

    return 0;
}

基于使用上述代码生成 1000 个粒子位置的示例图形如下所示:

使用算法的样本粒子分布