1363 lines
42 KiB
C++
1363 lines
42 KiB
C++
/**
|
|
* @file physics_system.h
|
|
*
|
|
* @author Jim Waters (jwaters6@gatech.edu)
|
|
*
|
|
*
|
|
*/
|
|
|
|
|
|
#ifndef MULTI_PHYSICS_SYSTEM_H
|
|
#define MULTI_PHYSICS_SYSTEM_H
|
|
|
|
#include "fastlib/fastlib.h"
|
|
#include "fastlib/fastlib_int.h"
|
|
#include "particle_tree.h"
|
|
#include "force_error.h"
|
|
#include "raddist.h"
|
|
#include "../thor_md/periodic_tree.h"
|
|
#define PI 3.14159265358979
|
|
|
|
|
|
const fx_entry_doc param_entries[] = {
|
|
{"leaf", FX_PARAM, FX_INT, NULL,
|
|
"Specifies maximum leaf size in tree. \n" },
|
|
{"lx", FX_PARAM, FX_DOUBLE, NULL,
|
|
"Specifies size of periodic box in x-dimension. \n"},
|
|
{"ly", FX_PARAM, FX_DOUBLE, NULL,
|
|
"Specifies size of periodic box in y-dimension. \n"},
|
|
{"lz", FX_PARAM, FX_DOUBLE, NULL,
|
|
"Specifies size of periodic box in z-dimension. \n"},
|
|
{"bc", FX_PARAM, FX_INT, NULL,
|
|
"Specifies type of boundary conditions. 0 is free boundary, "
|
|
"1 is periodic boundary. \n"},
|
|
{"force_bound", FX_PARAM, FX_DOUBLE, NULL,
|
|
"Determines pruning criterion for tree implementation \n"},
|
|
FX_ENTRY_DOC_DONE
|
|
};
|
|
|
|
const fx_module_doc param_doc = {
|
|
param_entries, NULL,
|
|
"Parameters of Simulated System \n"
|
|
};
|
|
|
|
|
|
class MultiPhysicsSystem{
|
|
|
|
static const int FREE = 0;
|
|
static const int PERIODIC = 1;
|
|
static const int FIXED = 2;
|
|
static const int CUTOFF = 0;
|
|
static const int POTENTIAL = 1;
|
|
static const int FORCE = 2;
|
|
|
|
private:
|
|
// Input data set
|
|
Matrix atoms_;
|
|
Matrix forces_;
|
|
Matrix axilrod_teller_;
|
|
Matrix diffusion_;
|
|
// Trees store current state of system
|
|
ParticleTree *system_, *query_;
|
|
ArrayList<int> old_from_new_map_, new_from_old_map_;
|
|
|
|
Vector dimensions_, signs_, powers_;
|
|
Vector power3a_, power3b_, power3c_;
|
|
Vector signs3_;
|
|
|
|
double time_step_, virial_, temperature_, cutoff_, cutoff3_;
|
|
bool three_body_;
|
|
int n_atoms_, boundary_, prune_;
|
|
double n_trips_;
|
|
int total_triples_, range_evals_;
|
|
double max_force_;
|
|
// Tree Parameters
|
|
double force_bound_, percent_pruned_, target_percent_;
|
|
int leaf_size_;
|
|
|
|
|
|
|
|
/**
|
|
* Force evaluation functions.
|
|
*/
|
|
|
|
// Two-body force between two nodes
|
|
void TwoBodyForce_(ParticleTree* left, ParticleTree* right){
|
|
Vector force;
|
|
la::SubInit(right->stat().centroid_, left->stat().centroid_, &force);
|
|
if (boundary_ == PERIODIC){
|
|
AdjustVector_(&force);
|
|
}
|
|
double dist, coef = 0, temp;
|
|
dist = sqrt(la::Dot(force, force));
|
|
for (int i = 0;i < forces_.n_rows(); i++){
|
|
temp = -signs_[i]*left->stat().interactions_[i].coef()*
|
|
right->stat().interactions_[i].coef();
|
|
coef = coef + temp*pow(dist, powers_[i]-2)*powers_[i];
|
|
}
|
|
virial_ = virial_ + coef*dist*dist;
|
|
la::Scale(coef*time_step_, &force);
|
|
left->stat().ApplyForce(force);
|
|
la::Scale(-1.0, &force);
|
|
right->stat().ApplyForce(force);
|
|
percent_pruned_ = percent_pruned_ + 1;
|
|
}
|
|
|
|
// Two body force between two atoms
|
|
void TwoBodyForce_(int left, int right){
|
|
Vector delta_r, left_vec, right_vec;
|
|
atoms_.MakeColumnSubvector(left, 0, 3, &left_vec);
|
|
atoms_.MakeColumnSubvector(right, 0, 3, &right_vec);
|
|
la::SubInit(right_vec, left_vec, &delta_r);
|
|
if (boundary_ == PERIODIC){
|
|
AdjustVector_(&delta_r);
|
|
}
|
|
double dist = sqrt(la::Dot(delta_r, delta_r));
|
|
if (prune_ == CUTOFF){
|
|
if (dist > cutoff_){
|
|
return;
|
|
}
|
|
}
|
|
double coef = 0, temp;
|
|
for (int i = 0; i < forces_.n_rows(); i++){
|
|
temp = -forces_.get(i, left)*forces_.get(i, right);
|
|
coef = coef + signs_[i]*temp*powers_[i]*pow(dist, powers_[i]-2);
|
|
}
|
|
|
|
virial_ = virial_ + coef*dist*dist;
|
|
la::Scale(coef, &delta_r);
|
|
// Apply forces to particles
|
|
left_vec.Destruct();
|
|
right_vec.Destruct();
|
|
atoms_.MakeColumnSubvector(left, 4, 3, &left_vec);
|
|
atoms_.MakeColumnSubvector(right, 4, 3, &right_vec);
|
|
la::AddExpert(time_step_ / atoms_.get(3,left), delta_r, &left_vec);
|
|
la::AddExpert(-time_step_ / atoms_.get(3,right), delta_r, &right_vec);
|
|
percent_pruned_ = percent_pruned_ + 1;
|
|
}
|
|
|
|
|
|
|
|
// Three body force between three nodes
|
|
void ThreeBodyForce_(ParticleTree* a, ParticleTree* b, ParticleTree* c){
|
|
Vector r_ij, r_jk, r_ki;
|
|
la::SubInit(a->stat().centroid_, b->stat().centroid_, &r_ij);
|
|
la::SubInit(b->stat().centroid_, c->stat().centroid_, &r_jk);
|
|
la::SubInit(c->stat().centroid_, a->stat().centroid_, &r_ki);
|
|
if (boundary_ == PERIODIC){
|
|
AdjustVector_(&r_ij);
|
|
AdjustVector_(&r_jk);
|
|
AdjustVector_(&r_ki);
|
|
}
|
|
double AA, BB, CC, AB, AC, BC, coef1, coef2, coef3;
|
|
double cosines, denom;
|
|
// Extra Terms
|
|
double denom2, coef1b, coef2b, coef3b;
|
|
AA = la::Dot(r_ij, r_ij);
|
|
CC = la::Dot(r_ki, r_ki);
|
|
BB = la::Dot(r_jk, r_jk);
|
|
AC = la::Dot(r_ij, r_ki);
|
|
AB = la::Dot(r_ij, r_jk);
|
|
BC = la::Dot(r_ki, r_jk);
|
|
cosines = BC*AC*AB;
|
|
denom = AA*BB*CC;
|
|
denom2 = pow(denom, 3.5);
|
|
denom = pow(denom, 2.5);
|
|
denom = 3.0 * a->stat().axilrod_[0] * b->stat().axilrod_[0] *
|
|
c->stat().axilrod_[0] / denom;
|
|
denom = denom * time_step_;
|
|
Vector force_i, force_j, force_k;
|
|
coef1 = denom*(2.0*AB*AC + BC*BC - 5.0*cosines/AA);
|
|
coef2 = denom*(2.0*BC*AC + AB*AB - 5.0*cosines/CC);
|
|
coef3 = denom*(2.0*AB*BC + AC*AC - 5.0*cosines/BB);
|
|
la::ScaleInit(-coef1, r_ij, &force_i);
|
|
la::AddExpert( coef2, r_ki, &force_i);
|
|
la::ScaleInit( coef1, r_ij, &force_j);
|
|
la::AddExpert(-coef3, r_jk, &force_j);
|
|
|
|
// Extra Term stuff
|
|
denom2 = 5.0 * a->stat().axilrod_[1] * b->stat().axilrod_[1] *
|
|
c->stat().axilrod_[1] / denom2;
|
|
|
|
denom2 = denom2 * time_step_;
|
|
coef1b = denom2*(BC*AA + BC*BC + 3.0*AC*AB - 14.0*cosines/AA);
|
|
coef2b = denom2*(AB*CC + AB*AB + 3.0*AC*BC - 14.0*cosines/CC);
|
|
coef3b = denom2*(AC*BB + AC*AC + 3.0*BC*AB - 14.0*cosines/BB);
|
|
la::AddExpert(-coef1b, r_ij, &force_i);
|
|
la::AddExpert( coef2b, r_ki, &force_i);
|
|
la::AddExpert( coef1b, r_ij, &force_j);
|
|
la::AddExpert(-coef3b, r_jk, &force_j);
|
|
la::AddInit(force_i, force_j, &force_k);
|
|
la::Scale(-1.0, &force_k);
|
|
//Apply forces
|
|
a->stat().ApplyForce(force_i);
|
|
b->stat().ApplyForce(force_j);
|
|
c->stat().ApplyForce(force_k);
|
|
total_triples_++;
|
|
}
|
|
|
|
|
|
// Three body force between three atoms
|
|
void ThreeBodyForce_(int i, int j, int k){
|
|
Vector pos_i, pos_j, pos_k;
|
|
Vector r_ij, r_jk, r_ki;
|
|
atoms_.MakeColumnSubvector(i, 0, 3, &pos_i);
|
|
atoms_.MakeColumnSubvector(j, 0, 3, &pos_j);
|
|
atoms_.MakeColumnSubvector(k, 0, 3, &pos_k);
|
|
la::SubInit(pos_i, pos_j, &r_ij);
|
|
la::SubInit(pos_j, pos_k, &r_jk);
|
|
la::SubInit(pos_k, pos_i, &r_ki);
|
|
if (boundary_ == PERIODIC){
|
|
AdjustVector_(&r_ij);
|
|
AdjustVector_(&r_jk);
|
|
AdjustVector_(&r_ki);
|
|
}
|
|
double AA, BB, CC, AB, AC, BC, coef1, coef2, coef3;
|
|
double cosines, denom;
|
|
|
|
// Extra Terms
|
|
double denom2, coef1b, coef2b, coef3b;
|
|
AA = la::Dot(r_ij, r_ij);
|
|
CC = la::Dot(r_ki, r_ki);
|
|
BB = la::Dot(r_jk, r_jk);
|
|
AC = la::Dot(r_ij, r_ki);
|
|
AB = la::Dot(r_ij, r_jk);
|
|
BC = la::Dot(r_ki, r_jk);
|
|
if (prune_ == CUTOFF){
|
|
if (sqrt(AA) > cutoff_ || sqrt(BB) > cutoff_ || sqrt(CC) > cutoff_){
|
|
return;
|
|
}
|
|
if (sqrt(AA) > cutoff3_ & sqrt(BB) > cutoff3_ & sqrt(CC) > cutoff3_){
|
|
return;
|
|
}
|
|
}
|
|
cosines = BC*AC*AB;
|
|
denom = AA*BB*CC;
|
|
denom2 = pow(denom, 3.5);
|
|
denom = pow(denom, 2.5);
|
|
denom = 3.0 * axilrod_teller_.get(0,i) * axilrod_teller_.get(0,j) *
|
|
axilrod_teller_.get(0,k) / denom;
|
|
Vector force_i, force_j, force_k;
|
|
coef1 = denom*(2.0*AB*AC + BC*BC - 5.0*cosines/AA);
|
|
coef2 = denom*(2.0*BC*AC + AB*AB - 5.0*cosines/CC);
|
|
coef3 = denom*(2.0*AB*BC + AC*AC - 5.0*cosines/BB);
|
|
la::ScaleInit(-coef1, r_ij, &force_i);
|
|
la::AddExpert( coef2, r_ki, &force_i);
|
|
la::ScaleInit( coef1, r_ij, &force_j);
|
|
la::AddExpert(-coef3, r_jk, &force_j);
|
|
|
|
// Extra Term stuff
|
|
|
|
denom2 = 5.0 * axilrod_teller_.get(1, i) * axilrod_teller_.get(1, j) *
|
|
axilrod_teller_.get(1, k) / denom2;
|
|
coef1b = denom2*(BC*AA + BC*BC + 3.0*AC*AB - 14.0*cosines/AA);
|
|
coef2b = denom2*(AB*CC + AB*AB + 3.0*AC*BC - 14.0*cosines/CC);
|
|
coef3b = denom2*(AC*BB + AC*AC + 3.0*BC*AB - 14.0*cosines/BB);
|
|
la::AddExpert(-coef1b, r_ij, &force_i);
|
|
la::AddExpert( coef2b, r_ki, &force_i);
|
|
la::AddExpert( coef1b, r_ij, &force_j);
|
|
la::AddExpert(-coef3b, r_jk, &force_j);
|
|
la::AddInit(force_i, force_j, &force_k);
|
|
la::Scale(-1.0, &force_k);
|
|
//Apply forces
|
|
pos_i.Destruct();
|
|
pos_j.Destruct();
|
|
pos_k.Destruct();
|
|
atoms_.MakeColumnSubvector(i, 4, 3, &pos_i);
|
|
atoms_.MakeColumnSubvector(j, 4, 3, &pos_j);
|
|
atoms_.MakeColumnSubvector(k, 4, 3, &pos_k);
|
|
la::AddExpert(time_step_ / atoms_.get(3,i), force_i, &pos_i);
|
|
la::AddExpert(time_step_ / atoms_.get(3,j), force_j, &pos_j);
|
|
la::AddExpert(time_step_ / atoms_.get(3,k), force_k, &pos_k);
|
|
total_triples_++;
|
|
}
|
|
|
|
/**
|
|
* Force bounding functions
|
|
*/
|
|
|
|
|
|
double GetForceTerm_(double R, double r, double Rnorm, double rnorm,
|
|
int nu){
|
|
double result = Rnorm*(pow(1-r, -nu-1) -1) / pow(R, nu+2);
|
|
result = result + nu*rnorm / ((nu+2)*pow(R*(1-r), nu+2));
|
|
return result;
|
|
}
|
|
|
|
double GetForceTermPt_(double R, double r, double Rnorm, double rnorm,
|
|
int nu){
|
|
double result = Rnorm*(pow(1-r, -nu-1) -1 -(nu+1)*r) / pow(R, nu+2);
|
|
result = result + nu*rnorm / ((nu+2)*pow(R, nu+2))*(1/pow(1-r, nu+2)-1);
|
|
return result;
|
|
}
|
|
|
|
|
|
double GetPotentialTerm_(double R, double r, int nu){
|
|
double result;
|
|
if (unlikely(r >= 1)){
|
|
return BIG_BAD_NUMBER;
|
|
} else {
|
|
result = (1 / pow(1-r, nu)) /(nu*pow(R, nu));
|
|
return result;
|
|
}
|
|
}
|
|
|
|
double GetPotentialTermPt_(double R, double r, int nu){
|
|
double result;
|
|
if (unlikely(r >= 1)){
|
|
return BIG_BAD_NUMBER;
|
|
} else {
|
|
result = (1 / pow(1-r, nu) - nu*r) /(nu*pow(R, nu));
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
void GetForceRangeDual_(ParticleTree* query, ParticleTree* ref,
|
|
Vector* bounds){
|
|
double range_q = 0, range_r = 0;
|
|
double rnorm = 0, Rnorm = 0;
|
|
Vector delta;
|
|
la::SubInit(query->stat().centroid_, ref->stat().centroid_, &delta);
|
|
AdjustVector_(&delta);
|
|
double Rad = sqrt(la::Dot(delta, delta));
|
|
|
|
Vector node_r;
|
|
node_r.Init(3);
|
|
for (int i = 0; i < 3; i++){
|
|
node_r[i] = (query->bound().width(i, dimensions_[i]) +
|
|
ref->bound().width(i, dimensions_[i]))/ 2;
|
|
rnorm = rnorm + node_r[i];
|
|
Rnorm = Rnorm + fabs(delta[i]);
|
|
}
|
|
double rad = sqrt(la::Dot(node_r, node_r)) / Rad;
|
|
rnorm = rnorm / Rnorm;
|
|
|
|
for (int i = 0; i < forces_.n_rows(); i++){
|
|
int power = abs((int)powers_[i]);
|
|
double coef = fabs(ref->stat().interactions_[i].coef()*
|
|
query->stat().interactions_[i].coef()*
|
|
GetForceTerm_(Rad, rad, Rnorm, rnorm, power));
|
|
range_q = range_q + coef;
|
|
range_r = range_r + coef;
|
|
}
|
|
|
|
double rmin = 2.8;
|
|
double Rmin = sqrt(prdc::MinDistanceSqWrap(ref->bound(), query->bound(),
|
|
dimensions_));
|
|
for (int d = 0; d < 10; d++){
|
|
int a = abs((int)power3a_[d]);
|
|
int b = abs((int)power3b_[d]);
|
|
int c = abs((int)power3c_[d]);
|
|
double coef = fabs(ref->stat().axilrod_[0]*query->stat().axilrod_[0]*
|
|
a*signs3_[d]);
|
|
range_q += coef*(Rnorm*ref->stat().axilrod_[0]/(pow(Rmin, a+b+2)*
|
|
pow(rmin,c))+query->stat().axilrod_[0]/(pow(Rmin, b+c)*pow(rmin,a+1)));
|
|
range_r += coef*(Rnorm*query->stat().axilrod_[0]/(pow(Rmin, a+b+2)*
|
|
pow(rmin,c))+ref->stat().axilrod_[0]/(pow(Rmin, b+c)*pow(rmin, a+1)));
|
|
|
|
}
|
|
range_q = range_q / query->count();
|
|
range_r = range_r / ref->count();
|
|
|
|
Vector err;
|
|
err.Init(2);
|
|
err[0] = fabs(range_q);
|
|
err[1] = fabs(range_r);
|
|
|
|
la::ScaleOverwrite(time_step_, err, bounds);
|
|
}
|
|
|
|
|
|
void GetPotentialRangeDual_(ParticleTree* query, ParticleTree* ref,
|
|
Vector* bounds){
|
|
double range_q = 0, range_r = 0;
|
|
Vector delta;
|
|
la::SubInit(query->stat().centroid_, ref->stat().centroid_, &delta);
|
|
AdjustVector_(&delta);
|
|
double Rad = sqrt(la::Dot(delta, delta));
|
|
|
|
Vector node_r;
|
|
node_r.Init(3);
|
|
for (int i = 0; i < 3; i++){
|
|
node_r[i] = (query->bound().width(i, dimensions_[i]) +
|
|
ref->bound().width(i, dimensions_[i]))/ 2;
|
|
}
|
|
double rad = sqrt(la::Dot(node_r, node_r)) / Rad;
|
|
|
|
double coef;
|
|
for (int i = 0; i < forces_.n_rows(); i++){
|
|
int power = abs((int)powers_[i]);
|
|
coef = fabs(ref->stat().interactions_[i].coef()*
|
|
query->stat().interactions_[i].coef()*
|
|
GetPotentialTerm_(Rad, rad, power));
|
|
range_q = range_q + coef;
|
|
range_r = range_r + coef;
|
|
}
|
|
|
|
double rmin = 2.8;
|
|
double Rmin = sqrt(prdc::MinDistanceSqWrap(ref->bound(),
|
|
query->bound(), dimensions_));
|
|
coef = fabs(4*ref->stat().axilrod_[0]*query->stat().axilrod_[0] /
|
|
(pow(rmin, 3)*pow(Rmin, 6)));
|
|
range_q = range_q + coef*(ref->stat().axilrod_[0] + query->stat().axilrod_[0]/2.0);
|
|
range_r = range_r + coef*(query->stat().axilrod_[0] + ref->stat().axilrod_[0]/2.0);
|
|
range_q = range_q / query->count();
|
|
range_r = range_r / ref->count();
|
|
|
|
Vector err;
|
|
err.Init(2);
|
|
err[0] = fabs(range_q);
|
|
err[1] = fabs(range_r);
|
|
|
|
la::ScaleOverwrite(time_step_, err, bounds);
|
|
}
|
|
|
|
void GetForceRangeTriple_(ParticleTree* i, ParticleTree* j, ParticleTree *k,
|
|
Vector* bounds){
|
|
double range_i = 0, range_j = 0, range_k = 0;
|
|
double Rij, Rjk, Rki, rij, rki, rjk, ri, rj, rk;
|
|
double rnij = 0, rnjk = 0, rnki = 0, Rnij = 0, Rnjk = 0, Rnki = 0;
|
|
Vector delta_ij, delta_jk, delta_ki;
|
|
la::SubInit(i->stat().centroid_, j->stat().centroid_, &delta_ij);
|
|
la::SubInit(j->stat().centroid_, k->stat().centroid_, &delta_jk);
|
|
la::SubInit(k->stat().centroid_, i->stat().centroid_, &delta_ki);
|
|
AdjustVector_(&delta_ij);
|
|
AdjustVector_(&delta_jk);
|
|
AdjustVector_(&delta_ki);
|
|
Rij = sqrt(la::Dot(delta_ij, delta_ij));
|
|
Rjk = sqrt(la::Dot(delta_jk, delta_jk));
|
|
Rki = sqrt(la::Dot(delta_ki, delta_ki));
|
|
|
|
Vector bi, bj, bk;
|
|
bi.Init(3);
|
|
bj.Init(3);
|
|
bk.Init(3);
|
|
for (int d = 0; d < 3; d++){
|
|
bi[d] = i->bound().width(d, dimensions_[d]) / 2;
|
|
bj[d] = j->bound().width(d, dimensions_[d]) / 2;
|
|
bk[d] = k->bound().width(d, dimensions_[d]) / 2;
|
|
rnij = rnij + bi[d] + bj[d];
|
|
rnjk = rnjk + bj[d] + bk[d];
|
|
rnki = rnki + bk[d] + bi[d];
|
|
Rnij = Rnij + fabs(delta_ij[d]);
|
|
Rnjk = Rnjk + fabs(delta_jk[d]);
|
|
Rnki = Rnki + fabs(delta_ki[d]);
|
|
}
|
|
ri = la::Dot(bi, bi);
|
|
rj = la::Dot(bj, bj);
|
|
rk = la::Dot(bk, bk);
|
|
rij = sqrt(ri+rj)/Rij;
|
|
rjk = sqrt(rj+rk)/Rjk;
|
|
rki = sqrt(rk+ri)/Rki;
|
|
|
|
for (int d = 0; d < 10; d++){
|
|
int a = abs((int)power3a_[d]);
|
|
int b = abs((int)power3b_[d]);
|
|
int c = abs((int)power3c_[d]);
|
|
double coef = i->stat().axilrod_[0]*j->stat().axilrod_[0]*
|
|
k->stat().axilrod_[0]*signs3_[d];
|
|
range_i += coef*GetPotentialTermPt_(Rjk, rjk, b)*
|
|
(GetForceTerm_(Rij, rij, Rnij, rnij,a)*GetPotentialTerm_(Rki, rki,c)+
|
|
GetForceTerm_(Rki, rki, Rnki, rnki,c)*GetPotentialTerm_(Rij, rij,a));
|
|
range_j+= coef*GetPotentialTermPt_(Rki, rki, c)*
|
|
(GetForceTerm_(Rij, rij, Rnij, rnij,a)*GetPotentialTerm_(Rjk, rjk,b)+
|
|
GetForceTerm_(Rjk, rjk, Rnjk, rnjk,b)*GetPotentialTerm_(Rij, rij,a));
|
|
range_k+= coef*GetPotentialTermPt_(Rij, rij, a)*
|
|
(GetForceTerm_(Rjk, rjk, Rnjk, rnjk,b)*GetPotentialTerm_(Rki, rki,c)+
|
|
GetForceTerm_(Rki, rki, Rnki, rnki,c)*GetPotentialTerm_(Rjk, rjk,b));
|
|
}
|
|
range_i = range_i / i->count();
|
|
range_j = range_j / j->count();
|
|
range_k = range_k / k->count();
|
|
Vector err;
|
|
err.Init(3);
|
|
|
|
err[0] = fabs(range_i);
|
|
err[1] = fabs(range_j);
|
|
err[2] = fabs(range_k);
|
|
la::ScaleOverwrite(time_step_, err, bounds);
|
|
}
|
|
|
|
void GetPotentialRangeTriple_(ParticleTree* i, ParticleTree* j, ParticleTree *k,
|
|
Vector* bounds){
|
|
double range_i = 0, range_j = 0, range_k = 0;
|
|
double Rij, Rjk, Rki, rij, rki, rjk;
|
|
|
|
Vector delta_ij, delta_jk, delta_ki;
|
|
la::SubInit(i->stat().centroid_, j->stat().centroid_, &delta_ij);
|
|
la::SubInit(j->stat().centroid_, k->stat().centroid_, &delta_jk);
|
|
la::SubInit(k->stat().centroid_, i->stat().centroid_, &delta_ki);
|
|
AdjustVector_(&delta_ij);
|
|
AdjustVector_(&delta_jk);
|
|
AdjustVector_(&delta_ki);
|
|
Rij = sqrt(la::Dot(delta_ij, delta_ij));
|
|
Rjk = sqrt(la::Dot(delta_jk, delta_jk));
|
|
Rki = sqrt(la::Dot(delta_ki, delta_ki));
|
|
|
|
Vector bi, bj, bk;
|
|
bi.Init(3);
|
|
bj.Init(3);
|
|
bk.Init(3);
|
|
for (int d = 0; d < 3; d++){
|
|
bi[d] = i->bound().width(d, dimensions_[d]) / 2;
|
|
bj[d] = j->bound().width(d, dimensions_[d]) / 2;
|
|
bk[d] = k->bound().width(d, dimensions_[d]) / 2;
|
|
}
|
|
la::AddOverwrite(bi, bj, &delta_ij);
|
|
la::AddOverwrite(bj, bk, &delta_jk);
|
|
la::AddOverwrite(bk, bi, &delta_ki);
|
|
rij = sqrt(la::Dot(delta_ij, delta_ij))/Rij;
|
|
rjk = sqrt(la::Dot(delta_jk, delta_jk))/Rjk;
|
|
rki = sqrt(la::Dot(delta_ki, delta_ki))/Rki;
|
|
delta_ij[0] = GetPotentialTerm_(Rij, rij, 3);
|
|
delta_ij[1] = GetPotentialTermPt_(Rij, rij, 3);
|
|
delta_jk[0] = GetPotentialTerm_(Rjk, rjk, 3);
|
|
delta_jk[1] = GetPotentialTermPt_(Rjk, rjk, 3);
|
|
delta_ki[0] = GetPotentialTerm_(Rki, rki, 3);
|
|
delta_ki[1] = GetPotentialTermPt_(Rki, rki, 3);
|
|
|
|
|
|
double coef = i->stat().axilrod_[0]*j->stat().axilrod_[0]*
|
|
k->stat().axilrod_[0]*6;
|
|
range_i = coef*delta_ij[0]*delta_ki[0]*delta_jk[1];
|
|
range_j = coef*delta_ij[0]*delta_ki[1]*delta_jk[0];
|
|
range_k = coef*delta_ij[1]*delta_ki[0]*delta_jk[0];
|
|
|
|
range_i = range_i / i->count();
|
|
range_j = range_j / j->count();
|
|
range_k = range_k / k->count();
|
|
Vector err;
|
|
err.Init(3);
|
|
|
|
err[0] = fabs(range_i);
|
|
err[1] = fabs(range_j);
|
|
err[2] = fabs(range_k);
|
|
la::ScaleOverwrite(time_step_, err, bounds);
|
|
}
|
|
|
|
|
|
/**
|
|
* Routines for calling force evaluations
|
|
*/
|
|
|
|
// These forces will always be between disjoint nodes
|
|
void EvaluateLeafForcesThree_(ParticleTree* query, ParticleTree* ref1,
|
|
ParticleTree* ref2){
|
|
for(int i = query->begin(); i < query->begin() + query->count(); i++){
|
|
for(int j = ref1->begin(); j < ref1->count() + ref1->begin(); j++){
|
|
for(int k = ref2->begin(); k < ref2->count() + ref2->begin(); k++){
|
|
ThreeBodyForce_(i,j,k);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// This will also cover overlap cases near the diagonal,
|
|
// so query and ref may be the same node. We can evalute three
|
|
// body forces between these two nodes by considering each triple.
|
|
void EvaluateLeafForcesDual_(ParticleTree* query, ParticleTree* ref){
|
|
// Two and Three Body
|
|
for(int i = query->begin(); i < query->begin()+query->count(); i++){
|
|
for(int j = ref->begin(); j < ref->count() + ref->begin(); j++){
|
|
TwoBodyForce_(i,j);
|
|
for(int k = j+1; k < ref->count() + ref->begin(); k++){
|
|
ThreeBodyForce_(i,j,k);
|
|
}
|
|
for(int k = i+1; k <query->begin() + query->count(); k++){
|
|
ThreeBodyForce_(i,j,k);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void EvaluateLeafForcesSame_(ParticleTree* query){
|
|
for (int i = query->begin(); i < query->begin() + query->count(); i++){
|
|
for (int j = i+1; j < query->begin() + query->count(); j++){
|
|
TwoBodyForce_(i,j);
|
|
for (int k = j+1; k < query->begin() + query->count(); k++){
|
|
ThreeBodyForce_(i,j,k);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// End Force Evaluation Routines.
|
|
|
|
|
|
|
|
void SplitDual_(ParticleTree* query, ParticleTree* ref, ForceError* err_q,
|
|
ForceError* err_r){
|
|
ForceError err_q2;
|
|
err_q2.Copy(err_q);
|
|
double d1, d2;
|
|
if (boundary_ == PERIODIC){
|
|
d1 = prdc::MinDistanceSqWrap(ref->bound(), query->left()->bound(),
|
|
dimensions_);
|
|
d2 = prdc::MinDistanceSqWrap(ref->bound(), query->right()->bound(),
|
|
dimensions_);
|
|
} else {
|
|
d1 = ref->bound().MinDistanceSq(query->left()->bound());
|
|
d2 = ref->bound().MinDistanceSq(query->right()->bound());
|
|
}
|
|
if (d1 > d2){
|
|
UpdateMomentumDual_(query->left(), ref, err_q, err_r);
|
|
UpdateMomentumDual_(query->right(), ref, &err_q2, err_r);
|
|
} else {
|
|
UpdateMomentumDual_(query->right(), ref, &err_q2, err_r);
|
|
UpdateMomentumDual_(query->left(), ref, err_q, err_r);
|
|
}
|
|
UpdateMomentumThree_(query->left(), query->right(), ref,
|
|
err_q, &err_q2, err_r);
|
|
err_q->Merge(err_q2);
|
|
}
|
|
|
|
void SplitThree_(ParticleTree* query, ParticleTree* ref1, ParticleTree* ref2,
|
|
ForceError* err_q, ForceError* err_r1, ForceError* err_r2){
|
|
ForceError err_q2;
|
|
err_q2.Copy(err_q);
|
|
double d1, d2;
|
|
if (boundary_ == PERIODIC){
|
|
d1 = prdc::MinDistanceSqWrap(ref1->bound(), query->left()->bound(),dimensions_)*
|
|
prdc::MinDistanceSqWrap(ref2->bound(), query->left()->bound(),dimensions_);
|
|
d2 = prdc::MinDistanceSqWrap(ref1->bound(), query->right()->bound(),dimensions_)*
|
|
prdc::MinDistanceSqWrap(ref2->bound(), query->right()->bound(),dimensions_);
|
|
} else {
|
|
d1 = ref1->bound().MinDistanceSq(query->left()->bound())*
|
|
ref2->bound().MinDistanceSq(query->left()->bound());
|
|
d2 = ref1->bound().MinDistanceSq(query->right()->bound())*
|
|
ref2->bound().MinDistanceSq(query->right()->bound());
|
|
}
|
|
if (d1 > d2){
|
|
UpdateMomentumThree_(query->left(), ref1, ref2, err_q, err_r1, err_r2);
|
|
UpdateMomentumThree_(query->right(),ref1, ref2, &err_q2, err_r1, err_r2);
|
|
} else {
|
|
UpdateMomentumThree_(query->right(),ref1, ref2, &err_q2, err_r1, err_r2);
|
|
UpdateMomentumThree_(query->left(), ref1, ref2, err_q, err_r1, err_r2);
|
|
}
|
|
err_q->Merge(err_q2);
|
|
}
|
|
|
|
|
|
|
|
int GetPrune_(ParticleTree* i, ParticleTree* j, ParticleTree* k,
|
|
ForceError* err_i, ForceError* err_j , ForceError* err_k){
|
|
int result = 0;
|
|
if (prune_ == CUTOFF){
|
|
double a_min, b_min, c_min;
|
|
if (boundary_ == PERIODIC){
|
|
a_min = sqrt(prdc::MinDistanceSqWrap(i->bound(), j->bound(),dimensions_));
|
|
b_min = sqrt(prdc::MinDistanceSqWrap(j->bound(), k->bound(),dimensions_));
|
|
c_min = sqrt(prdc::MinDistanceSqWrap(k->bound(), i->bound(),dimensions_));
|
|
} else {
|
|
a_min = sqrt(i->bound().MinDistanceSq(j->bound()));
|
|
b_min = sqrt(j->bound().MinDistanceSq(k->bound()));
|
|
c_min = sqrt(k->bound().MinDistanceSq(i->bound()));
|
|
}
|
|
result = (a_min > cutoff_)+(b_min > cutoff_)+(c_min > cutoff_);
|
|
result = result + (a_min > cutoff3_)*(b_min > cutoff3_)*(c_min > cutoff3_);
|
|
} else {
|
|
Vector range;
|
|
range.Init(3);
|
|
int c1, c2, c3;
|
|
c1 = j->count() * k->count();
|
|
c2 = k->count() * i->count();
|
|
c3 = i->count() * j->count();
|
|
if (prune_ == POTENTIAL){
|
|
GetPotentialRangeTriple_(i,j,k, &range);
|
|
} else {
|
|
GetForceRangeTriple_(i,j,k, &range);
|
|
}
|
|
result = err_i->Check(range[0], c1) * err_j->Check(range[1], c2) *
|
|
err_k->Check(range[2], c3);
|
|
if (result > 0){
|
|
ThreeBodyForce_(i, j, k);
|
|
err_i->AddVisited(range[0], c1);
|
|
err_j->AddVisited(range[1], c2);
|
|
err_k->AddVisited(range[2], c3);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
int GetPrune_(ParticleTree* i, ParticleTree* j,
|
|
ForceError* err_i, ForceError* err_j){
|
|
int result = 0;
|
|
if (prune_ == CUTOFF){
|
|
double a_min;
|
|
if (boundary_ == PERIODIC){
|
|
a_min = sqrt(prdc::MinDistanceSqWrap(i->bound(), j->bound(),dimensions_));
|
|
} else {
|
|
a_min = sqrt(i->bound().MinDistanceSq(j->bound()));
|
|
}
|
|
result = (a_min > cutoff_);
|
|
} else {
|
|
Vector range;
|
|
range.Init(2);
|
|
int c1, c2;
|
|
c1 = j->count() * (i->count()-1 + (j->count()-1)/2);
|
|
c2 = i->count() * (j->count()-1 + (i->count()-1)/2);
|
|
if (prune_ == POTENTIAL){
|
|
GetPotentialRangeDual_(i,j,&range);
|
|
} else {
|
|
GetForceRangeDual_(i,j,&range);
|
|
}
|
|
result = err_i->Check(range[0], c1) * err_j->Check(range[1], c2);
|
|
if (result > 0){
|
|
TwoBodyForce_(i, j);
|
|
err_i->AddVisited(range[0], c1);
|
|
err_j->AddVisited(range[1], c2);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Momentum updating routines.
|
|
*/
|
|
void UpdateMomentumDual_(ParticleTree* query, ParticleTree* ref,
|
|
ForceError* err_q, ForceError* err_r){
|
|
if (GetPrune_(query, ref, err_q, err_r) == 0){
|
|
// Or do we recurse down further?
|
|
int a,b;
|
|
a = query->count();
|
|
b = ref->count();
|
|
if (a >= b & !(query->is_leaf())){
|
|
SplitDual_(query, ref, err_q, err_r);
|
|
} else {
|
|
if (!(ref->is_leaf())){
|
|
SplitDual_(ref, query, err_r, err_q);
|
|
} else {
|
|
// Base Case
|
|
EvaluateLeafForcesDual_(query, ref);
|
|
// Update Error Terms
|
|
int c1, c2;
|
|
c1 = ref->count() * (query->count()-1 + (ref->count()-1)/2);
|
|
c2 = query->count() * (ref->count()-1 + (query->count()-1)/2);
|
|
err_r->AddVisited(0, c2);
|
|
err_q->AddVisited(0, c1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void UpdateMomentumMain_(ParticleTree* query, ForceError* err_q){
|
|
if (!query->is_leaf()){
|
|
ForceError err_q2;
|
|
err_q2.Copy(err_q);
|
|
UpdateMomentumMain_(query->left(), err_q);
|
|
UpdateMomentumMain_(query->right(), &err_q2);
|
|
UpdateMomentumDual_(query->left(), query->right(), err_q, &err_q2);
|
|
err_q->Merge(err_q2);
|
|
} else {
|
|
EvaluateLeafForcesSame_(query);
|
|
err_q->AddVisited(0, (query->count()-1.0)*(query->count()-2.0)/2.0);
|
|
}
|
|
}
|
|
|
|
|
|
void UpdateMomentumThree_(ParticleTree* query, ParticleTree* ref1,
|
|
ParticleTree* ref2, ForceError* err_q,
|
|
ForceError* err_r1, ForceError* err_r2){
|
|
if (GetPrune_(query, ref1, ref2, err_q, err_r1, err_r2) == 0) {
|
|
// Or do we recurse down further?
|
|
int a,b,c;
|
|
a = query->count();
|
|
b = ref1->count();
|
|
c = ref2->count();
|
|
if (a >= b & a >= c & !(query->is_leaf())){
|
|
SplitThree_(query, ref1, ref2, err_q, err_r1, err_r2);
|
|
} else {
|
|
if (b >= c & !(ref1->is_leaf())){
|
|
SplitThree_(ref1, query, ref2, err_r1, err_q, err_r2);
|
|
} else {
|
|
if (!(ref2->is_leaf())){
|
|
SplitThree_(ref2, query, ref1, err_r2, err_q, err_r1);
|
|
} else {
|
|
// Base Case
|
|
int c1, c2, c3;
|
|
c1 = ref1->count()*ref2->count();
|
|
c2 = query->count()*ref2->count();
|
|
c3 = query->count()*ref1->count();
|
|
|
|
EvaluateLeafForcesThree_(query, ref1, ref2);
|
|
err_q->AddVisited(0, c1);
|
|
err_r1->AddVisited(0, c2);
|
|
err_r2->AddVisited(0, c3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void UpdateMomentumNaive_(){
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
for (int j = i+1; j < n_atoms_; j++){
|
|
TwoBodyForce_(i,j);
|
|
for(int k = j+1; k < n_atoms_; k++){
|
|
ThreeBodyForce_(i, j, k);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// End momentum udpating routines.
|
|
|
|
|
|
|
|
/**
|
|
* Position Updating functions
|
|
*/
|
|
|
|
/**
|
|
* Update centroids and bounding boxes. Note that we may develop
|
|
* intersections between bounding boxes as the simulation progresses.
|
|
* The velocity of parent nodes is passed down the tree to the leaves.
|
|
*/
|
|
void UpdatePositionsRecursion_(ParticleTree* node, Vector* vel){
|
|
if (likely(!node->is_leaf())){
|
|
Vector temp1;
|
|
Vector temp2;
|
|
temp1.Init(3);
|
|
temp2.Init(3);
|
|
node->stat().GetVelocity(&temp1);
|
|
node->stat().GetVelocity(&temp2);
|
|
la::AddTo(*vel, &temp1);
|
|
la::AddTo(*vel, &temp2);
|
|
UpdatePositionsRecursion_(node->left(), &temp1);
|
|
UpdatePositionsRecursion_(node->right(), &temp2);
|
|
node->stat().UpdateCentroid(node->left()->stat(),node->right()->stat());
|
|
node->bound().Reset();
|
|
node->bound() |= node->left()->bound();
|
|
node->bound() |= node->right()->bound();
|
|
} else { // Base Case
|
|
node->bound().Reset();
|
|
Vector node_pos, node_vel;
|
|
node_pos.Init(3);
|
|
node_pos.SetZero();
|
|
node_vel.Init(3);
|
|
node_vel.SetZero();
|
|
for(int i = node->begin(); i < node->begin() + node->count(); i++){
|
|
Vector pos, temp;
|
|
atoms_.MakeColumnSubvector(i, 4, 3, &temp);
|
|
la::AddTo(*vel, &temp);
|
|
la::AddExpert(atoms_.get(3, i), temp, &node_vel);
|
|
atoms_.MakeColumnSubvector(i, 0, 3, &pos);
|
|
la::AddExpert(time_step_, temp, &pos);
|
|
la::AddExpert(atoms_.get(3, i), pos, &node_pos);
|
|
node->bound() |= pos;
|
|
}
|
|
node->stat().UpdateKinematics(node_pos, node_vel);
|
|
}
|
|
}
|
|
|
|
void UpdatePositionsNaive_(){
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
Vector pos, temp;
|
|
atoms_.MakeColumnSubvector(i, 4, 3, &temp);
|
|
atoms_.MakeColumnSubvector(i, 0, 3, &pos);
|
|
la::AddExpert(time_step_, temp, &pos);
|
|
}
|
|
}
|
|
|
|
void InitializeATStats_(ParticleTree* node){
|
|
if (!node->is_leaf()){
|
|
InitializeATStats_(node->left());
|
|
InitializeATStats_(node->right());
|
|
node->stat().MergeAT(node->left()->stat(), node->right()->stat());
|
|
} else {
|
|
node->stat().InitAT(&axilrod_teller_);
|
|
}
|
|
}
|
|
|
|
void UpdateAtomPosition_(int atom){
|
|
Vector pos, vel;
|
|
atoms_.MakeColumnSubvector(atom, 0, 3, &pos);
|
|
atoms_.MakeColumnSubvector(atom, 4, 3, &vel);
|
|
la::AddExpert(time_step_, vel, &pos);
|
|
}
|
|
|
|
// End position updating functions
|
|
|
|
|
|
void InitializeTreeStats_(ParticleTree* node){
|
|
if (!node->is_leaf()){
|
|
InitializeTreeStats_(node->left());
|
|
InitializeTreeStats_(node->right());
|
|
node->stat().MergeStats(node->left()->stat(),node->right()->stat());
|
|
} else{
|
|
node->stat().InitStats(atoms_, forces_, powers_);
|
|
}
|
|
}
|
|
|
|
void AdjustVector_(Vector* vector_in){
|
|
for(int i = 0; i < 3; i++){
|
|
(*vector_in)[i] = (*vector_in)[i] - dimensions_[i]*
|
|
floor((*vector_in)[i] / dimensions_[i] +0.5);
|
|
}
|
|
}
|
|
|
|
void AdjustVector_(Vector& vector_in, Vector& diff){
|
|
for(int i = 0; i < 3; i++){
|
|
diff[i] = -floor(vector_in[i] / dimensions_[i] +0.5);
|
|
vector_in[i] = vector_in[i] + dimensions_[i]*diff[i];
|
|
}
|
|
}
|
|
|
|
|
|
void RaddistInternal_(RadDist* raddist, ParticleTree* query){
|
|
if (query->is_leaf()) {
|
|
RaddistInternalLeaf_(raddist, query);
|
|
} else {
|
|
RaddistExternal_(raddist, query->left(), query->right());
|
|
RaddistInternal_(raddist, query->left());
|
|
RaddistInternal_(raddist, query->right());
|
|
}
|
|
}
|
|
|
|
void RaddistExternal_(RadDist* raddist, ParticleTree* query,
|
|
ParticleTree* ref){
|
|
double r_min;
|
|
if (boundary_ == PERIODIC){
|
|
r_min = sqrt(prdc::MinDistanceSqWrap(query->bound(), ref->bound(),
|
|
dimensions_));
|
|
} else {
|
|
r_min = sqrt(query->bound().MinDistanceSq(ref->bound()));
|
|
}
|
|
if (raddist->GetMax() < r_min){
|
|
return;
|
|
} else {
|
|
// Recurse further
|
|
if (query->left() != NULL){
|
|
if (ref->left() != NULL){
|
|
RaddistExternal_(raddist, query->left(), ref->left());
|
|
RaddistExternal_(raddist, query->left(),ref->right());
|
|
RaddistExternal_(raddist, query->right(),ref->left());
|
|
RaddistExternal_(raddist, query->right(),ref->right());
|
|
} else {
|
|
RaddistExternal_(raddist, query->left(), ref);
|
|
RaddistExternal_(raddist, query->right(), ref);
|
|
}
|
|
} else{
|
|
if (ref->left() != NULL){
|
|
RaddistExternal_(raddist, query, ref->left());
|
|
RaddistExternal_(raddist, query, ref->right());
|
|
} else {
|
|
RaddistExternalLeaf_(raddist, query, ref);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RaddistInternalLeaf_(RadDist* raddist, ParticleTree* query){
|
|
for (int i = query->begin(); i < query->begin() + query->count(); i++){
|
|
for (int j = i+1; j < query->begin() + query->count(); j++){
|
|
Vector left_vec, right_vec, delta_r;
|
|
atoms_.MakeColumnSubvector(i, 0, 3, &left_vec);
|
|
atoms_.MakeColumnSubvector(j, 0, 3, &right_vec);
|
|
la::SubInit(right_vec, left_vec, &delta_r);
|
|
if (boundary_ == PERIODIC){
|
|
AdjustVector_(&delta_r);
|
|
}
|
|
double dist = sqrt(la::Dot(delta_r, delta_r));
|
|
raddist->Add(dist);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RaddistExternalLeaf_(RadDist* raddist, ParticleTree* query,
|
|
ParticleTree* ref){
|
|
for (int i = query->begin(); i < query->begin() + query->count(); i++){
|
|
for (int j = ref->begin(); j < ref->begin() + ref->count(); j++){
|
|
Vector left_vec, right_vec, delta_r;
|
|
atoms_.MakeColumnSubvector(i, 0, 3, &left_vec);
|
|
atoms_.MakeColumnSubvector(j, 0, 3, &right_vec);
|
|
la::SubInit(right_vec, left_vec, &delta_r);
|
|
if (boundary_ == PERIODIC){
|
|
AdjustVector_(&delta_r);
|
|
}
|
|
double dist = sqrt(la::Dot(delta_r, delta_r));
|
|
raddist->Add(dist);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////// Constructors ////////////////////////////////
|
|
|
|
FORBID_ACCIDENTAL_COPIES(MultiPhysicsSystem);
|
|
|
|
public:
|
|
|
|
MultiPhysicsSystem(){
|
|
query_ = NULL;
|
|
}
|
|
|
|
|
|
~MultiPhysicsSystem(){
|
|
delete system_;
|
|
}
|
|
|
|
////////////////////////////// Public Functions ////////////////////////////
|
|
|
|
void Init(const Matrix& atoms_in, struct datanode* param){
|
|
atoms_.Copy(atoms_in);
|
|
n_atoms_ = atoms_.n_cols();
|
|
diffusion_.Init(3, n_atoms_);
|
|
diffusion_.SetZero();
|
|
force_bound_ = fx_param_double(param, "force_bound", 1.0e-3);
|
|
leaf_size_ = fx_param_int(param, "leaf", 4);
|
|
Vector dims;
|
|
dims.Init(3);
|
|
dims[0] = 0;
|
|
dims[1] = 1;
|
|
dims[2] = 2;
|
|
boundary_ = fx_param_int(param, "bc", PERIODIC);
|
|
dimensions_.Init(3);
|
|
dimensions_[0] = fx_param_double(param, "lx", 60);
|
|
dimensions_[1] = fx_param_double(param, "ly", 60);
|
|
dimensions_[2] = fx_param_double(param, "lz", 60);
|
|
system_ = tree::MakeKdTreeMidpointSelective<ParticleTree>(atoms_, dims,
|
|
leaf_size_, &new_from_old_map_, &old_from_new_map_);
|
|
cutoff_ = fx_param_double(param, "cutoff", -1);
|
|
cutoff3_ = pow(cutoff_, 4)*pow(2.7, 5);
|
|
cutoff3_ = pow(10*cutoff3_, 1.0/9.0);
|
|
printf("Cutoff 3: %f \n", cutoff3_);
|
|
n_trips_ = n_atoms_-1;
|
|
n_trips_ = n_trips_*(n_atoms_-2) / 2;
|
|
prune_ = fx_param_int(param, "prune", FORCE);
|
|
} //Init
|
|
|
|
|
|
|
|
/**
|
|
* Naive implementation computes all pairwise interactions, and can be
|
|
* used to validate approximations made by tree implementation.
|
|
*/
|
|
void InitNaive(const Matrix& atoms_in, struct datanode* param){
|
|
atoms_.Copy(atoms_in);
|
|
diffusion_.Init(0,0);
|
|
n_atoms_ = atoms_.n_cols();
|
|
boundary_ = fx_param_int(param, "bc", FREE);
|
|
dimensions_.Init(3);
|
|
dimensions_[0] = fx_param_double(param, "lx", 60);
|
|
dimensions_[1] = fx_param_double(param, "ly", 60);
|
|
dimensions_[2] = fx_param_double(param, "lz", 60);
|
|
// system_ = NULL;
|
|
old_from_new_map_.Init(0);
|
|
new_from_old_map_.Init(0);
|
|
} // InitNaive
|
|
|
|
|
|
|
|
/**
|
|
* Used to initialize electrostatics parameters, for both naive and
|
|
* tree-based instances of physics system.
|
|
*/
|
|
void InitStats(const Matrix& stats_in, const Vector& signs_in,
|
|
const Vector& power_in){
|
|
powers_.Init(stats_in.n_rows());
|
|
signs_.Init(stats_in.n_rows());
|
|
powers_.CopyValues(power_in);
|
|
signs_.CopyValues(signs_in);
|
|
if (&system_ != NULL){
|
|
forces_.Init(stats_in.n_rows(), stats_in.n_cols());
|
|
// Reindex cols of stats matrix.
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
int k = old_from_new_map_[i];
|
|
for (int j = 0; j < forces_.n_rows(); j++){
|
|
forces_.set(j, k, stats_in.get(j, i));
|
|
}
|
|
}
|
|
InitializeTreeStats_(system_);
|
|
} else {
|
|
forces_.Copy(stats_in);
|
|
}
|
|
}
|
|
|
|
void ReinitStats(const Matrix& stats_in){
|
|
forces_.Destruct();
|
|
forces_.Init(stats_in.n_rows(), stats_in.n_cols());
|
|
// Reindex cols of stats matrix.
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
int k = old_from_new_map_[i];
|
|
for (int j = 0; j < forces_.n_rows(); j++){
|
|
forces_.set(j, k, stats_in.get(j, i));
|
|
}
|
|
}
|
|
InitializeTreeStats_(system_);
|
|
}
|
|
|
|
|
|
|
|
void RadialDistribution(RadDist* raddist){
|
|
RaddistInternal_(raddist, system_);
|
|
raddist->Scale(2.0 / n_atoms_);
|
|
}
|
|
|
|
void InitAxilrodTeller(const Matrix& stats_in, const Matrix& powers){
|
|
// Reindex cols of stats matrix.
|
|
if (system_ != NULL){
|
|
axilrod_teller_.Init(2, n_atoms_);
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
int k = old_from_new_map_[i];
|
|
for (int j = 0; j < 2; j++){
|
|
axilrod_teller_.set(j, k, stats_in.get(j, i));
|
|
}
|
|
}
|
|
InitializeATStats_(system_);
|
|
} else {
|
|
axilrod_teller_.Copy(stats_in);
|
|
}
|
|
powers.MakeColumnVector(0, &signs3_);
|
|
powers.MakeColumnVector(1, &power3a_);
|
|
powers.MakeColumnVector(2, &power3b_);
|
|
powers.MakeColumnVector(3, &power3c_);
|
|
}
|
|
|
|
void ReinitAxilrodTeller(const Matrix& stats_in, const Matrix& powers){
|
|
// Reindex cols of stats matrix.
|
|
if (system_ != NULL){
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
int k = old_from_new_map_[i];
|
|
for (int j = 0; j < 2; j++){
|
|
axilrod_teller_.set(j, k, stats_in.get(j, i));
|
|
}
|
|
}
|
|
InitializeATStats_(system_);
|
|
} else {
|
|
axilrod_teller_.Copy(stats_in);
|
|
}
|
|
}
|
|
|
|
|
|
double GetPercent(){
|
|
return percent_pruned_;
|
|
}
|
|
|
|
int GetTrips(){
|
|
return total_triples_;
|
|
}
|
|
|
|
|
|
void UpdatePositions(double time_step_in){
|
|
time_step_ = time_step_in;
|
|
Vector temp;
|
|
temp.Init(3);
|
|
temp.SetZero();
|
|
if (system_ != NULL){
|
|
UpdatePositionsRecursion_(system_, &temp);
|
|
} else {
|
|
UpdatePositionsNaive_();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void RebuildTree(){
|
|
ArrayList<int> temp_old_new, temp_new_old;
|
|
Vector dims;
|
|
dims.Init(3);
|
|
dims[0] = 0;
|
|
dims[1] = 1;
|
|
dims[2] = 2;
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
Vector pos, temp, diff;
|
|
atoms_.MakeColumnSubvector(i, 0, 3, &pos);
|
|
temp.Init(3);
|
|
AdjustVector_(pos, temp);
|
|
diffusion_.MakeColumnVector(i, &diff);
|
|
la::AddTo(temp, &diff);
|
|
}
|
|
system_ = tree::MakeKdTreeMidpointSelective<ParticleTree>(atoms_, dims,
|
|
leaf_size_, &temp_new_old, &temp_old_new);
|
|
Matrix old_diff;
|
|
old_diff.Init(3, n_atoms_);
|
|
old_diff.CopyValues(diffusion_);
|
|
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
for (int j = 0; j < 3; j++){
|
|
int k = temp_old_new[i];
|
|
diffusion_.set(j, k, old_diff.get(j,i));
|
|
}
|
|
}
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
old_from_new_map_[i] = temp_old_new[old_from_new_map_[i]];
|
|
}
|
|
}
|
|
|
|
double ComputePressure(){
|
|
// printf("Virial: %f \n", virial_);
|
|
double pressure = (n_atoms_*temperature_ + virial_) /
|
|
(3.0*dimensions_[0]*dimensions_[1]*dimensions_[2]);
|
|
return pressure;
|
|
}
|
|
|
|
|
|
void UpdateMomentum(double time_step_in){
|
|
max_force_ = 0;
|
|
virial_ = 0;
|
|
time_step_ = time_step_in;
|
|
if (system_ != NULL){
|
|
total_triples_ = 0;
|
|
range_evals_ = 0;
|
|
percent_pruned_ = 0;
|
|
ForceError error_main;
|
|
error_main.Init(force_bound_, n_trips_);
|
|
UpdateMomentumMain_(system_, &error_main);
|
|
printf("Pairs: %4.0f Triples: %d \n", percent_pruned_, total_triples_);
|
|
percent_pruned_ = 1.0-2*percent_pruned_/(n_atoms_*n_atoms_ - n_atoms_);
|
|
} else {
|
|
UpdateMomentumNaive_();
|
|
}
|
|
} //UpdateMomentum
|
|
|
|
|
|
// Compute the average kinetic energy of the particles
|
|
double ComputeTemperature(){
|
|
temperature_ = 0;
|
|
double ke;
|
|
for(int i = 0; i < n_atoms_; i++){
|
|
Vector vel;
|
|
atoms_.MakeColumnSubvector(i, 4, 3, &vel);
|
|
ke = la::Dot(vel, vel) * atoms_.get(3, i);
|
|
temperature_ = temperature_ + ke;
|
|
}
|
|
temperature_ = temperature_ / n_atoms_;
|
|
return temperature_;
|
|
} //ComputeTemperature
|
|
|
|
|
|
|
|
void ScaleToTemperature(double temp_in){
|
|
double ratio;
|
|
ratio = temp_in / temperature_;
|
|
ratio = sqrt(ratio);
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
Vector vel;
|
|
atoms_.MakeColumnSubvector(i, 4, 3, &vel);
|
|
la::Scale(ratio, &vel);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* When called for a tree implementation, this function finds
|
|
* the RMS deviation between tree and naive results.
|
|
*/
|
|
void CompareToNaive(MultiPhysicsSystem* comp_sys){
|
|
if (system_ == NULL){
|
|
return;
|
|
}
|
|
double rms_deviation = 0;
|
|
int i;
|
|
for (i = 0; i < n_atoms_; i++){
|
|
Vector pos_1, pos_2, delta_r;
|
|
int j = old_from_new_map_[i];
|
|
atoms_.MakeColumnSubvector(j, 0, 3, &pos_1);
|
|
comp_sys->atoms_.MakeColumnSubvector(i, 0, 3, &pos_2);
|
|
la::SubInit(pos_1, pos_2, &delta_r);
|
|
if (boundary_ == PERIODIC){
|
|
AdjustVector_(&delta_r);
|
|
}
|
|
rms_deviation = rms_deviation + la::Dot(delta_r, delta_r);;
|
|
}
|
|
rms_deviation = sqrt(rms_deviation / n_atoms_);
|
|
printf("rms_deviation: %f \n", rms_deviation);
|
|
} // CompareToNaive
|
|
|
|
|
|
double ComputeDiffusion(const Matrix& old_positions){
|
|
double diff = 0.0;
|
|
for (int i = 0; i< n_atoms_; i++){
|
|
Vector oldp, newp, del;
|
|
int j = i;
|
|
if (system_ != NULL){
|
|
j = old_from_new_map_[j];
|
|
}
|
|
atoms_.MakeColumnSubvector(j, 0, 3, &newp);
|
|
del.Init(3);
|
|
del.SetZero();
|
|
for (int k = 0; k < 3; k++){
|
|
del[k] = newp[k] - dimensions_[k]*diffusion_.get(k,j);
|
|
}
|
|
old_positions.MakeColumnSubvector(i, 0, 3, &oldp);
|
|
la::AddExpert(-1.0, oldp, &del);
|
|
diff = diff + la::Dot(del, del);
|
|
}
|
|
return diff / n_atoms_;
|
|
}
|
|
|
|
|
|
// Write all atom positions to the specified file
|
|
void WritePositions(FILE* fp){
|
|
int i;
|
|
for (i = 0; i < n_atoms_; i++){
|
|
Vector temp;
|
|
int j = i;
|
|
if (system_ != NULL){
|
|
j = old_from_new_map_[j];
|
|
}
|
|
atoms_.MakeColumnSubvector(j, 0, 3, &temp);
|
|
if (boundary_ == PERIODIC){
|
|
AdjustVector_(&temp);
|
|
}
|
|
fprintf(fp, " %16.8f, %16.8f, %16.8f \n", temp[0], temp[1], temp[2]);
|
|
}
|
|
} // WritePositions
|
|
|
|
void RecordPositions(Matrix& out_positions){
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
Vector temp, temp2;
|
|
int j = i;
|
|
if (system_ != NULL){
|
|
j = old_from_new_map_[j];
|
|
}
|
|
atoms_.MakeColumnSubvector(j, 0, 3, &temp);
|
|
diffusion_.MakeColumnVector(j, &temp2);
|
|
for (int k = 0; k < 3; k++){
|
|
out_positions.set(k , i, temp[k]-temp2[k]*dimensions_[k]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WriteMomentum(FILE* fp){
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
Vector temp;
|
|
int j = i;
|
|
if (system_ != NULL){
|
|
j = old_from_new_map_[j];
|
|
}
|
|
atoms_.MakeColumnSubvector(j, 4, 3, &temp);
|
|
fprintf(fp, " %16.8f, %16.8f, %16.8f \n", temp[0], temp[1], temp[2]);
|
|
}
|
|
}
|
|
|
|
void WriteData(FILE* fp){
|
|
for (int i = 0; i < n_atoms_; i++){
|
|
Vector temp;
|
|
int j = i;
|
|
if (system_ != NULL){
|
|
j = old_from_new_map_[i];
|
|
}
|
|
atoms_.MakeColumnSubvector(j, 0, 3, &temp);
|
|
fprintf(fp, " %16.8f, %16.8f, %16.8f, ", temp[0], temp[1], temp[2]);
|
|
double mass = atoms_.get(3, j);
|
|
fprintf(fp, "%16.8f,", mass);
|
|
temp.Destruct();
|
|
atoms_.MakeColumnSubvector(j, 4, 3, &temp);
|
|
fprintf(fp, " %16.8f, %16.8f, %16.8f \n ", temp[0], temp[1], temp[2]);
|
|
}
|
|
}
|
|
|
|
|
|
}; // class PhysicsSystem
|
|
|
|
#endif
|