diff options
| author | Jaron Kent-Dobias <jaron@kent-dobias.com> | 2021-09-17 12:40:44 +0200 | 
|---|---|---|
| committer | Jaron Kent-Dobias <jaron@kent-dobias.com> | 2021-09-17 12:40:44 +0200 | 
| commit | 3140c22dc32ae525b491bcb9653ec755762321d2 (patch) | |
| tree | e8f5b54298da0b2fa300c71da5a0eadf4fdccf26 | |
| parent | db9b5ba4ac75b3d4a2151e1b577928cfbb212bc1 (diff) | |
| download | spheres-3140c22dc32ae525b491bcb9653ec755762321d2.tar.gz spheres-3140c22dc32ae525b491bcb9653ec755762321d2.tar.bz2 spheres-3140c22dc32ae525b491bcb9653ec755762321d2.zip | |
Some refactoring.
| -rw-r--r-- | animation.hpp | 8 | ||||
| -rw-r--r-- | hard_spheres.cpp | 99 | ||||
| -rw-r--r-- | soft_spheres.cpp | 67 | ||||
| -rw-r--r-- | spheres.hpp | 258 | 
4 files changed, 234 insertions, 198 deletions
| diff --git a/animation.hpp b/animation.hpp index 26b3d55..b1ff815 100644 --- a/animation.hpp +++ b/animation.hpp @@ -28,10 +28,12 @@ void initializeAnimation(int argc, char** argv, unsigned window_size = 1000) {  template <Particle<2> T> void draw(const Model<2, T>& m) {    glClear(GL_COLOR_BUFFER_BIT);    for (const Sphere<2>& p : m.particles) { -    draw(p); -    if (p.intersectsBoundary()) { +    Sphere<2> pTmp = p; +    pTmp.x = m.orientation.inverse().act(p.x); +    draw(pTmp); +    if (pTmp.intersectsBoundary()) {        for (Vector<2> Δy : {(Vector<2>){1,0}, {-1,0}, {0,1}, {0,-1}, {-1,1}, {1,1}, {1,-1},{-1,-1}}) { -        draw({(Vector<2>)p.x + Δy, p.r}); +        draw({(Vector<2>)pTmp.x + Δy, p.r});        }      }    } diff --git a/hard_spheres.cpp b/hard_spheres.cpp index 6a14953..b8be1cf 100644 --- a/hard_spheres.cpp +++ b/hard_spheres.cpp @@ -1,53 +1,82 @@  #include "animation.hpp" +template <int D> class HardSphere : public Sphere<D> { +public: +  using Sphere<D>::Sphere; + +  double U(const Position<D>& y, double ry) const { +    if (Sphere<D>::regularizedDistanceSquared(y, ry) < 1) { +      return 1e6; +    } else { +      return 0; +    } +  } +}; +  int main(int argc, char* argv[]) {    unsigned N = 1200; -  double R = 0.021; -  double ε = 0.01; +  double R = 0.024; +  double ε = 0.005;    unsigned steps = 1e6;    initializeAnimation(argc, argv);    Rng r; -  Model<2, HardSphere<2>> m(N, 2 * R); +  Model<2, HardSphere<2>> m(N, 2 * R, gContinuousPolydisperse(R), r); -  for (unsigned i = 0; i < N; i++) { -    Position<2> x = {r.uniform(0.0, 1.0), r.uniform(0.0, 1.0)}; -    double rad = pow(r.uniform(pow(2.219, -2), 1.0), -0.5) * R / 2.219; -    m.insert(HardSphere<2>(x, rad)); -  } +  for (unsigned i = 0; i < steps; i++) { +    for (unsigned j = 0; j < N; j++) { +      HardSphere<2>& p = r.pick(m.particles); +      Position<2> xNew = p.x; +      Vector<2> Δx; +      for (double& xi : Δx) { +        xi = r.variate<double, std::normal_distribution>(0, ε); +      } +      xNew += Δx; +      bool reject = false; +      for (const HardSphere<2>* neighbor : m.nl.neighborsOf(xNew)) { +        if (neighbor != &p) { +          if (neighbor->regularizedDistanceSquared(xNew, p.r) < 1 && neighbor->regularizedDistanceSquared(p.x, p.r) >= 1) { +            reject = true; +            break; +          } +        } +      } +      if (!reject) { +        m.move(p, xNew); +      } +       +      HardSphere<2>& p1 = r.pick(m.particles); +      HardSphere<2>& p2 = r.pick(m.particles); -  for (unsigned i = 0; i < steps * N; i++) { -    HardSphere<2>& s = r.pick(m.particles); -    Vector<2> Δx; -    for (double& Δxi : Δx) { -      Δxi = r.variate<double, std::normal_distribution>(0, ε); +      double rat = p1.r / p2.r; +      if (rat < 1.2 && rat > 0.83) { +        reject = false; +        for (const HardSphere<2>* neighbor : m.nl.neighborsOf(p2.x)) { +          if (neighbor != &p1 && neighbor != &p2) { +            if (neighbor->regularizedDistanceSquared(p2.x, p1.r) < 1 && neighbor->regularizedDistanceSquared(p1.x, p1.r) >= 1) { +              reject = true; +              break; +            } +          } +        } +        for (const HardSphere<2>* neighbor : m.nl.neighborsOf(p1.x)) { +          if (neighbor != &p1 && neighbor != &p2) { +            if (neighbor->regularizedDistanceSquared(p1.x, p2.r) < 1 && neighbor->regularizedDistanceSquared(p2.x, p2.r) >= 1) { +              reject = true; +              break; +            } +          } +        } +        if (!reject) { +          std::swap(p1.r, p2.r); +        } +      }      } -    m.metropolis(1, s, Δx, r); - -    HardSphere<2>& s1 = r.pick(m.particles); -    HardSphere<2>& s2 = r.pick(m.particles); - -    Vector<2> t1 = s1.x; -    Vector<2> t2 = s2.x; -    Vector<2> t = (t1 + t2) / 2; - -    Matrix<2> mat; - -    mat(0, 0) = -1; -    mat(1, 1) = -1; -    mat(0, 1) = 0; -    mat(1, 0) = 0; - -    Euclidean<2> g(t - mat * t, mat); - -    std::cout << m.clusterFlip(1, g, s1, r) << std::endl; - -    if (i % (N) == 0) { +    if (i % 10 == 0)        draw(m); -    }    }    return 0; diff --git a/soft_spheres.cpp b/soft_spheres.cpp index cdd6f31..7944aa0 100644 --- a/soft_spheres.cpp +++ b/soft_spheres.cpp @@ -1,55 +1,48 @@  #include "animation.hpp" +template <int D, int n> class SoftSphere : public Sphere<D> { +private: +  const double c0 = -pow(2, 2*n-3) * pow(5, -n) * (8 + 6*n + pow(n, 2)); +  const double c2 =  pow(2, 2+2*n) * pow(5, -n-2) * n * (4+n); +  const double c4 = -pow(2, 5+2*n) * pow(5, -n-4) * n * (2+n); +public: +  using Sphere<D>::Sphere; + +  double U(const Position<D>& y, double ry) const { +    double r2 = Sphere<D>::regularizedDistanceSquared(y, ry); +    if (r2 < pow(1.25, 2)) { +      return pow(1 / r2, n / 2) + c0 + c2 * r2 + c4 * pow(r2, 2); +    } else { +      return 0; +    } +  } +}; +  int main(int argc, char* argv[]) { -  const unsigned n = 12; +  const unsigned n = 24;    unsigned N = 1200; -  double R = 0.023; -  double ε = 0.01; +  double R = 0.025; +  double ε = 0.005;    unsigned steps = 1e6; -  double β = 1; +  double β = 0.5;    initializeAnimation(argc, argv);    Rng r; -  Model<2, SoftSphere<2, n>> m(N, 2 * R * 1.25); - -  for (unsigned i = 0; i < N; i++) { -    Position<2> x = {r.uniform(0.0, 1.0), r.uniform(0.0, 1.0)}; -    double rad = pow(r.uniform(pow(2.219, -2), 1.0), -0.5) * R / 2.219; -    m.insert(SoftSphere<2, n>(x, rad)); -  } +  Model<2, SoftSphere<2, n>> m(N, 2 * R * 1.25, gContinuousPolydisperse(R), r); -  for (unsigned i = 0; i < steps * N; i++) { -    SoftSphere<2, n>& s = r.pick(m.particles); -    Vector<2> Δx; -    for (double& Δxi : Δx) { -      Δxi = r.variate<double, std::normal_distribution>(0, ε); +  for (unsigned i = 0; i < steps; i++) { +    for (unsigned j = 0; j < N; j++) { +      m.randomMove(β, ε, r); +      m.randomSwap(β, r);      } -    for (unsigned j = 0; j < N; j++) -      m.metropolis(β, s, Δx, r); - -    SoftSphere<2, n>& s1 = r.pick(m.particles); -    SoftSphere<2, n>& s2 = r.pick(m.particles); - -    Vector<2> t1 = s1.x; -    Vector<2> t2 = s2.x; -    Vector<2> t = (t1 + t2) / 2; - -    Matrix<2> mat; - -    mat(0, 0) = -1; -    mat(1, 1) = -1; -    mat(0, 1) = 0; -    mat(1, 0) = 0; - -    Euclidean<2> g(t - mat * t, mat); - -    std::cout << m.clusterFlip(1, g, s1, r) << std::endl; +//    std::cout << m.randomClusterSwap(β, r) << std::endl; -    draw(m); +    if (i % 3 == 0) +      draw(m);    }    return 0; diff --git a/spheres.hpp b/spheres.hpp index ef6b665..6dd5b04 100644 --- a/spheres.hpp +++ b/spheres.hpp @@ -59,10 +59,30 @@ public:    }  }; -template <typename T, int D> concept Particle = requires(T v, const T& w) { +template <int D> class Euclidean { +public: +  Vector<D> t; +  Matrix<D> r; + +  Euclidean() { +    t.setZero(); +    r.setIdentity(); +  } + +  Euclidean(const Vector<D>& t0, const Matrix<D>& r0) : t(t0), r(r0) {} + +  Euclidean inverse() const { return Euclidean(-r.transpose() * t, r.transpose()); } + +  Vector<D> act(const Vector<D>& x) const { return r * x + t; } +  Position<D> act(const Position<D>& x) const { return (Position<D>)(r * x) + t; } +  Euclidean<D> act(const Euclidean<D>& R) const { return {act(R.t), r * R.r}; } +}; + +template <typename T, int D> concept Particle = requires(T v, const Position<D>& x, double r) {    { v.x } -> std::same_as<Position<D>>; +  { v.r } -> std::same_as<double>;    { v.m } -> std::same_as<bool>; -  { v.U(w) } -> std::same_as<double>; +  { v.U(x, r) } -> std::same_as<double>;  };  template <int D, Particle<D> T> class NeighborLists { @@ -157,162 +177,97 @@ public:    }  }; -template <int D> class Sphere { -public: -  Position<D> x; -  bool m; -  double r; - -  Sphere(const Position<D>& x, double r) : x(x), m(false), r(r) {}; - -  double regularizedDistanceSquared(const Sphere<D>& s) const { -    return (x - s.x).squaredNorm() / pow(r + s.r, 2); -  }  - -  bool intersectsBoundary() const { -    for (double xi : x) { -      if (xi < r || 1 - xi < r) { -        return true; -      } -    } -    return false; -  } -}; - -template <int D> class HardSphere : public Sphere<D> { -public: -  using Sphere<D>::Sphere; - -  double U(const HardSphere<D>& s) const { -    if (Sphere<D>::regularizedDistanceSquared(s) < 1) { -      return 1e6; -    } else { -      return 0; -    } -  } -}; - -template <int D, int n> class SoftSphere : public Sphere<D> { +template <int D, Particle<D> T> class Model {  private: -  const double c0 = -pow(2, 2*n-3) * pow(5, -n) * (8 + 6*n + pow(n, 2)); -  const double c2 =  pow(2, 2+2*n) * pow(5, -n-2) * n * (4+n); -  const double c4 = -pow(2, 5+2*n) * pow(5, -n-4) * n * (2+n); -public: -  using Sphere<D>::Sphere; - -  double U(const SoftSphere<D, n>& s) const { -    double r2 = Sphere<D>::regularizedDistanceSquared(s); -    if (r2 < pow(1.25, 2)) { -      return pow(1 / r2, n / 2) + c0 + c2 * r2 + c4 * pow(r2, 2); -    } else { -      return 0; -    } +  bool metropolisAccept(double β, double ΔE, Rng& r) const { +    return ΔE < 0 || exp(-β * ΔE) > r.uniform(0.0, 1.0);    } -}; -template <int D> class Euclidean { -public: -  Vector<D> t; -  Matrix<D> r; - -  Euclidean() { -    t.setZero(); -    r.setIdentity(); +  bool swapAccept(const T& p1, const T& p2) const { +    double rat = p1.r / p2.r; +    return (rat < 1.2 && rat > 0.83);    } -  Euclidean(const Vector<D>& t0, const Matrix<D>& r0) : t(t0), r(r0) {} - -  Euclidean inverse() const { return Euclidean(-r.transpose() * t, r.transpose()); } - -  Vector<D> act(const Vector<D>& x) const { return r * x; } -  Position<D> act(const Position<D>& x) const { return (Position<D>)(r * x) + t; } -}; - -template <int D, Particle<D> T> class Model { -private: -  NeighborLists<D, T> nl; -  public: +  NeighborLists<D, T> nl;    std::vector<T> particles; +  Euclidean<D> orientation; -  Model(unsigned N, double l) : nl(l) { -    particles.reserve(N); +  Model(unsigned N, double l, std::function<double(Rng& r)> g, Rng& r) : nl(l), particles(N) { +    for (T& p : particles) { +      for (double& xi : p.x) { +        xi = r.uniform(0.0, 1.0); +      } +      p.r = g(r); +      nl.insert(p); +    }    }; -  void insert(const T& p) {  -    particles.push_back(p); -    nl.insert(particles.back()); -  } -    void move(T& p, const Position<D>& x) {      nl.move(p, x);      p.x = x;    } -  std::set<T*> neighborsOf(const Position<D>& x) const { -    return nl.neighborsOf(x); -  } - -  double energyChange(const T& p, const T& pNew) const { +  bool move(double β, T& p, const Vector<D>& Δx, Rng& r) { +    Position<D> xNew = p.x + Δx;      double ΔE = 0; -    for (const T* neighbor : neighborsOf(p.x)) { + +    for (const T* neighbor : nl.neighborsOf(p.x)) {        if (neighbor != &p) { -        ΔE -= neighbor->U(p); +        ΔE -= neighbor->U(p.x, p.r);        }      } -    for (const T* neighbor : neighborsOf(pNew.x)) { + +    for (const T* neighbor : nl.neighborsOf(xNew)) {        if (neighbor != &p) { -        ΔE += neighbor->U(pNew); +        ΔE += neighbor->U(xNew, p.r);        }      } -    return ΔE; +    if (metropolisAccept(β, ΔE, r)) { +      move(p, xNew); +      return true; +    } else { +      return false; +    }    } -  double energyChangeSwap(const T& p1, const T& p2) const { +  bool swap(double β, T& p1, T& p2, Rng& r) { +    if (!swapAccept(p1, p2)) +      return false; +      double ΔE = 0; -    T p1New = p1; -    T p2New = p2; -    p1New.r = p2.r; -    p2New.r = p1.r; -    for (const T* neighbor : neighborsOf(p1.x)) { + +    for (const T* neighbor : nl.neighborsOf(p1.x)) {        if (neighbor != &p1 && neighbor != &p2) { -        ΔE -= neighbor->U(p1); -        ΔE += neighbor->U(p1New); +        ΔE += neighbor->U(p1.x, p2.r) - neighbor->U(p1.x, p1.r);        }      } -    for (const T* neighbor : neighborsOf(p2.x)) { + +    for (const T* neighbor : nl.neighborsOf(p2.x)) {        if (neighbor != &p1 && neighbor != &p2) { -        ΔE -= neighbor->U(p2); -        ΔE += neighbor->U(p2New); +        ΔE += neighbor->U(p2.x, p1.r) - neighbor->U(p2.x, p2.r);        }      } -    return ΔE; -  } - -  bool metropolis(double β, T& p, const Vector<D>& Δx, Rng& r) { -    T pNew = p; -    pNew.x += Δx; - -    double ΔE = energyChange(p, pNew); - -    if (exp(-β * ΔE) > r.uniform(0.0, 1.0)) { -      move(p, pNew.x); +    if (metropolisAccept(β, ΔE, r)) { +      std::swap(p1.r, p2.r);        return true;      } else {        return false;      }    } -  bool swap(double β, T& p1, T& p2, Rng& r) { -    double ΔE = energyChangeSwap(p1, p2); -    if (exp(-β * ΔE) > r.uniform(0.0, 1.0)) { -      std::swap(p1.r, p2.r); -      return true; -    } else { -      return false; +  bool randomSwap(double β, Rng& r) { +    return swap(β, r.pick(particles), r.pick(particles), r); +  } + +  bool randomMove(double β, double δ, Rng& r) { +    Vector<D> Δx; +    for (double& Δxi : Δx) { +      Δxi = r.variate<double, std::normal_distribution>(0, δ);      } +    return move(β, r.pick(particles), Δx, r);    }    unsigned clusterFlip(double β, const Euclidean<D>& R, T& p0, Rng& r) { @@ -326,19 +281,18 @@ public:        if (!p->m) {          p->m = true; -        T pNew = *p; -        pNew.x = R.act(p->x); +        Position<D> xNew = R.act(p->x); -        for (T* neighbor : neighborsOf(pNew.x)) { +        for (T* neighbor : nl.neighborsOf(xNew)) {            if (!neighbor->m) { -            double ΔE = neighbor->U(pNew) - neighbor->U(*p); +            double ΔE = neighbor->U(xNew, p->r) - neighbor->U(p->x, p->r);              if (1 - exp(-β * ΔE) > r.uniform(0.0, 1.0)) {                q.push(neighbor);              }            }          } -        move(*p, pNew.x); +        move(*p, xNew);          n++;        }      } @@ -347,6 +301,64 @@ public:        p.m = false;      } +    if (n > particles.size() / 2) { +      orientation = R.act(orientation); +    } +      return n;    } + +  unsigned clusterSwap(double β, T& s1, T& s2, Rng& r) { +    if (!swapAccept(s1, s2)) +      return 0; + +    Vector<2> t1 = s1.x; +    Vector<2> t2 = s2.x; +    Vector<2> t = (t1 + t2) / 2; + +    Matrix<2> mat; + +    mat(0, 0) = -1; +    mat(1, 1) = -1; +    mat(0, 1) = 0; +    mat(1, 0) = 0; + +    Euclidean<2> g(t - mat * t, mat); + +    return clusterFlip(β, g, s1, r); +  } + +  unsigned randomClusterSwap(double β, Rng& r) { +    return clusterSwap(β, r.pick(particles), r.pick(particles), r); +  } +}; + +template <int D> class Sphere { +public: +  Position<D> x; +  bool m; +  double r; + +  Sphere() : m(false) {}; + +  Sphere(const Position<D>& x, double r) : x(x), m(false), r(r) {}; + +  double regularizedDistanceSquared(const Position<D>& y, double ry) const { +    return (x - y).squaredNorm() / pow(r + ry, 2); +  }  + +  bool intersectsBoundary() const { +    for (double xi : x) { +      if (xi < r || 1 - xi < r) { +        return true; +      } +    } +    return false; +  }  }; + +std::function<double(Rng&)> gContinuousPolydisperse(double Rmax) { +  return [Rmax] (Rng& r) -> double { +    return pow(r.uniform(pow(2.219, -2), 1.0), -0.5) * Rmax / 2.219; +  }; +} | 
