From 93fe3738c9c4a51527473a0046fdf7c2a6400408 Mon Sep 17 00:00:00 2001 From: sjplimp Date: Thu, 24 Sep 2009 16:33:23 +0000 Subject: [PATCH] git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@3170 f3b2605a-c512-4ea7-a41b-209d697bcdaa --- lib/README | 16 +- lib/atc/ATC_Error.h | 47 + lib/atc/ATC_HardyKernel.cpp | 386 ++++ lib/atc/ATC_HardyKernel.h | 127 ++ lib/atc/ATC_Transfer.cpp | 1305 +++++++++++++ lib/atc/ATC_Transfer.h | 952 +++++++++ lib/atc/ATC_TransferHardy.cpp | 2230 ++++++++++++++++++++++ lib/atc/ATC_TransferHardy.h | 346 ++++ lib/atc/ATC_TransferThermal.cpp | 541 ++++++ lib/atc/ATC_TransferThermal.h | 96 + lib/atc/ATC_TransferUtility.cpp | 2221 +++++++++++++++++++++ lib/atc/ATC_TypeDefs.h | 207 ++ lib/atc/Array.h | 169 ++ lib/atc/Array2D.h | 171 ++ lib/atc/AtomicRegulator.cpp | 419 ++++ lib/atc/AtomicRegulator.h | 394 ++++ lib/atc/CG.h | 79 + lib/atc/CloneVector.h | 243 +++ lib/atc/DenseMatrix.h | 317 +++ lib/atc/DenseVector.h | 150 ++ lib/atc/DiagonalMatrix.h | 462 +++++ lib/atc/ElasticTimeIntegrator.cpp | 251 +++ lib/atc/ElasticTimeIntegrator.h | 209 ++ lib/atc/ElectronFlux.cpp | 66 + lib/atc/ElectronFlux.h | 96 + lib/atc/ElectronHeatCapacity.cpp | 54 + lib/atc/ElectronHeatCapacity.h | 76 + lib/atc/ElectronHeatFlux.cpp | 80 + lib/atc/ElectronHeatFlux.h | 112 ++ lib/atc/ElectronPhononExchange.cpp | 58 + lib/atc/ElectronPhononExchange.h | 69 + lib/atc/ExtrinsicModel.cpp | 290 +++ lib/atc/ExtrinsicModel.h | 228 +++ lib/atc/ExtrinsicModelTwoTemperature.cpp | 265 +++ lib/atc/ExtrinsicModelTwoTemperature.h | 82 + lib/atc/FE_Element.cpp | 515 +++++ lib/atc/FE_Element.h | 204 ++ lib/atc/FE_Engine.cpp | 1722 +++++++++++++++++ lib/atc/FE_Engine.h | 348 ++++ lib/atc/FE_Mesh.cpp | 1315 +++++++++++++ lib/atc/FE_Mesh.h | 387 ++++ lib/atc/FieldEulerIntegrator.cpp | 101 + lib/atc/FieldEulerIntegrator.h | 138 ++ lib/atc/GMRES.h | 150 ++ lib/atc/ImplicitSolveOperator.cpp | 160 ++ lib/atc/ImplicitSolveOperator.h | 129 ++ lib/atc/Kinetostat.cpp | 1234 ++++++++++++ lib/atc/Kinetostat.h | 488 +++++ lib/atc/LammpsInterface.cpp | 667 +++++++ lib/atc/LammpsInterface.h | 294 +++ lib/atc/Makefile.g++ | 129 ++ lib/atc/Makefile.icc | 129 ++ lib/atc/Makefile.serial | 129 ++ lib/atc/Material.cpp | 349 ++++ lib/atc/Material.h | 185 ++ lib/atc/Matrix.cpp | 146 ++ lib/atc/Matrix.h | 664 +++++++ lib/atc/MatrixDef.h | 185 ++ lib/atc/MatrixLibrary.h | 29 + lib/atc/OutputManager.cpp | 706 +++++++ lib/atc/OutputManager.h | 126 ++ lib/atc/PhysicsModel.h | 224 +++ lib/atc/PhysicsModelThermal.cpp | 84 + lib/atc/PhysicsModelThermal.h | 53 + lib/atc/PhysicsModelTwoTemperature.cpp | 123 ++ lib/atc/PhysicsModelTwoTemperature.h | 71 + lib/atc/PrescribedDataManager.cpp | 442 +++++ lib/atc/PrescribedDataManager.h | 157 ++ lib/atc/Quadrature.h | 50 + lib/atc/README | 25 + lib/atc/Solver.cpp | 147 ++ lib/atc/Solver.h | 13 + lib/atc/SparseMatrix-inl.h | 930 +++++++++ lib/atc/SparseMatrix.h | 180 ++ lib/atc/SparseVector-inl.h | 188 ++ lib/atc/SparseVector.h | 85 + lib/atc/StringManip.h | 122 ++ lib/atc/Thermostat.cpp | 1579 +++++++++++++++ lib/atc/Thermostat.h | 629 ++++++ lib/atc/TimeFilter.cpp | 345 ++++ lib/atc/TimeFilter.h | 696 +++++++ lib/atc/TimeIntegrator.cpp | 176 ++ lib/atc/TimeIntegrator.h | 280 +++ lib/atc/Utility.h | 109 ++ lib/atc/Vector.cpp | 30 + lib/atc/Vector.h | 195 ++ lib/atc/XT_Function.cpp | 257 +++ lib/atc/XT_Function.h | 172 ++ 88 files changed, 30800 insertions(+), 5 deletions(-) create mode 100644 lib/atc/ATC_Error.h create mode 100644 lib/atc/ATC_HardyKernel.cpp create mode 100644 lib/atc/ATC_HardyKernel.h create mode 100644 lib/atc/ATC_Transfer.cpp create mode 100644 lib/atc/ATC_Transfer.h create mode 100644 lib/atc/ATC_TransferHardy.cpp create mode 100644 lib/atc/ATC_TransferHardy.h create mode 100644 lib/atc/ATC_TransferThermal.cpp create mode 100644 lib/atc/ATC_TransferThermal.h create mode 100644 lib/atc/ATC_TransferUtility.cpp create mode 100644 lib/atc/ATC_TypeDefs.h create mode 100644 lib/atc/Array.h create mode 100644 lib/atc/Array2D.h create mode 100644 lib/atc/AtomicRegulator.cpp create mode 100644 lib/atc/AtomicRegulator.h create mode 100644 lib/atc/CG.h create mode 100644 lib/atc/CloneVector.h create mode 100644 lib/atc/DenseMatrix.h create mode 100644 lib/atc/DenseVector.h create mode 100644 lib/atc/DiagonalMatrix.h create mode 100644 lib/atc/ElasticTimeIntegrator.cpp create mode 100644 lib/atc/ElasticTimeIntegrator.h create mode 100644 lib/atc/ElectronFlux.cpp create mode 100644 lib/atc/ElectronFlux.h create mode 100644 lib/atc/ElectronHeatCapacity.cpp create mode 100644 lib/atc/ElectronHeatCapacity.h create mode 100644 lib/atc/ElectronHeatFlux.cpp create mode 100644 lib/atc/ElectronHeatFlux.h create mode 100644 lib/atc/ElectronPhononExchange.cpp create mode 100644 lib/atc/ElectronPhononExchange.h create mode 100644 lib/atc/ExtrinsicModel.cpp create mode 100644 lib/atc/ExtrinsicModel.h create mode 100644 lib/atc/ExtrinsicModelTwoTemperature.cpp create mode 100644 lib/atc/ExtrinsicModelTwoTemperature.h create mode 100644 lib/atc/FE_Element.cpp create mode 100644 lib/atc/FE_Element.h create mode 100644 lib/atc/FE_Engine.cpp create mode 100644 lib/atc/FE_Engine.h create mode 100644 lib/atc/FE_Mesh.cpp create mode 100644 lib/atc/FE_Mesh.h create mode 100644 lib/atc/FieldEulerIntegrator.cpp create mode 100644 lib/atc/FieldEulerIntegrator.h create mode 100644 lib/atc/GMRES.h create mode 100644 lib/atc/ImplicitSolveOperator.cpp create mode 100644 lib/atc/ImplicitSolveOperator.h create mode 100644 lib/atc/Kinetostat.cpp create mode 100644 lib/atc/Kinetostat.h create mode 100644 lib/atc/LammpsInterface.cpp create mode 100644 lib/atc/LammpsInterface.h create mode 100644 lib/atc/Makefile.g++ create mode 100644 lib/atc/Makefile.icc create mode 100644 lib/atc/Makefile.serial create mode 100644 lib/atc/Material.cpp create mode 100644 lib/atc/Material.h create mode 100644 lib/atc/Matrix.cpp create mode 100644 lib/atc/Matrix.h create mode 100644 lib/atc/MatrixDef.h create mode 100644 lib/atc/MatrixLibrary.h create mode 100644 lib/atc/OutputManager.cpp create mode 100644 lib/atc/OutputManager.h create mode 100644 lib/atc/PhysicsModel.h create mode 100644 lib/atc/PhysicsModelThermal.cpp create mode 100644 lib/atc/PhysicsModelThermal.h create mode 100644 lib/atc/PhysicsModelTwoTemperature.cpp create mode 100644 lib/atc/PhysicsModelTwoTemperature.h create mode 100644 lib/atc/PrescribedDataManager.cpp create mode 100644 lib/atc/PrescribedDataManager.h create mode 100644 lib/atc/Quadrature.h create mode 100644 lib/atc/README create mode 100644 lib/atc/Solver.cpp create mode 100644 lib/atc/Solver.h create mode 100644 lib/atc/SparseMatrix-inl.h create mode 100644 lib/atc/SparseMatrix.h create mode 100644 lib/atc/SparseVector-inl.h create mode 100644 lib/atc/SparseVector.h create mode 100644 lib/atc/StringManip.h create mode 100644 lib/atc/Thermostat.cpp create mode 100644 lib/atc/Thermostat.h create mode 100644 lib/atc/TimeFilter.cpp create mode 100644 lib/atc/TimeFilter.h create mode 100644 lib/atc/TimeIntegrator.cpp create mode 100644 lib/atc/TimeIntegrator.h create mode 100644 lib/atc/Utility.h create mode 100644 lib/atc/Vector.cpp create mode 100644 lib/atc/Vector.h create mode 100644 lib/atc/XT_Function.cpp create mode 100644 lib/atc/XT_Function.h diff --git a/lib/README b/lib/README index cd213217cf..192589c8b0 100644 --- a/lib/README +++ b/lib/README @@ -7,9 +7,15 @@ will need to copy one of the Makefile.* files to Makefile before building a library. If a Makefile.* suitable for your machine does not exist, you will need to edit one of the existing Makefiles. -The libraries included with LAMMPS are the following: +The libraries included in the LAMMPS distribution are the following: -gpu graphical processor (GPU) routines, currently NVIDIA specific -poems POEMS rigid-body integration package from RPI -meam modified embedded atom method (MEAM) potential from Greg Wagner -reax ReaxFF potential, from Adri van Duin via Aidan Thompson +atc atomistic-to-continuum methods + from Reese Jones, Jeremy Templeton, Jon Zimmerman (Sandia) +gpu graphical processor (GPU) routines, currently NVIDIA specific, + from Mike Brown (Sandia) +poems POEMS rigid-body integration package + from RPI +meam modified embedded atom method (MEAM) potential + from Greg Wagner (Sandia) +reax ReaxFF potential + from Adri van Duin (Penn State) and Aidan Thompson (Sandia) diff --git a/lib/atc/ATC_Error.h b/lib/atc/ATC_Error.h new file mode 100644 index 0000000000..97e29e9e2c --- /dev/null +++ b/lib/atc/ATC_Error.h @@ -0,0 +1,47 @@ +// ATC_Error : a base class for atom-continuum errors + +#ifndef ATC_ERROR +#define ATC_ERROR + +#include + + +namespace ATC { + +class ATC_Error { + + public: + + // constructor + ATC_Error(int procID, std::string errorDescription) { + procID_ = procID; + errorDescription_ = errorDescription; + }; + +//ATC_Error(std::string errorDescription) { +// procID_ = LammpsInterface::instance()->comm_rank(); +// errorDescription_ = errorDescription; +//}; + + // data access + virtual int get_id() { + return procID_; + }; + + virtual std::string get_error_description() { + return errorDescription_; + }; + + + private: + + // id of the processor reporting the error + int procID_; + + // string describing the type of error + std::string errorDescription_; + +}; + +} +#endif diff --git a/lib/atc/ATC_HardyKernel.cpp b/lib/atc/ATC_HardyKernel.cpp new file mode 100644 index 0000000000..f29a368221 --- /dev/null +++ b/lib/atc/ATC_HardyKernel.cpp @@ -0,0 +1,386 @@ +#include "ATC_HardyKernel.h" +#include "math.h" +#include //for debugging purposes; take this out after I'm done +#include +#include "ATC_Error.h" +#include "Quadrature.h" + +using namespace std; + +static const double Pi = 4.0*atan(1.0); +static const double tol = 1.0e-8; + +namespace ATC { + + + //------------------------------------------------------------------------ + // constructor + ATC_HardyKernel::ATC_HardyKernel(int nparameters, double* parameters): + lammpsInterface_(LammpsInterface::instance()), + Rc_(0),invRc_(0),nsd_(3) + { + Rc_ = parameters[0]; + invRc_ = 1.0/Rc_; + Rc_ = parameters[0]; + invRc_ = 1.0/Rc_; + invVol_ = 1.0/(4.0/3.0*Pi*pow(Rc_,3)); + + set_line_quadrature(line_ngauss,line_xg,line_wg); + + // get periodicity and box bounds/lengths + lammpsInterface_->get_box_periodicity(periodicity[0], + periodicity[1],periodicity[2]); + lammpsInterface_->get_box_bounds(box_bounds[0][0],box_bounds[1][0], + box_bounds[0][1],box_bounds[1][1], + box_bounds[0][2],box_bounds[1][2]); + for (int k = 0; k < 3; k++) { + box_length[k] = box_bounds[1][k] - box_bounds[0][k]; + } + }; + + // bond function value via quadrature + double ATC_HardyKernel::bond(DENS_VEC& xa, DENS_VEC&xb, double lam1, double lam2) + { + DENS_VEC xab(nsd_), q(nsd_); + double lamg; + double bhsum=0.0; + xab = xa - xb; + for (int i = 0; i < line_ngauss; i++) { + lamg=0.5*((lam2-lam1)*line_xg[i]+(lam2+lam1)); + q = lamg*xab + xb; + double locg_value=this->value(q); + bhsum+=locg_value*line_wg[i]; + } + return 0.5*(lam2-lam1)*bhsum; + } + + // localization-volume intercepts for bond calculation + // bond intercept values assuming spherical support + void ATC_HardyKernel::bond_intercepts(DENS_VEC& xa, + DENS_VEC& xb, double &lam1, double &lam2) + { + if (nsd_ == 2) {// for cylinders, axis is always z! + const int iaxis = 2; + xa[iaxis] = 0.0; + xb[iaxis] = 0.0; + } + lam1 = lam2 = -1; + double ra_n = invRc_*xa.norm(); // lambda = 1 + double rb_n = invRc_*xb.norm(); // lambda = 0 + bool a_in = (ra_n <= 1.0); + bool b_in = (rb_n <= 1.0); + if (a_in && b_in) { + lam1 = 0.0; + lam2 = 1.0; + return; + } + DENS_VEC xab = xa - xb; + double rab_n = invRc_*xab.norm(); + double a = rab_n*rab_n; // always at least an interatomic distance + double b = 2.0*invRc_*invRc_*xab.dot(xb); + double c = rb_n*rb_n - 1.0; + double discrim = b*b - 4.0*a*c; // discriminant + if (discrim < 0) return; // no intersection + // num recipes: + double s1, s2; + if (b < 0.0) { + double aux = -0.5*(b-sqrt(discrim)); + s1 = c/aux; + s2 = aux/a; + } + else { + double aux = -0.5*(b+sqrt(discrim)); + s1 = aux/a; + s2 = c/aux; + } + if (a_in && !b_in) { + lam1 = s1; + lam2 = 1.0; + } + else if (!a_in && b_in) { + lam1 = 0.0; + lam2 = s2; + } + else { + if (s1 >= 0.0 && s2 <= 1.0) { + lam1 = s1; + lam2 = s2; + } + } + }; + + //------------------------------------------------------------------------ + // constructor + ATC_HardyKernelStep::ATC_HardyKernelStep + (int nparameters, double* parameters): + ATC_HardyKernel(nparameters, parameters) + { + for (int k = 0; k < nsd_; k++ ) { + if ((bool) periodicity[k]) { + if (Rc_ > 0.5*box_length[k]) { + throw ATC_Error(0,"Size of localization volume is too large for periodic boundary condition"); + }; + }; + }; + } + + // function value + double ATC_HardyKernelStep::value(DENS_VEC& x_atom) + { + double rn=invRc_*x_atom.norm(); + if (rn <= 1.0) { return 1.0; } + else { return 0.0; } + }; + + //------------------------------------------------------------------------ + /** a step with rectangular support suitable for a rectangular grid */ + // constructor + ATC_HardyKernelCell::ATC_HardyKernelCell + (int nparameters, double* parameters): + ATC_HardyKernel(nparameters, parameters) + { + hx = parameters[0]; + hy = parameters[1]; + hz = parameters[2]; + invVol_ = 1.0/8.0/(hx*hy*hz); + cellBounds_.reset(6); + cellBounds_(0) = -hx; + cellBounds_(1) = hx; + cellBounds_(2) = -hy; + cellBounds_(3) = hy; + cellBounds_(4) = -hz; + cellBounds_(5) = hz; + + for (int k = 0; k < nsd_; k++ ) { + if ((bool) periodicity[k]) { + if (parameters[k] > 0.5*box_length[k]) { + throw ATC_Error(0,"Size of localization volume is too large for periodic boundary condition"); + }; + }; + }; + } + + // function value + double ATC_HardyKernelCell::value(DENS_VEC& x_atom) + { + if ((cellBounds_(0) <= x_atom(0)) && (x_atom(0) < cellBounds_(1)) + && (cellBounds_(2) <= x_atom(1)) && (x_atom(1) < cellBounds_(3)) + && (cellBounds_(4) <= x_atom(2)) && (x_atom(2) < cellBounds_(5))) { + return 1.0; + } + else { + return 0.0; + } + }; + + // bond intercept values for rectangular region : origin is the node position + void ATC_HardyKernelCell::bond_intercepts(DENS_VEC& xa, + DENS_VEC& xb, double &lam1, double &lam2) + { + lam1 = 0.0; // start + lam2 = 1.0; // end + + bool a_in = (value(xa) > 0.0); + bool b_in = (value(xb) > 0.0); + + // (1) both in, no intersection needed + if (a_in && b_in) { + return; + } + // (2) for one in & one out -> one plane intersection + // determine the points of intersection between the line joining + // atoms a and b and the bounding planes of the localization volume + else if (a_in || b_in) { + DENS_VEC xab = xa - xb; + for (int i = 0; i < nsd_; i++) { + // check if segment is parallel to face + if (fabs(xab(i)) > tol) { + for (int j = 0; j < 2; j++) { + double s = (cellBounds_(2*i+j) - xb(i))/xab(i); + // check if between a & b + if (s >= 0 && s <= 1) { + bool in_bounds = false; + DENS_VEC x = xb + s*xab; + if (i == 0) { + if ((cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3)) + && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { + in_bounds = true; + } + } + else if (i == 1) { + if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) + && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { + in_bounds = true; + } + } + else if (i == 2) { + if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) + && (cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3))) { + in_bounds = true; + } + } + if (in_bounds) { + if (a_in) { lam1 = s;} + else { lam2 = s;} + return; + } + } + } + } + } + throw ATC_Error(0,"logic failure in HardyKernel Cell for single intersection\n"); + } + // (3) both out -> corner intersection + else { + lam2 = lam1; // default to no intersection + DENS_VEC xab = xa - xb; + double ss[6] = {-1,-1,-1,-1,-1,-1}; + int is = 0; + for (int i = 0; i < nsd_; i++) { + // check if segment is parallel to face + if (fabs(xab(i)) > tol) { + for (int j = 0; j < 2; j++) { + double s = (cellBounds_(2*i+j) - xb(i))/xab(i); + // check if between a & b + if (s >= 0 && s <= 1) { + // check if in face + DENS_VEC x = xb + s*xab; + if (i == 0) { + if ((cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3)) + && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { + ss[is++] = s; + } + } + else if (i == 1) { + if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) + && (cellBounds_(4) <= x(2)) && (x(2) <= cellBounds_(5))) { + ss[is++] = s; + } + } + else if (i == 2) { + if ((cellBounds_(0) <= x(0)) && (x(0) <= cellBounds_(1)) + && (cellBounds_(2) <= x(1)) && (x(1) <= cellBounds_(3))) { + ss[is++] = s; + } + } + } + } + } + } + if (is == 1) { + // intersection occurs at a box edge - leave lam1 = lam2 + } + else if (is == 2) { + lam1 = min(ss[0],ss[1]); + lam2 = max(ss[0],ss[1]); + } + else if (is == 3) { + // intersection occurs at a box vertex - leave lam1 = lam2 + } + else { + if (is != 0) throw ATC_Error(0,"logic failure in HardyKernel Cell for corner intersection\n"); + } + } + } + + //------------------------------------------------------------------------ + // constructor + ATC_HardyKernelCubicSphere::ATC_HardyKernelCubicSphere + (int nparameters, double* parameters): + ATC_HardyKernel(nparameters, parameters) + { + for (int k = 0; k < nsd_; k++ ) { + if ((bool) periodicity[k]) { + if (Rc_ > 0.5*box_length[k]) { + throw ATC_Error(0,"Size of localization volume is too large for periodic boundary condition"); + }; + }; + }; + } + + // function value + double ATC_HardyKernelCubicSphere::value(DENS_VEC& x_atom) + { + double r=x_atom.norm(); + double rn=r/Rc_; + if (rn < 1.0) { return 5.0*(1.0-3.0*rn*rn+2.0*rn*rn*rn); } + else { return 0.0; } + } + //------------------------------------------------------------------------ + // constructor + ATC_HardyKernelQuarticSphere::ATC_HardyKernelQuarticSphere + (int nparameters, double* parameters): + ATC_HardyKernel(nparameters, parameters) + { + for (int k = 0; k < nsd_; k++ ) { + if ((bool) periodicity[k]) { + if (Rc_ > 0.5*box_length[k]) { + throw ATC_Error(0,"Size of localization volume is too large for periodic boundary condition"); + }; + }; + }; + } + + // function value + double ATC_HardyKernelQuarticSphere::value(DENS_VEC& x_atom) + { + double r=x_atom.norm(); + double rn=r/Rc_; + if (rn < 1.0) { return 35.0/8.0*pow((1.0-rn*rn),2); } + else { return 0.0; } + } + //------------------------------------------------------------------------ + // constructor + ATC_HardyKernelCubicCyl::ATC_HardyKernelCubicCyl + (int nparameters, double* parameters): + ATC_HardyKernel(nparameters, parameters) + { + nsd_ = 2; + double Lz = box_length[2]; + invVol_ = 1.0/(Pi*pow(Rc_,2)*Lz); + for (int k = 0; k < nsd_; k++ ) { + if ((bool) periodicity[k]) { + if (Rc_ > 0.5*box_length[k]) { + throw ATC_Error(0,"Size of localization volume is too large for periodic boundary condition"); + }; + }; + }; + } + + // function value + double ATC_HardyKernelCubicCyl::value(DENS_VEC& x_atom) + { + double r=sqrt(pow(x_atom(0),2)+pow(x_atom(1),2)); + double rn=r/Rc_; + if (rn < 1.0) { return 10.0/3.0*(1.0-3.0*rn*rn+2.0*rn*rn*rn); } + else { return 0.0; } + } + + + //------------------------------------------------------------------------ + // constructor + ATC_HardyKernelQuarticCyl::ATC_HardyKernelQuarticCyl + (int nparameters, double* parameters): + ATC_HardyKernel(nparameters, parameters) + { + nsd_ = 2; + double Lz = box_length[2]; + invVol_ = 1.0/(Pi*pow(Rc_,2)*Lz); + for (int k = 0; k < nsd_; k++ ) { + if ((bool) periodicity[k]) { + if (Rc_ > 0.5*box_length[k]) { + throw ATC_Error(0,"Size of localization volume is too large for periodic boundary condition"); + }; + }; + }; + } + + // function value + double ATC_HardyKernelQuarticCyl::value(DENS_VEC& x_atom) + { + double r=sqrt(pow(x_atom(0),2)+pow(x_atom(1),2)); + double rn=r/Rc_; + if (rn < 1.0) { return 3.0*pow((1.0-rn*rn),2); } + else { return 0.0; } + } +}; diff --git a/lib/atc/ATC_HardyKernel.h b/lib/atc/ATC_HardyKernel.h new file mode 100644 index 0000000000..96b07d05a7 --- /dev/null +++ b/lib/atc/ATC_HardyKernel.h @@ -0,0 +1,127 @@ +/** ATC_HardyKernel: Hardy smoothing */ +#ifndef ATC_HARDY_KERNEL_H +#define ATC_HARDY_KERNEL_H + +#include "LammpsInterface.h" +#include "MatrixLibrary.h" + + +namespace ATC { + + + class ATC_HardyKernel { + + public: + + // constructor + ATC_HardyKernel(int nparameters, double* parameters); + // destructor + virtual ~ATC_HardyKernel() {}; + // function value + virtual double value(DENS_VEC& x_atom)=0; + // bond function value via quadrature + virtual double bond(DENS_VEC& xa, DENS_VEC&xb, double lam1, double lam2); + // localization-volume intercepts for bond calculation + virtual void bond_intercepts(DENS_VEC& xa, + DENS_VEC& xb, double &lam1, double &lam2); + double inv_vol(void) { return invVol_; } + + protected: + double invVol_; // normalization factor + double Rc_, invRc_; // cutoff radius + int nsd_ ; // number of dimensions + + /** pointer to lammps interface class */ + LammpsInterface * lammpsInterface_; + + /** periodicity flags and lengths */ + int periodicity[3]; + double box_bounds[2][3]; + double box_length[3]; + + }; + + /** a step with spherical support */ + class ATC_HardyKernelStep : public ATC_HardyKernel { + + public: + // constructor + ATC_HardyKernelStep(int nparameters, double* parameters); + // destructor + virtual ~ATC_HardyKernelStep() {}; + // function value + double value(DENS_VEC& x_atom); + // bond function value + virtual double bond(DENS_VEC& xa, DENS_VEC&xb, double lam1, double lam2) + {return lam2 -lam1;} + }; + + /** a step with rectangular support suitable for a rectangular grid */ + class ATC_HardyKernelCell : public ATC_HardyKernel { + + public: + // constructor + ATC_HardyKernelCell(int nparameters, double* parameters); + // destructor + virtual ~ATC_HardyKernelCell() {}; + // function value + virtual double value(DENS_VEC& x_atom); + // bond function value + virtual double bond(DENS_VEC& xa, DENS_VEC&xb, double lam1, double lam2) + {return lam2 -lam1;} + // bond intercept values : origin is the node position + void bond_intercepts(DENS_VEC& xa, + DENS_VEC& xb, double &lam1, double &lam2); + + protected: + double hx, hy, hz; + DENS_VEC cellBounds_; + }; + + class ATC_HardyKernelCubicSphere : public ATC_HardyKernel { + + public: + // constructor + ATC_HardyKernelCubicSphere(int nparameters, double* parameters); + // destructor + virtual ~ATC_HardyKernelCubicSphere() {}; + // function value + virtual double value(DENS_VEC& x_atom); + }; + + class ATC_HardyKernelQuarticSphere : public ATC_HardyKernel { + + public: + // constructor + ATC_HardyKernelQuarticSphere(int nparameters, double* parameters); + // destructor + virtual ~ATC_HardyKernelQuarticSphere() {}; + // function value + virtual double value(DENS_VEC& x_atom); + }; + + + class ATC_HardyKernelCubicCyl : public ATC_HardyKernel { + + public: + // constructor + ATC_HardyKernelCubicCyl(int nparameters, double* parameters); + // destructor + virtual ~ATC_HardyKernelCubicCyl() {}; + // function value + virtual double value(DENS_VEC& x_atom) ; + }; + + class ATC_HardyKernelQuarticCyl : public ATC_HardyKernel { + + public: + + // constructor + ATC_HardyKernelQuarticCyl(int nparameters, double* parameters); + // destructor + virtual ~ATC_HardyKernelQuarticCyl() {}; + // function value + virtual double value(DENS_VEC& x_atom); + }; +}; +#endif diff --git a/lib/atc/ATC_Transfer.cpp b/lib/atc/ATC_Transfer.cpp new file mode 100644 index 0000000000..a10026de64 --- /dev/null +++ b/lib/atc/ATC_Transfer.cpp @@ -0,0 +1,1305 @@ +// ATC_Transfer headers +#include "ATC_Transfer.h" +#include "FE_Engine.h" +#include "Array.h" +#include "Array2D.h" +#include "ATC_Error.h" +#include "CG.h" +#include "XT_Function.h" +#include "PrescribedDataManager.h" +#include "TimeIntegrator.h" +#include "PhysicsModel.h" +#include "PhysicsModelThermal.h" +#include "PhysicsModelTwoTemperature.h" + +namespace ATC { + + ATC_Transfer::ATC_Transfer(void) : + lammpsInterface_(LammpsInterface::instance()), + physicsModel_(NULL), + feEngine_(NULL), + prescribedDataMgr_(NULL), + simTime_(0.0), + stepCounter_(0), + sampleCounter_(0), + outputFrequency_(0), + sampleFrequency_(1), + outputFrequencyAtom_(0), + nLocal_(0), + nLocalTotal_(0), + nLocalGhost_(0), + nLocalMask_(0), + nInternal_(0), + nGhost_(0), + nLocalLambda_(0), + equilibriumStart_(false), + mdOutputManager_(), + bndyIntType_(NO_QUADRATURE), + bndyFaceSet_(NULL), + globalSwitch_(0), + resetSwitch_(false), + atomToElementMapType_(LAGRANGIAN), + atomToElementMapFrequency_(0), + neighborResetFrequency_(0), + scalarFlag_(0), + vectorFlag_(0), + sizeVector_(0), + scalarVectorFreq_(0), + extScalar_(0), + extVector_(0), + extList_(NULL), + timeFilterManager_(this), + groupbit_(0), + groupbitGhost_(0), + atomSwitch_(false), + initialized_(false), + atomQuadForInternal_(true), + useLocalizedLambda_(false), + useLumpedLambda_(false), + regionID_(-1), + atomWeightType_(LATTICE), + xref_(NULL), + readXref_(false), + timeIntegrator_(NULL), + extrinsicModelManager_(this), + useRestart_(false), + trackCharge_(false) + { + // Defaults + grow_arrays(lammpsInterface_->nmax()); + fieldMask_.reset(NUM_FIELDS,NUM_FLUX); + fieldMask_ = false; + + // fe_engine + feEngine_ = new FE_Engine(this); + + // check to see if lammps has any charges + double * atomicCharge = lammpsInterface_->atom_charge(); + if (atomicCharge) + trackCharge_ = true; + } + + ATC_Transfer::~ATC_Transfer() + { + lammpsInterface_->destroy_2d_double_array(xref_); + if (feEngine_) delete feEngine_; + if (physicsModel_) delete physicsModel_; + if (prescribedDataMgr_) delete prescribedDataMgr_; + } + + //-------------------------------------------------- + // pack_fields + // bundle all allocated field matrices into a list + // for output needs + //-------------------------------------------------- + void ATC_Transfer::pack_fields(OUTPUT_LIST & data) + { + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + string fieldName = field_to_string(thisField); + string matrixName; + + // copy all fields from ATC_Transfer.h + matrixName = "fields_" + fieldName; + data[matrixName] = & fields_[thisField]; + matrixName = "dot_fields_" + fieldName; + data[matrixName] = & dot_fields_[thisField]; + matrixName = "ddot_fields_" + fieldName; + data[matrixName] = &ddot_fields_[thisField]; + matrixName = "dddot_fields_" + fieldName; + data[matrixName] = & dddot_fields_[thisField]; + matrixName = "dot_fieldsMD_" + fieldName; + data[matrixName] = & dot_fieldsMD_[thisField]; + matrixName = "ddot_fieldsMD_" + fieldName; + data[matrixName] = & ddot_fieldsMD_[thisField]; + matrixName = "dot_dot_fieldsMD_"; + data[matrixName] = & dot_dot_fieldsMD_[thisField]; + + matrixName = "fieldNdOld_" + fieldName; + data[matrixName] = & fieldNdOld_[thisField]; + matrixName = "fieldNdFiltered_" + fieldName; + data[matrixName] = & fieldNdFiltered_[thisField]; + matrixName = "fieldRateNdOld_"; + data[matrixName] = & fieldRateNdOld_[thisField]; + matrixName = "fieldRateNdFiltered_" + fieldName; + data[matrixName] = & fieldRateNdFiltered_[thisField]; + matrixName = "dot_fieldRateNdOld_" + fieldName; + data[matrixName] = & dot_fieldRateNdOld_[thisField]; + matrixName = "dot_fieldRateNdFiltered_" + fieldName; + data[matrixName] = & dot_fieldRateNdFiltered_[thisField]; + + matrixName = "auxStorage_" + fieldName; + data[matrixName] = & auxStorage_[thisField]; + } + } + + //-------------------------------------------------- + // write_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_Transfer::write_restart_data(string fileName, OUTPUT_LIST & data) + { + pack_fields(data); + feEngine_->write_restart_file(fileName,&data); + } + + //-------------------------------------------------- + // read_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_Transfer::read_restart_data(string fileName, OUTPUT_LIST & data) + { + pack_fields(data); + feEngine_->read_restart_file(fileName,&data); + } + + //-------------------------------------------------- + // Interactions with LAMMPS fix commands + // parse input command and pass on to finite element engine + // or physics specific transfers if necessary + // revert to physics-specific transfer if no command matches input + // first keyword is unique to particular class + // base class keyword matching must apply to ALL physics + // order: derived, base, owned objects + //-------------------------------------------------- + bool ATC_Transfer::modify(int narg, char **arg) + { + FieldName thisField; + int thisIndex; + int argIdx; + + bool match = false; + + if (strcmp(arg[0],"transfer")==0) { + /*! \page man_transfer_output fix_modify AtC transfer output + \section syntax + fix_modify AtC transfer output + [text | vector_components | tensor_components ] + - filename_prefix (string) = prefix for data files + - frequency (integer) = frequency of output in time-steps + - options (keyword/s): \n + text = creates text output as well as binary Ensight output \n + vector_components = outputs vectors as scalar components \n + tensor_components = outputs tensor as scalar components + (use this for Paraview)\n + \section examples + fix_modify AtC transfer output heatFE 100 \n + fix_modify AtC transfer output hardyFE 1 text tensor_components \n + \section description + Creates (binary, "gold" format) Ensight output of nodal/mesh data + which is transfer/physics specific. + \section restrictions + \section related + see \ref man_fix_atc + \section default + none + */ + if (strcmp(arg[1],"output")==0) { + if (strcmp(arg[2],"nodeset")==0) { + string nset = arg[3]; + if (strcmp(arg[4],"sum")==0) { + string field = arg[5]; + pair < string, FieldName > id(nset,string_to_field(field)); + nsetCompute_[id] = 0.0; + match = true; + } + } + else { + outputPrefix_ = arg[2]; + outputFrequency_ = atoi(arg[3]); + bool text_output = false, vect_comp = false, tensor_comp = false; + int rank = lammpsInterface_->comm_rank(); + for (int i = 4; i0) { + if (text_output) { + feEngine_->initialize_output(rank,outputPrefix_,GNUPLOT); + if (rank == 0) + cout << " ATC:: Warning : text output can create _LARGE_ files\n"; + } + else feEngine_->initialize_output(rank,outputPrefix_); + if (vect_comp) + feEngine_->output_manager() + ->set_option(OUTPUT_VECTOR_COMPONENTS,true); + if (tensor_comp) + feEngine_->output_manager() + ->set_option(OUTPUT_TENSOR_COMPONENTS,true); + } + match = true; + } + } + + + /*! \page man_transfer_atomic_output fix_modify AtC transfer atomic_output + \section syntax + fix_modify AtC transfer atomic_output + [text] + - filename_prefix (string) = prefix for data files + - frequency (integer) = frequency of output in time-steps + - option "text" = creates a text version of the output as well as + the binary Ensight output + \section examples + fix_modify AtC transfer atomic_output heatMD 100 \n + fix_modify AtC transfer atomic_output nanoMD 1 text \n + \section description + Creates (binary, "gold" format) Ensight output of atom (point) data + which is transfer/physics specific. + \section restrictions + \section related + \section default + none + */ + else if (strcmp(arg[1],"atomic_output")==0) { + outputPrefixAtom_ = arg[2]; + outputFrequencyAtom_ = atoi(arg[3]); + if (outputFrequencyAtom_>0) { + if (narg == 5 && strcmp(arg[4],"text")==0) { + mdOutputManager_.initialize(outputPrefixAtom_,GNUPLOT); + cout << " ATC:: Warning : text output can create _LARGE_ files\n"; + } + else mdOutputManager_.initialize(outputPrefixAtom_); + } + match = true; + } + + /*! \page man_transfer_internal fix_modify AtC transfer internal + \section syntax + fix_modify AtC transfer internal type + - = type id for non-fixed atoms internal to the FE mesh + \section examples + fix_modify AtC transfer internal type internal_atoms + \section description + Command to define the atoms to couple to finite elements. + This definition is required for a AtC simulation. + \section restrictions + \section related + see \ref man_transfer_boundary + \section default + none + */ + else if ((strcmp(arg[1],"internal")==0) && (strcmp(arg[2],"type")==0)) { + int igroup = lammpsInterface_->find_group(arg[3]); + groupbit_ |= lammpsInterface_->group_bit(igroup); + igroups_.insert(igroup); + match = true; + } + + /*! \page man_transfer_boundary fix_modify AtC transfer boundary + \section syntax + fix_modify AtC transfer boundary type + - = type id for atoms that represent a ficticious + boundary internal to the FE mesh + \section examples + fix_modify AtC transfer boundary type ghost_atoms + \section description + Command to define the atoms that represent the ficticious + boundary internal to the FE mesh. For fully overlapped MD/FE + domains with periodic boundary conditions no boundary atoms should + be defined. + \section restrictions + \section related + see \ref man_transfer_internal + \section default + none + */ + else if ((strcmp(arg[1],"boundary")==0) && (strcmp(arg[2],"type")==0)) { + int igroup = lammpsInterface_->find_group(arg[3]); + groupbitGhost_ |= lammpsInterface_->group_bit(igroup); + igroupsGhost_.insert(igroup); + match = true; + } + + /*! \page man_internal_quadrature fix_modify AtC transfer internal_quadrature + \section syntax + fix_modify atc transfer internal_quadrature < on | off > + \section examples + fix_modify atc transfer internal_quadrature off + \section description + Command use or not use atomic quadrature on internal elements + fully filled with atoms. By turning the internal quadrature off + these elements do not contribute to the governing PDE and the fields + at the internal nodes follow the weighted averages of the atomic data. + \section restrictions + \section related + \section default + on + */ + else if (strcmp(arg[1],"internal_quadrature")==0) { + if (strcmp(arg[2],"on")==0) { + atomQuadForInternal_ = true; + match = true; + } + else if (strcmp(arg[2],"off")==0) { + atomQuadForInternal_ = false; + regionID_ = -1; + match = true; + } + else if (strcmp(arg[3],"off")==0) { + for (regionID_ = 0; regionID_ < lammpsInterface_->nregion(); regionID_++) + if (strcmp(arg[2],lammpsInterface_->region_name(regionID_)) == 0) break; + if (regionID_ < lammpsInterface_->nregion()) { + atomQuadForInternal_ = false; + match = true; + } + else + regionID_ = -1; + } + } + + + /*! \page man_initial fix_modify AtC transfer initial + \section syntax + fix_modify AtC transfer initial + - = field name valid for type of physics, temperature | electron_temperature + - = name of set of nodes to apply boundary condition + - = value or name of function followed by its + parameters + \section examples + fix_modify atc transfer initial temperature groupNAME 10. + \section description + Sets the initial values for the specified field at the specified nodes. + \section restrictions + keyword 'all' reserved in nodeset name + \section related + see \ref man_transfer_internal + \section default + none + */ + // set initial conditions + else if (strcmp(arg[1],"initial")==0) { + argIdx = 2; + get_field(arg,argIdx,thisField,thisIndex); + string nsetName(arg[argIdx++]); + XT_Function * f = NULL; + // parse constant + if (narg == argIdx+1) { + f = XT_Function_Mgr::instance()->get_constant_function(atof(arg[argIdx])); + } + // parse function + else { + f = XT_Function_Mgr::instance()->get_function(&(arg[argIdx]),narg-argIdx); + } + prescribedDataMgr_->fix_initial_field(nsetName,thisField,thisIndex,f); + match = true; + } + + /*! \page man_fix_nodes fix_modify AtC transfer fix + \section syntax + fix_modify AtC transfer fix + - = field name valid for type of physics + - = name of set of nodes to apply boundary condition + - = value or name of function followed by its + parameters + \section examples + fix_modify AtC transfer fix temperature groupNAME 10. \n + fix_modify AtC transfer fix temperature groupNAME 0 0 0 10.0 0 0 1.0 \n + \section description + Creates a constraint on the values of the specified field at specified nodes. + \section restrictions + keyword 'all' reserved in nodeset name + \section related + see \ref man_unfix_nodes + \section default + none + */ + // fix and unfix nodes + else if (strcmp(arg[1],"fix")==0) { + argIdx = 2; + get_field(arg,argIdx,thisField,thisIndex); + string nsetName(arg[argIdx++]); + XT_Function * f = NULL; + // parse constant + if (narg == argIdx+1) { + f = XT_Function_Mgr::instance()->get_constant_function(atof(arg[argIdx])); + } + // parse function + else { + f = XT_Function_Mgr::instance()->get_function(&(arg[argIdx]),narg-argIdx); + } + prescribedDataMgr_->fix_field(nsetName,thisField,thisIndex,f); + match = true; + } + + /*! \page man_unfix_nodes fix_modify AtC transfer unfix + \section syntax + fix_modify AtC transfer unfix + - = field name valid for type of physics + - = name of set of nodes + \section examples + fix_modify AtC transfer unfix temperature groupNAME + \section description + Removes constraint on field values for specified nodes. + \section restrictions + keyword 'all' reserved in nodeset name + \section related + see \ref man_fix_nodes + \section default + none + */ + else if (strcmp(arg[1],"unfix")==0) { + argIdx = 2; + get_field(arg,argIdx,thisField,thisIndex); + string nsetName(arg[argIdx++]); + prescribedDataMgr_->unfix_field(nsetName,thisField,thisIndex); + match = true; + } + + /*! \page man_source fix_modify AtC transfer source + \section syntax + fix_modify AtC transfer source + - = field name valid for type of physics + - = name of set of elements + \section examples + fix_modify atc transfer source temperature middle temporal_ramp 10. 0. + \section description + Add domain sources to the mesh. The units are consistent with LAMMPS's + units for mass, length and time and are defined by the PDE being solved, + e.g. for thermal transfer the balance equation is for energy and source + is energy per time. + \section restrictions + keyword 'all' reserved in element_set name + \section related + see \ref man_remove_source + \section default + none + */ + else if (strcmp(arg[1],"source")==0) { + argIdx = 2; + get_field(arg,argIdx,thisField,thisIndex); + string esetName(arg[argIdx++]); + XT_Function * f = NULL; + // parse constant + if (narg == argIdx+1) { + f = XT_Function_Mgr::instance()->get_constant_function(atof(arg[argIdx])); + } + // parse function + else { + f = XT_Function_Mgr::instance()->get_function(&(arg[argIdx]),narg-argIdx); + } + prescribedDataMgr_->fix_source(esetName,thisField,thisIndex,f); + fieldMask_(thisField,PRESCRIBED_SOURCE) = true; + match = true; + } + + /*! \page man_remove_source fix_modify AtC transfer remove_source + \section syntax + fix_modify AtC transfer remove_source + - = field name valid for type of physics + - = name of set of elements + \section examples + fix_modify atc transfer remove_source temperature groupNAME + \section description + Remove a domain source. + \section restrictions + keyword 'all' reserved in element_set name + \section related + see \ref man_source + \section default + */ + else if (strcmp(arg[1],"remove_source")==0) { + argIdx = 2; + get_field(arg,argIdx,thisField,thisIndex); + string esetName(arg[argIdx++]); + prescribedDataMgr_->unfix_source(esetName,thisField,thisIndex); + fieldMask_(thisField,PRESCRIBED_SOURCE) = false; + match = true; + } + + /*! \page man_fix_flux fix_modify AtC transfer fix_flux + \section syntax + fix_modify AtC transfer fix_flux + - = field name valid for type of physics, temperature | electron_temperature + - = name of set of element faces + \section examples + fix_modify atc transfer fix_flux temperature faceSet 10.0 \n + + \section description + Command for fixing normal fluxes e.g. heat_flux. + This command only prescribes the normal component of the physical flux, e.g. heat (energy) flux. + The units are in AtC units, i.e. derived from the LAMMPS length, time, and mass scales. + \section restrictions + Only normal fluxes (Neumann data) can be prescribed. + \section related + see \ref man_unfix_flux + \section default + */ + else if (strcmp(arg[1],"fix_flux")==0) { + argIdx = 2; + get_field(arg,argIdx,thisField,thisIndex); + string fsetName(arg[argIdx++]); + XT_Function * f = NULL; + // parse constant + if (narg == argIdx+1) { + f = XT_Function_Mgr::instance()->get_constant_function(atof(arg[argIdx])); + } + // parse function + else { + f = XT_Function_Mgr::instance()->get_function(&(arg[argIdx]),narg-argIdx); + } + prescribedDataMgr_->fix_flux(fsetName,thisField,thisIndex,f); + fieldMask_(thisField,PRESCRIBED_SOURCE) = true; + match = true; + } + + /*! \page man_unfix_flux fix_modify AtC transfer unfix_flux + \section syntax + fix_modify AtC transfer fix_flux + - = field name valid for type of physics, temperature | electron_temperature + - = name of set of element faces + \section examples + fix_modify atc transfer unfix_flux temperature faceSet \n + + \section description + Command for removing prescribed normal fluxes e.g. heat_flux, stress. + \section restrictions + \section related + see \ref man_unfix_flux + \section default + */ + else if (strcmp(arg[1],"unfix_flux")==0) { + argIdx = 2; + get_field(arg,argIdx,thisField,thisIndex); + string fsetName(arg[argIdx++]); + prescribedDataMgr_->unfix_flux(fsetName,thisField,thisIndex); + fieldMask_(thisField,PRESCRIBED_SOURCE) = false; + match = true; + } + + // pass off to time filter + else if (strcmp(arg[1],"filter")==0) { + match = timeFilterManager_.modify(narg-2,&arg[2]); + if (match && timeIntegrator_) timeIntegrator_->force_reset(); + } + + } + + // pass off to extrinsic model + else if (strcmp(arg[0],"extrinsic")==0) { + match = extrinsicModelManager_.modify(narg-1,&arg[1]); + } + + /*! \page man_atom_element_map fix_modify AtC transfer atom_element_map + \section syntax + fix_modify AtC transfer atom_element_map \n + - frequency (int) : frequency of updating atom-to-continuum maps based on the + current configuration - only for eulerian + \section examples + fix_modify atc transfer atom_element_map eulerian 100 + \section description + Changes frame of reference from eulerian to lagrangian and sets the + frequency for which the map from atoms to elements is reformed and + all the attendant data is recalculated. + \section restrictions + Cannot change map type after initialization. + \section related + \section default + lagrangian + */ + else if (strcmp(arg[0],"atom_element_map")==0) { + if (strcmp(arg[1],"eulerian")==0) { + atomToElementMapType_ = EULERIAN; + atomToElementMapFrequency_ = atoi(arg[2]); + } + else { + atomToElementMapType_ = LAGRANGIAN; + atomToElementMapFrequency_ = 0; + } + match = true; + } + + /*! \page man_neighbor_reset_frequency fix_modify AtC transfer neighbor_reset_frequency + \section syntax + fix_modify AtC transfer neighbor_reset_frequency + - frequency (int) : reset neighbor map for AtC analysis every "frequency" steps + \section examples + fix_modify AtC transfer neighbor_reset_frequency 1000 + \section description + This command allows the control of how often the lists + of which atoms pairs are in the support of a node + are reformed + \section restrictions + Only currently relevant to Hardy post-processing see \ref man_fix_atc + \section related + This command is different from \ref man_atom_element_map + in that it only concerns neighbor lists internal to ATC not + the map of atom locations to elements which define the shape function + values at atoms. + \section default + The default is for the internal neighbor lists to be recalculated on + the next output step (or sample step if time filtering is used). + */ + else if (strcmp(arg[0],"neighbor_reset_frequency")==0) { + neighborResetFrequency_ = atoi(arg[1]); + match = true; + } + + /*! \page man_read_restart fix_modify AtC transfer read_restart + \section syntax + fix_modify AtC transfer read_restart [file_name] \n + \section examples + fix_modify AtC transfer read_restart ATC_state \n + \section description + Reads the current state of the fields from a named text-based restart + file. + \section restrictions + The restart file only contains fields and their time derivatives. + The reference positions of the atoms and the commands that initialize + the fix are not saved e.g. an identical mesh containing the same atoms + will have to be recreated. + \section related + see write_restart \ref man_write_restart + \section default + none + */ + else if (strcmp(arg[0],"read_restart")==0) { + restartFileName_ = arg[1]; + useRestart_ = true; + match = true; + } + /*! \page man_write_restart fix_modify AtC transfer write_restart + \section syntax + fix_modify AtC transfer write_restart [file_name] \n + \section examples + fix_modify AtC transfer write_restart restart.mydata \n + \section description + Dumps the current state of the fields to a named text-based restart file. + This done when the command is invoked and not repeated, unlike the + similar lammps command. + \section restrictions + The restart file only contains fields and their time derivatives. + The reference positions of the atoms and the commands that initialize + the fix are not saved e.g. an identical mesh containing the same atoms + will have to be recreated. + \section related + see read_restart \ref man_read_restart + \section default + none + */ + else if (strcmp(arg[0],"write_restart")==0) { + string restartFileName(arg[1]); + OUTPUT_LIST data; + write_restart_data(restartFileName,data); + match = true; + } + + + // pass off to fe engine + if (!match) { + match = feEngine_->modify(narg, arg); + } + + return match; + + } + + //-------------------------------------------------- + // parse_boundary_integration + // parses the boundary integration to determine + // the type of boundary integration being used + //-------------------------------------------------- + ATC::ATC_Transfer::BoundaryIntegrationType ATC_Transfer::parse_boundary_integration(int narg, + char **arg, + const set< pair > * boundaryFaceSet) + { + int argIndex = 0; + BoundaryIntegrationType myBoundaryIntegrationType; + if (narg > 0) { + if(strcmp(arg[argIndex],"faceset")==0) { + argIndex++; + myBoundaryIntegrationType = FE_QUADRATURE; + string name(arg[argIndex]); + boundaryFaceSet = & ( (feEngine_->get_feMesh())->get_faceset(name)); + set_boundary_face_set(boundaryFaceSet); + } + else if (strcmp(arg[argIndex],"interpolate")==0) { + myBoundaryIntegrationType = FE_INTERPOLATION; + } + else if (strcmp(arg[argIndex],"no_boundary")==0) { + myBoundaryIntegrationType = NO_QUADRATURE; + } + else { + throw ATC_Error(0,"Bad boundary integration type"); + } + } + else { // default is interpolation + myBoundaryIntegrationType = FE_INTERPOLATION; + } + + set_boundary_integration_type(myBoundaryIntegrationType); + return myBoundaryIntegrationType; + } + + //-------------------------------------------------- + // helper function for parser + // handles : "displacement x" and "temperature" by indexing argIdx + // for fluxes : only normal fluxes can be prescribed + //-------------------------------------------------- + void ATC_Transfer::get_field(/*const*/ char ** args, int & argIdx, + FieldName & thisField, int & thisIndex) + { + string thisName = args[argIdx++]; + thisField = string_to_field(thisName); + map::const_iterator iter = fieldSizes_.find(thisField); + if (iter == fieldSizes_.end()) { + throw ATC_Error(0,"Bad field name: "+thisName); + } + string thisDim = args[argIdx]; + thisIndex = 0; + if (string_to_index(thisDim,thisIndex)) { + if ( !( thisIndex < fieldSizes_[thisField]) ) { + throw ATC_Error(0,"Bad field dimension "+thisDim); + } + argIdx++; + } + } + + void ATC_Transfer::init_filter() + { + if (timeFilterManager_.need_reset()) { + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + + // Allocate restricted fields + fieldRateNdFiltered_[thisField].reset(nNodes_,thisSize); + fieldRateNdOld_[thisField].reset(nNodes_,thisSize); + dot_fieldRateNdFiltered_[thisField].reset(nNodes_,thisSize); + dot_fieldRateNdOld_[thisField].reset(nNodes_,thisSize); + } + } + } + + void ATC_Transfer::set_xref() + { + double **x = lammpsInterface_->xatom(); + for (int i = 0; i < lammpsInterface_->nmax(); i++) { + for (int j = 0; j < nsd_; j++) { + xref_[i][j] = x[i][j]; + } + } + Xprd_ = lammpsInterface_->domain_xprd(); + Yprd_ = lammpsInterface_->domain_yprd(); + Zprd_ = lammpsInterface_->domain_zprd(); + XY_ = lammpsInterface_->domain_xy(); + XZ_ = lammpsInterface_->domain_xz(); + YZ_ = lammpsInterface_->domain_yz(); + + } + + void ATC_Transfer::initialize() + { + // initialized_ is set to true by derived class initialize() + int i, j; + if (!initialized_) { + if (!trackCharge_) { + fieldSizes_.erase(CHARGE_DENSITY); + prescribedDataMgr_->remove_field(CHARGE_DENSITY); + for (i = 0; i < NUM_FLUX; i++) + fieldMask_(CHARGE_DENSITY,i) = false; + } + + // Initialize finite elements shape function, mapping variables + feEngine_->initialize(); + + nNodes_ = feEngine_->get_nNodes(); + nsd_ = feEngine_->get_nsd(); + if (nsd_!=lammpsInterface_->dimension()) + throw ATC_Error(0,"Spatial dimensions inconsistent between LAMMPS and ATC"); + + // set reference position of atoms + set_xref(); + + } + + // determine which elements are strictly in the MD region and mask out for intrinsic fields + // start by finding local atom counts + reset_nlocal(); + int nelts = feEngine_->get_feMesh()->get_nElements(); + elementMask_.reset(nelts); + if (atomQuadForInternal_) { + elementMask_ = true; + } + else { + // determine which elements contain internal atoms + Array hasInternal(nelts); + Array hasInternal_local(nelts); + hasInternal_local = 0; + for (int i = 0; i < nLocal_; ++i) { + DENS_VEC coords(3); + coords(0) = atomicCoords_(0,i); + coords(1) = atomicCoords_(1,i); + coords(2) = atomicCoords_(2,i); + int eltID = feEngine_->get_feMesh()->map_to_element(coords); + hasInternal_local(eltID) = 1; + } + // swap contributions + lammpsInterface_->logical_or(hasInternal_local.get_ptr(), + hasInternal.get_ptr(),hasInternal.size()); + // determine which elements contain ghost atoms + Array hasGhost(nelts); + Array hasGhost_local(nelts); + hasGhost_local = 0; + for (int i = 0; i < nLocalGhost_; ++i) { + DENS_VEC coords(3); + coords(0) = ghostAtomCoords_(0,i); + coords(1) = ghostAtomCoords_(1,i); + coords(2) = ghostAtomCoords_(2,i); + int eltID = feEngine_->get_feMesh()->map_to_element(coords); + hasGhost_local(eltID) = 1; + } + //swap contributions + lammpsInterface_->logical_or(hasGhost_local.get_ptr(), + hasGhost.get_ptr(),hasGhost.size()); + for (int i = 0; i < nelts; ++i) + elementMask_(i) = !hasInternal(i) || hasGhost(i); + } + set & nullElements = feEngine_->null_elements(); + set::const_iterator iset; + for (iset = nullElements.begin(); iset != nullElements.end(); iset++) { + int ielem = *iset; + elementMask_(ielem) = false; + } + + // set atomic weights + reset_shape_functions(); // creates atom->element map + set_atomic_weights(); + + // group atoms by material + int nMatls = physicsModel_->get_nMaterials(); + atomMaterialGroups_.reset(nMatls); + for (int i = 0; i < nLocal_; i++) { + int matId = elementToMaterialMap_(atomToElementMap_(i)); + atomMaterialGroups_(matId).insert(i);// not internalToAtom_ + } + if (atomToElementMapType_ == EULERIAN && nMatls >1 ) { + throw ATC_Error(0," multiple materials & eulerian map not supported"); + } + + // compute necessary restructuring matrices + maskMat_.reset(nNodes_,nNodes_); + if (!useLocalizedLambda_) { + for (i = 0; i < nNodes_; ++i) + if (shpWeight_(i,i) > 0.) + maskMat_(i,i) = 1.; + } + else { // select nodes for thermostat + // 1) their shape functions have both mask and ghost atoms in their support + DENS_VEC hasInternalLocal(nNodes_); + DENS_VEC hasInternal(nNodes_); + if (nLocal_>0) hasInternalLocal = shpFcn_.col_sum(); + lammpsInterface_->allsum(hasInternalLocal.get_ptr(),hasInternal.get_ptr(),nNodes_); + + DENS_VEC hasGhostLocal(nNodes_); + DENS_VEC hasGhost(nNodes_); + if (nLocalGhost_>0) hasGhostLocal = shpFcnGhost_.col_sum(); + lammpsInterface_->allsum(hasGhostLocal.get_ptr(),hasGhost.get_ptr(),nNodes_); + + for (i = 0; i < nNodes_; ++i) { + if (hasInternal(i) > 0. && hasGhost(i) > 0.) { + maskMat_(i,i) = 1.; + } + } + + // 2) they are in a specified boundary faceset + set::const_iterator iter; + for (iter = boundaryFaceNames_.begin(); iter != boundaryFaceNames_.end(); iter++) { + set nodeSet; + feEngine_->get_feMesh()->faceset_to_nodeset(*iter,nodeSet); + set::const_iterator nodeIter; + for (nodeIter = nodeSet.begin(); nodeIter != nodeSet.end(); nodeIter++) { + maskMat_(*nodeIter,*nodeIter) = 1.; + } + } + } + + // determine non-zero values and row-column indices + // also used in creating overlap map and mapping from atoms to elements + // note we assume the overlap map is time-independent + nodeToOverlapMap_.reset(nNodes_); + nNodeOverlap_ = 0; + for (i = 0; i < nNodes_; i++) { + if (maskMat_(i,i) > 0.) { + nodeToOverlapMap_(i) = nNodeOverlap_; + nNodeOverlap_++; + } + else { + nodeToOverlapMap_(i) = -1; + } + } + + overlapToNodeMap_.reset(nNodeOverlap_); + int counter = 0; + for (i = 0; i < nNodes_; i++) { + if (nodeToOverlapMap_(i)!=-1) { + overlapToNodeMap_(counter) = i; + counter++; + } + } + + // reset local overlap shape function storage + reset_NhatOverlap(); + + // Shape sparse matrix used in Hoover-type thermostats + // first get local pattern sized nNodeOverlap X nNodeOverlap + //compute_consistent_md_mass_matrix(NhatOverlap_,tmp); + DENS_MAT tmpLocal(nNodeOverlap_,nNodeOverlap_); + DENS_MAT tmp(nNodeOverlap_,nNodeOverlap_); + if (nLocalLambda_>0) { + DENS_VEC T(nLocalLambda_); + T = 1.0; + for (i = 0; i < nNodeOverlap_; i++) { + for (j = 0; j < nNodeOverlap_; j++) { + for (int iatom = 0; iatom < nLocalLambda_; iatom++) { + tmpLocal(i,j) += NhatOverlap_(iatom,i)*T(iatom)*NhatOverlap_(iatom,j); + } + } + } + } + // second accumulate total pattern + lammpsInterface_->allsum(tmpLocal.get_ptr(), tmp.get_ptr(), tmp.size()); + // third extract non-zero entries & construct + M_T_Template.reset(nNodeOverlap_,nNodeOverlap_); + for (i = 0; i < nNodeOverlap_; i++) { + for (j = 0; j < nNodeOverlap_; j++) { + if (abs(tmp(i,j))>0) { + M_T_Template.add(i,j,0.); + } + } + } + M_T_Template.compress(); + + // \int_\Omega N_I dV + NodeVolumes_.reset(nNodes_,nNodes_); + invNodeVolumes_.reset(nNodes_,nNodes_); + feEngine_->compute_lumped_mass_matrix(NodeVolumes_); + invNodeVolumes_ = NodeVolumes_.inv(); + + // this is exact only for uniform meshes and certain types of atomic weights + // \int_{\Omega_MD} N_I dV = \sum_\alpha N_I\alpha V_\alpha + // DENS_VEC atom_weights_vec(atomicWeights_.size(), + // atomicWeights_.get_ptr()); + DENS_VEC subsetNodeVolume(NodeVolumes_.size()); + DENS_VEC unitVec(atomicWeights_.size()); + unitVec = 1.0; + restrict_unscaled(unitVec, subsetNodeVolume); + fluxMask_.reset(invNodeVolumes_* subsetNodeVolume); + DIAG_MAT id(fluxMask_.nRows(),fluxMask_.nCols()); + id = 1.0; + fluxMaskComplement_ = id + -1.0*fluxMask_; + + // set flux masks for nodes we can tell by geometry + DENS_VEC hasInternalLocal(nNodes_); + DENS_VEC hasInternal(nNodes_); + if (nLocal_>0) hasInternalLocal = shpFcn_.col_sum(); + lammpsInterface_->allsum(hasInternalLocal.get_ptr(),hasInternal.get_ptr(),nNodes_); + + DENS_VEC hasGhostLocal(nNodes_); + DENS_VEC hasGhost(nNodes_); + if (nLocalGhost_>0) hasGhostLocal = shpFcnGhost_.col_sum(); + lammpsInterface_->allsum(hasGhostLocal.get_ptr(),hasGhost.get_ptr(),nNodes_); + + for (i = 0; i < nNodes_; ++i) { + if (hasInternal(i) > 0.) { + if (hasGhost(i) == 0.) { + fluxMask_(i,i) = 1.; + fluxMaskComplement_(i,i) = 0.; + } + } + else { + fluxMask_(i,i) = 0.; + fluxMaskComplement_(i,i) = 1.; + } + } + + // Initialize fields and set initial conditions + if (!initialized_) { + nodeType_.reset(nNodes_); + for (int iNode = 0; iNode < nNodes_; ++iNode) + nodeType_(iNode) = check_shape_function_type(iNode); + + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + + // Allocate fields, initialize to default values, set up initial schedule + fields_[thisField].reset(nNodes_,thisSize); + dot_fields_[thisField].reset(nNodes_,thisSize); + ddot_fields_[thisField].reset(nNodes_,thisSize); + dddot_fields_[thisField].reset(nNodes_,thisSize); + + dot_fieldsMD_[thisField].reset(nNodes_,thisSize); + ddot_fieldsMD_[thisField].reset(nNodes_,thisSize); + dot_dot_fieldsMD_[thisField].reset(nNodes_,thisSize); + + // Allocate restricted fields + fieldRateNdFiltered_[thisField].reset(nNodes_,thisSize); + fieldRateNdOld_[thisField].reset(nNodes_,thisSize); + dot_fieldRateNdFiltered_[thisField].reset(nNodes_,thisSize); + dot_fieldRateNdOld_[thisField].reset(nNodes_,thisSize); + + fieldNdFiltered_[thisField].reset(nNodes_,thisSize); + fieldNdOld_[thisField].reset(nNodes_,thisSize); + + // Allocate auxilliary storage + auxStorage_[thisField].reset(nNodes_,thisSize); + + // Dimension finite element rhs matrix + rhs_[thisField].reset(nNodes_,thisSize); + rhsAtomDomain_[thisField].reset(nNodes_,thisSize); + sources_[thisField].reset(nNodes_,thisSize); + extrinsicSources_[thisField].reset(nNodes_,thisSize); + boundaryFlux_[thisField].reset(nNodes_,thisSize); + + if (is_intrinsic(thisField)) { + // compute FE mass matrix in full domain + massMatInv_[thisField].reset(nNodes_,nNodes_); + Array massMask(1); + massMask(0) = thisField; + feEngine_->compute_lumped_mass_matrix(massMask,fields_,physicsModel_, + elementToMaterialMap_,massMats_, + &elementMask_); + + // atomic quadrature for FE mass matrix in atomic domain + map massMatAq; + feEngine_->compute_lumped_mass_matrix(massMask,fields_,physicsModel_, + atomMaterialGroups_,atomicWeightsMask_, + shpFcnMasked_,massMatAq); + // remove contributions from overlap by approximate quadrature + // and fully remove contributions from internal + massMats_[thisField] -= massMatAq[thisField]; + for (int iNode = 0; iNode < nNodes_; iNode++) + if (nodeType_(iNode)==MD_ONLY) + massMats_[thisField](iNode,iNode) = 0.; + + // set up mass MD matrices + massMatMDInv_[thisField].reset(nNodes_,nNodes_); + compute_md_mass_matrix(thisField,massMatsMD_); + massMats_[thisField] += massMatsMD_[thisField]; + + // compute inverse mass matrices since we're using lumped masses + for (int iNode = 0; iNode < nNodes_; iNode++) { + if (fabs(massMatsMD_[thisField](iNode,iNode))>0) { + massMatMDInv_[thisField](iNode,iNode) = 1./massMatsMD_[thisField](iNode,iNode); + } + if (fabs(massMats_[thisField](iNode,iNode))>0) { + massMatInv_[thisField](iNode,iNode) = 1./massMats_[thisField](iNode,iNode); + } + } + } + else { + // no MD mass matrices needed, regular matrices computed in extrinsic model + massMats_[thisField].reset(nNodes_,nNodes_); + massMatInv_[thisField].reset(nNodes_,nNodes_); + } + } + + // compute inverse consistent mass for the continuity equation + SPAR_MAT consistentMass; + feEngine_->compute_mass_matrix(consistentMass); + consistentMass.compress(); + consistentMassInverse_ = inv(consistentMass.dense_copy()); + + + // Apply integration masking and new ICs + // initialize schedule derivatives + try { + set_initial_conditions(); + } + catch (ATC::ATC_Error& atcError) { + if (!useRestart_) + throw; + } + } + + // set lists of fixed nodes per field + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + isFixedNode_[thisField].reset(nNodes_,thisSize); + for (int i = 0; i < nNodes_; i++) + for (int j = 0; j < thisSize; j++) + isFixedNode_[thisField](i,j) = prescribedDataMgr_->is_fixed(i,thisField,j); + } + + return; + } + + void ATC_Transfer::pre_final_integrate() + { + // atomSwtich is triggered by a lammps call to pre_exchange + // (see ATC_Transfer.h) + int localSwitch = 0; + if (atomSwitch_ || resetSwitch_) localSwitch = 1; + lammpsInterface_->int_allmax(&localSwitch,&globalSwitch_); + if (globalSwitch_>0) { + reset_nlocal(); + reset_shape_functions(); + set_atomic_weights(); + reset_NhatOverlap(); + atomSwitch_ = false; + globalSwitch_ = 0; + } + else if (atomToElementMapType_ == EULERIAN + && stepCounter_ % atomToElementMapFrequency_ == 0 ) { + reset_coordinates(); + reset_shape_functions(); + set_atomic_weights(); + reset_NhatOverlap(); + } + + } + + void ATC_Transfer::init_integrate_velocity() + { + int nlocal = lammpsInterface_->nlocal(); + int *type = lammpsInterface_->atom_type(); + int *mask = lammpsInterface_->atom_mask(); + double *mass = lammpsInterface_->atom_mass(); + double **v = lammpsInterface_->vatom(); + double **f = lammpsInterface_->fatom(); + double dtf = 0.5 * lammpsInterface_->dt() * lammpsInterface_->ftm2v(); + + if (mass) { + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit_) { + double dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + } + } + + } else { + double *rmass = lammpsInterface_->atom_rmass(); + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit_) { + double dtfm = dtf / rmass[i]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + } + } + } + } + + void ATC_Transfer::init_integrate_position() + { + int nlocal = lammpsInterface_->nlocal(); + double **x = lammpsInterface_->xatom(); + double **v = lammpsInterface_->vatom(); + double dtv = lammpsInterface_->dt(); + int *mask = lammpsInterface_->atom_mask(); + + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit_) { + x[i][0] += dtv * v[i][0]; + x[i][1] += dtv * v[i][1]; + x[i][2] += dtv * v[i][2]; + } + } + } + + void ATC_Transfer::final_integrate() + { + int nlocal = lammpsInterface_->nlocal(); + int *type = lammpsInterface_->atom_type(); + int *mask = lammpsInterface_->atom_mask(); + double *mass = lammpsInterface_->atom_mass(); + double **v = lammpsInterface_->vatom(); + double **f = lammpsInterface_->fatom(); + double dtf = 0.5 * lammpsInterface_->dt() * lammpsInterface_->ftm2v(); + + if (mass) { + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit_) { + double dtfm = dtf / mass[type[i]]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + } + } + + } else { + double *rmass = lammpsInterface_->atom_rmass(); + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit_) { + double dtfm = dtf / rmass[i]; + v[i][0] += dtfm * f[i][0]; + v[i][1] += dtfm * f[i][1]; + v[i][2] += dtfm * f[i][2]; + } + } + } + } + + + void ATC_Transfer::finish() + { + // possibly resetting flags and deallocating corresponding memory + + // FE Engine + feEngine_->finish(); + + return; + } + + //-------------------------------------------------------------- + // create_physics_model + // - method to create physics model + //-------------------------------------------------------------- + void ATC_Transfer::create_physics_model(const PhysicsType & physicsType, + string matFileName) + { + if (physicsModel_) { + throw ATC_Error(0,"Attempted to create PhysicsModel multiple times in ATC_Transfer"); + } + // Create PhysicsModel based on physicsType + switch (physicsType) { + case NO_PHYSICS : + break; + case THERMAL : + physicsModel_ = new PhysicsModelThermal(matFileName, this); + break; + default: + throw ATC_Error(0,"Unknown physics type in ATC_Transfer::create_physics_model"); + } + + } + + //-------------------------------------------------------------- + /** method to trigger construction of mesh data after mesh construction */ + //-------------------------------------------------------------- + void ATC_Transfer::initialize_mesh_data(void) + { + int nelts = feEngine_->get_feMesh()->get_nElements(); + elementToMaterialMap_.reset(nelts); + elementToMaterialMap_ = 0; + + construct_prescribed_data_manager(); + } + + //-------------------------------------------------------------- + /** method to add new fields to the included list */ + //-------------------------------------------------------------- + void ATC_Transfer::add_fields(map & newFieldSizes) + { + map::const_iterator field; + for (field = newFieldSizes.begin(); field!=newFieldSizes.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + if (fieldSizes_.find(thisField)==fieldSizes_.end()) { + fieldSizes_[thisField] = thisSize; + } + } + } + +}; // namespace ATC diff --git a/lib/atc/ATC_Transfer.h b/lib/atc/ATC_Transfer.h new file mode 100644 index 0000000000..e41d218f21 --- /dev/null +++ b/lib/atc/ATC_Transfer.h @@ -0,0 +1,952 @@ +// ATC_Transfer : a base class for atom-continuum transfers & control +// (derived classes are physics dependent) +// note init() must be called before most functions will work +// we expect only one derived class to exist, i.e. don't call initilize/modify/etc more than once + +// NOTE: implement print + +#ifndef ATC_TRANSFER_H +#define ATC_TRANSFER_H + +// ATC_Transfer headers +#include "PhysicsModel.h" +#include "MatrixLibrary.h" +#include "Array.h" +#include "Array2D.h" +#include "OutputManager.h" +#include "XT_Function.h" +#include "FE_Element.h" +#include "TimeFilter.h" +#include "LammpsInterface.h" +#include "FE_Engine.h" +#include "ExtrinsicModel.h" + +// Other headers +#include +#include + +using namespace std; + +namespace ATC { + + // Forward declarations + class PrescribedDataManager; + class TimeIntegrator; + + /** + * @class ATC_Transfer + * @brief Base class for atom-continuum transfer operators + */ + + class ATC_Transfer { + + public: + // NOTE should remove friends and use existing ATC hooks + friend class ExtrinsicModel; // friend is not inherited + friend class ExtrinsicModelTwoTemperature; + friend class ExtrinsicModelDriftDiffusion; + friend class ExtrinsicModelElectrostatic; + friend class ExtrinsicModelElectrostaticElastic; + + //--------------------------------------------------------------- + /** \name enumerated types */ + //--------------------------------------------------------------- + /*@{*/ + /** boundary integration */ + enum BoundaryIntegrationType { + NO_QUADRATURE=0, + FE_QUADRATURE, + FE_INTERPOLATION + }; + /** boundary integration */ + enum IntegrationDomainType { + FULL_DOMAIN=0, + ATOM_DOMAIN, + FE_DOMAIN + }; + /** atomic weight specification */ + enum AtomicWeightType { + USER=0, + LATTICE, + ELEMENT, + REGION, + GROUP, + MULTISCALE + }; + /** shape function location with respect to MD domain */ + enum ShapeFunctionType { + FE_ONLY = 0, + MD_ONLY, + BOUNDARY + }; + /** */ + enum AtomToElementMapType { + LAGRANGIAN=0, + EULERIAN + }; + /* enumerated type for coupling matrix structure */ + enum MatrixStructure { + FULL=0, // contributions from all nodes + LOCALIZED, // contributions only from nodes with sources + LUMPED // row-sum lumped version of full matrix + }; + /** */ + enum GroupThermostatType { + RESCALE_TEMPERATURE, + RESCALE_TEMPERATURE_RATE, + RESCALE_RATE + }; + /** */ + enum GroupThermostatMomentumControl { + NO_MOMENTUM_CONTROL, + ZERO_MOMENTUM, + PRESERVE_MOMENTUM + }; + /** */ + enum ATC_GroupComputes { LAMBDA_POWER }; + /*@}*/ + + /** constructor */ + ATC_Transfer(void); + + /** destructor */ + virtual ~ATC_Transfer(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre integration run */ + virtual void initialize(); + + /** post integration run : called at end of run or simulation */ + virtual void finish(); + + /** Predictor phase, executed before Verlet */ + virtual void pre_init_integrate() = 0; + + /** Predictor phase, Verlet first step for velocity */ + virtual void init_integrate_velocity(); + + /** Predictor phase, executed between velocity and position Verlet */ + virtual void mid_init_integrate() = 0; + + /** Predictor phase, Verlet first step for position */ + virtual void init_integrate_position(); + + /** Predictor phase, executed after Verlet */ + virtual void post_init_integrate() = 0; + + /** Corrector phase, executed before Verlet */ + virtual void pre_final_integrate(); + + /** Corrector phase, Verlet second step for velocity */ + virtual void final_integrate(); + + /** Corrector phase, executed after Verlet*/ + virtual void post_final_integrate()=0; + + //--------------------------------------------------------------- + /** \name memory management and processor information exchange */ + //--------------------------------------------------------------- + /*@{*/ + /** pre_exchange is our indicator that atoms have moved across processors */ + void pre_exchange() { + atomSwitch_ = true; + } + int memory_usage(); + void grow_arrays(int); + void copy_arrays(int, int); + int pack_exchange(int, double *); + int unpack_exchange(int, double *); + int pack_comm(int , int *, double *, int, int *); + void unpack_comm(int, int, double *); + /*@}*/ + + /** Get grouping bit for LAMMPS compatability */ + int get_groupbit() { return groupbit_; } + + //--------------------------------------------------------------- + /** \name Wrappers for calls to lammps region.h information */ + //--------------------------------------------------------------- + /*@{*/ + bool get_region_bounds(const char * regionName, + double &xmin, double &xmax, + double &ymin, double & ymax, + double &zmin, double &zmax, + double &xscale, + double &yscale, + double &zscale); + + /** return pointer to PrescribedDataManager */ + PrescribedDataManager * get_prescribed_data_manager() { + return prescribedDataMgr_; + } + + /** return referece to ExtrinsicModelManager */ + ExtrinsicModelManager & get_extrinsic_model_manager() { + return extrinsicModelManager_; + } + + /** compute scalar for output */ + virtual double compute_scalar() {return 0.;} + + /** compute vector for output */ + virtual double compute_vector(int n) {return 0.;} + /*@}*/ + + //--------------------------------------------------------------- + /** \name access methods for output computation data */ + //--------------------------------------------------------------- + /*@{*/ + int scalar_flag() const {return scalarFlag_;} + int vector_flag() const {return vectorFlag_;} + int size_vector() const {return sizeVector_;} + int scalar_vector_freq() const {return scalarVectorFreq_;}; + int extscalar() const {return extScalar_;}; + int extvector() const {return extVector_;}; + int * extlist() {return extList_;}; + /*@}*/ + + //--------------------------------------------------------------- + /** \name Access methods for data used by various methods */ + //--------------------------------------------------------------- + /*@{*/ + /** access to name FE fields */ + DENS_MAT &get_field(FieldName thisField){return fields_[thisField];}; + /** access to FE field time derivatives */ + DENS_MAT &get_dot_field(FieldName thisField){return dot_fields_[thisField];}; + /** access to name FE source terms */ + DENS_MAT &get_source(FieldName thisField){return sources_[thisField];}; + /** access to name atomic source terms */ + DENS_MAT &get_atomic_source(FieldName thisField){return atomicSources_[thisField];}; + /** access to name extrinsic source terms */ + DENS_MAT &get_extrinsic_source(FieldName thisField){return extrinsicSources_[thisField];}; + /** access to boundary fluxes */ + DENS_MAT &get_boundary_flux(FieldName thisField){return boundaryFlux_[thisField];}; + /** access to nodal fields of atomic variables */ + DENS_MAT &get_atomic_field(FieldName thisField) + { return fieldNdFiltered_[thisField]; }; + /** access to auxilliary storage */ + DENS_MAT &get_aux_storage(FieldName thisField) + { return auxStorage_[thisField]; }; + /** access to all fields */ + FIELDS &get_fields() {return fields_;}; + /** access to all fields rates of change (roc) */ + FIELDS &get_fields_roc() {return dot_fields_;}; + /** access to all boundary fluxes */ + FIELDS &get_boundary_fluxes() {return boundaryFlux_;}; + /** add a new field */ + void add_fields(map & newFieldSizes); + /** access to finite element right-hand side data */ + DENS_MAT &get_field_rhs(FieldName thisField) + { return rhs_[thisField]; }; + /** access to inverse mass matrices */ + DIAG_MAT &get_mass_mat_inv(FieldName thisField) + { return massMatInv_[thisField];}; + DIAG_MAT &get_mass_mat_md(FieldName thisField) + { return massMatsMD_[thisField];}; + /** access FE rate of change */ + DENS_MAT &get_field_roc(FieldName thisField) + { return dot_fields_[thisField]; }; + /** access atomic rate of change contributions to finite element equation */ + DENS_MAT &get_fe_atomic_field_roc(FieldName thisField) + { return fieldRateNdFiltered_[thisField]; }; + /** access to atomic rate of change */ + DENS_MAT &get_atomic_field_roc(FieldName thisField) + {return dot_fieldRateNdFiltered_[thisField]; }; + /** access to second time derivative (2roc) */ + DENS_MAT &get_field_2roc(FieldName thisField) + { return ddot_fields_[thisField]; }; + /** access for field mask */ + Array2D &get_field_mask() {return fieldMask_;}; + /** access for shape function weigths */ + DIAG_MAT &get_shape_function_weights(){return shpWeight_;}; + /** access to template matrix for control equations */ + SPAR_MAT &get_m_t_template(){return M_T_Template;}; + /** access for mapping from global nodes to nodes overlapping MD */ + Array &get_node_to_overlap_map(){return nodeToOverlapMap_;}; + /** access for mapping from nodes overlapping MD to global nodes */ + Array &get_overlap_to_node_map(){return overlapToNodeMap_;}; + /** number of nodes whose shape function support overlaps with MD */ + int get_nNode_overlap(){return nNodeOverlap_;}; + /** check if atomic quadrature is being used for MD_ONLY nodes */ + bool atom_quadrature_on(){return atomQuadForInternal_;}; + /** check if lambda is localized */ + bool use_localized_lambda(){return useLocalizedLambda_;}; + /** check if matrix should be lumpted for lambda solve */ + bool use_lumped_lambda_solve(){return useLumpedLambda_;}; + /** get scaled shape function matrix */ + SPAR_MAT &get_nhat_overlap() {return NhatOverlap_;}; + /** get scaled shape function matrix weights */ + DENS_VEC &get_nhat_overlap_weights() {return NhatOverlapWeights_;}; + /** get map general atomic shape function matrix to overlap region */ + SPAR_MAT &get_atom_to_overlap_map() {return Trestrict_;}; + /** internal atom to global map */ + Array &get_internal_to_atom_map() {return internalToAtom_;}; + /** ghost atom to global map */ + Array &get_ghost_to_atom_map() {return ghostToAtom_;}; + /** get number of unique FE nodes */ + int get_nNodes() {return nNodes_;}; + /** get number of spatial dimensions */ + int get_nsd() {return nsd_;}; + /** get number of ATC internal atoms on this processor */ + int get_nlocal() {return nLocal_;}; + /** get total number of LAMMPS atoms on this processor */ + int get_nlocal_total() {return nLocalTotal_;}; + /** get number of ATC ghost atoms on this processor */ + int get_nlocal_ghost() {return nLocalGhost_;}; + /** get number of ATC mask atoms on this processor */ + int get_nlocal_mask() {return nLocalMask_;}; + /** get number of ATC atoms used in lambda computation on this processor */ + int get_nlocal_lambda() {return nLocalLambda_;}; + /** get number of ATC internal atoms */ + int get_ninternal() {return nInternal_;} + /** get number of ATC ghost atoms */ + int get_nghost() {return nGhost_;}; + /** get the current simulation time */ + double get_sim_time() {return simTime_;}; + /** access to lammps atomic positions */ + double ** get_x() {return lammpsInterface_->xatom();}; + /** access to lammps atomic velocities */ + double ** get_v() {return lammpsInterface_->vatom();}; + /** access to lammps atomic forces */ + double ** get_f() {return lammpsInterface_->fatom();}; + /** access to physics model */ + PhysicsModel * get_physics_model() {return physicsModel_; }; + /** access to time filter */ + TimeFilterManager * get_time_filter_manager() {return &timeFilterManager_;}; + /** access to time integrator */ + TimeIntegrator * get_time_integrator() {return timeIntegrator_;}; + /** access to FE engine */ + FE_Engine * get_fe_engine() {return feEngine_;}; + /** access to faceset names */ + const set &get_faceset(const string & name) const {return (feEngine_->get_feMesh())->get_faceset(name);}; + /** access to overlapped ghost shape function */ + SPAR_MAT & get_shape_function_ghost_overlap(){return shpFcnGhostOverlap_;}; + /** return fixed node flag for a field */ + Array2D & get_fixed_node_flags(FieldName thisField) {return isFixedNode_[thisField];}; + Array & get_node_type() {return nodeType_;}; + void get_field(/*const*/ char ** args, int &argIndex, + FieldName &thisField, int &thisIndex); + DIAG_MAT & get_atomic_weights() {return atomicWeights_;}; + bool track_charge() {return trackCharge_;}; + /** access to set of DENS_MATs accessed by tagging */ + DENS_MAT & get_tagged_dens_mat(const string & tag) {return taggedDensMats_[tag];}; + /** access to set of SPAR_MATs accessed by tagging */ + SPAR_MAT & get_tagged_spar_mat(const string & tag) {return taggedSparMats_[tag];}; + /*@}*/ + + //--------------------------------------------------------------- + /** \name boundary integration */ + //--------------------------------------------------------------- + /*@{*/ + void set_boundary_integration_type(int boundaryIntegrationType) + {bndyIntType_ = boundaryIntegrationType;}; + void set_boundary_face_set(const set< pair > * boundaryFaceSet) + {bndyFaceSet_ = boundaryFaceSet;}; + BoundaryIntegrationType parse_boundary_integration + (int narg, char **arg, const set< pair > * boundaryFaceSet); + /*@}*/ + + //--------------------------------------------------------------- + /** \name FE nodesets/sidesets functions */ + //--------------------------------------------------------------- + /*@{*/ + /** mask for computation of fluxes */ + void set_fixed_nodes(); + + /** set initial conditions by changing fields */ + void set_initial_conditions(); + + /** calculate and set matrix of sources_ */ + void set_sources(); + + /** array indicating fixed nodes for all fields */ + map > isFixedNode_; + + /** array indicating if the node is boundary, MD, or FE */ + Array nodeType_; + + /** wrapper for FE_Engine's compute_flux functions */ + void compute_flux(const Array2D & rhs_mask, + const FIELDS &fields, + GRAD_FIELDS &flux, + const PhysicsModel * physicsModel=NULL); + /** wrapper for FE_Engine's compute_boundary_flux functions */ + void compute_boundary_flux(const Array2D & rhs_mask, + const FIELDS &fields, + FIELDS &rhs); + + /** wrapper for FE_Engine's compute_rhs_vector functions */ + void compute_rhs_vector(const Array2D & rhs_mask, + const FIELDS &fields, + FIELDS &rhs, + const IntegrationDomainType domain, // = FULL_DOMAIN + const PhysicsModel * physicsModel=NULL); + + /** evaluate rhs on a specified domain defined by mask and physics model */ + void evaluate_rhs_integral(const Array2D & rhs_mask, + const FIELDS &fields, + FIELDS &rhs, + const IntegrationDomainType domain, + const PhysicsModel * physicsModel=NULL); + + /** assemble various contributions to the heat flux in the atomic region */ + void compute_atomic_sources(const Array2D & rhs_mask, + const FIELDS &fields, + FIELDS &atomicSources); + + /** multiply inverse mass matrix times given data in place */ + // NOTE change massMatInv to map of pointers and allow for consistent mass matrices + // inverted using CG + void apply_inverse_mass_matrix(MATRIX & data, FieldName thisField) + { + data = massMatInv_[thisField]*data; + }; + /** multiply inverse mass matrix times given data and return result */ + void apply_inverse_mass_matrix(const MATRIX & data_in, MATRIX & data_out, + FieldName thisField) + { + data_out = massMatInv_[thisField]*data_in; + }; + + void apply_inverse_md_mass_matrix(MATRIX & data, FieldName thisField) + { data = massMatMDInv_[thisField]*data; }; + void apply_inverse_md_mass_matrix(const MATRIX & data_in, MATRIX & data_out, + FieldName thisField) + { data_out = massMatMDInv_[thisField]*data_in; }; + /*@}*/ + + //---------------------------------------------------------------- + /** \name FE mapping operations */ + //---------------------------------------------------------------- + /*@{*/ + /** Mapping between unique nodes and nodes overlapping MD region */ + void map_unique_to_overlap(const MATRIX & uniqueData, + MATRIX & overlapData); + + /** Mapping between nodes overlapping MD region to unique nodes */ + void map_overlap_to_unique(const MATRIX & overlapData, + MATRIX & uniqueData); + /*@}*/ + + //---------------------------------------------------------------- + /** \name Interscale operators */ + //---------------------------------------------------------------- + /*@{*/ + /** Restrict (number density) : given w_\alpha, w_I = 1/V_I \sum_\alpha N_{I\alpha} w_\alpha */ + void restrict(const MATRIX &atomData, + MATRIX &nodeData); + + /** Restrict based on atomic volume integration : given w_\alpha, w_I = \sum_\alpha N_{I\alpha} w_\alpha V_\alpha */ + void restrict_unscaled(const MATRIX &atomData, + MATRIX &nodeData); + + /** Restrict based on atomic volume integration for volumetric quantities : given w_\alpha, w_I = \sum_\alpha N_{I\alpha} w_\alpha */ + void restrict_volumetric_quantity(const MATRIX &atomData, + MATRIX &nodeData); + void restrict_volumetric_quantity(const MATRIX &atomData, + MATRIX &nodeData, + const SPAR_MAT &shpFcn); + + /** Project based on an atomic volume integration : given w_\alpha, \sum_\alpha M_\alpha w_I = \sum_\alpha N_{I\alpha} w_\alpha V_\alpha (note mass matrix has V_\alpha in it) */ + void project(const MATRIX &atomData, + MATRIX &nodeData, + FieldName thisField); + void project_md(const MATRIX &atomData, + MATRIX &nodeData, + FieldName thisField); + + /** Project based on an atomic volume integration for volumetric quantities : given w_\alpha, \sum_\alpha M_\alpha w_I = \sum_\alpha N_{I\alpha} w_\alpha */ + void project_volumetric_quantity(const MATRIX &atomData, + MATRIX &nodeData, + FieldName thisField); + void project_volumetric_quantity(const MATRIX &atomData, + MATRIX &nodeData, + const SPAR_MAT &shpFcn, + FieldName thisField); + void project_md_volumetric_quantity(const MATRIX &atomData, + MATRIX &nodeData, + FieldName thisField); + void project_md_volumetric_quantity(const MATRIX &atomData, + MATRIX &nodeData, + const SPAR_MAT &shpFcn, + FieldName thisField); + + /** Prolong : given w_I, w_\alpha = \sum_I N_{I\alpha} w_I */ + void prolong(const MATRIX &nodeData, + MATRIX &atomData); + + /** Prolong based on scaled shape functions : given w_I, w_\alpha = \sum_I 1/V_I N_{I\alpha} w_I */ + void prolong_scaled(const MATRIX &nodeData, + MATRIX &atomData); + + /** Prolong onto ghost atoms*/ + void prolong_ghost(const MATRIX &nodeData, + MATRIX &atomData); + /*@}*/ + + //---------------------------------------------------------------- + /** \name Interscale physics constructors */ + //---------------------------------------------------------------- + /*@{*/ + /** Compute atomic masses */ + void compute_atomic_mass(MATRIX &atomicMasses); + + /** Compute atomic charges */ + void compute_atomic_charge(MATRIX &atomicCharges); + + /** Compute atomic temperature + possibly using dot product of two velocities */ + void compute_atomic_temperature(MATRIX &T, + const double * const* v, + double ** v2 = NULL); + /** Compute atomic kinetic energy + possibly using dot product of two velocities */ + void compute_atomic_kinetic_energy(MATRIX &T, + const double * const* v, + double ** v2 = NULL); + /** Compute atomic power : in units dT/dt */ + void compute_atomic_power(MATRIX &dot_T, + const double * const* v, + const double * const* f); + void compute_atomic_temperature_roc(MATRIX &dot_T, + const double * const* v, + const double * const* f); + void compute_atomic_power(MATRIX &dot_T, + const double * const* v, + const MATRIX & f); + void compute_atomic_temperature_roc(MATRIX &dot_T, + const double * const* v, + const MATRIX & f); + + // NOTE change these when physical mass matrix is used + /** Compute magnitude of atomic force */ + void compute_atomic_force_strength(MATRIX &forceStrength, + const double * const* f); + void compute_atomic_force_strength(MATRIX &forceStrength, + const MATRIX & f); + void compute_atomic_force_dot(MATRIX &forceDot, + const double * const* f1, + const MATRIX & f2); + + /** Compute lambda power at atoms : in units dT/dt */ + void compute_atomic_temperature_roc(MATRIX &atomicVdotflam, + const MATRIX &lambda, + const double * const* v); + void compute_atomic_power(MATRIX &atomicVdotflam, + const MATRIX &lambda, + const double * const* v); + + /** Compute lambda power at atoms : in units dT/dt */ + void compute_atomic_lambda_power(MATRIX &atomicPower, + const MATRIX &force, + const double * const* v); + void compute_atomic_temperature_lambda_roc(MATRIX &atomicPower, + const MATRIX &force, + const double * const* v); + + /** Compute lambda power at atoms with explicit application */ + void compute_lambda_power_explicit(MATRIX &lambdaPower, + const MATRIX &lambda, + const double * const* v, + const double dt); + + /** Compute atomic position */ + void compute_atomic_position(DENS_MAT &atomicDisplacement, + const double * const* x); + + /** Compute atomic center of mass */ + void compute_atomic_centerOfMass(DENS_MAT &atomicCom, + const double * const* x); + + /** Compute atomic displacement */ + void compute_atomic_displacement(DENS_MAT &atomicDisplacement, + const double * const* x); + + /** Compute atomic mass displacement */ + void compute_atomic_centerOfMass_displacement(DENS_MAT &atomicMd, + const double * const* x); + + /** Compute atomic velocity */ + void compute_atomic_velocity(DENS_MAT &atomicVelocity, + const double * const* v); + + /** Compute atomic momentum */ + void compute_atomic_momentum(DENS_MAT &atomicMomentum, + const double * const* v); + + /** Compute atomic acceleration */ + void compute_atomic_acceleration(DENS_MAT &atomicAcceleration, + const double * const* f); + + /** Compute atomic force */ + void compute_atomic_force(DENS_MAT &atomicForce, + const double * const* f); + /*@}*/ + + /** allow FE_Engine to construct ATC structures after mesh is constructed */ + void initialize_mesh_data(void); + + protected: + + /** pointer to lammps interface class */ + LammpsInterface * lammpsInterface_; + + /** pointer to physics model */ + PhysicsModel * physicsModel_; + + /** manager for extrinsic models */ + ExtrinsicModelManager extrinsicModelManager_; + + /** method to create physics model */ + void create_physics_model(const PhysicsType & physicsType, + string matFileName); + + /** global flag to indicate atoms have changed processor + keyed off of atomSwitch (see pre_exchange() which sets atomSwitch =true) + */ + int globalSwitch_ ; + + /** a global flag to trigger reset of shape function at will */ + bool resetSwitch_; + + /** flag on if initialization has been performed */ + bool initialized_; + + /** flag to determine if charge is tracked */ + bool trackCharge_; + + TimeFilterManager timeFilterManager_; + TimeIntegrator * timeIntegrator_; + + /** finite element handler */ + FE_Engine * feEngine_; + + /** prescribed data handler */ + PrescribedDataManager * prescribedDataMgr_; + + /** reference atomic coordinates */ + DENS_MAT atomicCoords_; + DENS_MAT atomicCoordsMask_; + + /** number of unique FE nodes */ + int nNodes_; + + /** Number of Spatial Dimensions */ + int nsd_; + + /** data for handling atoms crossing processors */ + bool atomSwitch_; + + /** reference position of the atoms */ + double ** xref_; + double Xprd_,Yprd_,Zprd_; + double XY_,YZ_,XZ_; + void set_xref(); + + /** current time in simulation */ + double simTime_; + + /** re-read reference positions */ + bool readXref_; + string xRefFile_; + + //--------------------------------------------------------------- + /** \name FE nodesets/sidesets data */ + //--------------------------------------------------------------- + /*@{*/ + /** mask for computation of fluxes */ + Array2D fieldMask_; + + /** sources */ + FIELDS sources_; + FIELDS atomicSources_; + FIELDS extrinsicSources_; + + /** boundary flux quadrature */ + int bndyIntType_; + const set< pair > * bndyFaceSet_; + set boundaryFaceNames_; + /*@}*/ + + //--------------------------------------------------------------- + /** \name output data */ + //--------------------------------------------------------------- + /*@{*/ + /** base name for output files */ + string outputPrefix_; + + /** output frequency */ + int outputFrequency_; + + /** sample frequency */ + int sampleFrequency_; + + /** step counter */ + int stepCounter_; + + /** sample counter */ + int sampleCounter_; + + /** atomic output */ + /** base name for output files */ + string outputPrefixAtom_; + + /** output frequency */ + int outputFrequencyAtom_; + + /** output object */ + OutputManager mdOutputManager_; + set atomicOutputMask_; + /*@}*/ + //--------------------------------------------------------------- + /** \name output functions */ + //--------------------------------------------------------------- + /*@{*/ + void output(); + void atomic_output(); + /*@}*/ + + + //--------------------------------------------------------------- + /** \name member data related to compute_scalar() and compute_vector() */ + //--------------------------------------------------------------- + /*@{*/ + int scalarFlag_; // 0/1 if compute_scalar() function exists + int vectorFlag_; // 0/1 if compute_vector() function exists + int sizeVector_; // N = size of global vector + int scalarVectorFreq_; // frequency compute s/v data is available at + int extScalar_; // 0/1 if scalar is intensive/extensive + int extVector_; // 0/1/-1 if vector is all int/ext/extlist + int *extList_; // list of 0/1 int/ext for each vec component + /*@}*/ + + //--------------------------------------------------------------- + /** \name fields and necessary data for FEM */ + //--------------------------------------------------------------- + /*@{*/ + map fieldSizes_; + FIELDS fields_; + map massMats_; + map massMatInv_; + map massMatsMD_; + map massMatMDInv_; + virtual void compute_md_mass_matrix(FieldName thisField, + map & massMats) {}; + DENS_MAT consistentMassInverse_; + FIELDS rhs_; // for pde + FIELDS rhsAtomDomain_; // for thermostat + FIELDS boundaryFlux_; // for thermostat & rhs pde + /*@}*/ + + + //--------------------------------------------------------------- + /** \name time integration and filtering fields */ + //--------------------------------------------------------------- + /*@{*/ + + FIELDS dot_fields_; + FIELDS ddot_fields_; + FIELDS dddot_fields_; + FIELDS dot_fieldsMD_; + FIELDS ddot_fieldsMD_; + FIELDS dot_dot_fieldsMD_; + + /** Restricted Fields */ + FIELDS fieldNdOld_; + FIELDS fieldNdFiltered_; + FIELDS fieldRateNdOld_; + FIELDS fieldRateNdFiltered_; + FIELDS dot_fieldRateNdOld_; + FIELDS dot_fieldRateNdFiltered_; + + /** auxilliary storage */ + FIELDS auxStorage_; + /*@}*/ + + + //--------------------------------------------------------------- + /** \name quadrature weights */ + //--------------------------------------------------------------- + /*@{*/ + DIAG_MAT NodeVolumes_; + DIAG_MAT invNodeVolumes_; + /** atomic quadrature integration weights (V_\alpha) */ + DIAG_MAT atomicWeights_; + DIAG_MAT atomicWeightsMask_; + double atomicVolume_; // global atomic volume for homogeneous set of atoms + map Valpha_; + AtomicWeightType atomWeightType_; + /** weighting factor per shape function: + shpWeight_(I,I) = 1/N_I = 1/(\sum_\alpha N_{I\alpha}) */ + DIAG_MAT shpWeight_; + DIAG_MAT fluxMask_; + DIAG_MAT fluxMaskComplement_; + /*@}*/ + //--------------------------------------------------------------- + /** \name quadrature weight function */ + //--------------------------------------------------------------- + /*@{*/ + /** determine weighting method for atomic integration */ + void reset_atomicWeightsLattice(); + void reset_atomicWeightsElement(); + void reset_atomicWeightsRegion(); + void reset_atomicWeightsGroup(); + void reset_atomicWeightsMultiscale(const SPAR_MAT & shapeFunctionMatrix, + DIAG_MAT & atomicVolumeMatrix); + + void compute_consistent_md_mass_matrix(const SPAR_MAT & shapeFunctionMatrix, + SPAR_MAT & mdMassMatrix); + + /** resets shape function matrices based on atoms on this processor */ + virtual void reset_nlocal(); + void reset_coordinates(); + void set_atomic_weights(); + virtual void reset_shape_functions(); + void reset_NhatOverlap(); + /*@}*/ + + //--------------------------------------------------------------- + /** \name atom data */ + //--------------------------------------------------------------- + /*@{*/ + /** bitwise comparisons for boundary (ghost) atoms */ + int groupbit_; + int groupbitGhost_; + set igroups_; + set igroupsGhost_; + + /** number of atoms of correct type, + ghosts are atoms outside our domain of interest + boundary are atoms contributing to boundary flux terms */ + /** Number of "internal" atoms on this processor */ + int nLocal_; + /** Number of atoms on this processor */ + int nLocalTotal_; + int nLocalGhost_; + int nLocalMask_; + int nLocalLambda_; + int nInternal_; + int nGhost_; + Array internalToAtom_; + std::map atomToInternal_; + Array ghostToAtom_; + DENS_MAT ghostAtomCoords_; + /*@}*/ + //---------------------------------------------------------------- + /** \name maps and masks */ + //---------------------------------------------------------------- + /*@{*/ + AtomToElementMapType atomToElementMapType_; + int atomToElementMapFrequency_; + Array atomToElementMap_; + Array ghostAtomToElementMap_; + /** overlap map, from shapeWeights */ + // -1 is no overlap, otherwise entry is overlap index + Array nodeToOverlapMap_; + // mapping from overlap nodes to unique nodes + Array overlapToNodeMap_; + int nNodeOverlap_; + Array elementMask_; + Array elementToMaterialMap_; + Array< set > atomMaterialGroups_; + int regionID_; + bool atomQuadForInternal_; + bool useLocalizedLambda_; + bool useLumpedLambda_; + /*@}*/ + //---------------------------------------------------------------- + /** \name Map related functions */ + //---------------------------------------------------------------- + /*@{*/ + bool check_internal(int eltIdx); + int check_shape_function_type(int nodeIdx); + bool intersect_ghost(int eltIdx); + virtual void set_ghost_atoms() = 0; + /*@}*/ + + //---------------------------------------------------------------- + /** \name shape function matrices */ + //---------------------------------------------------------------- + /*@{*/ + // sparse matrix where columns correspond to global node numbering + // dimensions are numAtoms X numNodes (the transpose of N_{I\alpha} ) + /** shpFcn_ is N_{I\alpha} the un-normalized shape function evaluated at the atoms */ + SPAR_MAT shpFcn_; + vector shpFcnDerivs_; + SPAR_MAT shpFcnGhost_; + SPAR_MAT shpFcnGhostOverlap_; + vector shpFcnDerivsGhost_; + SPAR_MAT shpFcnMasked_; + vector shpFcnDerivsMask_; + Array atomMask_; + + /** map from species string tag to the species density */ + map taggedDensMats_; + /** map from species string tag to shape function and weight matrices */ + map taggedSparMats_; + + /** weighted shape function matrices at overlap nodes + for use with thermostats */ + // dimensions are numAtoms X numNodesOverlap + SPAR_MAT NhatOverlap_; + SPAR_MAT Trestrict_; + DENS_VEC NhatOverlapWeights_; + /*@}*/ + + + //--------------------------------------------------------------- + /** \name thermostat data */ + //--------------------------------------------------------------- + /*@{*/ + /** sparse matrix to store elements needed for CG solve */ + SPAR_MAT M_T_Template; + DIAG_MAT maskMat_; + + bool equilibriumStart_; + //--------------------------------------------------------------- + /** \name time filtering */ + //--------------------------------------------------------------- + /*@{*/ + /** allocate memory for time filter */ + void init_filter(); + void update_filter(MATRIX &filteredQuantity, + const MATRIX &unfilteredQuantity, + MATRIX &unfilteredQuantityOld, + const double dt); + + double get_unfiltered_coef(const double dt); + + void update_filter_implicit(MATRIX &filteredQuantity, + const MATRIX &unfilteredQuantity, + const double dt); + /*@}*/ + + /** group computes : type, group_id -> value */ + map< pair < int, int > , double> groupCompute_; + + /** group computes : type, group_id -> value */ + map< pair < string, FieldName > , double> nsetCompute_; + + /** allow FE_Engine to construct data manager after mesh is constructed */ + void construct_prescribed_data_manager (void); + + //--------------------------------------------------------------- + /** \name restart procedures */ + //--------------------------------------------------------------- + bool useRestart_; + string restartFileName_; + virtual void read_restart_data(string fileName_, OUTPUT_LIST & data); + virtual void write_restart_data(string fileName_, OUTPUT_LIST & data); + void pack_fields(OUTPUT_LIST & data); + + //--------------------------------------------------------------- + /** \name neighbor reset frequency */ + //--------------------------------------------------------------- + int neighborResetFrequency_; + }; + +}; + +#endif diff --git a/lib/atc/ATC_TransferHardy.cpp b/lib/atc/ATC_TransferHardy.cpp new file mode 100644 index 0000000000..e27d1f58d5 --- /dev/null +++ b/lib/atc/ATC_TransferHardy.cpp @@ -0,0 +1,2230 @@ +// ATC_Transfer headers ? +#include "ATC_TransferHardy.h" +#include "ATC_Error.h" +#include "FE_Engine.h" +#include "ATC_HardyKernel.h" +#include "LammpsInterface.h" +#include "Quadrature.h" + +// Other Headers +#include +#include +#include +#include +#include +#include + +static int sgn(double x) { return (int) ((x>0) - (x<0)); } +static int rnd(double x) { return (int) (x+sgn(x)*0.5); } + +using namespace std; + +namespace ATC { + + ATC_TransferHardy::ATC_TransferHardy(string groupName, + string matParamFile) + : ATC_Transfer(), + setRefPE_(false), + setRefPEvalue_(false), + xPointer_(NULL), + kernelFunction_(NULL), + cauchyBornStress_(NULL), + bondOnTheFly_(false), + kernelOnTheFly_(false), + useAtomicShapeFunctions_(true) + { + // assign default "internal" group + int igroup = lammpsInterface_->find_group(groupName.c_str()); + groupbit_ |= lammpsInterface_->group_bit(igroup); + igroups_.insert(igroup); + + nTypes_ = lammpsInterface_->ntypes(); + + + // Defaults + simTime_ = 0.; + + fieldFlags_.reset(NUM_HARDY_FIELDS); + fieldFlags_ = true; + gradFlags_.reset(NUM_HARDY_FIELDS); + gradFlags_ = false; + rateFlags_.reset(NUM_HARDY_FIELDS); + rateFlags_ = false; + + fieldSizes_.reset(NUM_HARDY_FIELDS); + fieldSizes_ = 1; + fieldSizes_(HARDY_DISPLACEMENT) =3; + fieldSizes_(HARDY_MOMENTUM) =3; + fieldSizes_(HARDY_VELOCITY) =3; + fieldSizes_(HARDY_PROJECTED_VELOCITY) =3; + fieldSizes_(HARDY_HEAT_FLUX) =3; + fieldSizes_(HARDY_STRESS) =9; // LAGRANGIAN + fieldSizes_(HARDY_ESHELBY_STRESS) =9; + fieldSizes_(HARDY_CAUCHY_BORN_STRESS) =6; + fieldSizes_(HARDY_TRANSFORMED_STRESS) =9; + fieldSizes_(HARDY_TYPE_CONCENTRATION) =nTypes_; + + atomicOutputMask_.insert("displacement"); + atomicOutputMask_.insert("velocity"); + atomicOutputMask_.insert("temperature"); + atomicOutputMask_.insert("potential_energy"); + atomicOutputMask_.insert("force"); + atomicOutputMask_.insert("virial"); + atomicOutputMask_.insert("centrosymmetry"); + + set_line_quadrature(line_ngauss,line_xg,line_wg); + + int computeID; + // create compute for pe/atom + computeID = lammpsInterface_->atomPE_create(); + if (lammpsInterface_->comm_rank() == 0 ) { + cout << "atomPE compute created with ID: " << computeID << "\n" << flush; + } + + } + + //------------------------------------------------------------------- + ATC_TransferHardy::~ATC_TransferHardy() + { + // Deallocate + if (kernelFunction_) delete kernelFunction_; + } + + //------------------------------------------------------------------- + // called before the beginning of a "run" + void ATC_TransferHardy::initialize() + { + // Base class initalizations + if (!initialized_) { + feEngine_->initialize(); + nNodes_ = feEngine_->get_nNodes(); + nsd_ = feEngine_->get_nsd(); + // set ref positions + set_xref(); + + // set nlocal & internal to atom map + reset_nlocal(); + if (useAtomicShapeFunctions_) { + try { + reset_shape_functions(); + } + catch(bad_alloc&) { + if (lammpsInterface_->comm_rank() == 0) { + cout << " ATC:: insufficient memory for reset_shape_functions() to execute\n"; + throw; + } + } + set_atomic_weights(); + } + + NodeVolumes_.reset(nNodes_,nNodes_); + invNodeVolumes_.reset(nNodes_,nNodes_); + feEngine_->compute_lumped_mass_matrix(NodeVolumes_); + invNodeVolumes_ = inv(NodeVolumes_); + } + + // positions + set_xPointer(); + + // set up workspaces + if (!initialized_) { + + nNodesGlobal_ = feEngine_->get_feMesh()->get_nNodes(); + + // get periodicity and box bounds/lengths + lammpsInterface_->get_box_periodicity(periodicity[0], + periodicity[1],periodicity[2]); + lammpsInterface_->get_box_bounds(box_bounds[0][0],box_bounds[1][0], + box_bounds[0][1],box_bounds[1][1], + box_bounds[0][2],box_bounds[1][2]); + + for (int k = 0; k < 3; k++) { + box_length[k] = box_bounds[1][k] - box_bounds[0][k]; + } + + if (atomToElementMapType_ == EULERIAN) { + fieldSizes_(HARDY_STRESS) =6; + } + + // PE compute + lammpsInterface_->atomPE_init(); + + // ground state for PE + nodalRefPotentialEnergy_.reset(nNodes_,1); + + // copy requested fieldFlags_ settings to outputFlags_ + outputFlags_ = fieldFlags_; + + // perform consistency check on fieldFlags + check_fieldFlags_consistency(); + + // check whether single_enable==0 for stress/heat flux calculation + if (fieldFlags_(HARDY_STRESS) || fieldFlags_(HARDY_HEAT_FLUX)) { + if (lammpsInterface_->single_enable()==0) { + throw ATC_Error(0,"Calculation of Hardy stress field not possible with selected pair type."); + } + } + + // size arrays for requested/required Hardy fields + for(int index=0; index < NUM_HARDY_FIELDS; ++index) { + string name; + int size; + if (fieldFlags_(index)) { + hardy_field_to_string(index,name); + size = fieldSizes_(index); + hardyData_ [name].reset(nNodes_,size); + hardyDataOld_ [name].reset(nNodes_,size); + filteredHardyData_[name].reset(nNodes_,size); + } + } + + // compute table of Hardy bond functions for processors that own atoms + if ((! bondOnTheFly_) + && ( ( fieldFlags_(HARDY_STRESS) + || fieldFlags_(HARDY_ESHELBY_STRESS) + || fieldFlags_(HARDY_HEAT_FLUX) ) ) ) { + try { + compute_bond_matrix(); + } + catch(bad_alloc&) { + if (lammpsInterface_->comm_rank() == 0) { + cout << "\n ATC:: stress/heat_flux will be computed on-the-fly\n"; + bondOnTheFly_ = true; + } + } + } + + // compute shape function matrix for kernel functions + if (kernelFunction_ && (! kernelOnTheFly_)) { + try{ + compute_kernel_matrix(); + } + catch(bad_alloc&) { + if (lammpsInterface_->comm_rank() == 0) { + cout << "\n ATC:: kernel will be computed on-the-fly\n"; + kernelOnTheFly_ = true; + } + } + } + + // compute table of gradient of Hardy functions + if (fieldFlags_(HARDY_ESHELBY_STRESS) + || fieldFlags_(HARDY_CAUCHY_BORN_STRESS) + || fieldFlags_(HARDY_VACANCY_CONCENTRATION)) { + gradFlags_(HARDY_DISPLACEMENT) = true; + } + gradientTable_ = vector(nsd_); + if (gradFlags_.has_member(true)) { + compute_gradient_matrix(); + } + + // construct & initialize filter + init_filter(); + } + initialized_ = true; + + // addstep needs to be done _every_ initialize not just first + lammpsInterface_->atomPE_addstep(lammpsInterface_->ntimestep()+1); + + if (lammpsInterface_->comm_rank() == 0) { + cout << " ATC:: conversion factor for energy/vol -> stress " + << lammpsInterface_->nktv2p() << "\n"; + cout << " ATC:: cutoff radius " + << lammpsInterface_->pair_cutoff() << "\n"; + } + } + + //------------------------------------------------------------------- + // sets initial values of filtered quantities + void ATC_TransferHardy::init_filter() + { + timeFilters_.reset(NUM_HARDY_FIELDS); + sampleCounter_ = 0; + for(int index=0; index < NUM_HARDY_FIELDS; ++index) { + string name; + hardy_field_to_string(index,name); + filteredHardyData_[name] = 0.0; + timeFilters_(index) = timeFilterManager_.construct(); + } + } + + + //------------------------------------------------------------------- + // called after the end of a "run" + void ATC_TransferHardy::finish() + { + // base class + ATC_Transfer::finish(); + } + + //------------------------------------------------------------------- + // this is the parser + bool ATC_TransferHardy::modify(int narg, char **arg) + { + bool match = false; + double val, dval; + + // check to see if it is a transfer class command + if (strcmp(arg[0],"transfer")==0) { + /*! \page man_hardy_fields fix_modify AtC transfer fields + \section syntax + fix_modify AtC transfer fields \n + fix_modify AtC transfer fields \n + - all | none (keyword) = output all or no fields \n + - add | delete (keyword) = add or delete the listed output fields \n + - fields (keyword) = \n + density : mass per unit volume \n + displacement : displacement vector \n + momentum : momentum per unit volume \n + velocity : defined by momentum divided by density \n + projected_velocity : simple kernel estimation of atomic velocities \n + temperature : temperature derived from the relative atomic kinetic energy (as done by Hardy) \n + kinetic_temperature : temperature derived from the full kinetic energy \n + energy : total energy (potential + kinetic) per unit volume \n + number_density : simple kernel estimation of number of atoms per unit volume \n + stress : + Cauchy stress tensor for eulerian analysis (atom_element_map), or + 1st Piola-Kirchhoff stress tensor for lagrangian analysis \n + transformed_stress : + 1st Piola-Kirchhoff stress tensor for eulerian analysis (atom_element_map), or + Cauchy stress tensor for lagrangian analysis \n + heat_flux : spatial heat flux vector for eulerian, + or referential heat flux vector for lagrangian \n + \section examples + fix_modify AtC transfer fields add velocity temperature + \section description + Allows modification of the fields calculated and output by the Hardy + transfer class. The commands are cumulative, e.g.\n + fix_modify AtC transfer fields none \n + followed by \n + fix_modify AtC transfer fields add velocity temperature \n + will only output the velocity and temperature fields. + \section restrictions + Must be used with the hardy AtC transfer, see \ref man_fix_atc. + Currently, the stress and heat flux formulas are only correct for + central force potentials, e.g. Lennard-Jones and EAM + but not Stillinger-Weber. + \section related + See \ref man_hardy_gradients , \ref man_hardy_rates and \ref man_hardy_computes + \section default + All fields are output by default + */ + if (strcmp(arg[1],"fields")==0) { + if (strcmp(arg[2],"all")==0) { + fieldFlags_ = true; + match = true; + } + else if (strcmp(arg[2],"none")==0) { + fieldFlags_ = false; + match = true; + } + else if (strcmp(arg[2],"add")==0) { + hardyFieldName field_name; + for (int i = 3; i < narg; ++i) { + if (string_to_hardy_field(arg[i],field_name)) { + fieldFlags_(field_name) = true; } + else { throw ATC_Error(0,"unsupported Hardy field"); } + } + match = true; + } + else if (strcmp(arg[2],"delete")==0) { + hardyFieldName field_name; + for (int i = 3; i < narg; ++i) { + if (string_to_hardy_field(arg[i],field_name)) { + fieldFlags_(field_name) = false; } + else { throw ATC_Error(0,"unsupported Hardy field"); } + } + match = true; + } + } + + /*! \page man_hardy_gradients fix_modify AtC transfer gradients + \section syntax + fix_modify AtC transfer gradients \n + - add | delete (keyword) = add or delete the calculation of gradients for the listed output fields \n + - fields (keyword) = \n + gradients can be calculated for all fields listed in \ref man_hardy_fields + + \section examples + fix_modify AtC transfer gradients add temperature velocity stress \n + fix_modify AtC transfer gradients delete velocity \n + \section description + Requests calculation and ouput of gradients of the fields from the Hardy + transfer class. These gradients will be with regard to spatial or material + coordinate for eulerian or lagrangian analysis, respectively, as specified by + atom_element_map (see \ref man_atom_element_map ) + \section restrictions + Must be used with the hardy AtC transfer + ( see \ref man_fix_atc ) + \section related + \section default + No gradients are calculated by default + */ + else if (strcmp(arg[1],"gradients")==0) { + if (strcmp(arg[2],"add")==0) { + hardyFieldName field_name; + for (int i = 3; i < narg; ++i) { + if (string_to_hardy_field(arg[i],field_name)) { + gradFlags_(field_name) = true; } + else { throw ATC_Error(0,"unsupported Hardy field"); } + } + match = true; + } + else if (strcmp(arg[2],"delete")==0) { + hardyFieldName field_name; + for (int i = 3; i < narg; ++i) { + if (string_to_hardy_field(arg[i],field_name)) { + gradFlags_(field_name) = false; } + else { throw ATC_Error(0,"unsupported Hardy field"); } + } + match = true; + } + } + + /*! \page man_hardy_rates fix_modify AtC transfer rates + \section syntax + fix_modify AtC transfer rates \n + - add | delete (keyword) = add or delete the calculation of rates (time derivatives) for the listed output fields \n + - fields (keyword) = \n + rates can be calculated for all fields listed in \ref man_hardy_fields + + \section examples + fix_modify AtC transfer rates add temperature velocity stress \n + fix_modify AtC transfer rates delete stress \n + \section description + Requests calculation and ouput of rates (time derivatives) of the fields from the Hardy + transfer class. For eulerian analysis (see \ref man_atom_element_map ), these rates + are the partial time derivatives of the nodal fields, not the full (material) time + derivatives. \n + \section restrictions + Must be used with the hardy AtC transfer + ( see \ref man_fix_atc ) + \section related + \section default + No rates are calculated by default + */ + else if (strcmp(arg[1],"rates")==0) { + if (strcmp(arg[2],"add")==0) { + hardyFieldName field_name; + for (int i = 3; i < narg; ++i) { + if (string_to_hardy_field(arg[i],field_name)) { + rateFlags_(field_name) = true; } + else { throw ATC_Error(0,"unsupported Hardy field"); } + } + match = true; + } + else if (strcmp(arg[2],"delete")==0) { + hardyFieldName field_name; + for (int i = 3; i < narg; ++i) { + if (string_to_hardy_field(arg[i],field_name)) { + rateFlags_(field_name) = false; } + else { throw ATC_Error(0,"unsupported Hardy field"); } + } + match = true; + } + } + + /*! \page man_hardy_computes fix_modify AtC transfer computes + \section syntax + fix_modify AtC transfer computes [per-atom compute id] \n + - add | delete (keyword) = add or delete the calculation of an equivalent continuum field + for the specified per-atom compute as volume or number density quantity \n + - per-atom compute id = name/id for per-atom compute, + fields can be calculated for all per-atom computes available from LAMMPS \n + - volume | number (keyword) = field created is a per-unit-volume quantity + or a per-atom quantity as weighted by kernel functions \n + + \section examples + compute virial all stress/atom \n + fix_modify AtC transfer computes add virial volume \n + fix_modify AtC transfer computes delete virial \n + \n + compute centrosymmetry all centro/atom \n + fix_modify AtC transfer computes add centrosymmetry number \n + \section description + Calculates continuum fields corresponding to specified per-atom computes created by LAMMPS \n + \section restrictions + Must be used with the hardy AtC transfer ( see \ref man_fix_atc ) \n + Per-atom compute must be specified before corresponding continuum field can be requested \n + \section related + See manual page for compute + \section default + No defaults exist for this command + */ + else if (strcmp(arg[1],"computes")==0) { + if (strcmp(arg[2],"add")==0) { + string tag(arg[3]); + int icompute = lammpsInterface_->find_compute(tag.c_str()); + if (icompute < 0) + throw ATC_Error(0,"Could not find compute "+tag); + int normalization = NO_NORMALIZATION; + if (narg > 4) { + if (strcmp(arg[4],"volume")==0) { + normalization = VOLUME_NORMALIZATION; + } + else if (strcmp(arg[4],"number")==0) { + normalization = NUMBER_NORMALIZATION; + } + } + computes_[tag] = normalization; + match = true; + } + else if (strcmp(arg[2],"delete")==0) { + string tag(arg[3]); + if (computes_.find(tag) != computes_.end()) { + computes_.erase(tag); + } + else { + throw ATC_Error(0,tag+" compute is not in list"); + } + match = true; + } + } + + /*! \page man_hardy_kernel fix_modify AtC kernel + \section syntax + fix_modify AtC transfer kernel + - type (keyword) = mesh, step, cell, cubic_cylinder, cubic_sphere, quartic_cylinder, quartic_sphere \n + - parameters :\n + mesh = none\n + step = radius (double) \n + cell = hx, hy, hz (double) or h (double) \n + cubic_cylinder = radius (double) \n + cubic_sphere = radius (double) \n + quartic_cylinder = radius (double) \n + quartic_sphere = radius (double) \n + \section examples + fix_modify AtC transfer kernel cell 1.0 1.0 1.0 + \section description + + \section restrictions + Must be used with the hardy AtC transfer \n + For cylinder kernel types, cylindrical axis is assumed to be in z-direction \n + ( see \ref man_fix_atc ) + \section related + \section default + Default to the mesh based kernel + */ + else if (strcmp(arg[1],"kernel")==0) { + if (kernelFunction_) delete kernelFunction_; + if (strcmp(arg[2],"mesh")==0) { + match = true; + useAtomicShapeFunctions_ = true; + } + else if (strcmp(arg[2],"step")==0) { + double parameters[1] = {atof(arg[3])}; + kernelFunction_ = new ATC_HardyKernelStep(1,parameters); + match = true; + } + else if (strcmp(arg[2],"cell")==0) { + double parameters[3]; + parameters[0] = parameters[1] = parameters[2] = atof(arg[3]); + if (narg > 5) { + for (int i = 1; i < 3; i++) { parameters[i] = atof(arg[3+i]); } + } + kernelFunction_ = new ATC_HardyKernelCell(2,parameters); + match = true; + } + else if (strcmp(arg[2],"cubic_cylinder")==0) { + double parameters[1] = {atof(arg[3])}; // cutoff radius + kernelFunction_ = new ATC_HardyKernelCubicCyl(1,parameters); + match = true; + } + else if (strcmp(arg[2],"cubic_sphere")==0) { + double parameters[1] = {atof(arg[3])}; // cutoff radius + kernelFunction_ = new ATC_HardyKernelCubicSphere(1,parameters); + match = true; + } + else if (strcmp(arg[2],"quartic_cylinder")==0) { + double parameters[1] = {atof(arg[3])}; // cutoff radius + kernelFunction_ = new ATC_HardyKernelQuarticCyl(1,parameters); + match = true; + } + else if (strcmp(arg[2],"quartic_sphere")==0) { + double parameters[1] = {atof(arg[3])}; // cutoff radius + kernelFunction_ = new ATC_HardyKernelQuarticSphere(1,parameters); + match = true; + } + } + + /*! \page man_hardy_on_the_fly fix_modify AtC transfer on_the_fly + \section syntax + fix_modify AtC transfer on_the_fly \n + - bond | kernel (keyword) = specifies on-the-fly calculation of bond or kernel + matrix elements \n + - on | off (keyword) = activate or discontinue on-the-fly mode \n + + \section examples + fix_modify AtC transfer on_the_fly bond on \n + fix_modify AtC transfer on_the_fly kernel \n + fix_modify AtC transfer on_the_fly kernel off \n + \section description + Overrides normal mode of pre-calculating and storing bond pair-to-node and + kernel atom-to-node matrices. If activated, will calculate elements of these + matrices during repeated calls of field computations (i.e. "on-the-fly") and not store them for + future use. \n + on flag is optional - if omitted, on_the_fly will be activated for the specified + matrix. Can be deactivated using off flag. \n + \section restrictions + Must be used with the hardy AtC transfer + ( see \ref man_fix_atc ) + \section related + \section default + By default, on-the-fly calculation is not active (i.e. off). However, code does a memory allocation + check to determine if it can store all needed bond and kernel matrix elements. If this allocation + fails, on-the-fly is activated. \n + */ + else if (strcmp(arg[1],"on_the_fly")==0) { + if (strcmp(arg[2],"bond")==0) { + bondOnTheFly_ = true; + if (narg > 3 && strcmp(arg[3],"off")==0) bondOnTheFly_ = false; + } + else if (strcmp(arg[2],"kernel")==0) { + kernelOnTheFly_ = true; + if (narg > 3 && strcmp(arg[3],"off")==0) kernelOnTheFly_ = false; + } + else { throw ATC_Error(0,"unsupported on_the_fly type"); } + match = true; + } + + /*! \page man_hardy_set fix_modify AtC set + \section syntax + fix_modify AtC transfer set reference_potential_energy + - value (double) : optional user specified zero point for PE + \section examples + fix_modify AtC transfer set reference_potential_energy \n + fix_modify AtC transfer set reference_potential_energy -0.05 \n + \section description + Used to set various quantities for the post-processing algorithms. + Currently it only + sets the zero point for the potential energy density using + the value provided for all nodes, or from the current + configuration of the lattice if no value is provided + \section restrictions + Must be used with the hardy AtC transfer + ( see \ref man_fix_atc ) + \section related + \section default + Defaults to lammps zero point i.e. isolated atoms + */ + else if (strcmp(arg[1],"set")==0) { + if (strcmp(arg[2],"reference_potential_energy")==0) { + if (narg > 3) { + double value = atof(arg[3]); + nodalRefPEvalue_ = value; + setRefPEvalue_ = true; + } + else { // NOTE make this part of initialize + setRefPE_ = true; + } + match = true; + } + } // end "set" + + /*! \page man_boundary_integral fix_modify AtC boundary_integral + \section syntax + fix_modify AtC transfer boundary_integral [field] faceset [name] + - field (string) : name of hardy field + - name (string) : name of faceset + \section examples + fix_modify AtC transfer boundary_integral stress faceset loop1 \n + \section description + Calculates a surface integral of the given field dotted with the + outward normal of the faces and puts output in the "GLOBALS" file + \section restrictions + Must be used with the hardy AtC transfer + ( see \ref man_fix_atc ) + \section related + \section default + none + */ + else if (strcmp(arg[1],"boundary_integral")==0) { + hardyFieldName field; +// if (string_to_hardy_field(arg[2],field)) { } +// else { throw ATC_Error(0,"unsupported Hardy field"); } + if(strcmp(arg[3],"faceset")==0) { + string name(arg[4]); + string field(arg[2]); + const set< pair > * faceSet + = & ( (feEngine_->get_feMesh())->get_faceset(name)); + pair pair_name(name,field); + bndyIntegralData_[pair_name] = faceSet; + match = true; + } + } // end "boundary_integral" + + /*! \page man_contour_integral fix_modify AtC contour_integral + \section syntax + fix_modify AtC transfer contour_integral [field] faceset [name] + - field (string) : name of hardy field + - name (string) : name of faceset + - axis (string) : x or y or z + \section examples + fix_modify AtC transfer contour_integral stress faceset loop1 \n + \section description + Calculates a surface integral of the given field dotted with the + outward normal of the faces and puts output in the "GLOBALS" file + \section restrictions + Must be used with the hardy AtC transfer + ( see \ref man_fix_atc ) + \section related + \section default + none + */ + else if (strcmp(arg[1],"contour_integral")==0) { + hardyFieldName field; +// if (string_to_hardy_field(arg[2],field)) { } +// else { throw ATC_Error(0,"unsupported Hardy field"); } + if(strcmp(arg[3],"faceset")==0) { + string name(arg[4]); + string field(arg[2]); + const set< pair > * faceSet + = & ( (feEngine_->get_feMesh())->get_faceset(name)); + pair pair_name(name,field); + contourIntegralData_[pair_name] = faceSet; + match = true; + } + } // end "contour_integral" + } + + // no match, call base class parser + if (!match) { + match = ATC_Transfer::modify(narg, arg); + } + + return match; + } + + //------------------------------------------------------------------- + // called at the beginning of a timestep + void ATC_TransferHardy::pre_init_integrate() + { + // output initial configuration + if (stepCounter_ == 0 && outputFrequency_ > 0) { + double dt = lammpsInterface_->dt(); + time_filter_pre (dt); + compute_fields(); + // initialize filtered data + for(int index=0; index < NUM_HARDY_FIELDS; ++index) { + string name; + hardy_field_to_string(index,name); + filteredHardyData_[name] = hardyData_[name]; + } + time_filter_post(dt); + compute_boundary_integrals(); + output(); + } + } + //------------------------------------------------------------------- + // called at the end of first half of a timestep + void ATC_TransferHardy::post_init_integrate() + { + } + + //------------------------------------------------------------------- + // called at the begining of second half timestep + void ATC_TransferHardy::pre_final_integrate() + { + // cases to recompute transfer matrices: + // (1) if atoms have changed processors + bool atomSwitch = atomSwitch_; + int localSwitch = atomSwitch_, globalSwitch = 0; + lammpsInterface_->int_allmax(&localSwitch,&globalSwitch); + atomSwitch = globalSwitch; + // (2) if eulerian and we are at the atom_to_element_map reset frequency + // which is used to track the convection of atoms + bool eulerianReset = (atomToElementMapType_ == EULERIAN) + && (stepCounter_ % atomToElementMapFrequency_ ==0); + + bool needsKernel = ( kernelFunction_ && (! kernelOnTheFly_ ) ); + + bool needsBond = (! bondOnTheFly_ ) && + (fieldFlags_(HARDY_STRESS) + || fieldFlags_(HARDY_ESHELBY_STRESS) + || fieldFlags_(HARDY_HEAT_FLUX)); + + // (3) if we suspect neighbor lists changing and are + // at the neighbor reset frequency + int ago = lammpsInterface_->neighbor_ago(); + int interval = outputFrequency_; + if (timeFilterManager_.filter_dynamics() && sampleFrequency_ < interval) + { interval = sampleFrequency_; } + bool neighborReset = (ago <= interval); + if (neighborResetFrequency_ > 0) { + neighborReset = (neighborReset + || (stepCounter_ % neighborResetFrequency_ == 0) ); + } + + if (atomSwitch || eulerianReset) { + reset_nlocal(); + if (needsKernel) { + compute_kernel_matrix(); + } + if (useAtomicShapeFunctions_) { + reset_shape_functions(); + set_atomic_weights(); + } + if (needsBond) { + compute_bond_matrix(); + } + if (gradFlags_.has_member(true)) { + compute_gradient_matrix(); + } + } + else if (neighborReset && needsBond) { + check_pair_map(); // will recompute bond matrix if necessary + } + + } + + //------------------------------------------------------------------- + // called at the end of second half timestep + void ATC_TransferHardy::post_final_integrate() + { + double dt = lammpsInterface_->dt(); + simTime_ += dt; + if (dt == 0.0) simTime_ = stepCounter_; + ++stepCounter_; + + bool output_now = ( (outputFrequency_ > 0) + && ((stepCounter_ % outputFrequency_ == 0)) ); + bool sample_now = ( (output_now) + || (timeFilterManager_.filter_dynamics() && (stepCounter_ % sampleFrequency_ == 0))); + + // compute spatially smoothed quantities + if (sample_now) { + time_filter_pre (dt, output_now); // NOTE drop output_now + compute_fields(); + time_filter_post(dt, output_now); + compute_boundary_integrals(); + } + + // output + if ( output_now ) output(); + if ( (outputFrequencyAtom_ > 0) + && ((stepCounter_ % outputFrequencyAtom_ == 0)) ) + atomic_output(); + + // "1" should be outputFrequency_ or sampleFrequency_ + lammpsInterface_->atomPE_addstep(lammpsInterface_->ntimestep()+1); + } + + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_fields(void) + { + if (setRefPE_) { + compute_potential_energy(nodalRefPotentialEnergy_); + setRefPE_ = false; + } + + if (setRefPEvalue_) { + nodalRefPotentialEnergy_ = nodalRefPEvalue_; + setRefPEvalue_ = false; + } + + +// NOTE remove this + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ** v = lammpsInterface_->vatom(); + double ** x = lammpsInterface_->xatom(); + int atomIdx; + + bool needs_velocity = fieldFlags_(HARDY_VELOCITY); + bool needs_projected_velocity = fieldFlags_(HARDY_PROJECTED_VELOCITY); + bool needs_momentum = fieldFlags_(HARDY_MOMENTUM); + bool needs_density = fieldFlags_(HARDY_DENSITY); + bool needs_displacement = fieldFlags_(HARDY_DISPLACEMENT); + bool needs_temperature = fieldFlags_(HARDY_TEMPERATURE); + bool needs_kinetic_temperature = fieldFlags_(HARDY_KINETIC_TEMPERATURE); + bool needs_stress = fieldFlags_(HARDY_STRESS); + bool needs_eshelby_stress = fieldFlags_(HARDY_ESHELBY_STRESS); + bool needs_cauchy_born_stress = fieldFlags_(HARDY_CAUCHY_BORN_STRESS); + bool needs_heat = fieldFlags_(HARDY_HEAT_FLUX); + bool needs_energy = fieldFlags_(HARDY_ENERGY); + bool needs_number_density = fieldFlags_(HARDY_NUMBER_DENSITY); + bool needs_transformed_stress = fieldFlags_(HARDY_TRANSFORMED_STRESS); + bool needs_vacancy_concentration = fieldFlags_(HARDY_VACANCY_CONCENTRATION); + bool needs_type_concentration = fieldFlags_(HARDY_TYPE_CONCENTRATION); + + // (1) direct quantities + //compute : kernel norm is a _weighted_ number density + if (needs_number_density) { + compute_number_density(hardyData_["number_density"]); + } + // compute: density + if (needs_density) { + compute_mass_density(hardyData_["density"]); + } + // compute: displacement + if (needs_displacement) { + compute_displacement(hardyData_["displacement"],hardyData_["density"]); + } + // compute: momentum + if (needs_momentum) { + compute_momentum(hardyData_["momentum"]); + } + //compute: projected velocity + if (needs_projected_velocity) { + compute_projected_velocity(hardyData_["projected_velocity"]); + } + // compute: velocity + if (needs_velocity) { + compute_velocity(hardyData_["velocity"], + hardyData_["density"], hardyData_["momentum"]); +// NOTE make this only for mesh + compute_variation_velocity(uVariationVelocity_, hardyData_["velocity"]); + } + + // compute : temperature & (full) kinetic temperature + if (needs_temperature) { + compute_temperature(hardyData_["temperature"]); + } + if (needs_kinetic_temperature) { + compute_kinetic_temperature(hardyData_["kinetic_temperature"]); + } + // compute: stress + if (needs_stress) { + compute_stress(hardyData_["stress"]); + } + // compute: heat flux + if (needs_heat) { + compute_heatflux(hardyData_["heat_flux"]); + } + // compute: energy + if (needs_energy) { + compute_total_energy(hardyData_["energy"]); + } + + // (2) derived quantities + // compute: gradients + if (gradFlags_.has_member(true)) { + for(int index=0; index < NUM_HARDY_FIELDS; ++index) { + if (gradFlags_(index)) { + string field; + hardy_field_to_string(index,field); + string grad_field = field + "_gradient"; + if (hardyData_.find(field) == hardyData_.end() ) { + throw ATC_Error(0,"field " + field + " needs to be defined for gradient"); + } + gradient_compute(hardyData_[field], hardyData_[grad_field]); + } + } + } + + // (3) computes + map ::const_iterator iter; + for (iter = computes_.begin(); iter != computes_.end(); iter++) { + string tag = iter->first; + int projection = iter->second; + int ncols = lammpsInterface_->compute_ncols(tag.c_str());; + DENS_MAT atomicData(nLocal_,ncols); + if (ncols == 1) { + double * atomData = lammpsInterface_->compute_scalar_data(tag.c_str()); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + atomicData(i,0) = atomData[atomIdx]; + } + } + else { + double ** atomData = lammpsInterface_->compute_vector_data(tag.c_str()); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int k = 0; k < ncols; k++) { + atomicData(i,k) = atomData[atomIdx][k]; + } + } + } + if (projection == VOLUME_NORMALIZATION) { + project_volume_normalized(atomicData, hardyData_[tag]); + } + else if (projection == NUMBER_NORMALIZATION) { + project_count_normalized(atomicData, hardyData_[tag]); + } + else { + ATC_Transfer::restrict_unscaled(atomicData, hardyData_[tag]); + } + } + + + }// end of compute_fields routine + + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_boundary_integrals(void) + { + if (! bndyIntegralData_.empty()) { + map < pair, const set< PAIR > * >::const_iterator iter; + DENS_MAT values; + for (iter = bndyIntegralData_.begin(); + iter != bndyIntegralData_.end(); iter++) { + string bndyName = (iter->first).first; + string fieldName = (iter->first).second; + const set< PAIR > & faceSet = *(iter->second); + const DENS_MAT & data = (hardyData_.find(fieldName)->second); + feEngine_->field_surface_flux(data,faceSet,values); + for (int i = 0; i < values.nRows() ; ++i ) { + string name = bndyName + "_" + fieldName + "_" + + ATC_STRING::tostring(i+1); + feEngine_->add_global(name, values(i,0)); + } + } + } + if (! contourIntegralData_.empty()) { + map < pair, const set< PAIR > * >::const_iterator iter; + DENS_MAT values; + for (iter = contourIntegralData_.begin(); + iter != contourIntegralData_.end(); iter++) { + string bndyName = (iter->first).first; + string fieldName = (iter->first).second; + const set< PAIR > & faceSet = *(iter->second); + const DENS_MAT & data = (hardyData_.find(fieldName)->second); + feEngine_->field_surface_flux(data,faceSet,values,true); + for (int i = 0; i < values.nRows() ; ++i ) { + string name = "contour_"+bndyName + "_" + fieldName + "_" + + ATC_STRING::tostring(i+1); + feEngine_->add_global(name, values(i,0)); + } + } + } + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_potential_energy(DENS_MAT & nodalPE) + { + DENS_MAT atomicEnergy(nLocal_,1); + + // compute pair energy per atom + double * atomPE = lammpsInterface_->atomPE_compute(); + + // add up potential energy per atom + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + atomicEnergy(i,0) += atomPE[atomIdx]; + } + project_volume_normalized(atomicEnergy, nodalPE ); + } + + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_number_density(DENS_MAT & density) + { + DENS_MAT atomCnt(nLocal_,1); + atomCnt = 1; + project_volume_normalized(atomCnt, density); + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_mass_density(DENS_MAT & density) + { + atomicDensity_.reset(nLocal_,1); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ma; + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (mass) ma = mass[type[atomIdx]]; + else ma = rmass[atomIdx]; + // density + atomicDensity_(i,0) = ma; + } + project_volume_normalized(atomicDensity_, density); + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_displacement(DENS_MAT & displacement, + const DENS_MAT & density) + { + atomicDisplacement_.reset(nLocal_,nsd_); + double ** x = lammpsInterface_->xatom(); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ma; + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (mass) ma = mass[type[atomIdx]]; + else ma = rmass[atomIdx]; + for (int j = 0; j < nsd_; j++) { + // displacement + atomicDisplacement_(i,j) = x[atomIdx][j] - xref_[atomIdx][j]; + // the cheap version of unwrapping atomic positions + if ((bool) periodicity[j]) { + double u = atomicDisplacement_(i,j); + if (u >= 0.5*box_length[j]) { u -= box_length[j]; } + if (u <= -0.5*box_length[j]) { u += box_length[j]; } + atomicDisplacement_(i,j) = u; + } + atomicDisplacement_(i,j) *= ma; + } + } + project_volume_normalized(atomicDisplacement_, displacement); + for (int i = 0; i < nNodes_; i++) { + double rho_i = density(i,0); + if (rho_i > 0.0) { + for (int j = 0; j < nsd_; j++) { + displacement(i,j) = displacement(i,j)/rho_i; + } + } + } + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_momentum(DENS_MAT & momentum) + { + atomicMomentum_.reset(nLocal_,nsd_); + double ** v = lammpsInterface_->vatom(); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ma; + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (mass) ma = mass[type[atomIdx]]; + else ma = rmass[atomIdx]; + // momentum + for (int j = 0; j < nsd_; j++) { + atomicMomentum_(i,j) = ma*(v[atomIdx][j]); + } + } + project_volume_normalized(atomicMomentum_, momentum); + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_projected_velocity(DENS_MAT & velocity) + { + atomicVelocity_.reset(nLocal_,nsd_); + double ** v = lammpsInterface_->vatom(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) { + // velocity + atomicVelocity_(i,j) += v[atomIdx][j]; + } + } + project_count_normalized(atomicVelocity_, velocity); + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_velocity(DENS_MAT & velocity, + const DENS_MAT & density, + const DENS_MAT & momentum) + { + velocity.reset(nNodes_,nsd_); + for (int i = 0; i < nNodes_; i++) { + double rho_i = density(i,0); + if (rho_i > 0.0) { + for (int j = 0; j < nsd_; j++) { + velocity(i,j) = momentum(i,j)/rho_i; + } + } + } + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_variation_velocity(DENS_MAT & velocity, + const DENS_MAT & vI) + { + if (nLocal_>0) { + // interpolate nodal velocities to the atoms + vbar_.reset(nLocal_,nsd_); + double ** v = lammpsInterface_->vatom(); + // use of prolong assumes atom system contained within mesh + if (useAtomicShapeFunctions_) { + ATC_Transfer::prolong(vI,vbar_); + } + // compute and store variation velocities of atoms + uVariationVelocity_.reset(nLocal_,nsd_); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) { + velocity(i,j) = v[atomIdx][j] - vbar_(i,j); + } + } + } + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_temperature(DENS_MAT & temperature) + { + atomicTemperature_.reset(nLocal_,1); + double kB = lammpsInterface_->kBoltzmann(); + double Tcoef = 1./(nsd_*kB); + double ** v = lammpsInterface_->vatom(); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ma; + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (mass) ma = mass[type[atomIdx]]; + else ma = rmass[atomIdx]; + for (int j = 0; j < nsd_; j++) { + // variation temperature + atomicTemperature_(i,0) += Tcoef*ma*(uVariationVelocity_(i,j)*uVariationVelocity_(i,j)); + } + } + project_count_normalized(atomicTemperature_, temperature); + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_kinetic_temperature(DENS_MAT & temperature) + { + atomicKineticTemperature_.reset(nLocal_,1); + double kB = lammpsInterface_->kBoltzmann(); + double Tcoef = 1./(nsd_*kB); + double ** v = lammpsInterface_->vatom(); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ma; + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (mass) ma = mass[type[atomIdx]]; + else ma = rmass[atomIdx]; + for (int j = 0; j < nsd_; j++) { + // full temperature + atomicKineticTemperature_(i,0) += Tcoef*ma*(v[atomIdx][j]*v[atomIdx][j]); + } + } + project_count_normalized(atomicKineticTemperature_, temperature); + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_stress(DENS_MAT & stress) + { + // table of bond functions already calculated in initialize function + // get conversion factor for mvv to e units + double mvv2e = lammpsInterface_->mvv2e(); + // get conversion factor for nktV to p units + double nktv2p = lammpsInterface_->nktv2p(); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ma; + + // calculate kinetic energy tensor part of stress for Eulerian analysis + DENS_MAT & vI = uVariationVelocity_; + if (atomToElementMapType_ == EULERIAN && nLocal_>0) { + atomicStress_.reset(nLocal_,6); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (mass) ma = mass[type[atomIdx]]; + else ma = rmass[atomIdx]; + // convert mass to appropriate units + ma = mvv2e*ma; + atomicStress_(i,0) -= ma*vI(i,0)*vI(i,0); + atomicStress_(i,1) -= ma*vI(i,1)*vI(i,1); + atomicStress_(i,2) -= ma*vI(i,2)*vI(i,2); + atomicStress_(i,3) -= ma*vI(i,0)*vI(i,1); + atomicStress_(i,4) -= ma*vI(i,0)*vI(i,2); + atomicStress_(i,5) -= ma*vI(i,1)*vI(i,2); + } + project_volume_normalized(atomicStress_, stress); + } + else { + // zero stress table for Lagrangian analysis or if nLocal_ = 0 + stress.zero(); + } + // add-in potential part of stress tensor + int nrows = stress.nRows(); + int ncols = stress.nCols(); + DENS_MAT local_potential_hardy_stress(nrows,ncols); + if (nLocal_>0) { + if (bondOnTheFly_) { + compute_potential_stress(local_potential_hardy_stress); + } + else { + // compute table of force & position dyad + compute_force_matrix(); + // calculate force part of stress tensor + local_potential_hardy_stress = atomicBondTable_*atomicForceTable_; + local_potential_hardy_stress *= 0.5; // NOTE: work around for matrix multiplication + } + } + // global summation of potential part of stress tensor + DENS_MAT potential_hardy_stress(nrows,ncols); + int count = nrows*ncols; + lammpsInterface_->allsum(local_potential_hardy_stress.get_ptr(), + potential_hardy_stress.get_ptr(), count); + stress += potential_hardy_stress; + stress = nktv2p*stress; + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_heatflux(DENS_MAT & flux) + { + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ma; + // calculate kinetic part of heat flux + if (atomToElementMapType_ == EULERIAN && nLocal_>0) { + // get conversion factor for mvv to e units + double mvv2e = lammpsInterface_->mvv2e(); + // compute pair energy per atom + double * atomPE = lammpsInterface_->atomPE_compute(); + double atomKE, atomEnergy; + atomicHeat_.reset(nLocal_,3); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (mass) ma = mass[type[atomIdx]]; + else ma = rmass[atomIdx]; + // convert mass to appropriate units + ma = mvv2e*ma; + atomKE = 0.0; + for (int j = 0; j < nsd_; j++) { + atomKE += 0.5*ma*(uVariationVelocity_(i,j)*uVariationVelocity_(i,j)); + } + atomEnergy = atomKE + atomPE[atomIdx]; + for (int j = 0; j < nsd_; j++) { + atomicHeat_(i,j) += atomEnergy*uVariationVelocity_(i,j); + } + } + project_volume_normalized(atomicHeat_,flux); + } + else { + // zero stress table for Lagrangian analysis or if nLocal_ = 0 + flux.zero(); + } + // add-in potential part of heat flux vector + int nrows = flux.nRows(); + int ncols = flux.nCols(); + DENS_MAT local_hardy_heat(nrows,ncols); + if (nLocal_>0) { + if (bondOnTheFly_) { + compute_potential_heatflux(local_hardy_heat); + } + else { + // compute table of heat vectors + compute_heat_matrix(); + // calculate force/potential-derivative part of heat flux + local_hardy_heat = atomicBondTable_*atomicHeatTable_; + } + } + // global summation of potential part of heat flux vector + DENS_MAT hardy_heat(nrows,ncols); + int count = nrows*ncols; + lammpsInterface_->allsum(local_hardy_heat.get_ptr(), + hardy_heat.get_ptr(), count); + flux += hardy_heat; + } + + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_total_energy(DENS_MAT & energy) + { + atomicEnergy_.reset(nLocal_,1); + // compute pair energy per atom + double * atomPE = lammpsInterface_->atomPE_compute(); + // get conversion factor for mvv to e units + double mvv2e = lammpsInterface_->mvv2e(); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ma; + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (mass) ma = mass[type[atomIdx]]; + else ma = rmass[atomIdx]; + // convert mass to appropriate units + ma = mvv2e*ma; + // compute kinetic energy per atom + double atomKE = 0.0; + for (int k = 0; k < nsd_; k++) { + atomKE += 0.5*ma*(uVariationVelocity_(i,k)*uVariationVelocity_(i,k)); + } + // add up total energy per atom + atomicEnergy_(i,0) += atomPE[atomIdx] + atomKE; + } + project_volume_normalized(atomicEnergy_, energy); + // subtract zero point energy + energy -= nodalRefPotentialEnergy_; + } + //------------------------------------------------------------------- + void ATC_TransferHardy::time_filter_pre(double dt, bool output_now) + { + sampleCounter_++; + string name; + double delta_t = dt*sampleFrequency_; + for(int index=0; index < NUM_HARDY_FIELDS; ++index) { + hardy_field_to_string(index,name); + timeFilters_(index)->apply_pre_step1(filteredHardyData_[name], + hardyData_[name], delta_t); + } + // NOTE add computes_ here + } + + //------------------------------------------------------------------- + void ATC_TransferHardy::time_filter_post(double dt, bool output_now) + { + sampleCounter_++; + string name; + double delta_t = dt*sampleFrequency_; + for(int index=0; index < NUM_HARDY_FIELDS; ++index) { + hardy_field_to_string(index,name); + timeFilters_(index)->apply_post_step2(filteredHardyData_[name], + hardyData_[name], delta_t); + } + if (rateFlags_.has_member(true)) { + for(int index=0; index < NUM_HARDY_FIELDS; ++index) { + if (rateFlags_(index)) { + string field; + hardy_field_to_string(index,field); + string rate_field = field + "_rate"; + timeFilters_(index)->rate(hardyData_[rate_field], + filteredHardyData_[field], + hardyData_[field], delta_t); + } + } + } + for(int index=0; index < NUM_HARDY_FIELDS; ++index) { + hardy_field_to_string(index,name); + if (rateFlags_(index)) { + string rate_field = name + "_rate"; + filteredHardyData_[rate_field] = hardyData_[rate_field]; + } + if (gradFlags_(index)) { + string grad_field = name + "_gradient"; + filteredHardyData_[grad_field] = hardyData_[grad_field]; + } + } + // NOTE add computes_ here + } + + //------------------------------------------------------------------- + void ATC_TransferHardy::output() + { + if (lammpsInterface_->comm_rank() == 0) { + // data + OUTPUT_LIST output_data; + for(int index=0; index < NUM_HARDY_FIELDS; ++index) { + string name; + hardy_field_to_string(index,name); + if (outputFlags_(index)) { + output_data[name] = & ( filteredHardyData_[name]); + } + if (rateFlags_(index)) { + string rate_name = name + "_rate"; + output_data[rate_name] = & ( filteredHardyData_[rate_name]); + } + if (gradFlags_(index)) { + string grad_name = name + "_gradient"; + output_data[grad_name] = & ( filteredHardyData_[grad_name]); + } + } + map ::const_iterator iter; + for (iter = computes_.begin(); iter != computes_.end(); iter++) { + string tag = iter->first; + output_data[tag] = & ( hardyData_[tag]); // NOTE should be filtered + } + + // output + feEngine_->write_data(simTime_, & output_data); + } + } + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_kernel_matrix(void) + { + if (nLocal_>0) { + set_xPointer(); + kernelShpFcn_.reset(nNodes_,nLocal_); + DENS_VEC xI(nsd_),xa(nsd_),xaI(nsd_); + if (lammpsInterface_->comm_rank() == 0 ) { + cout << "ATC:: computing kernel matrix " << flush; + } + int heartbeatFreq = (nNodes_ <= 10 ? 1 : (int) nNodes_ / 10); + for (int i = 0; i < nNodes_; i++) { + if (lammpsInterface_->comm_rank() == 0 && i % heartbeatFreq == 0 ) { + cout << "." << flush; + } + // Hardy point + xI = (feEngine_->get_feMesh())->nodal_coordinates(i); + //xI = (feEngine_->get_feMesh())->global_coordinates(i); + int ii = i; + //int ii = feEngine_->get_feMesh()->map_global_to_unique(i); + for (int j = 0; j < nLocal_; j++) { + // atom location + int lammps_j = internalToAtom_(j); + xa.copy(xPointer_[lammps_j],3); + // difference vector + xaI = xa - xI; + periodicity_correction(xaI); + // compute kernel value & add it to the matrix + double val = kernelFunction_->value(xaI); + if (val > 0) kernelShpFcn_.add(ii,j,val); + } + } + kernelShpFcn_.compress(); + if (lammpsInterface_->comm_rank() == 0) { + cout << "done\n"; + } + } + } + + //------------------------------------------------------------------- + // count normalized + void ATC_TransferHardy::project_count_normalized(const DENS_MAT & atomData, + DENS_MAT & nodeData) + { + int ncols_atomData = atomData.nCols(); + nodeData.reset(nNodes_,ncols_atomData); + nodeData.zero(); + // kernel functions + if (kernelFunction_ ) { + project_volume_normalized(atomData,nodeData); + for (int i = 0; i < nNodes_; i++) { + double numDens = hardyData_["number_density"](i,0); + if (numDens > 0) { + double invNumDens = 1.0/numDens; + for (int j = 0; j < ncols_atomData; j++) { + nodeData(i,j) *= invNumDens; + } + } + else { + for (int j = 0; j < ncols_atomData; j++) { + nodeData(i,j) = 0; + } + } + } + } + // mesh-based kernel functions + else { + ATC_Transfer::restrict(atomData,nodeData); + } + } + + //------------------------------------------------------------------- + // volume normalized + void ATC_TransferHardy::project_volume_normalized(const DENS_MAT & atomData, + DENS_MAT & nodeData) + { + int ncols_atomData = atomData.nCols(); + DENS_MAT workNodeArray(nNodes_,ncols_atomData); + workNodeArray.zero(); + nodeData.reset(workNodeArray.nRows(),workNodeArray.nCols()); + nodeData.zero(); + // kernel functions + if (kernelFunction_ ) { + if (nLocal_>0) { + double invVol = kernelFunction_->inv_vol(); + // on the fly calculation + if (kernelOnTheFly_) { + set_xPointer(); + DENS_VEC xI(nsd_),xa(nsd_),xaI(nsd_); + double val; + for (int i = 0; i < nNodes_; i++) { + xI = (feEngine_->get_feMesh())->nodal_coordinates(i); + for (int j = 0; j < nLocal_; j++) { + int lammps_j = internalToAtom_(j); + xa.copy(xPointer_[lammps_j],3); + xaI = xa - xI; + periodicity_correction(xaI); + val = kernelFunction_->value(xaI); + if (val > 0) { + for (int k=0; k < ncols_atomData; k++) { + workNodeArray(i,k) += val*atomData(j,k); + } + } + } + } + } + // matrix calculation + else { + workNodeArray = kernelShpFcn_*atomData; + } + workNodeArray *= invVol; + } + } + // mesh-based kernel functions + else { + // computes nodeData = N*atomData w/ N: volume normalized shape functions + if (nLocal_>0) + workNodeArray = invNodeVolumes_*shpFcn_.transMat(atomData); + } + // accumulate across processors + int count = workNodeArray.nRows()*workNodeArray.nCols(); + lammpsInterface_->allsum(workNodeArray.get_ptr(),nodeData.get_ptr(),count); + } + + //------------------------------------------------------------------- + void ATC_TransferHardy::gradient_compute(const DENS_MAT & inNodeData, + DENS_MAT & outNodeData) + { + int nrows = inNodeData.nRows(); + int ncols = inNodeData.nCols(); + outNodeData.reset(nrows,ncols*nsd_); + for (int j = 0; j < nNodes_; j++) { + for (int i = 0; i < nNodes_; i++) { + int index = 0; + for (int n = 0; n < ncols; n++) { //output v1,1 v1,2 v1,3 ... + for (int m = 0; m < nsd_; m++) { + outNodeData(j,index) += gradientTable_[m](j,i)*inNodeData(i,n); + index++; + } + } + } + } + } + + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_pair_map() + { + // neighbor lists for "internal" group + // a: a "real" atom in the "internal" group + // b: any neighbor to a (it should project into the mesh for mesh-based) + int inum = lammpsInterface_->neighbor_list_inum(); + if (inum != nLocalTotal_) + throw ATC_Error(0,"size mismatch in neighbor list"); + int *ilist = lammpsInterface_->neighbor_list_ilist(); + int *numneigh = lammpsInterface_->neighbor_list_numneigh(); + int **firstneigh = lammpsInterface_->neighbor_list_firstneigh(); + int * mask = lammpsInterface_->atom_mask(); + + pairMap_.clear(); + int pair_index = 0; + pair< int,int > pair_ij; + for (int i = 0; i < nLocalTotal_; i++) { // NOTE why nLocal__Total__? + int lammps_i = ilist[i]; + // filter out atoms not in the internal group + if (mask[lammps_i] & groupbit_) { + //int tag_i = (lammpsInterface_->atom_tag())[lammps_i]; + for (int j = 0; j < numneigh[lammps_i]; j++) { + int lammps_j = firstneigh[lammps_i][j]; + //int tag_j = (lammpsInterface_->atom_tag())[lammps_j]; + //if (lammps_i > lammps_j) continue; // skip a > b pairs + //if (tag_i > tag_j) continue; // skip a > b pairs + pair_ij.first = lammps_i; // alpha + pair_ij.second = lammps_j; // beta + pairMap_[pair_ij] = pair_index; + pair_index++; + } + } + } + nPairs_ = pair_index; + } + + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_bond_matrix() + { + set_xPointer(); + if (lammpsInterface_->comm_rank() == 0) { + cout << " ATC:: computing bond matrix " << flush; + } + // compute pair map + compute_pair_map(); + + // neighbor lists + int *numneigh = lammpsInterface_->neighbor_list_numneigh(); + int **firstneigh = lammpsInterface_->neighbor_list_firstneigh(); + + Array latticePeriodicity(3); + latticePeriodicity(0) = (bool) periodicity[0]; + latticePeriodicity(1) = (bool) periodicity[1]; + latticePeriodicity(2) = (bool) periodicity[2]; + + + double lam1,lam2; + double bond_value; + pair< int,int > pair_jk; + map< pair< int,int >,int >::iterator pairMapIterator; + int pair_index; + + + atomicBondTable_.reset(nNodes_,nPairs_); + + // process differently for mesh vs translation-invariant kernels + if (kernelFunction_ ) { + int heartbeatFreq = (nNodes_ <= 10 ? 1 : (int) nNodes_ / 10); + //int heartbeatFreq = (nNodesGlobal_ <= 10 ? 1 : (int) nNodesGlobal_ / 10); + // "normal" kernel functions + DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_); + double kernel_inv_vol = kernelFunction_->inv_vol(); + for (int i = 0; i < nNodes_; i++) { + //for (int i = 0; i < nNodesGlobal_; i++) { + if (lammpsInterface_->comm_rank() == 0 && i % heartbeatFreq == 0 ) { + cout << "." << flush; + } + int ii = i; + //int ii = feEngine_->get_feMesh()->map_global_to_unique(i); + // Hardy point + xI = (feEngine_->get_feMesh())->nodal_coordinates(i); + //xI = (feEngine_->get_feMesh())->global_coordinates(i); + for (pairMapIterator = pairMap_.begin(); + pairMapIterator != pairMap_.end(); pairMapIterator++){ + int lammps_j = (pairMapIterator->first).first ; + int lammps_k = (pairMapIterator->first).second; + xa.copy(xPointer_[lammps_j],3); + xaI = xa - xI; + periodicity_correction(xaI); + xb.copy(xPointer_[lammps_k],3); + xba = xb - xa; + xbI = xba + xaI; + // intersect segment with kernel support + kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2); + if (lam1 < lam2) { + bond_value + = kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2)); + pair_index = pairMapIterator->second; + atomicBondTable_.add(ii,pair_index,bond_value); + } // if lam1 < lam2 + } // pair map + }// end nodes loop + atomicBondTable_.compress(); + } + else { + // mesh-based kernel functions + int heartbeatFreq = (int) pairMap_.size() / 10; + int i=0; + int nodes_per_element = feEngine_->get_feMesh()->get_nNodesPerElement(); + Array node_list(nodes_per_element); + DENS_VEC shp(nodes_per_element); + DENS_VEC xa(nsd_),xb(nsd_),xab(nsd_),xlambda(nsd_); + double **xatom = lammpsInterface_->xatom(); + for (pairMapIterator = pairMap_.begin(); + pairMapIterator != pairMap_.end(); pairMapIterator++){ + if (lammpsInterface_->comm_rank() == 0 && i++ % heartbeatFreq == 0 ) { + cout << "." << flush; + } + int lammps_j = (pairMapIterator->first).first ; + int lammps_k = (pairMapIterator->first).second; + pair_index = pairMapIterator->second; + xa.copy(xPointer_[lammps_j],3); + xb.copy(xPointer_[lammps_k],3); + lam1 = 0.0; lam2 = 1.0; + double del_lambda = 0.5*(lam2 - lam1); + double avg_lambda = 0.5*(lam2 + lam1); + xab = xa - xb; + for (int i = 0; i < line_ngauss; i++) { + double lambda = del_lambda*line_xg[i] +avg_lambda; + xlambda = lambda*xab + xb; + // NOTE doesn't work for the case that fe_region \subset md_region + int dummyEltID; + feEngine_->shape_functions(xlambda,shp,dummyEltID, + node_list,latticePeriodicity); + // accumulate to nodes whose support overlaps the integration point + for (int I = 0; I < nodes_per_element; I++) { + // Use factor of 0.5 to account for line integration + // domain of 0 to 1 instead of -1 to 1 + int inode = node_list(I); + double val = invNodeVolumes_(inode,inode)*shp(I)*line_wg[i]*0.5; + atomicBondTable_.add(inode,pair_index,val); + } + } + } + atomicBondTable_.compress(); + }// end if kernelFunction_ + if (lammpsInterface_->comm_rank() == 0) { + cout << "done\n"; + } + } + + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_gradient_matrix() + { + feEngine_->compute_gradient_matrix(gradientTable_); + } + + //------------------------------------------------------------------- + // checks if atoms have changed neighbors + void ATC_TransferHardy::check_pair_map() + { + // (1) compute fresh temporary pairMap + // (2) check if tmpPairMap == pairMap_ + + // neighbor lists + int *numneigh = lammpsInterface_->neighbor_list_numneigh(); + int **firstneigh = lammpsInterface_->neighbor_list_firstneigh(); + + pair< int,int > pair_ij; + map< pair< int,int >,int >::iterator pairMapIterator; + + for (int i = 0; i < nLocal_; i++) { + int lammps_i = internalToAtom_(i); + for (int k = 0; k < numneigh[lammps_i]; k++) { + int j = firstneigh[lammps_i][k]; + pair_ij.first = lammps_i; + pair_ij.second = j; + pairMapIterator = pairMap_.find(pair_ij); + // if pair is not found in pairMap_, recompute pair map and bond table + // NOTE need to check if pairMap_ has a stale pair <<<< + // construct new pair map and use "==" + if (pairMapIterator == pairMap_.end()) { + cout << "ATC::check_pair_map has found that pair map and bond table need to be recomputed" << endl; + compute_bond_matrix(); + return; + } + } + } + } + + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_force_matrix() + { + // reset force table + if (atomToElementMapType_ == LAGRANGIAN) { + atomicForceTable_.reset(nPairs_,9); + } + else if (atomToElementMapType_ == EULERIAN) { + atomicForceTable_.reset(nPairs_,6); + } + // calculate force table + double **xatom = lammpsInterface_->xatom(); + map< pair< int,int >,int >::iterator pairMapIterator; + for (pairMapIterator = pairMap_.begin(); + pairMapIterator != pairMap_.end(); pairMapIterator++){ + int lammps_i = (pairMapIterator->first).first ; + int lammps_j = (pairMapIterator->first).second; + int pair_index = pairMapIterator->second; + double * xi = xatom[lammps_i]; + double * xj = xatom[lammps_j]; + double delx = xi[0] - xj[0]; + double dely = xi[1] - xj[1]; + double delz = xi[2] - xj[2]; + double rsq = delx*delx + dely*dely + delz*delz; + double fforce = 0; + lammpsInterface_->pair_force(lammps_i,lammps_j,rsq,fforce); + if (atomToElementMapType_ == LAGRANGIAN) { + double delX = xref_[lammps_i][0] - xref_[lammps_j][0]; + double delY = xref_[lammps_i][1] - xref_[lammps_j][1]; + double delZ = xref_[lammps_i][2] - xref_[lammps_j][2]; + atomicForceTable_(pair_index,0)=-delx*fforce*delX; + atomicForceTable_(pair_index,1)=-delx*fforce*delY; + atomicForceTable_(pair_index,2)=-delx*fforce*delZ; + atomicForceTable_(pair_index,3)=-dely*fforce*delX; + atomicForceTable_(pair_index,4)=-dely*fforce*delY; + atomicForceTable_(pair_index,5)=-dely*fforce*delZ; + atomicForceTable_(pair_index,6)=-delz*fforce*delX; + atomicForceTable_(pair_index,7)=-delz*fforce*delY; + atomicForceTable_(pair_index,8)=-delz*fforce*delZ; + } + else if (atomToElementMapType_ == EULERIAN) { + atomicForceTable_(pair_index,0)=-delx*delx*fforce; + atomicForceTable_(pair_index,1)=-dely*dely*fforce; + atomicForceTable_(pair_index,2)=-delz*delz*fforce; + atomicForceTable_(pair_index,3)=-delx*dely*fforce; + atomicForceTable_(pair_index,4)=-delx*delz*fforce; + atomicForceTable_(pair_index,5)=-dely*delz*fforce; + } + } + } + + //------------------------------------------------------------------- + void ATC_TransferHardy::compute_heat_matrix() + { + // reset heat flux and energy tables + atomicHeatTable_.reset(nPairs_,3); + // calculate force table + map< pair< int,int >,int >::iterator pairMapIterator; + double ** xatom = lammpsInterface_->xatom(); + double fforce; + for (pairMapIterator = pairMap_.begin(); + pairMapIterator != pairMap_.end(); pairMapIterator++){ + int lammps_i = (pairMapIterator->first).first ; + int lammps_j = (pairMapIterator->first).second; + int pair_index = pairMapIterator->second; + double * xi = xatom[lammps_i]; + double * xj = xatom[lammps_j]; + double energy_i = 0.0; + double delx = xi[0] - xj[0]; + double dely = xi[1] - xj[1]; + double delz = xi[2] - xj[2]; + double rsq = delx*delx + dely*dely + delz*delz; + double pair_pe=lammpsInterface_->pair_force(lammps_i,lammps_j,rsq,fforce); + fforce *= 0.5; + // This term is correct ONLY for pair potentials. + // fforce = - (1/x_{ij})*(d(phi_i)/d(x_{ij}) + d(phi_j)/d(x_{ij})) + // What we need for the heat flux calculation is the portion of this that belongs only to + // atom j: - (1/x_{ij})*d(phi_j)/d(x_{ij}). + int i = lammps_i; // NOTE is this right? + fforce *= (delx*uVariationVelocity_(i,0) + + dely*uVariationVelocity_(i,1) + + delz*uVariationVelocity_(i,2)); + // NOTE REJ don't think this is right, where is energy_i from above? + if (atomToElementMapType_ == LAGRANGIAN) { + double delX = xref_[lammps_i][0] - xref_[lammps_j][0]; + double delY = xref_[lammps_i][1] - xref_[lammps_j][1]; + double delZ = xref_[lammps_i][2] - xref_[lammps_j][2]; + atomicHeatTable_(pair_index,0)=delX*fforce; + atomicHeatTable_(pair_index,1)=delY*fforce; + atomicHeatTable_(pair_index,2)=delZ*fforce; + } + else if (atomToElementMapType_ == EULERIAN) { + atomicHeatTable_(pair_index,0)=delx*fforce; + atomicHeatTable_(pair_index,1)=dely*fforce; + atomicHeatTable_(pair_index,2)=delz*fforce; + } + } + } + + //------------------------------------------------------------------- + // on-the-fly calculation of stress + void ATC_TransferHardy::compute_potential_stress(DENS_MAT& stress) + { + set_xPointer(); + stress.zero(); + // neighbor lists + int *numneigh = lammpsInterface_->neighbor_list_numneigh(); + int **firstneigh = lammpsInterface_->neighbor_list_firstneigh(); + double ** xatom = lammpsInterface_->xatom(); + Array latticePeriodicity(3); + latticePeriodicity(0) = (bool) periodicity[0]; + latticePeriodicity(1) = (bool) periodicity[1]; + latticePeriodicity(2) = (bool) periodicity[2]; + double lam1,lam2; + double bond_value; + // process differently for mesh vs translation-invariant kernels + if (lammpsInterface_->comm_rank() == 0) { + cout << "ATC:: computing potential stress: " << flush; + } + int heartbeatFreq = (nNodes_ <= 10 ? 1 : (int) nNodes_ / 10); + if (kernelFunction_ ) { + // "normal" kernel functions + DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_); + double kernel_inv_vol = kernelFunction_->inv_vol(); + for (int i = 0; i < nNodes_; i++) { + //for (int i = 0; i < nNodesGlobal_; i++) { + if (lammpsInterface_->comm_rank() == 0 && i % heartbeatFreq == 0 ) { + cout << "." << flush; + } + // Hardy point + xI = (feEngine_->get_feMesh())->nodal_coordinates(i); + //xI = (feEngine_->get_feMesh())->global_coordinates(i); + int inode = i; + //int inode = feEngine_->get_feMesh()->map_global_to_unique(i); + for (int j = 0; j < nLocal_; j++) { + // second (neighbor) atom location + int lammps_j = internalToAtom_(j); + xa.copy(xPointer_[lammps_j],3); + double * xj = xatom[lammps_j]; + // difference vector + xaI = xa - xI; + periodicity_correction(xaI); + for (int k = 0; k < numneigh[lammps_j]; ++k) { + int lammps_k = firstneigh[lammps_j][k]; + // first atom location + xb.copy(xPointer_[lammps_k],3); + double * xk = xatom[lammps_k]; + // difference vector + xba = xb - xa; + xbI = xba + xaI; + kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2); + // compute virial + if (lam1 < lam2) { + bond_value + = kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2)); + double delx = xatom[lammps_j][0] - xatom[lammps_k][0]; + double dely = xatom[lammps_j][1] - xatom[lammps_k][1]; + double delz = xatom[lammps_j][2] - xatom[lammps_k][2]; + double rsq = delx*delx + dely*dely + delz*delz; + double fforce = 0; + lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce); + fforce *= 0.5; // dbl count + if (atomToElementMapType_ == LAGRANGIAN) { + double delX = xref_[lammps_j][0] - xref_[lammps_k][0]; + double delY = xref_[lammps_j][1] - xref_[lammps_k][1]; + double delZ = xref_[lammps_j][2] - xref_[lammps_k][2]; + stress(inode,0) +=-delx*fforce*delX*bond_value; + stress(inode,1) +=-delx*fforce*delY*bond_value; + stress(inode,2) +=-delx*fforce*delZ*bond_value; + stress(inode,3) +=-dely*fforce*delX*bond_value; + stress(inode,4) +=-dely*fforce*delY*bond_value; + stress(inode,5) +=-dely*fforce*delZ*bond_value; + stress(inode,6) +=-delz*fforce*delX*bond_value; + stress(inode,7) +=-delz*fforce*delY*bond_value; + stress(inode,8) +=-delz*fforce*delZ*bond_value; + } + else { //if (atomToElementMapType_ == EULERIAN) { + stress(inode,0) +=-delx*delx*fforce*bond_value; + stress(inode,1) +=-dely*dely*fforce*bond_value; + stress(inode,2) +=-delz*delz*fforce*bond_value; + stress(inode,3) +=-delx*dely*fforce*bond_value; + stress(inode,4) +=-delx*delz*fforce*bond_value; + stress(inode,5) +=-dely*delz*fforce*bond_value; + } + } + } + } + } + } + else { + // mesh-based kernel functions + int nodes_per_element = feEngine_->get_feMesh()->get_nNodesPerElement(); + Array node_list(nodes_per_element); + DENS_VEC shp(nodes_per_element); + DENS_VEC xa(nsd_),xb(nsd_),xab(nsd_),xlambda(nsd_); + for (int j = 0; j < nLocal_; j++) { + if (lammpsInterface_->comm_rank() == 0 && j % heartbeatFreq == 0 ) { + cout << "." << flush; + } + // first atom location + int lammps_j = internalToAtom_(j); + xa.copy(xPointer_[lammps_j],3); + for (int k = 0; k < numneigh[lammps_j]; ++k) { + int lammps_k = firstneigh[lammps_j][k]; + //if (lammps_k < lammps_j) continue; // full neighbor list + // second (neighbor) atom location + xb.copy(xPointer_[lammps_k],3); + double delx = xatom[lammps_j][0] - xatom[lammps_k][0]; + double dely = xatom[lammps_j][1] - xatom[lammps_k][1]; + double delz = xatom[lammps_j][2] - xatom[lammps_k][2]; + double rsq = delx*delx + dely*dely + delz*delz; + double fforce = 0; + lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce); + fforce *= 0.5; // 1/2 sum_ab = sum_(ab) + double virial[9]; + if (atomToElementMapType_ == LAGRANGIAN) { + double delX = xref_[lammps_j][0] - xref_[lammps_k][0]; + double delY = xref_[lammps_j][1] - xref_[lammps_k][1]; + double delZ = xref_[lammps_j][2] - xref_[lammps_k][2]; + virial[0] =-delx*fforce*delX; + virial[1] =-delx*fforce*delY; + virial[2] =-delx*fforce*delZ; + virial[3] =-dely*fforce*delX; + virial[4] =-dely*fforce*delY; + virial[5] =-dely*fforce*delZ; + virial[6] =-delz*fforce*delX; + virial[7] =-delz*fforce*delY; + virial[8] =-delz*fforce*delZ; + } + else {//if (atomToElementMapType_ == EULERIAN) { + virial[0] =-delx*delx*fforce; + virial[1] =-dely*dely*fforce; + virial[2] =-delz*delz*fforce; + virial[3] =-delx*dely*fforce; + virial[4] =-delx*delz*fforce; + virial[5] =-dely*delz*fforce; + } + lam1 = 0.0; lam2 = 1.0; + double del_lambda = 0.5*(lam2 - lam1); + double avg_lambda = 0.5*(lam2 + lam1); + xab = xa - xb; + for (int i = 0; i < line_ngauss; i++) { + double lambda = del_lambda*line_xg[i] +avg_lambda; + xlambda = lambda*xab + xb; + // NOTE doesn't work for the case that fe_region \subset md_region + int dummyEltID; + feEngine_->shape_functions(xlambda,shp,dummyEltID, + node_list,latticePeriodicity); + // accumulate to nodes whose support overlaps the integration point + for (int I = 0; I < nodes_per_element; I++) { + // Use factor of 0.5 to account for line integration + // domain of 0 to 1 instead of -1 to 1 + int inode = node_list(I); + double bond_value + = invNodeVolumes_(inode,inode)*shp(I)*line_wg[i]*0.5; + stress(inode,0) += virial[0]*bond_value; + stress(inode,1) += virial[1]*bond_value; + stress(inode,2) += virial[2]*bond_value; + stress(inode,3) += virial[3]*bond_value; + stress(inode,4) += virial[4]*bond_value; + stress(inode,5) += virial[5]*bond_value; + if (atomToElementMapType_ == LAGRANGIAN) { + stress(inode,6) += virial[6]*bond_value; + stress(inode,7) += virial[7]*bond_value; + stress(inode,8) += virial[8]*bond_value; + } + } + } + } + } + }// end if kernelFunction_ + if (lammpsInterface_->comm_rank() == 0) { + cout << "done\n" << flush; + } + } + + //------------------------------------------------------------------- + // on-the-fly calculation of the heat flux + void ATC_TransferHardy::compute_potential_heatflux(DENS_MAT& flux) + { + set_xPointer(); + flux.zero(); + // neighbor lists + int *numneigh = lammpsInterface_->neighbor_list_numneigh(); + int **firstneigh = lammpsInterface_->neighbor_list_firstneigh(); + double ** xatom = lammpsInterface_->xatom(); + Array latticePeriodicity(3); + latticePeriodicity(0) = (bool) periodicity[0]; + latticePeriodicity(1) = (bool) periodicity[1]; + latticePeriodicity(2) = (bool) periodicity[2]; + double lam1,lam2; + double bond_value; + // process differently for mesh vs translation-invariant kernels + if (kernelFunction_ ) { + // "normal" kernel functions + DENS_VEC xa(nsd_),xI(nsd_),xaI(nsd_),xb(nsd_),xbI(nsd_),xba(nsd_); + double kernel_inv_vol = kernelFunction_->inv_vol(); + for (int i = 0; i < nNodes_; i++) { + //for (int i = 0; i < nNodesGlobal_; i++) { + int inode = i; + //int inode = feEngine_->get_feMesh()->map_global_to_unique(i); + // Hardy point + xI = (feEngine_->get_feMesh())->nodal_coordinates(i); + //xI = (feEngine_->get_feMesh())->global_coordinates(i); + for (int j = 0; j < nLocal_; j++) { + // second (neighbor) atom location + int lammps_j = internalToAtom_(j); + xa.copy(xPointer_[lammps_j],3); + double * xj = xatom[lammps_j]; + // difference vector + xaI = xa - xI; + periodicity_correction(xaI); + for (int k = 0; k < numneigh[lammps_j]; ++k) { + int lammps_k = firstneigh[lammps_j][k]; + // first atom location + xb.copy(xPointer_[lammps_k],3); + double * xk = xatom[lammps_k]; + // difference vector + xba = xb - xa; + xbI = xba + xaI; + kernelFunction_->bond_intercepts(xaI,xbI,lam1,lam2); + // compute virial + if (lam1 < lam2) { + bond_value + = kernel_inv_vol*(kernelFunction_->bond(xaI,xbI,lam1,lam2)); + double delx = xatom[lammps_j][0] - xatom[lammps_k][0]; + double dely = xatom[lammps_j][1] - xatom[lammps_k][1]; + double delz = xatom[lammps_j][2] - xatom[lammps_k][2]; + double rsq = delx*delx + dely*dely + delz*delz; + double fforce = 0; + double pair_pe = lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce); + fforce *= 0.5; // dbl count + fforce *= (delx*uVariationVelocity_(j,0) + + dely*uVariationVelocity_(j,1) + + delz*uVariationVelocity_(j,2)); + if (atomToElementMapType_ == LAGRANGIAN) { + double delX = xref_[lammps_j][0] - xref_[lammps_k][0]; + double delY = xref_[lammps_j][1] - xref_[lammps_k][1]; + double delZ = xref_[lammps_j][2] - xref_[lammps_k][2]; + flux(inode,0) +=fforce*delX*bond_value; + flux(inode,1) +=fforce*delY*bond_value; + flux(inode,2) +=fforce*delZ*bond_value; + } + else { //if (atomToElementMapType_ == EULERIAN) { + flux(inode,0) +=fforce*delx*bond_value; + flux(inode,1) +=fforce*dely*bond_value; + flux(inode,2) +=fforce*delz*bond_value; + } + } + } + } + } + } + else { + // mesh-based kernel functions + int nodes_per_element = feEngine_->get_feMesh()->get_nNodesPerElement(); + Array node_list(nodes_per_element); + DENS_VEC shp(nodes_per_element); + DENS_VEC xa(nsd_),xb(nsd_),xab(nsd_),xlambda(nsd_); + for (int j = 0; j < nLocal_; j++) { + // first atom location + int lammps_j = internalToAtom_(j); + xa.copy(xPointer_[lammps_j],3); + for (int k = 0; k < numneigh[lammps_j]; ++k) { + int lammps_k = firstneigh[lammps_j][k]; + //if (lammps_k < lammps_j) continue; // full neighbor list + // second (neighbor) atom location + xb.copy(xPointer_[lammps_k],3); + double delx = xatom[lammps_j][0] - xatom[lammps_k][0]; + double dely = xatom[lammps_j][1] - xatom[lammps_k][1]; + double delz = xatom[lammps_j][2] - xatom[lammps_k][2]; + double rsq = delx*delx + dely*dely + delz*delz; + double fforce = 0; + double pair_pe = lammpsInterface_->pair_force(lammps_j,lammps_k,rsq,fforce); + fforce *= 0.5; // 1/2 sum_ab = sum_(ab) + fforce *= (delx*uVariationVelocity_(j,0) + + dely*uVariationVelocity_(j,1) + + delz*uVariationVelocity_(j,2)); + double flux_vec[3]; + if (atomToElementMapType_ == LAGRANGIAN) { + double delX = xref_[lammps_j][0] - xref_[lammps_k][0]; + double delY = xref_[lammps_j][1] - xref_[lammps_k][1]; + double delZ = xref_[lammps_j][2] - xref_[lammps_k][2]; + flux_vec[0] =fforce*delX; + flux_vec[1] =fforce*delY; + flux_vec[2] =fforce*delZ; + } + else {//if (atomToElementMapType_ == EULERIAN) { + flux_vec[0] =fforce*delx; + flux_vec[1] =fforce*dely; + flux_vec[2] =fforce*delz; + } + lam1 = 0.0; lam2 = 1.0; + double del_lambda = 0.5*(lam2 - lam1); + double avg_lambda = 0.5*(lam2 + lam1); + xab = xa - xb; + for (int i = 0; i < line_ngauss; i++) { + double lambda = del_lambda*line_xg[i] +avg_lambda; + xlambda = lambda*xab + xb; + // NOTE doesn't work for the case that fe_region \subset md_region + int dummyEltID; + feEngine_->shape_functions(xlambda,shp,dummyEltID, + node_list,latticePeriodicity); + // accumulate to nodes whose support overlaps the integration point + for (int I = 0; I < nodes_per_element; I++) { + // Use factor of 0.5 to account for line integration + // domain of 0 to 1 instead of -1 to 1 + int inode = node_list(I); + double bond_value + = invNodeVolumes_(inode,inode)*shp(I)*line_wg[i]*0.5; + flux(inode,0) += flux_vec[0]*bond_value; + flux(inode,1) += flux_vec[1]*bond_value; + flux(inode,2) += flux_vec[2]*bond_value; + } + } + } + } + }// end if kernelFunction_ + } + + //------------------------------------------------------------------- + // correction of node-pair distances due to periodicity + void ATC_TransferHardy::periodicity_correction(DENS_VEC& xaI) + { + for (int m = 0; m < nsd_; m++) { + xaI(m) -= periodicity[m]*box_length[m]*rnd(xaI(m)/box_length[m]); + } + } + + //------------------------------------------------------------------- + // set xPointer_ to xref or xatom depending on Lagrangian/Eulerian analysis + void ATC_TransferHardy::set_xPointer() + { + xPointer_ = xref_; + if (atomToElementMapType_ == EULERIAN) { + xPointer_ = lammpsInterface_->xatom(); + } + } + + //------------------------------------------------------------------- + // check consistency of fieldFlags_ + void ATC_TransferHardy::check_fieldFlags_consistency() + { + if (fieldFlags_(HARDY_TRANSFORMED_STRESS)) { + fieldFlags_(HARDY_STRESS) = true; + fieldFlags_(HARDY_DISPLACEMENT) = true; + } + if (fieldFlags_(HARDY_ESHELBY_STRESS)) { + fieldFlags_(HARDY_STRESS) = true; + fieldFlags_(HARDY_ENERGY) = true; + fieldFlags_(HARDY_DISPLACEMENT) = true; + } + if (fieldFlags_(HARDY_CAUCHY_BORN_STRESS)) { + if (! (cauchyBornStress_) ) { + throw ATC_Error(0,"can't compute cauchy-born stress w/o cauchy born model"); + } + fieldFlags_(HARDY_DISPLACEMENT) = true; + } + if (fieldFlags_(HARDY_VACANCY_CONCENTRATION)) { + fieldFlags_(HARDY_DISPLACEMENT) = true; + fieldFlags_(HARDY_NUMBER_DENSITY) = true; + } + if (fieldFlags_(HARDY_TEMPERATURE) || fieldFlags_(HARDY_HEAT_FLUX) || + fieldFlags_(HARDY_ENERGY) || (fieldFlags_(HARDY_STRESS) && + atomToElementMapType_ == EULERIAN) ) { + fieldFlags_(HARDY_VELOCITY) = true; + } + if (fieldFlags_(HARDY_VELOCITY)) { + fieldFlags_(HARDY_DENSITY) = true; + fieldFlags_(HARDY_MOMENTUM) = true; + } + if (fieldFlags_(HARDY_DISPLACEMENT)) { + fieldFlags_(HARDY_DENSITY) = true; + } + if (fieldFlags_(HARDY_TEMPERATURE) || + fieldFlags_(HARDY_TYPE_CONCENTRATION)) { + fieldFlags_(HARDY_NUMBER_DENSITY) = true; + } + + } + +} // end namespace ATC diff --git a/lib/atc/ATC_TransferHardy.h b/lib/atc/ATC_TransferHardy.h new file mode 100644 index 0000000000..ec21925dc4 --- /dev/null +++ b/lib/atc/ATC_TransferHardy.h @@ -0,0 +1,346 @@ +/** ATC_TransferHardy : Hardy smoothing */ +#ifndef ATC_TRANSFER_HARDY_H +#define ATC_TRANSFER_HARDY_H + +// ATC_Transfer headers +#include "ATC_Transfer.h" +#include "LammpsInterface.h" +#include "ATC_HardyKernel.h" +#include "TimeFilter.h" + +// Other headers +#include +#include +using std::list; + +namespace ATC { + + enum hardyNormalization { + NO_NORMALIZATION=0, + VOLUME_NORMALIZATION, NUMBER_NORMALIZATION, MASS_NORMALIZATION + }; + + enum hardyFieldName { + HARDY_DENSITY=0, + HARDY_DISPLACEMENT, + HARDY_MOMENTUM, + HARDY_VELOCITY, + HARDY_PROJECTED_VELOCITY, + HARDY_TEMPERATURE, + HARDY_KINETIC_TEMPERATURE, + HARDY_STRESS, + HARDY_HEAT_FLUX, + HARDY_ENERGY, + HARDY_NUMBER_DENSITY, + HARDY_ESHELBY_STRESS, + HARDY_CAUCHY_BORN_STRESS, + HARDY_TRANSFORMED_STRESS, + HARDY_VACANCY_CONCENTRATION, + HARDY_TYPE_CONCENTRATION, + NUM_HARDY_FIELDS + }; + + /** string to field enum */ + static bool string_to_hardy_field(const string & name, hardyFieldName & index) { + if (name=="density") + index = HARDY_DENSITY; + else if (name=="displacement") + index = HARDY_DISPLACEMENT; + else if (name=="momentum") + index = HARDY_MOMENTUM; + else if (name=="velocity") + index = HARDY_VELOCITY; + else if (name=="projected_velocity") + index = HARDY_PROJECTED_VELOCITY; + else if (name=="temperature") + index = HARDY_TEMPERATURE; + else if (name=="kinetic_temperature") + index = HARDY_KINETIC_TEMPERATURE; // temperature from full KE + else if (name=="stress") + index = HARDY_STRESS; + else if (name=="eshelby_stress") + index = HARDY_ESHELBY_STRESS; + else if (name=="cauchy_born_stress") + index = HARDY_CAUCHY_BORN_STRESS; + else if (name=="heat_flux") + index = HARDY_HEAT_FLUX; + else if (name=="energy") + index = HARDY_ENERGY; + else if (name=="number_density") + index = HARDY_NUMBER_DENSITY; + else if (name=="transformed_stress") + index = HARDY_TRANSFORMED_STRESS; + else if (name=="vacancy_concentration") + index = HARDY_VACANCY_CONCENTRATION; + else if (name=="type_concentration") + index = HARDY_TYPE_CONCENTRATION; + else + return false; + + return true; + }; + + /** string to field enum */ + static bool hardy_field_to_string(const int & index,string & name) + { + if (index==HARDY_DENSITY) + name = "density"; + else if (index==HARDY_DISPLACEMENT) + name = "displacement"; + else if (index==HARDY_MOMENTUM) + name = "momentum"; + else if (index == HARDY_VELOCITY) + name="velocity"; + else if (index == HARDY_PROJECTED_VELOCITY) + name="projected_velocity"; + else if (index == HARDY_TEMPERATURE) + name="temperature"; + else if (index == HARDY_KINETIC_TEMPERATURE) + name="kinetic_temperature"; + else if (index == HARDY_STRESS) + name="stress"; + else if (index == HARDY_ESHELBY_STRESS) + name="eshelby_stress"; + else if (index == HARDY_CAUCHY_BORN_STRESS) + name="cauchy_born_stress"; + else if (index == HARDY_HEAT_FLUX) + name="heat_flux"; + else if (index == HARDY_ENERGY) + name="energy"; + else if (index == HARDY_NUMBER_DENSITY) + name="number_density"; + else if (index == HARDY_TRANSFORMED_STRESS) + name="transformed_stress"; + else if (index == HARDY_VACANCY_CONCENTRATION) + name="vacancy_concentration"; + else if (index == HARDY_TYPE_CONCENTRATION) + name="type_concentration"; + else + return false; + + return true; + }; + +// Forward declarations +class FE_Engine; +class StressCauchyBorn; + +class ATC_TransferHardy : public ATC_Transfer { + + public: + + // constructor + ATC_TransferHardy(std::string groupName, std::string matParamFile = "none"); + + // destructor + ~ATC_TransferHardy(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + /** post time integration */ + virtual void finish(); + + /** first time substep routines */ + virtual void pre_init_integrate(); + virtual void init_integrate_velocity(){}; + virtual void mid_init_integrate(){}; + virtual void init_integrate_position(){}; + virtual void post_init_integrate(); + + + /** second time substep routine */ + virtual void pre_final_integrate(); + virtual void final_integrate(){}; + virtual void post_final_integrate(); + + virtual void set_ghost_atoms() {}; + + private: + /** pointer to position data : either x_reference or x_current */ + double ** xPointer_; + + /** data */ + map hardyData_; + map hardyDataOld_; + map filteredHardyData_; + DENS_MAT atomicTemperature_; + DENS_MAT atomicKineticTemperature_; + DENS_MAT atomicDensity_; + DENS_MAT atomicMomentum_; + DENS_MAT atomicDisplacement_; + DENS_MAT atomicVelocity_; + DENS_MAT atomicStress_; + DENS_MAT atomicHeat_; + DENS_MAT atomicEnergy_; + + /** reference data */ + bool setRefPE_; + DENS_MAT nodalRefPotentialEnergy_; + double nodalRefPEvalue_; + bool setRefPEvalue_; + + /** contour/boundary integral data */ + map < pair, const set< PAIR > * > bndyIntegralData_; + map < pair, const set< PAIR > * > contourIntegralData_; + + /** workset data */ + SPAR_MAT kernelShpFcn_; + SPAR_MAT atomicBondTable_; + DENS_MAT vbar_; + DENS_MAT ubar_; + DENS_MAT atomicForceTable_; + DENS_MAT atomicHeatTable_; + DENS_MAT uVariationVelocity_; + vector< SPAR_MAT > gradientTable_; + + /** calculation flags */ + Array fieldFlags_; + Array outputFlags_; + Array gradFlags_; + Array rateFlags_; + map computes_; + + /** calculation flags */ + Array fieldSizes_; + + /** compute nodal quantities */ + void compute_potential_energy(DENS_MAT & nodalPE); + void compute_number_density(DENS_MAT & density); + void compute_mass_density(DENS_MAT & density); + void compute_displacement(DENS_MAT & displacement, + const DENS_MAT & density); + void compute_momentum(DENS_MAT & momentum); + void compute_projected_velocity(DENS_MAT & velocity); + void compute_velocity(DENS_MAT & velocity, + const DENS_MAT & density, const DENS_MAT & momentum); + void compute_variation_velocity(DENS_MAT & velocity, + const DENS_MAT & vI); + void compute_temperature(DENS_MAT & temperature); + void compute_kinetic_temperature(DENS_MAT & temperature); + void compute_stress(DENS_MAT & stress); + void compute_heatflux(DENS_MAT & flux); + void compute_total_energy(DENS_MAT & energy); + void compute_eshelby_stress(DENS_MAT & eshebly_stress, + const DENS_MAT & energy, const DENS_MAT & stress, + const DENS_MAT & displacement_gradient); + void compute_cauchy_born_stress(DENS_MAT & cb_stress, + const DENS_MAT & displacement_gradient); + void compute_transformed_stress(DENS_MAT & stress, + const DENS_MAT & T, const DENS_MAT & displacement_gradient); + void compute_vacancy_concentration(DENS_MAT & vacancy_concentration, + const DENS_MAT & displacement_gradient, + const DENS_MAT & number_density); + void compute_type_concentration(DENS_MAT & type_concentration); + + /** compute smooth fields */ + void compute_fields(void); + void time_filter_pre (double dt, bool output_now = true); + void time_filter_post(double dt, bool output_now = true); + + /** compute boundary integral */ + void compute_boundary_integrals(void); + + /** output function */ + void output(); + + /** physics specific filter initialization */ + void init_filter(); + + /** kernel */ + ATC_HardyKernel* kernelFunction_; + + /** mapping of atomic pairs to pair index value */ + map< pair< int,int >,int > pairMap_; + int nPairs_; + + /** routine to compute pair map */ + void compute_pair_map(); + + /** routine to calculate matrix of Hardy bond functions */ + void compute_bond_matrix(); + + /** routine to calculate matrix of kernel shape functions */ + void compute_kernel_matrix(); + + /** routine to calculate matrix of gradient of Hardy functions */ + void compute_gradient_matrix(); + + /** routine to check pair map */ + void check_pair_map(); + + /** routine to calculate matrix of force & position dyads */ + void compute_force_matrix(); + + /** routine to calculate matrix of heat flux vector components */ + void compute_heat_matrix(); + + /** routine to calculate stress on-the-fly */ + void compute_potential_stress(DENS_MAT& stress); + + /** routine to calculate force part of the heat flux on-the-fly */ + void compute_potential_heatflux(DENS_MAT& flux); + + /** periodicity flags and lengths */ + int periodicity[3]; + double box_bounds[2][3]; + double box_length[3]; + + /** routine to adjust node-pair distances due to periodicity */ + void periodicity_correction(DENS_VEC& xaI); + + /** routine to set xPointer to xref or xatom */ + void set_xPointer(); + + /** number of atom types */ + int nTypes_; + + protected: + + /** project (number density): given w_\alpha, + w_I = \sum_\alpha N_{I\alpha} w_\alpha */ + void project_count_normalized(const DENS_MAT & atomData, + DENS_MAT & nodeData); + + /** hardy_project (volume density): given w_\alpha, + w_I = 1/\Omega_I \sum_\alpha N_{I\alpha} w_\alpha + where \Omega_I = \int_{support region of node I} N_{I} dV */ + void project_volume_normalized(const DENS_MAT & atomData, + DENS_MAT & nodeData); + + /** gradient_compute: given w_I, + w_J = \sum_I N_I'{xJ} \dyad w_I + where N_I'{xJ} is the gradient of the normalized + shape function of node I evaluated at node J */ + void gradient_compute(const DENS_MAT & inNodeData, + DENS_MAT & outNodeData); + + int nNodesGlobal_; + + /** compute kernel shape functions on-the-fly w/o storing N_Ia */ + bool kernelOnTheFly_; + + /** compute stress or heat flux on-the-fly w/o storing B_Iab */ + bool bondOnTheFly_; + + /** if false, no coarse velocity is calculated for kernel-based estimates */ + bool useAtomicShapeFunctions_; + + /** a continuum model to compare to and/or estimate Hardy quantities */ + StressCauchyBorn * cauchyBornStress_; + + const LammpsInterface * constLammpsInterface_; + + Array timeFilters_; + + /** check consistency of fieldFlags_ */ + void check_fieldFlags_consistency(); + +}; + +}; + +#endif diff --git a/lib/atc/ATC_TransferThermal.cpp b/lib/atc/ATC_TransferThermal.cpp new file mode 100644 index 0000000000..2986a1597b --- /dev/null +++ b/lib/atc/ATC_TransferThermal.cpp @@ -0,0 +1,541 @@ +// ATC_Transfer headers +#include "ATC_TransferThermal.h" +#include "ATC_Error.h" +#include "LammpsInterface.h" +#include "PrescribedDataManager.h" + +// Other Headers +#include +#include +#include + +namespace ATC { + + ATC_TransferThermal::ATC_TransferThermal(string groupName, + string matParamFile, + ExtrinsicModelType extrinsicModel) + : ATC_Transfer(), + thermostat_(this), + pmfcOn_(false) + { + // assign default "internal" group + int igroup = lammpsInterface_->find_group(groupName.c_str()); + groupbit_ |= lammpsInterface_->group_bit(igroup); + igroups_.insert(igroup); + + // Allocate PhysicsModel + create_physics_model(THERMAL, matParamFile); + + // create extrinsic physics model + if (extrinsicModel != NO_MODEL) { + extrinsicModelManager_.create_model(extrinsicModel,matParamFile); + } + + simTime_ = 0.; + integrationType_ = TimeIntegrator::VERLET; + atomicOutputMask_.insert("temperature"); + + // set up field data based on physicsModel + physicsModel_->get_num_fields(fieldSizes_,fieldMask_); + + // output variable vector info: + // output[1] = total coarse scale thermal energy + // output[2] = average temperature + vectorFlag_ = 1; + sizeVector_ = 2; + scalarVectorFreq_ = 1; + extVector_ = 1; + if (extrinsicModel != NO_MODEL) + sizeVector_ += extrinsicModelManager_.size_vector(sizeVector_); + } + + ATC_TransferThermal::~ATC_TransferThermal() + { + } + + void ATC_TransferThermal::initialize() + { + // Base class initalizations + ATC_Transfer::initialize(); + + if (!timeFilterManager_.filter_dynamics()) { + DENS_VEC atomicKineticEnergy(nLocal_); + compute_atomic_kinetic_energy(atomicKineticEnergy, lammpsInterface_->vatom()); + project_volumetric_quantity(atomicKineticEnergy,fieldNdFiltered_[TEMPERATURE],TEMPERATURE); + fieldNdFiltered_[TEMPERATURE] *= 2.; + } + + thermostat_.initialize(); + extrinsicModelManager_.initialize(); + + if (!initialized_) { + // initialize sources based on initial FE temperature + double dt = lammpsInterface_->dt(); + prescribedDataMgr_->set_sources(simTime_+.5*dt,sources_); + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + thermostat_.compute_boundary_flux(fields_); + compute_atomic_sources(fieldMask_,fields_,atomicSources_); + } + + if (timeFilterManager_.need_reset()) { + init_filter(); + timeFilterManager_.initialize(); + } + + // reset integration field mask + temperatureMask_.reset(NUM_FIELDS,NUM_FLUX); + temperatureMask_ = false; + for (int i = 0; i < NUM_FLUX; i++) + temperatureMask_(TEMPERATURE,i) = fieldMask_(TEMPERATURE,i); + initialized_ = true; + + + if (pmfcOn_) { + oldFieldTemp_.reset(nNodes_,1); + } + + // read in field data if necessary + if (useRestart_) { + OUTPUT_LIST data; + read_restart_data(restartFileName_,data); + useRestart_ = false; + } + } + + void ATC_TransferThermal::init_filter() + { + // NOTE assume total filtered time derivatives are zero + ATC_Transfer::init_filter(); + + if (integrationType_==TimeIntegrator::VERLET) { + // initialize restricted fields + // NOTE: comment in to initialize fieldRateNdOld_ to current time deriviative + // current assumption is that it is zero + //DenseMatrix atomicPower(nLocal_,1); + //compute_atomic_power(atomicPower, + // lammpsInterface_->vatom(), + // lammpsInterface_->fatom()); + //restrict(atomicPower,fieldRateNdOld_[TEMPERATURE]); + //fieldRateNdOld *= 2.; + + DENS_MAT atomicKineticEnergy(nLocal_,1); + DENS_MAT nodalAtomicTemperature(nNodes_,1); + compute_atomic_temperature(atomicKineticEnergy, + lammpsInterface_->vatom()); + restrict(atomicKineticEnergy, nodalAtomicTemperature); + nodalAtomicTemperature *= 2.; + fieldNdOld_[TEMPERATURE] = nodalAtomicTemperature; + } + + if (timeFilterManager_.end_equilibrate()) { // set up correct initial lambda power to enforce initial temperature rate of 0 + if (integrationType_==TimeIntegrator::VERLET) { + if (equilibriumStart_) { + if (thermostat_.get_thermostat_type()==Thermostat::FLUX) { // based on FE equation + DENS_MAT vdotflamMat(-2.*fieldRateNdFiltered_[TEMPERATURE]); // note 2 is for 1/2 vdotflam addition + thermostat_.reset_lambda_power(vdotflamMat); + } + else { // based on MD temperature equation + DENS_MAT vdotflamMat(-1.*fieldRateNdFiltered_[TEMPERATURE]); + thermostat_.reset_lambda_power(vdotflamMat); + } + } + } + } + else { // set up space for filtered atomic power + // should set up all data structures common to equilibration and filtering, + // specifically filtered atomic power + if (integrationType_==TimeIntegrator::FRACTIONAL_STEP) { + dot_atomicTemp.reset(nNodes_,1); + dot_atomicTempOld.reset(nNodes_,1); + dot_dot_atomicTemp.reset(nNodes_,1); + dot_dot_atomicTempOld.reset(nNodes_,1); + } + } + } + + void ATC_TransferThermal::compute_md_mass_matrix(FieldName thisField, + map & massMats) + { + if (thisField == TEMPERATURE) { + DENS_VEC atomicCapacity(nLocal_); + atomicCapacity = nsd_*(LammpsInterface::instance()->kBoltzmann()); + DENS_VEC nodalAtomicCapacity(nNodes_); + restrict_volumetric_quantity(atomicCapacity,nodalAtomicCapacity); + massMats[thisField].reset(nodalAtomicCapacity); + } + } + + + void ATC_TransferThermal::finish() + { + // base class + ATC_Transfer::finish(); + + // nothing specific to thermal + } + + bool ATC_TransferThermal::modify(int narg, char **arg) + { + bool foundMatch = false; + int argIndx = 0; + + // check to see if input is a transfer class command + // check derived class before base class + if (strcmp(arg[argIndx],"transfer")==0) { + argIndx++; + + // pass-through to thermostat + if (strcmp(arg[argIndx],"thermal")==0) { + argIndx++; + if (strcmp(arg[argIndx],"control")==0) { + argIndx++; + foundMatch = thermostat_.modify(narg-argIndx,&arg[argIndx]); + } + } + + // switch for equilibrium filtering start + /*! \page man_equilibrium_start fix_modify AtC transfer equilibrium_start + \section syntax + fix_modify AtC transfer equilibrium_start + + \section examples + fix_modify atc transfer equilibrium_start on \n + + \section description + Starts filtered calculations assuming they start in equilibrium, i.e. perfect finite element force balance. + + \section restrictions + only needed before filtering is begun + + \section related + see \ref man_time_filter + + \section default + on + */ + else if (strcmp(arg[argIndx],"equilibrium_start")==0) { + argIndx++; + if (strcmp(arg[argIndx],"on")==0) { + equilibriumStart_ = true; + foundMatch = true; + } + else if (strcmp(arg[argIndx],"off")==0) { + equilibriumStart_ = false; + foundMatch = true; + } + } + + // time integration scheme + /*! \page man_time_integration fix_modify AtC transfer pmfc + \section syntax + fix_modify AtC transfer pmfc + + \section examples + fix_modify atc transfer pmfc on + + \section description + Switches the poor man's fractional step algorithm on where the finite element data lags the exact atomic data by one time step for overlap nodes + + \section restrictions + \section related + \section default + off + */ + else if (strcmp(arg[argIndx],"pmfc")==0) { + if (integrationType_!=TimeIntegrator::VERLET || timeFilterManager_.filter_dynamics()) + throw ATC_Error(0,"Can only use poor man's fractional step with Verlet integration without filtering"); + pmfcOn_ = !pmfcOn_; + foundMatch = true; + } + } + + // no match, call base class parser + if (!foundMatch) { + foundMatch = ATC_Transfer::modify(narg, arg); + } + + return foundMatch; + + } + + //-------------------------------------------------- + // pack_fields + // bundle all allocated field matrices into a list + // for output needs + //-------------------------------------------------- + void ATC_TransferThermal::pack_thermal_fields(OUTPUT_LIST & data) + { + data["ddFieldTemp"] = & oldFieldTemp_; + data["lambdaPowerFiltered"] = &(thermostat_.get_filtered_lambda_power()); + } + + //-------------------------------------------------- + // write_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_TransferThermal::write_restart_data(string fileName, OUTPUT_LIST & data) + { + pack_thermal_fields(data); + ATC_Transfer::write_restart_data(fileName,data); + } + + //-------------------------------------------------- + // write_restart_file + // bundle matrices that need to be saved and call + // fe_engine to write the file + //-------------------------------------------------- + void ATC_TransferThermal::read_restart_data(string fileName, OUTPUT_LIST & data) + { + pack_thermal_fields(data); + ATC_Transfer::read_restart_data(fileName,data); + } + + void ATC_TransferThermal::reset_nlocal() + { + ATC_Transfer::reset_nlocal(); + thermostat_.reset_nlocal(); + } + + void ATC_TransferThermal::pre_init_integrate() + { + //ATC_Transfer::pre_init_integrate(); + double dt = lammpsInterface_->dt(); + double dtLambda = 0.5*dt; + + if (pmfcOn_) { + oldFieldTemp_ = fields_[TEMPERATURE]; + } + + // Apply thermostat to atom velocities + thermostat_.apply_pre_predictor(dtLambda,lammpsInterface_->ntimestep()); + + + // Predict nodal temperatures and time derivatives based on FE data + // 4th order Gear + // switch call based on filter + if (timeFilterManager_.filter_dynamics()) + gear1_4_predict(fields_[TEMPERATURE],dot_fields_[TEMPERATURE], + ddot_fields_[TEMPERATURE],dddot_fields_[TEMPERATURE],dt); + else + gear1_3_predict(fields_[TEMPERATURE],dot_fields_[TEMPERATURE], + ddot_fields_[TEMPERATURE],dt); + + extrinsicModelManager_.pre_init_integrate(); + + // fixed values, non-group bcs handled through FE + set_fixed_nodes(); + + simTime_ += .5*dt; + } + + void ATC_TransferThermal::post_init_integrate() + { + // Nothing to do here + //ATC_Transfer::post_init_integrate(); + } + + void ATC_TransferThermal::pre_final_integrate() + { + ATC_Transfer::pre_final_integrate(); + } + + + void ATC_TransferThermal::post_final_integrate() + { + //ATC_Transfer::post_final_integrate(); + double dt = lammpsInterface_->dt(); + double dtLambda = dt*0.5; + // predict thermostat contributions + // compute sources based on predicted FE temperature + prescribedDataMgr_->set_sources(simTime_+.5*dt,sources_); + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + thermostat_.compute_boundary_flux(fields_); + compute_atomic_sources(temperatureMask_,fields_,atomicSources_); + thermostat_.apply_pre_corrector(dtLambda,lammpsInterface_->ntimestep()); + + // Determine FE contributions to d theta/dt + // Compute atom-integrated rhs + // parallel communication happens within FE_Engine + compute_rhs_vector(temperatureMask_,fields_,rhs_,FE_DOMAIN); + + // For flux matching, add 1/2 of "drag" power + thermostat_.add_to_rhs(rhs_); + + extrinsicModelManager_.post_final_integrate(); + // add in atomic FE contributions for verlet method + if (integrationType_==TimeIntegrator::VERLET) { + DENS_MAT atomicPower(nLocal_,1); + compute_atomic_power(atomicPower, + lammpsInterface_->vatom(), + lammpsInterface_->fatom()); + DENS_MAT nodalAtomicPower(nNodes_,1); + restrict_volumetric_quantity(atomicPower,nodalAtomicPower); + nodalAtomicPower *= 2.; + + if (timeFilterManager_.filter_variables()) + update_filter_implicit(fieldRateNdFiltered_[TEMPERATURE],nodalAtomicPower,dt); + else + fieldRateNdFiltered_[TEMPERATURE] = nodalAtomicPower; + if (!timeFilterManager_.filter_variables() || timeFilterManager_.filter_dynamics()) + rhs_[TEMPERATURE] += fieldRateNdFiltered_[TEMPERATURE]; + else + rhs_[TEMPERATURE] += nodalAtomicPower; + } + + // Finish updating temperature + DENS_MAT R_theta(nNodes_,1); + apply_inverse_mass_matrix(rhs_[TEMPERATURE],TEMPERATURE); + R_theta = (rhs_[TEMPERATURE] - dot_fields_[TEMPERATURE])*dt; + if (timeFilterManager_.filter_dynamics()) + gear1_4_correct(fields_[TEMPERATURE],dot_fields_[TEMPERATURE], + ddot_fields_[TEMPERATURE],dddot_fields_[TEMPERATURE], + R_theta,dt); + else + gear1_3_correct(fields_[TEMPERATURE],dot_fields_[TEMPERATURE], + ddot_fields_[TEMPERATURE],R_theta,dt); + + // only requirecd for poor man's fractional step + if (pmfcOn_) { + DENS_MAT atomicKineticEnergy(nLocal_,1); + compute_atomic_kinetic_energy(atomicKineticEnergy, + lammpsInterface_->vatom()); + DENS_MAT temperatureNd(nNodes_,1); + project_md_volumetric_quantity(atomicKineticEnergy, + temperatureNd, + TEMPERATURE); + temperatureNd *= 2.; + dot_fields_[TEMPERATURE] = 1.0/dt * ( temperatureNd - oldFieldTemp_); + fields_[TEMPERATURE] = temperatureNd; + } + + // fix nodes, non-group bcs applied through FE + set_fixed_nodes(); + + // compute sources based on final FE updates + prescribedDataMgr_->set_sources(simTime_+.5*dt,sources_); + extrinsicModelManager_.set_sources(fields_,extrinsicSources_); + thermostat_.compute_boundary_flux(fields_); + compute_atomic_sources(temperatureMask_,fields_,atomicSources_); + + // apply corrector phase of thermostat + thermostat_.apply_post_corrector(dtLambda,lammpsInterface_->ntimestep()); + + // add in MD contributions to time derivative + // update filtered temperature + DENS_MAT atomicKineticEnergy(nLocal_,1); + compute_atomic_kinetic_energy(atomicKineticEnergy, + lammpsInterface_->vatom()); + DENS_MAT temperatureNd(nNodes_,1); + project_md_volumetric_quantity(atomicKineticEnergy,temperatureNd,TEMPERATURE); + temperatureNd *= 2.; + if (!timeFilterManager_.filter_dynamics()) + fieldNdFiltered_[TEMPERATURE] = temperatureNd; + else if (integrationType_==TimeIntegrator::VERLET) + update_filter_implicit(fieldNdFiltered_[TEMPERATURE],temperatureNd,dt); + + simTime_ += .5*dt; + output(); + } + + //-------------------------------------------------------------------- + // compute_vector + //-------------------------------------------------------------------- + // this is for direct output to lammps thermo + double ATC_TransferThermal::compute_vector(int n) + { + // output[1] = total coarse scale thermal energy + // output[2] = average temperature + + double mvv2e = lammpsInterface_->mvv2e(); // convert to lammps energy units + + if (n == 0) { + Array mask(1); + FIELDS energy; + mask(0) = TEMPERATURE; + feEngine_->compute_energy(mask, // NOTE make this a base class function + fields_, + physicsModel_, + elementToMaterialMap_, + energy, + &elementMask_); + + double phononEnergy = mvv2e * energy[TEMPERATURE].col_sum(); + return phononEnergy; + } + else if (n == 1) { + double aveT = fields_[TEMPERATURE].col_sum()/nNodes_; + return aveT; + } + else if (n > 1) { + double extrinsicValue = extrinsicModelManager_.compute_vector(n); + return extrinsicValue; + } + + return 0.; + + } + + + + //-------------------------------------------------------------------- + // output + //-------------------------------------------------------------------- + void ATC_TransferThermal::output() + { + double dt = lammpsInterface_->dt(); + + double step = (double) lammpsInterface_->ntimestep(); + ++stepCounter_; + + if ((outputFrequency_ > 0) + && ((stepCounter_ ==1) || (stepCounter_ % outputFrequency_ == 0)) ) { + + OUTPUT_LIST output_data; + + // Add some outputs only on Proc 0 + if (lammpsInterface_->comm_rank() == 0) { + // base class output + ATC_Transfer::output(); + + // global data + double T_mean = fields_[TEMPERATURE].col_sum(0)/nNodes_; + feEngine_->add_global("temperature_mean", T_mean); + double T_stddev = fields_[TEMPERATURE].col_stdev(0); + feEngine_->add_global("temperature_std_dev", T_stddev); + double Ta_mean = fieldNdFiltered_[TEMPERATURE].col_sum(0)/nNodes_; + feEngine_->add_global("atomic_temperature_mean", Ta_mean); + double Ta_stddev = fieldNdFiltered_[TEMPERATURE].col_stdev(0); + feEngine_->add_global("atomic_temperature_std_dev", Ta_stddev); + + // mesh data + output_data["nodalAtomicTemperature"] = & fieldNdFiltered_[TEMPERATURE]; + output_data["dot_temperature"] = & dot_fields_[TEMPERATURE]; + output_data["ddot_temperature"] = & ddot_fields_[TEMPERATURE]; + output_data["nodalAtomicPower"] = & fieldRateNdFiltered_[TEMPERATURE]; + + // auxilliary data + thermostat_.output(dt,output_data); + extrinsicModelManager_.output(dt,output_data); + } + + + // Output fe data on proc 0 + if (lammpsInterface_->comm_rank() == 0) { + feEngine_->write_data(simTime_, fields_, & output_data); + } + + } + + if ((outputFrequencyAtom_ > 0) + && ((stepCounter_ ==1) || (stepCounter_ % outputFrequencyAtom_ == 0)) ) + atomic_output(); + } + + + void ATC_TransferThermal::set_ghost_atoms() + { + // Nothing to do here + } + +}; diff --git a/lib/atc/ATC_TransferThermal.h b/lib/atc/ATC_TransferThermal.h new file mode 100644 index 0000000000..0c9e5d5bcc --- /dev/null +++ b/lib/atc/ATC_TransferThermal.h @@ -0,0 +1,96 @@ +/** ATC_TransferThermal : a class for atom-continuum transfers & + control involving heat transport */ + +#ifndef ATC_TRANSFER_THERMAL_H +#define ATC_TRANSFER_THERMAL_H + +// ATC_Transfer headers +#include "ATC_Transfer.h" +#include "Thermostat.h" +#include "TimeIntegrator.h" + +// Other headers +#include + +namespace ATC { + + class ATC_TransferThermal : public ATC_Transfer { + + public: + + // constructor + ATC_TransferThermal(std::string groupName, std::string matParamFile, + ExtrinsicModelType extrinsic = NO_MODEL); + + // destructor + ~ATC_TransferThermal(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + /** post time integration */ + virtual void finish(); + + /** first time substep routines */ + virtual void pre_init_integrate(); + virtual void mid_init_integrate(){}; + virtual void post_init_integrate(); + + /** second time substep routine */ + virtual void pre_final_integrate(); + virtual void post_final_integrate(); + + /** compute vector for output */ + virtual double compute_vector(int n); + + private: + + /** sets the position/velocity of the ghost atoms */ + virtual void set_ghost_atoms(); + + /** resets any arrays associated with local atom count */ + virtual void reset_nlocal(); + + /** compute the mass matrix components coming from MD integration */ + virtual void compute_md_mass_matrix(FieldName thisField, + map & massMats); + + /** time integration flag and access method */ + TimeIntegrator::TimeIntegrationType integrationType_; + virtual TimeIntegrator::TimeIntegrationType get_integration_type(){return integrationType_;}; + + /** fractional step auxilliary storage */ + DENS_MAT delTheta; + DENS_MAT delThetaV; + DENS_MAT dot_atomicTemp; + DENS_MAT dot_atomicTempOld; + DENS_MAT dot_dot_atomicTemp; + DENS_MAT dot_dot_atomicTempOld; + + /** physics specific filter initialization */ + void init_filter(); + + /** field mask for velocity integration */ + Array2D temperatureMask_; + + void output(); + + /** thermostat manager */ + Thermostat thermostat_; + + // Data for poor man's fractional step + bool pmfcOn_; + DENS_MAT oldFieldTemp_; + + // Add in fields for restarting + virtual void read_restart_data(string fileName_, OUTPUT_LIST & data); + virtual void write_restart_data(string fileName_, OUTPUT_LIST & data); + void pack_thermal_fields(OUTPUT_LIST & data); + + }; + +}; +#endif diff --git a/lib/atc/ATC_TransferUtility.cpp b/lib/atc/ATC_TransferUtility.cpp new file mode 100644 index 0000000000..d24fddf772 --- /dev/null +++ b/lib/atc/ATC_TransferUtility.cpp @@ -0,0 +1,2221 @@ +// ATC_Transfer headers +#include "ATC_Transfer.h" +#include "FE_Engine.h" +#include "Array.h" +#include "Array2D.h" +#include "ATC_Error.h" +#include "CG.h" +#include "XT_Function.h" +#include "PrescribedDataManager.h" +#include "LammpsInterface.h" + +#include + +namespace ATC { + + //================================================================= + // quadrature weight functions + //================================================================= + + //----------------------------------------------------------------- + void ATC_Transfer::set_atomic_weights(void) + { + switch (atomWeightType_) { + case USER: + reset_atomicWeightsGroup(); + break; + case LATTICE: + reset_atomicWeightsLattice(); + break; + case ELEMENT: + reset_atomicWeightsElement(); + break; + case REGION: + if (!initialized_) { + // Uses region bounding box as total volume + // note will *only* work if atoms exactly fill the region + int nregion = lammpsInterface_->nregion(); + Array volume(nregion); + + for (int i = 0; i < nregion; ++i) { + volume(i) = + (lammpsInterface_->region_xhi(i)-lammpsInterface_->region_xlo(i)) + *(lammpsInterface_->region_yhi(i)-lammpsInterface_->region_ylo(i)) + *(lammpsInterface_->region_zhi(i)-lammpsInterface_->region_zlo(i)); + } + + DenseVector localCount(nregion); + DenseVector globalCount(nregion); + + // loop over atoms + localCount = 0; + for (int i = 0; i < nLocal_; ++i) { + for (int j = 0; j < nregion; ++j) { + if (lammpsInterface_->region_match(j, + atomicCoords_(0,i), + atomicCoords_(1,i), + atomicCoords_(2,i))) { + localCount(j)++; + } + } + } + // communication to get total atom counts per group + lammpsInterface_->int_allsum(localCount.get_ptr(), + globalCount.get_ptr(),nregion); + + for (int i = 0; i < nregion; ++i) { + if (globalCount(i) > 0) + Valpha_[i] = volume(i)/globalCount(i); + else + Valpha_[i] = 0; + } + } + + reset_atomicWeightsRegion(); + break; + case GROUP: + if (!initialized_) { + // Uses group bounding box as total volume + // note will *only* work if atoms exactly fill the box + int ngroup = lammpsInterface_->ngroup(); + int *mask = lammpsInterface_->atom_mask(); + double * bounds; + map volume; + + bounds = new double[6]; + for (int i = 0; i < ngroup; ++i) { + lammpsInterface_->group_bounds(i, bounds); + volume[lammpsInterface_->group_bit(i)] = + (bounds[1]-bounds[0])*(bounds[3]-bounds[2])*(bounds[5]-bounds[4]); + } + delete [] bounds; + + DenseVector localCount(ngroup); + DenseVector globalCount(ngroup); + + // loop over atoms + localCount = 0; + for (int i = 0; i < nLocal_; ++i) { + for (int j = 0; j < ngroup; ++j) { + if (mask[internalToAtom_(i)] & lammpsInterface_->group_bit(j)) + localCount(j)++; + } + } + + // communication to get total atom counts per group + lammpsInterface_->int_allsum(localCount.get_ptr(), + globalCount.get_ptr(),ngroup); + + for (int i = 0; i < ngroup; ++i) { + int iGroupBit = lammpsInterface_->group_bit(i); + if (globalCount(i) > 0) + Valpha_[iGroupBit] = + volume[iGroupBit]/globalCount(i); + else + Valpha_[iGroupBit] = 0; + } + } + reset_atomicWeightsGroup(); + break; + case MULTISCALE: + reset_atomicWeightsMultiscale(shpFcn_,atomicWeights_); + break; + } + + if( nLocalMask_ > 0 ) { + atomicWeightsMask_.reset(nLocalMask_,nLocalMask_); + int nmask = 0; + for (int i = 0; i < nLocal_; ++i) { + if (atomMask_(i)) { + atomicWeightsMask_(nmask,nmask) = atomicWeights_(i,i); + nmask++; + } + } + } + } + + //----------------------------------------------------------------- + // check to see if shape function contains internal or ghost atoms + //----------------------------------------------------------------- + int ATC_Transfer::check_shape_function_type(int nodeIdx) + { + ShapeFunctionType thisShpType; + // NOTE change this check to not use shpWeight + if (shpWeight_(nodeIdx,nodeIdx) > 0.) { // NOTE need tolerance check + DENS_VEC tmpWeights(nNodes_); + DENS_VEC weights(nNodes_); + if (nLocalGhost_>0) + tmpWeights = shpFcnGhost_.col_sum(); + lammpsInterface_->allsum(tmpWeights.get_ptr(),weights.get_ptr(),nNodes_); + + if (weights(nodeIdx) > 0.) // NOTE need tolerance check + thisShpType = BOUNDARY; + else + thisShpType = MD_ONLY; + } + else + thisShpType = FE_ONLY; + + return thisShpType; + } + + //----------------------------------------------------------------- + // checks to see if an element is completely inside the MD domain + //----------------------------------------------------------------- + bool ATC_Transfer::check_internal(int eltIdx) + { + // NOTE we use a check only on the bounding boxes, not the actual shapes + int nNode; + DENS_MAT coords; + bool notInMD = false; + + feEngine_->element_coordinates(eltIdx,coords); + nNode = coords.nCols(); + + if (regionID_ < 0) { // loop over groups to find bounds + double xhi,xlo,yhi,ylo,zhi,zlo; + double * tmpBounds = new double[6]; + set::iterator iset; + iset = igroups_.begin(); // NOTE inefficient, use some bounds as initial values + lammpsInterface_->group_bounds(*iset,tmpBounds); + xlo = tmpBounds[0]; + xhi = tmpBounds[1]; + ylo = tmpBounds[2]; + yhi = tmpBounds[3]; + zlo = tmpBounds[4]; + zhi = tmpBounds[5]; + for (iset = igroups_.begin(); iset != igroups_.end(); iset++) { + lammpsInterface_->group_bounds(*iset,tmpBounds); + if (xlo>tmpBounds[0]) xlo = tmpBounds[0]; + if (xhitmpBounds[2]) ylo = tmpBounds[2]; + if (yhitmpBounds[4]) zlo = tmpBounds[4]; + if (zhixperiodic() == 1) checkx = false; + if (lammpsInterface_->yperiodic() == 1) checky = false; + if (lammpsInterface_->zperiodic() == 1) checkz = false; + for (int i = 0; i < nNode; ++i) { + if (((coords(0,i)xhi))&&checkx) notInMD = true; + if (((coords(1,i)yhi))&&checky) notInMD = true; + if (((coords(2,i)zhi))&&checkz) notInMD = true; + } + delete [] tmpBounds; + } + else { // use region denoted by regionID_ + for (int i = 0; i < nNode; ++i) { + if (!lammpsInterface_->region_match(regionID_,coords(0,i),coords(1,i),coords(2,i))) { + notInMD = true; + } + } + } + + return notInMD; + } + + //----------------------------------------------------------------- + // checks to see if an element intersects the ghost atoms + //----------------------------------------------------------------- + bool ATC_Transfer::intersect_ghost(int eltIdx) + { + // NOTE we use a check only on the bounding boxes, not the actual shapes + int nNode; + DENS_MAT coords; + bool intersectGhost = false; + + feEngine_->element_coordinates(eltIdx,coords); + nNode = coords.nCols(); + + double xhi,xlo,yhi,ylo,zhi,zlo; + double * tmpBounds; + tmpBounds = new double[6]; + set::iterator iset; + iset = igroupsGhost_.begin(); // NOTE inefficient, use some bounds as initial values + lammpsInterface_->group_bounds(*iset,tmpBounds); + xlo = tmpBounds[0]; + xhi = tmpBounds[1]; + ylo = tmpBounds[2]; + yhi = tmpBounds[3]; + zlo = tmpBounds[4]; + zhi = tmpBounds[5]; + for (iset = igroupsGhost_.begin(); iset != igroupsGhost_.end(); iset++) { + lammpsInterface_->group_bounds(*iset,tmpBounds); + if (xlotmpBounds[1]) xhi = tmpBounds[1]; + if (ylotmpBounds[3]) yhi = tmpBounds[3]; + if (zlotmpBounds[5]) zhi = tmpBounds[5]; + } + + bool xlower = true; + bool xhigher = true; + bool ylower = true; + bool yhigher = true; + bool zlower = true; + bool zhigher = true; + for (int i = 0; i < nNode; ++i) { + if (coords(0,i)>=xlo) + xlower = false; + if (coords(0,i)<=xhi) + xhigher = false; + if (coords(0,i)>=ylo) + ylower = false; + if (coords(0,i)<=yhi) + yhigher = false; + if (coords(0,i)>=zlo) + zlower = false; + if (coords(0,i)<=zhi) + zhigher = false; + } + if (!(xlower||xhigher||ylower||yhigher||zlower||zhigher)) + intersectGhost = true; + delete [] tmpBounds; + + return intersectGhost; + } + + //================================================================= + // memory management and processor information exchange + //================================================================= + + //----------------------------------------------------------------- + // memory usage of local atom-based arrays + //----------------------------------------------------------------- + int ATC_Transfer::memory_usage() + { + int bytes = lammpsInterface_->nmax() * 3 * sizeof(double); + return bytes; + } + + //----------------------------------------------------------------- + // allocate local atom-based arrays + //----------------------------------------------------------------- + void ATC_Transfer::grow_arrays(int nmax) + { + xref_ = + lammpsInterface_->grow_2d_double_array(xref_,nmax,3,"fix_atc:xref"); + } + + //----------------------------------------------------------------- + // copy values within local atom-based arrays + //----------------------------------------------------------------- + void ATC_Transfer::copy_arrays(int i, int j) + { + xref_[j][0] = xref_[i][0]; + xref_[j][1] = xref_[i][1]; + xref_[j][2] = xref_[i][2]; + } + + //----------------------------------------------------------------- + // pack values in local atom-based arrays for exchange with another proc + //----------------------------------------------------------------- + int ATC_Transfer::pack_exchange(int i, double *buf) + { + buf[0] = xref_[i][0]; + buf[1] = xref_[i][1]; + buf[2] = xref_[i][2]; + return 3; + } + + //----------------------------------------------------------------- + // unpack values in local atom-based arrays from exchange with another proc + //----------------------------------------------------------------- + int ATC_Transfer::unpack_exchange(int nlocal, double *buf) + { + xref_[nlocal][0] = buf[0]; + xref_[nlocal][1] = buf[1]; + xref_[nlocal][2] = buf[2]; + return 3; + } + + //----------------------------------------------------------------- + // pack values in local atom-based arrays from exchange with another proc + //----------------------------------------------------------------- + int ATC_Transfer::pack_comm(int n, int *list, double *buf, + int pbc_flag, int *pbc) + { + int i,j,m; + double dx,dy,dz; + + m = 0; + if (pbc_flag == 0) { + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = xref_[j][0]; + buf[m++] = xref_[j][1]; + buf[m++] = xref_[j][2]; + } + } + else { + if (lammpsInterface_->domain_triclinic() == 0) { + dx = pbc[0]*Xprd_; + dy = pbc[1]*Yprd_; + dz = pbc[2]*Zprd_; + } + else { + dx = pbc[0]*Xprd_ + pbc[5]*XY_ + pbc[4]*XZ_; + dy = pbc[1]*Yprd_ + pbc[3]*YZ_; + dz = pbc[2]*Zprd_; + } + for (i = 0; i < n; i++) { + j = list[i]; + buf[m++] = xref_[j][0] + dx; + buf[m++] = xref_[j][1] + dy; + buf[m++] = xref_[j][2] + dz; + } + } + return 3; + } + + //----------------------------------------------------------------- + // unpack values in local atom-based arrays from exchange with another proc + //----------------------------------------------------------------- + void ATC_Transfer::unpack_comm(int n, int first, double *buf) + { + int i,m,last; + + m = 0; + last = first + n; + for (i = first; i < last; i++) { + xref_[i][0] = buf[m++]; + xref_[i][1] = buf[m++]; + xref_[i][2] = buf[m++]; + } + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + bool ATC_Transfer::get_region_bounds(const char * regionName, + double & xmin, double & xmax, + double & ymin, double & ymax, + double & zmin, double & zmax, + double & xscale, + double & yscale, + double & zscale) + { + int iRegion = lammpsInterface_->get_region_id(regionName); + + xscale = lammpsInterface_->region_xscale(iRegion); + yscale = lammpsInterface_->region_yscale(iRegion); + zscale = lammpsInterface_->region_zscale(iRegion); + xmin = lammpsInterface_->region_xlo(iRegion); + xmax = lammpsInterface_->region_xhi(iRegion); + ymin = lammpsInterface_->region_ylo(iRegion); + ymax = lammpsInterface_->region_yhi(iRegion); + zmin = lammpsInterface_->region_zlo(iRegion); + zmax = lammpsInterface_->region_zhi(iRegion); + + if (strcmp(lammpsInterface_->region_style(iRegion),"block")==0) { + return true; + } + else { + return false; + } + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + void ATC_Transfer::reset_nlocal() + { + nLocalTotal_ = lammpsInterface_->nlocal(); + int * mask = lammpsInterface_->atom_mask(); + nLocal_ = 0; + nLocalGhost_ = 0; + for (int i = 0; i < nLocalTotal_; ++i) { + if (mask[i] & groupbit_) nLocal_++; + if (mask[i] & groupbitGhost_) nLocalGhost_++; + } + int local_data[2] = {nLocal_, nLocalGhost_}; + int data[2] = {0, 0}; + lammpsInterface_->int_allsum(local_data,data,2); + nInternal_ = data[0]; + nGhost_ = data[1]; + + // set up internal & ghost maps & coordinates + if (nLocal_>0) { + // set map + internalToAtom_.reset(nLocal_); + int j = 0; + // construct internalToAtom map + // : internal index -> local lammps atom index + for (int i = 0; i < nLocalTotal_; ++i) { + if (mask[i] & groupbit_) internalToAtom_(j++) = i; + } + // construct reverse map + for (int i = 0; i < nLocal_; ++i) { + atomToInternal_[internalToAtom_(i)] = i; + } + atomicCoords_.reset(nsd_,nLocal_); + } + if (nLocalGhost_>0) { + // set map + ghostToAtom_.reset(nLocalGhost_); + int j = 0; + for (int i = 0; i < nLocalTotal_; ++i) { + if (mask[i] & groupbitGhost_) ghostToAtom_(j++) = i; + } + ghostAtomCoords_.reset(nsd_,nLocalGhost_); + } + + reset_coordinates(); + } + + void ATC_Transfer::reset_coordinates() + { + // reset atomic coordinates + double ** xx = xref_; + if (atomToElementMapType_ == EULERIAN) xx = lammpsInterface_->xatom(); + + // fill coordinates + if (nLocal_ > 0) { + for (int i = 0; i < nLocal_; i++) { + for (int j = 0; j < nsd_; j++) + atomicCoords_(j,i) = xx[internalToAtom_(i)][j]; + } + } + + // fill ghost coordinates + if (nLocalGhost_ > 0) { + for (int i = 0; i < nLocalGhost_; i++) { + for (int j = 0; j < nsd_; j++) + ghostAtomCoords_(j,i) = xx[ghostToAtom_(i)][j]; + } + } + } + + // shape functions are based on reference positions + // and only change when atoms cross processor boundaries + // (x_ref migrates with atoms but does not change value) + void ATC_Transfer::reset_shape_functions() + { + // FE_Engine allocates all required memory + // assume initial atomic position is the reference position for now + if (!initialized_) { + shpFcnDerivs_ = vector(nsd_); + shpFcnDerivsGhost_ = vector(nsd_); + shpFcnDerivsMask_ = vector(nsd_); + } + + // use FE_Engine to compute shape functions at atomic points + if (nLocal_>0) { + feEngine_->evaluate_shape_functions(atomicCoords_, + shpFcn_, + shpFcnDerivs_, + atomToElementMap_); + } + if (nLocalGhost_>0) { + feEngine_->evaluate_shape_functions(ghostAtomCoords_, + shpFcnGhost_, + shpFcnDerivsGhost_, + ghostAtomToElementMap_); + } + + // shpFcnMasked are the shape functions over atoms for which we need to do approximate + // atomic quadrature to remove the FE contributions, i.e. all elements with + // internal atoms if atomQuadForInternal is true and all elements containing + // but not fully filled by atoms (e.g. has internal and ghost types) if + // atomQuadForInternal is false (this has *nothing* to do with thermostats!) + // determine full/partial/empty elements + if (nLocal_>0) { + atomMask_.reset(nLocal_); + if (atomQuadForInternal_) { + atomMask_ = true; + nLocalMask_ = nLocal_; + } + else { + Array shapeFunctionMask(nNodes_); + shapeFunctionMask = false; + atomMask_ = false; + nLocalMask_ = 0; + for (int i = 0; i < nLocal_; ++i) { + DENS_VEC coords(3); + coords(0) = atomicCoords_(0,i); + coords(1) = atomicCoords_(1,i); + coords(2) = atomicCoords_(2,i); + int eltID = feEngine_->get_feMesh()->map_to_element(coords); + atomMask_(i) = elementMask_(eltID); + if (atomMask_(i)) nLocalMask_++; + } + } + + if( nLocalMask_ > 0 ) { + atomicCoordsMask_.reset(nsd_,nLocalMask_); + int nmask = 0; + for (int i = 0; i < nLocal_; ++i) { + if (atomMask_(i)) { + for (int j = 0; j < nsd_; ++j) { + atomicCoordsMask_(j,nmask) = atomicCoords_(j,i); + } + nmask++; + } + } + + // NOTE inefficient but easy to implement + Array tempArray; + feEngine_->evaluate_shape_functions(atomicCoordsMask_, + shpFcnMasked_, + shpFcnDerivsMask_, + tempArray); + + } + } + + shpWeight_.reset(nNodes_,nNodes_); + DENS_VEC tmpWeights(nNodes_); + DENS_VEC weights(nNodes_); + // Computed shape function weights + // If weight is function of shape function index only, must be (sum_alpha + // N_I^alpha)^-1 + if (nLocal_>0) tmpWeights = shpFcn_.col_sum(); + lammpsInterface_->allsum(tmpWeights.get_ptr(),weights.get_ptr(),nNodes_); + + for (int i = 0; i < nNodes_; i++) { + if (weights(i) > 0.) { + shpWeight_(i,i) = 1./weights(i); + } + } + + return; + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + void ATC_Transfer::reset_NhatOverlap() + { + // determine which atoms are being thermostatted, if we use a global lambda + // it is all of them or if we used a local atom it is only those in the support + // of shape functions on the boundary, i.e. those containing atoms in their support + // but not having their support completely in the MD region (e.g. they contain both + // internal and ghost atoms). (this has *nothing* to do with FE integration!) + if (nLocal_ > 0) { + // form mask + Array atomMask(nLocal_); + if (!useLocalizedLambda_) { + atomMask = true; + nLocalLambda_ = nLocal_; + } + else { + atomMask = false; + nLocalLambda_ = 0; + DENS_VEC coords(nsd_); + for (int i = 0; i < nLocal_; ++i) { + for (int j = 0; j < nsd_; ++j) { + coords(j) = atomicCoords_(j,i); + } + int eltID = feEngine_->get_feMesh()->map_to_element(coords); + Array nodes; + feEngine_->get_feMesh()->element_connectivity_unique(eltID,nodes); + for (int j = 0; j < nodes.size(); ++j) { + if (maskMat_(nodes(j),nodes(j)) > 0.) { + atomMask(i) = true; + nLocalLambda_++; + break; + } + } + } + } + + // compute shape functions and normalize + NhatOverlap_.reset(nLocalLambda_,nNodeOverlap_); + if( nLocalLambda_ > 0 ) { + DENS_MAT atomicCoords(nsd_,nLocalLambda_); + int count = 0; + for (int i = 0; i < nLocal_; ++i) { + if (atomMask(i)) { + for (int j = 0; j < nsd_; ++j) { + atomicCoords(j,count) = atomicCoords_(j,i); + } + count++; + } + } + // from shpFcnLambda get nz and map sparsity pattern + SPAR_MAT shpFcnLambda; + Array tempArray; + feEngine_->evaluate_shape_functions(atomicCoords,shpFcnLambda,tempArray); + + // NhatOverlap__ = shpFcnLambda*maskMat_ + DIAG_MAT scaling(maskMat_*shpWeight_); + CLON_VEC scaleVector(scaling); + NhatOverlapWeights_.reset(NhatOverlap_.nCols(),NhatOverlap_.nCols()); + for (int i = 0; i < shpFcnLambda.size(); ++i) { + TRIPLET myTriplet = shpFcnLambda.get_triplet(i); + int myCol = nodeToOverlapMap_(myTriplet.j); + if (myCol > -1) { + NhatOverlap_.set(myTriplet.i,myCol,myTriplet.v); + NhatOverlapWeights_(myCol) = scaleVector(myTriplet.j); + } + } + NhatOverlap_.compress(); + + // set up matrix that grabs only atoms to be used in the thermostat + Trestrict_.reset(nLocalLambda_,nLocal_); + int j = 0; + for (int i = 0; i < nLocal_; ++i) { + if (atomMask(i)) Trestrict_.set(j++,i,1.0); + } + Trestrict_.compress(); + } + } + else { + nLocalLambda_ = 0; + } + + // compute overlap ghost shape function + // NOTE if all ghosts do not lie in the support of boundary nodes + // this process will not be correct + if (nLocalGhost_>0) { + shpFcnGhostOverlap_ = shpFcnGhost_*maskMat_; + + DENS_VEC activeNodes(nNodes_); + for (int i = 0; i < nNodes_; ++i) { + activeNodes(i) = maskMat_(i,i); + } + DENS_VEC weights = shpFcnGhost_*activeNodes; + DIAG_MAT weightMatrix(nLocalGhost_,nLocalGhost_); + for (int i = 0; i < nLocalGhost_; ++i) { + weightMatrix(i,i) = 1./weights(i); + } + shpFcnGhostOverlap_ = weightMatrix*shpFcnGhostOverlap_; + } + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + void ATC_Transfer::reset_atomicWeightsGroup() + { + if (nLocal_>0) { + atomicWeights_.reset(nLocal_,nLocal_); + int *mask = lammpsInterface_->atom_mask(); + map::const_iterator igroup; + for (igroup = Valpha_.begin(); igroup != Valpha_.end(); igroup++) { + int gid = igroup->first; + double weight = igroup->second; + for (int i = 0; i < nLocal_; ++i) { + if (mask[internalToAtom_(i)] & gid) { + atomicWeights_(i,i) = weight; + } + } + } + } + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + void ATC_Transfer::reset_atomicWeightsRegion() + { + if (nLocal_>0) { + atomicWeights_.reset(nLocal_,nLocal_); + map::const_iterator iregion; + for (iregion = Valpha_.begin(); iregion != Valpha_.end(); iregion++) { + int rid = iregion->first; + double weight = iregion->second; + for (int i = 0; i < nLocal_; ++i) { + if (lammpsInterface_->region_match(rid, + atomicCoords_(0,i), + atomicCoords_(1,i), + atomicCoords_(2,i))) + atomicWeights_(i,i) = weight; + } + } + } + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + void ATC_Transfer::reset_atomicWeightsLattice() + { + // setup quadrature weights uniformly + if (nLocal_>0) { + atomicWeights_.reset(nLocal_,nLocal_); + double volume_per_atom = lammpsInterface_->volume_per_atom(); + atomicWeights_ = volume_per_atom; + } + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + void ATC_Transfer::reset_atomicWeightsElement() + { + const FE_Mesh * feMesh = feEngine_->get_feMesh(); + int nElts = feMesh->get_nElements(); + DenseVector eltAtomCtLocal(nElts); + DenseVector eltAtomCt(nElts); + DenseVector atomEltID; + DENS_VEC eltAtomVol(nElts); + + for (int i = 0; i < nElts; ++i) + eltAtomCtLocal(i) = 0; + + if (nLocal_>0) { + atomicWeights_.reset(nLocal_,nLocal_); + atomEltID.reset(nLocal_); + + // determine number of atoms in each element, partial sum + const FE_Mesh * feMesh = feEngine_->get_feMesh(); + int nElts = feMesh->get_nElements(); + + for (int i = 0; i < nLocal_; ++i) { + DENS_VEC coords(3); + coords(0) = xref_[internalToAtom_(i)][0]; + coords(1) = xref_[internalToAtom_(i)][1]; + coords(2) = xref_[internalToAtom_(i)][2]; + int thisElt = feMesh->map_to_element(coords); + eltAtomCtLocal(thisElt) += 1; + atomEltID(i) = thisElt; + } + } + + // mpi to determine total atoms + lammpsInterface_->int_allsum(eltAtomCtLocal.get_ptr(),eltAtomCt.get_ptr(),nElts); + + // divide element volume by total atoms to get atomic volume + if (nLocal_>0) { + for (int i = 0; i < nElts; ++i) { + + double minx, maxx, miny, maxy, minz, maxz; + DENS_MAT nodalCoords; + feMesh->element_coordinates(i,nodalCoords); + minx = nodalCoords(0,0); maxx = nodalCoords(0,0); + miny = nodalCoords(1,0); maxy = nodalCoords(1,0); + minz = nodalCoords(2,0); maxz = nodalCoords(2,0); + for (int j = 1; j < nodalCoords.nCols(); ++j) { + if (nodalCoords(0,j)maxx) maxx = nodalCoords(0,j); + if (nodalCoords(1,j)maxy) maxy = nodalCoords(1,j); + if (nodalCoords(2,j)maxz) maxz = nodalCoords(2,j); + } + double eltVol = (maxx-minx)*(maxy-miny)*(maxz-minz); + if (eltVol<0) eltVol *= -1.; + eltAtomVol(i) = eltVol/eltAtomCt(i); + } + + for (int i = 0; i < nLocal_; ++i) + atomicWeights_(i,i) = eltAtomVol(atomEltID(i)); + } + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + void ATC_Transfer::reset_atomicWeightsMultiscale(const SPAR_MAT & shapeFunctionMatrix, + DIAG_MAT & atomicVolumeMatrix) + { + // solve equation \sum_a N_Ia \sum_J N_Ja dV_J = \int_Omega N_I dV + // form left-hand side + SPAR_MAT lhs; + compute_consistent_md_mass_matrix(shapeFunctionMatrix,lhs); + // form right-hand side + DIAG_MAT rhsMatrix; + feEngine_->compute_lumped_mass_matrix(rhsMatrix); + DENS_VEC rhs; + rhs.copy(rhsMatrix.get_ptr(),rhsMatrix.size(),1); + + // change entries for all entries if no atoms in shape function support + double totalVolume = rhs.sum(); + double averageVolume = totalVolume/nInternal_; + DENS_VEC scale(nNodes_); + for (int i = 0; i < nNodes_; i++) { + if ((abs(lhs(i,i)) > 0.)) + scale(i) = 1.; + else + scale(i) = 0.; + } + lhs.row_scale(scale); + for (int i = 0; i < nNodes_; i++) { + if (scale(i) < 0.5) { + lhs.set(i,i,1.); + rhs(i) = averageVolume; + } + } + lhs.compress(); + + // solve equation + DIAG_MAT preConditioner = lhs.get_diag(); + int myMaxIt = lhs.nRows(); // NOTE could also use a fixed parameter + double myTol = 1.e-10; // NOTE could also use a fixed parameter + + DENS_VEC nodalAtomicVolume(nNodes_); + int convergence = CG(lhs, nodalAtomicVolume, rhs, preConditioner, myMaxIt, myTol); + + if (convergence>0) // error if didn't converge + throw ATC_Error(0,"CG solver did not converge in ATC_Transfer::reset_atomicWeightsMultiscale"); + + // compute each atom's volume + DENS_VEC atomicVolume(nLocal_); + prolong(nodalAtomicVolume,atomicVolume); + atomicVolumeMatrix.reset(atomicVolume); + } + + //----------------------------------------------------------------- + // + //----------------------------------------------------------------- + void ATC_Transfer::compute_consistent_md_mass_matrix(const SPAR_MAT & shapeFunctionMatrix, + SPAR_MAT & mdMassMatrix) + { + // NOTE could use sparse matrix and do communication broadcasting to exchange triplets + int nRows = shapeFunctionMatrix.nRows(); + int nCols = shapeFunctionMatrix.nCols(); + DENS_MAT massMatrixLocal(nCols,nCols); + DENS_MAT denseMdMassMatrix(nCols,nCols); + if (nLocal_>0) + massMatrixLocal = shapeFunctionMatrix.transMat(shapeFunctionMatrix); + + lammpsInterface_->allsum(massMatrixLocal.get_ptr(), + denseMdMassMatrix.get_ptr(), + denseMdMassMatrix.size()); + mdMassMatrix.reset(denseMdMassMatrix,1.e-10); + } + + //================================================================= + // Interscale operators + //================================================================= + //-------------------------------------------------------- + void ATC_Transfer::restrict(const MATRIX & atomData, + MATRIX & nodeData) + { + // computes nodeData = N*atomData where N are the normalized shape functions + DENS_MAT workNodeArray(nodeData.nRows(),nodeData.nCols()); + if (nLocal_>0) { + workNodeArray = shpWeight_*shpFcn_.transMat(atomData); + } + int count = nodeData.nRows()*nodeData.nCols(); + lammpsInterface_->allsum(workNodeArray.get_ptr(),nodeData.get_ptr(),count); + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::restrict_unscaled(const MATRIX & atomData, + MATRIX & nodeData) + { + // computes nodeData = N*DeltaVAtom*atomData where N are the shape functions + DENS_MAT workNodeArray(nodeData.nRows(),nodeData.nCols()); + + if (nLocal_>0) { + workNodeArray = shpFcn_.transMat(atomicWeights_*atomData); + } + int count = nodeData.nRows()*nodeData.nCols(); + lammpsInterface_->allsum(workNodeArray.get_ptr(),nodeData.get_ptr(),count); + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::restrict_volumetric_quantity(const MATRIX & atomData, + MATRIX & nodeData, + const SPAR_MAT & shpFcn) + { + // computes nodeData = N*DeltaVAtom*atomData where N are the shape functions + DENS_MAT workNodeArray(nodeData.nRows(),nodeData.nCols()); + + if (nLocal_>0) { + workNodeArray = shpFcn.transMat(atomData); + } + int count = nodeData.nRows()*nodeData.nCols(); + lammpsInterface_->allsum(workNodeArray.get_ptr(),nodeData.get_ptr(),count); + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::restrict_volumetric_quantity(const MATRIX & atomData, + MATRIX & nodeData) + { + restrict_volumetric_quantity(atomData,nodeData,shpFcn_); + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::project(const MATRIX & atomData, + MATRIX & nodeData, + FieldName thisField) + { + // computes M*nodeData = N*DeltaVAtom*atomData where N are the shape functions + restrict_unscaled(atomData,nodeData); + + apply_inverse_mass_matrix(nodeData,thisField); + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::project_volumetric_quantity(const MATRIX & atomData, + MATRIX & nodeData, + FieldName thisField) + { + project_volumetric_quantity(atomData,nodeData,shpFcn_,thisField); + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::project_volumetric_quantity(const MATRIX & atomData, + MATRIX & nodeData, + const SPAR_MAT & shpFcn, + FieldName thisField) + { + // computes nodeData = N*atomData where N are the shape functions + restrict_volumetric_quantity(atomData,nodeData,shpFcn); + + apply_inverse_mass_matrix(nodeData,thisField); + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::project_md(const MATRIX & atomData, + MATRIX & nodeData, + FieldName thisField) + { + // computes M*nodeData = N*DeltaVAtom*atomData where N are the shape functions + restrict_unscaled(atomData,nodeData); + + apply_inverse_md_mass_matrix(nodeData,thisField); + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::project_md_volumetric_quantity(const MATRIX & atomData, + MATRIX & nodeData, + FieldName thisField) + { + project_md_volumetric_quantity(atomData,nodeData,shpFcn_,thisField); + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::project_md_volumetric_quantity(const MATRIX & atomData, + MATRIX & nodeData, + const SPAR_MAT & shpFcn, + FieldName thisField) + { + // computes nodeData = N*atomData where N are the shape functions + restrict_volumetric_quantity(atomData,nodeData,shpFcn); + + apply_inverse_md_mass_matrix(nodeData,thisField); + + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::prolong(const MATRIX & nodeData, + MATRIX & atomData) + { + // computes the finite element interpolation at atoms atomData = N*nodeData + if (nLocal_>0) { + atomData = shpFcn_*nodeData; + } + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::prolong_scaled(const MATRIX & nodeData, + MATRIX & atomData) + { + // computes the finite element interpolation at atoms atomData = N*nodeData + if (nLocal_>0) { + atomData = shpFcn_*(shpWeight_*nodeData); + } + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::prolong_ghost(const MATRIX & nodeData, + MATRIX & atomData) + { + // computes the finite element interpolation at atoms atomData = N*nodeData + if (nLocalGhost_>0) { + atomData = shpFcnGhost_*nodeData; + } + return; + } + + //================================================================= + // Interscale physics constructors + //================================================================= + void ATC_Transfer::compute_atomic_mass(MATRIX & atomicMasses) + { + atomicMasses.reset(nLocal_, 1); + if (nLocal_>0) { + int * type = LammpsInterface::instance()->atom_type(); + double * mass = LammpsInterface::instance()->atom_mass(); + double * rmass = LammpsInterface::instance()->atom_rmass(); + int atomIndex; + double atomMass; + for (int i = 0; i < nLocal_; i++) { + atomIndex = internalToAtom_(i); + if (mass) { + atomMass = mass[type[atomIndex]]; + } + else { + atomMass = rmass[atomIndex]; + } + atomicMasses[i] = atomMass; + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_charge(MATRIX & atomicCharges) + { + atomicCharges.reset(nLocal_, 1); + if (nLocal_>0) { + double * charge = LammpsInterface::instance()->atom_charge(); + //double coef = LammpsInterface::instance()->elemental_charge(); + int atomIndex; + for (int i = 0; i < nLocal_; i++) { + atomIndex = internalToAtom_(i); + atomicCharges[i] = charge[atomIndex]; + //atomicCharges[i] = coef*charge[atomIndex]; + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_temperature(MATRIX & T, + const double * const* v, + double ** v2) + { + // v2 is for fractional step + if (v2==NULL) v2 = const_cast(v); + // T_alpha = 2/(3kB) * 1/2*m_alpha*(v_alpha \dot v2_alpha) + T.reset(nLocal_, 1); + if (nLocal_>0) { + double ma; + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double kBoltzmann = lammpsInterface_->kBoltzmann(); + int atomIdx; + double coef = 1./(nsd_*kBoltzmann); + for (int i = 0; i < nLocal_; i++) { + atomIdx = internalToAtom_(i); + if (mass) { + ma = mass[type[atomIdx]]; + } + else { + ma = rmass[atomIdx]; + } + for (int j = 0; j < nsd_; j++) { + T[i] += coef*ma*(v[atomIdx][j]*v2[atomIdx][j]); + } + } + } + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_kinetic_energy(MATRIX & kineticEnergy, + const double * const* v, + double ** v2) + { + // v2 is for fractional step + if (v2==NULL) v2 = const_cast(v); + // KE_alpha = 1/2*m_alpha*(v_alpha \dot v2_alpha) + if (nLocal_>0) { + kineticEnergy.reset(nLocal_, 1); + double ma; + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + int atomIdx; + for (int i = 0; i < nLocal_; i++) { + atomIdx = internalToAtom_(i); + if (mass) { + ma = mass[type[atomIdx]]; + } + else { + ma = rmass[atomIdx]; + } + for (int j = 0; j < nsd_; j++) { + kineticEnergy[i] += ma*(v[atomIdx][j]*v2[atomIdx][j]); + } + } + kineticEnergy *= 0.5; + } + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_temperature_roc(MATRIX & atomicPower, + const double * const* v, + const double * const* f) + { + // atom_power = 2/(3kB) * v_alpha * f + atomicPower.reset(nLocal_,1); + if (nLocal_>0) { + double coef = 2./(nsd_*lammpsInterface_->boltz()); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) { + atomicPower[i] += coef*(v[atomIdx][j]*f[atomIdx][j]); + } + } + } + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_power(MATRIX & atomicPower, + const double * const* v, + const double * const* f) + { + // atom_power = v_alpha * f + atomicPower.reset(nLocal_,1); + if (nLocal_>0) { + double coef = lammpsInterface_->ftm2v(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) { + atomicPower[i] += coef*(v[atomIdx][j]*f[atomIdx][j]); + } + } + } + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_temperature_roc(MATRIX & atomicPower, + const double * const* v, + const MATRIX & f) + { + // atom_power = 2/(3kB) * v_alpha * f + atomicPower.reset(nLocal_,1); + if (nLocal_>0) { + double coef = 2./(nsd_*(lammpsInterface_->kBoltzmann())); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) { + atomicPower[i] += coef*(v[atomIdx][j]*f(i,j)); + } + } + } + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_power(MATRIX & atomicPower, + const double * const* v, + const MATRIX & f) + { + // atom_power = v_alpha * f + atomicPower.reset(nLocal_,1); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) { + atomicPower[i] += v[atomIdx][j]*f(i,j); + } + } + } + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_force_strength(MATRIX & atomicForceStrength, + const double * const* f) + { + // atom_force_strength = 2/(3kB) * f * f / ma + atomicForceStrength.reset(nLocal_,1); + if (nLocal_>0) { + double mvv2e = lammpsInterface_->mvv2e(); + double coef = 2./(nsd_*(lammpsInterface_->kBoltzmann())) / mvv2e / mvv2e; + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ma; + + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (mass) { + ma = mass[type[atomIdx]]; + } + else { + ma = rmass[atomIdx]; + } + for (int j = 0; j < nsd_; j++) { + atomicForceStrength[i] += coef*(f[atomIdx][j]*f[atomIdx][j])/ma; + } + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_force_strength(MATRIX & atomicForceStrength, + const MATRIX & f) + { + // atom_force_strength = 1/(3kB) * f * f / ma + atomicForceStrength.reset(nLocal_,1); + if (nLocal_>0) { + double coef = 1./(nsd_*(lammpsInterface_->kBoltzmann())); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ma; + + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (mass) { + ma = mass[type[atomIdx]]; + } + else { + ma = rmass[atomIdx]; + } + for (int j = 0; j < nsd_; j++) { + atomicForceStrength[i] += coef*(f(i,j)*f(i,j))/ma; + } + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_force_dot(MATRIX & atomicForceDot, + const double * const* f1, + const MATRIX & f2) + { + // atom_force_strength = 2/(3kB) * f * f / ma + atomicForceDot.reset(nLocal_,1); + if (nLocal_>0) { + double mvv2e = lammpsInterface_->mvv2e(); + double coef = 1./(nsd_*(lammpsInterface_->kBoltzmann())) / mvv2e; + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + double ma; + + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + if (mass) { + ma = mass[type[atomIdx]]; + } + else { + ma = rmass[atomIdx]; + } + for (int j = 0; j < nsd_; j++) { + atomicForceDot[i] += coef*(f1[atomIdx][j]*f2(i,j))/ma; + } + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_lambda_power(MATRIX & lambdaPower, + const MATRIX & lambda, + const double * const* v) + { + // atom_lambda_power = -(1/2) m_a * v_a^2 * lambda + lambdaPower.reset(nLocal_,1); + if (nLocal_>0) { + DENS_VEC lambdaAtom(nLocal_); + prolong_scaled(lambda,lambdaAtom); + + double ma; + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + int atomIdx; + double coef = 0.5; + for (int i = 0; i < nLocal_; i++) { + atomIdx = internalToAtom_(i); + if (mass) { + ma = mass[type[atomIdx]]; + } + else { + ma = rmass[atomIdx]; + } + for (int j = 0; j < nsd_; j++) { + lambdaPower[i] -= coef*ma*v[atomIdx][j]*v[atomIdx][j]*lambdaAtom(i); + } + } + } + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_temperature_roc(MATRIX & atomicPower, + const MATRIX & force, + const double * const* v) + { + // atom_lambda_power = v_{\alpha} \dot force + // NOTE account for missing mass + atomicPower.reset(nLocal_,1); + if (nLocal_>0) { + double ma; + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + int atomIdx; + double coef = 2./(nsd_*(lammpsInterface_->kBoltzmann())); + for (int i = 0; i < nLocal_; i++) { + atomIdx = internalToAtom_(i); + if (mass) { + ma = mass[type[atomIdx]]; + } + else { + ma = rmass[atomIdx]; + } + for (int j = 0; j < nsd_; j++) { + atomicPower[i] += coef*ma*v[atomIdx][j]*force(i,j); + } + } + } + return; + } + + //-------------------------------------------------------- + // atomic kinematic quantities + void ATC_Transfer::compute_atomic_displacement(DENS_MAT & atomicDisplacement, + const double * const* x) + { + atomicDisplacement.reset(nLocal_, nsd_); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; ++j) { + atomicDisplacement(i,j) = x[atomIdx][j] - xref_[atomIdx][j]; + } + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_centerOfMass_displacement(DENS_MAT & atomicComD, + const double * const* x) + { + atomicComD.reset(nLocal_, nsd_); + if (nLocal_>0) { + double *mass = lammpsInterface_->atom_mass(); + if (mass) { + int *type = lammpsInterface_->atom_type(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) + atomicComD(i,j) = mass[type[atomIdx]]*(x[atomIdx][j]- xref_[atomIdx][j]); + } + } + else { + double *rmass = lammpsInterface_->atom_rmass(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) + atomicComD(i,j) = rmass[atomIdx]*(x[atomIdx][j]- xref_[atomIdx][j]); + } + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_position(DENS_MAT & atomicDisplacement, + const double * const* x) + { + atomicDisplacement.reset(nLocal_, nsd_); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; ++j) + atomicDisplacement(i,j) = x[atomIdx][j]; + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_centerOfMass(DENS_MAT & atomicCom, + const double * const* x) + { + atomicCom.reset(nLocal_, nsd_); + if (nLocal_>0) { + double *mass = lammpsInterface_->atom_mass(); + if (mass) { + int *type = lammpsInterface_->atom_type(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) + atomicCom(i,j) = mass[type[atomIdx]]*x[atomIdx][j]; + } + } + else { + double *rmass = lammpsInterface_->atom_rmass(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) + atomicCom(i,j) = rmass[atomIdx]*x[atomIdx][j]; + } + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_velocity(DENS_MAT & atomicVelocity, + const double * const* v) + { + atomicVelocity.reset(nLocal_, nsd_); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; ++j) + atomicVelocity(i,j) = v[atomIdx][j]; + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_momentum(DENS_MAT & atomicMomentum, + const double * const* v) + { + atomicMomentum.reset(nLocal_, nsd_); + if (nLocal_>0) { + double *mass = lammpsInterface_->atom_mass(); + if (mass) { + int *type = lammpsInterface_->atom_type(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) + atomicMomentum(i,j) = mass[type[atomIdx]]*v[atomIdx][j]; + } + } + else { + double *rmass = lammpsInterface_->atom_rmass(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) + atomicMomentum(i,j) = rmass[atomIdx]*v[atomIdx][j]; + } + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_acceleration(DENS_MAT & atomicAcceleration, + const double * const* f) + { + atomicAcceleration.reset(nLocal_, nsd_); + if (nLocal_>0) { + double coef = lammpsInterface_->ftm2v(); + double *mass = lammpsInterface_->atom_mass(); + if (mass) { + int *type = lammpsInterface_->atom_type(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) + atomicAcceleration(i,j) = coef*f[atomIdx][j]/mass[type[atomIdx]]; + } + } + else { + double *rmass = lammpsInterface_->atom_rmass(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) + atomicAcceleration(i,j) = coef*f[atomIdx][j]/rmass[atomIdx]; + } + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::compute_atomic_force(DENS_MAT & atomicForce, + const double * const* f) + { + atomicForce.reset(nLocal_, nsd_); + if (nLocal_>0) { + double coef = lammpsInterface_->ftm2v(); + for (int i = 0; i < nLocal_; i++) { + int atomIdx = internalToAtom_(i); + for (int j = 0; j < nsd_; j++) + atomicForce(i,j) = coef*f[atomIdx][j]; + } + } + } + + //================================================================= + // FE mapping operations + //================================================================= + // Mappings between overlap nodes and unique nodes + void ATC_Transfer::map_unique_to_overlap(const MATRIX & uniqueData, + MATRIX & overlapData) + { + for (int i = 0; i < nNodes_; i++) { + if (nodeToOverlapMap_(i)!=-1) { + for (int j = 0; j < uniqueData.nCols(); j++) { + overlapData(nodeToOverlapMap_(i),j) = uniqueData(i,j); + } + } + } + } + + void ATC_Transfer::map_overlap_to_unique(const MATRIX & overlapData, + MATRIX & uniqueData) + { + uniqueData.reset(nNodes_,overlapData.nCols()); + for (int i = 0; i < nNodeOverlap_; i++) { + for (int j = 0; j < overlapData.nCols(); j++) { + uniqueData(overlapToNodeMap_(i),j) = overlapData(i,j); + } + } + } + + //-------------------------------------------------------- + // + //-------------------------------------------------------- + void ATC_Transfer::construct_prescribed_data_manager (void) { + prescribedDataMgr_ = new PrescribedDataManager(feEngine_,fieldSizes_); + } + + //======================================================== + // FE functions + //======================================================== + + //-------------------------------------------------------- + // handle nodesets + //-------------------------------------------------------- + void ATC_Transfer::set_fixed_nodes() + { + // set fields + prescribedDataMgr_->set_fixed_fields(simTime_, + fields_,dot_fields_,ddot_fields_,dddot_fields_); + + + // set related data + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + if (prescribedDataMgr_->is_fixed(inode,thisField,thisIndex)) { + dot_fieldsMD_ [thisField](inode,thisIndex) = 0.; + ddot_fieldsMD_ [thisField](inode,thisIndex) = 0.; + dot_dot_fieldsMD_[thisField](inode,thisIndex) = 0.; + rhs_[thisField](inode,thisIndex) = 0.; + } + } + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::set_initial_conditions() + { + // set fields + prescribedDataMgr_->set_initial_conditions(simTime_, + fields_,dot_fields_,ddot_fields_,dddot_fields_); + + // set (all) related data + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + dot_fieldsMD_ [thisField](inode,thisIndex) = 0.; + ddot_fieldsMD_ [thisField](inode,thisIndex) = 0.; + dot_dot_fieldsMD_[thisField](inode,thisIndex) = 0.; + rhs_[thisField](inode,thisIndex) = 0.; + } + } + } + } + + //-------------------------------------------------------- + void ATC_Transfer::set_sources() + { + // set fields + prescribedDataMgr_->set_sources(simTime_,sources_); + + } + + //-------------------------------------------------------- + void ATC_Transfer::output() + { + if (lammpsInterface_->comm_rank() == 0) { + map< pair , double >::iterator iter; + for (iter = nsetCompute_.begin(); iter != nsetCompute_.end();iter++){ + pair id = iter->first; + string nsetName = id.first; + FieldName field = id.second; + double sum = 0.0; + const set nset = feEngine_->get_feMesh()->get_nodeset(nsetName); + set< int >::const_iterator itr; + for (itr = nset.begin(); itr != nset.end();itr++){ + int node = *itr; + sum += fields_[field](node,0); + } + iter->second = sum; + string name = nsetName + "_" + field_to_string(field); + feEngine_->add_global(name, sum); + } + } + } + + + //-------------------------------------------------------- + // use part, return to OutputManager maps of data & geometry + // base class atomic output or just data creation + void ATC_Transfer::atomic_output() + { + int me = lammpsInterface_->comm_rank(); + int nTotal = (int) lammpsInterface_->natoms(); + double ** x = lammpsInterface_->xatom(); + double ** v = lammpsInterface_->vatom(); + double ** f = lammpsInterface_->fatom(); + int * type = lammpsInterface_->atom_type(); + double * mass = lammpsInterface_->atom_mass(); + double * rmass = lammpsInterface_->atom_rmass(); + // map for output data + OUTPUT_LIST atom_data; + + // geometry + // NOTE this does not properly handle the output of subset of the entire system + DENS_MAT atomicCoordinatesGlobal(nsd_,nTotal); + DENS_MAT atomicCoordinatesLocal(nsd_,nTotal); + DENS_MAT atomicTypeLocal(nTotal,1); + DENS_MAT atomicType(nTotal,1); + int * tag = lammpsInterface_->atom_tag(); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + //cout << "procID : " << me << ", iLocal_ : " << i << flush; + int atom_index = internalToAtom_(i); + //cout << ", internalToAtom_ : " << atom_index << flush; + int global_index = tag[atom_index]-1; + //cout << ", global_index : " << global_index << flush; + for (int j = 0; j < nsd_; j++) { + atomicCoordinatesLocal(j,global_index) = x[atom_index][j]; + //cout << ", x : " << x[atom_index][j] << flush; + } + atomicTypeLocal(global_index,0) = type[atom_index]; + //cout << "\n"; + } + } + if (nLocalGhost_>0) { + for (int i = 0; i < nLocalGhost_; ++i) { + int atom_index = ghostToAtom_(i); + int global_index = tag[atom_index]-1; + atomicTypeLocal(global_index,0) = 1.0; + for (int j = 0; j < nsd_; j++) { + atomicCoordinatesLocal(j,global_index) = x[atom_index][j]; + } + } + } + lammpsInterface_->allsum(atomicCoordinatesLocal.get_ptr(), + atomicCoordinatesGlobal.get_ptr(), nTotal*nsd_); + if (me==0) mdOutputManager_.write_geometry(atomicCoordinatesGlobal); + + lammpsInterface_->allsum(atomicTypeLocal.get_ptr(), + atomicType.get_ptr(), nTotal); + + DENS_MAT atomicPositionGlobal(nTotal,nsd_); + if (atomicOutputMask_.count("position") > 0) { + DENS_MAT atomicPositionLocal(nTotal,nsd_); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + int atom_index = internalToAtom_(i); + int global_index = tag[atom_index]-1; + for (int j = 0; j < nsd_; j++) { + atomicPositionLocal(global_index,j) = x[atom_index][j]; + } + } + } + if (nLocalGhost_>0) { + for (int i = 0; i < nLocalGhost_; ++i) { + int atom_index = ghostToAtom_(i); + int global_index = tag[atom_index]-1; + for (int j = 0; j < nsd_; j++) { + atomicPositionLocal(global_index,j) = x[atom_index][j]; + } + } + } + lammpsInterface_->allsum(atomicPositionLocal.get_ptr(), + atomicPositionGlobal.get_ptr(), nTotal*nsd_); + if (me==0) atom_data["atomicPosition"] = & atomicPositionGlobal; + } + + // point data, output: displacement, velocity, temperature + if (me==0) atom_data["atomicType"] = & atomicType; + + // displacement + // NOTE make this a stand alone function +#define CHEAP_UNWRAP +#ifdef CHEAP_UNWRAP + double box_bounds[3][3]; + lammpsInterface_->get_box_bounds(box_bounds[0][0],box_bounds[1][0], + box_bounds[0][1],box_bounds[1][1], + box_bounds[0][2],box_bounds[1][2]); + double box_length[3]; + for (int k = 0; k < 3; k++) { + box_length[k] = box_bounds[1][k] - box_bounds[0][k]; + } +#endif + bool latticePeriodicity[3]; + latticePeriodicity[0] = (bool) lammpsInterface_->xperiodic(); + latticePeriodicity[1] = (bool) lammpsInterface_->yperiodic(); + latticePeriodicity[2] = (bool) lammpsInterface_->zperiodic(); + + DENS_MAT atomicDisplacementGlobal(nTotal,nsd_); + + if (atomicOutputMask_.count("displacement") > 0) { + DENS_MAT atomicDisplacementLocal(nTotal,nsd_); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + int atom_index = internalToAtom_(i); + int global_index = tag[atom_index]-1; + for (int j = 0; j < nsd_; j++) { + atomicDisplacementLocal(global_index,j) + = x[atom_index][j] -xref_[atom_index][j]; + if (latticePeriodicity[j]) { +#ifdef CHEAP_UNWRAP + // the cheap version of unwrapping atomic positions + double u = atomicDisplacementLocal(global_index,j); + if (u >= 0.5*box_length[j]) { u -= box_length[j]; } + if (u <= -0.5*box_length[j]) { u += box_length[j]; } + atomicDisplacementLocal(global_index,j) = u; +#else + double x_a[3]; + lammpsInterface_->unwrap_coordinates(atom_index,x_a); + atomicDisplacementLocal(global_index,j) + = x_a[j] -xref_[atom_index][j]; +#endif + } + } + } + } + if (nLocalGhost_>0) { + for (int i = 0; i < nLocalGhost_; ++i) { + int atom_index = ghostToAtom_(i); + int global_index = tag[atom_index]-1; + for (int j = 0; j < nsd_; j++) { + atomicDisplacementLocal(global_index,j) + = x[atom_index][j] -xref_[atom_index][j]; + if (latticePeriodicity[j]) { +#ifdef CHEAP_UNWRAP + // the cheap version of unwrapping atomic positions + double u = atomicDisplacementLocal(global_index,j); + if (u >= 0.5*box_length[j]) { u -= box_length[j]; } + if (u <= -0.5*box_length[j]) { u += box_length[j]; } + atomicDisplacementLocal(global_index,j) = u; +#else + double x_a[3]; + lammpsInterface_->unwrap_coordinates(atom_index,x_a); + atomicDisplacementLocal(global_index,j) + = x_a[j] -xref_[atom_index][j]; +#endif + } + } + } + } + lammpsInterface_->allsum(atomicDisplacementLocal.get_ptr(), + atomicDisplacementGlobal.get_ptr(), nTotal*nsd_); + if (me==0) atom_data["atomicDisplacement"] = & atomicDisplacementGlobal; + } + + // velocity + DENS_MAT atomicVelocityGlobal(nTotal,nsd_); + if (atomicOutputMask_.count("velocity") > 0) { + DENS_MAT atomicVelocityLocal(nTotal,nsd_); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + int atom_index = internalToAtom_(i); + int global_index = tag[atom_index]-1; + for (int j = 0; j < nsd_; j++) { + atomicVelocityLocal(global_index,j) = v[atom_index][j]; + } + } + } + if (nLocalGhost_>0) { + for (int i = 0; i < nLocalGhost_; ++i) { + int atom_index = ghostToAtom_(i); + int global_index = tag[atom_index]-1; + for (int j = 0; j < nsd_; j++) { + atomicVelocityLocal(global_index,j) = v[atom_index][j]; + } + } + } + lammpsInterface_->allsum(atomicVelocityLocal.get_ptr(), + atomicVelocityGlobal.get_ptr(), nTotal*nsd_); + if (me==0) atom_data["atomicVelocity"] = & atomicVelocityGlobal; + } + + // temperature + DENS_MAT atomicTemperatureGlobal(nTotal,1); + if (atomicOutputMask_.count("temperature") > 0) { + DENS_MAT atomicTemperatureLocal(nTotal,1); + atomicTemperatureLocal = 0.0; + double ma; + double coef = 1./(nsd_*(lammpsInterface_->kBoltzmann())); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + int atom_index = internalToAtom_(i); + int global_index = tag[atom_index]-1; + if (mass) ma = mass[type[atom_index]]; + else ma = rmass[atom_index]; + for (int j = 0; j < nsd_; j++) { + double vj = v[atom_index][j]; + atomicTemperatureLocal(global_index,0) += coef*ma*vj*vj; + } + } + } + if (nLocalGhost_>0) { + for (int i = 0; i < nLocalGhost_; ++i) { + int atom_index = ghostToAtom_(i); + int global_index = tag[atom_index]-1; + if (mass) ma = mass[type[atom_index]]; + else ma = rmass[atom_index]; + for (int j = 0; j < nsd_; j++) { + double vj = v[atom_index][j]; + atomicTemperatureLocal(global_index,0) += coef*ma*vj*vj; + } + } + } + lammpsInterface_->allsum(atomicTemperatureLocal.get_ptr(), + atomicTemperatureGlobal.get_ptr(), nTotal); + if (me==0) atom_data["atomicTemperature"] = & atomicTemperatureGlobal; + } + + // force + DENS_MAT atomicForceGlobal(nTotal,nsd_); + if (atomicOutputMask_.count("force") > 0) { + DENS_MAT atomicForceLocal(nTotal,nsd_); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + int atom_index = internalToAtom_(i); + int global_index = tag[atom_index]-1; + for (int j = 0; j < nsd_; j++) { + atomicForceLocal(global_index,j) = f[atom_index][j]; + } + } + } + if (nLocalGhost_>0) { + for (int i = 0; i < nLocalGhost_; ++i) { + int atom_index = ghostToAtom_(i); + int global_index = tag[atom_index]-1; + for (int j = 0; j < nsd_; j++) { + atomicForceLocal(global_index,j) = f[atom_index][j]; + } + } + } + lammpsInterface_->allsum(atomicForceLocal.get_ptr(), + atomicForceGlobal.get_ptr(), nTotal*nsd_); + if (me==0) atom_data["atomicForce"] = & atomicVelocityGlobal; + } + + + // atomic stress + double ** atomStress = NULL; + try{ + atomStress = lammpsInterface_->compute_vector_data("stress/atom"); + } + catch(ATC::ATC_Error& atcError) { + atomStress = NULL; + } + // if compute stress/atom isn't create, this code is skipped + DENS_MAT atomicVirialGlobal(nTotal,6); + if (atomicOutputMask_.count("virial") > 0 && atomStress) { + DENS_MAT atomicVirialLocal(nTotal,6); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + int atom_index = internalToAtom_(i); + int global_index = tag[atom_index]-1; + for (int k = 0; k < 6; k++) { + atomicVirialLocal(global_index,k) = atomStress[atom_index][k]; + } + } + } + if (nLocalGhost_>0) { + for (int i = 0; i < nLocalGhost_; ++i) { + int atom_index = ghostToAtom_(i); + int global_index = tag[atom_index]-1; + for (int k = 0; k < 6; k++) { + atomicVirialLocal(global_index,k) = atomStress[atom_index][k]; + } + } + } + lammpsInterface_->allsum(atomicVirialLocal.get_ptr(), + atomicVirialGlobal.get_ptr(), nTotal*6); + //atomicVirialGlobal *= nktv2p; + if (me==0) atom_data["atomicVirial"] = & atomicVirialGlobal; + } + + // potential energy + // if compute pe/atom isn't created, this code is skipped + double * atomPE = NULL; + try{ + atomPE = lammpsInterface_->compute_scalar_data("pe/atom"); + } + catch(ATC::ATC_Error& atcError) { + atomPE = NULL; + } + DENS_MAT atomicEnergyGlobal(nTotal,1); + if (atomicOutputMask_.count("potential_energy") > 0 && atomPE) { + DENS_MAT atomicEnergyLocal(nTotal,1); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + int atom_index = internalToAtom_(i); + int global_index = tag[atom_index]-1; + atomicEnergyLocal(global_index,0) = atomPE[atom_index]; + } + } + if (nLocalGhost_>0) { + for (int i = 0; i < nLocalGhost_; ++i) { + int atom_index = ghostToAtom_(i); + int global_index = tag[atom_index]-1; + atomicEnergyLocal(global_index,0) = atomPE[atom_index]; + } + } + lammpsInterface_->allsum(atomicEnergyLocal.get_ptr(), + atomicEnergyGlobal.get_ptr(), nTotal); + if (me==0) atom_data["atomicPotentialEnergy"] = & atomicEnergyGlobal; + } + + // centrosymmetry + // NOTE if hardy doesn't create compute centro/atom this is skipped + // we should move this to base class with a request from derived + //double * atomCentro = lammpsInterface_->atomCentro_compute(); + double * atomCentro = NULL; + try{ + atomCentro = lammpsInterface_->compute_scalar_data("centro/atom"); + } + catch(ATC::ATC_Error& atcError) { + atomCentro = NULL; + } + // if compute centro/atom isn't create, this code is skipped + DENS_MAT atomicCentroGlobal(nTotal,1); + if (atomicOutputMask_.count("centrosymmetry") > 0 && atomCentro) { + DENS_MAT atomicCentroLocal(nTotal,1); + if (nLocal_>0) { + for (int i = 0; i < nLocal_; ++i) { + int atom_index = internalToAtom_(i); + int global_index = tag[atom_index]-1; + for (int j = 0; j < nsd_; j++) { + atomicCentroLocal(global_index,0) = atomCentro[atom_index]; + } + } + } + if (nLocalGhost_>0) { + for (int i = 0; i < nLocalGhost_; ++i) { + int atom_index = ghostToAtom_(i); + int global_index = tag[atom_index]-1; + atomicCentroLocal(global_index,0) = atomCentro[atom_index]; + } + } + lammpsInterface_->allsum(atomicCentroLocal.get_ptr(), + atomicCentroGlobal.get_ptr(), nTotal); + if (me==0) atom_data["atomicCentrosymmetry"] = & atomicCentroGlobal; + } + + if (me==0) mdOutputManager_.write_data(simTime_, & atom_data); + + } + + //================================================================= + // FE interfaces + //================================================================= + void ATC_Transfer::compute_boundary_flux(const Array2D & rhsMask, + const FIELDS & fields, + FIELDS & rhs) + { + if (bndyIntType_ == FE_QUADRATURE) { + //cout << "FE QUAD " << lammpsInterface_->comm_rank() << endl; + feEngine_->compute_boundary_flux(rhsMask, + fields, + physicsModel_, + elementToMaterialMap_, + (* bndyFaceSet_), + rhs); + } + else if (bndyIntType_ == FE_INTERPOLATION) { + //cout << "FE INTERP " << lammpsInterface_->comm_rank() << endl; + feEngine_->compute_boundary_flux(rhsMask, + fields, + physicsModel_, + elementToMaterialMap_, + atomMaterialGroups_, + atomicWeights_, + shpFcn_, + shpFcnDerivs_, + fluxMask_, + rhs); + } + else if (bndyIntType_ == NO_QUADRATURE) { + //cout << "RESET BOUNDARY FLUX " << lammpsInterface_->comm_rank() << endl; + FIELDS::const_iterator field; + for (field = fields.begin(); field != fields.end(); field++) { + FieldName thisFieldName = field->first; + if (rhsMask(thisFieldName,FLUX)) { + int nrows = (field->second).nRows(); + int ncols = (field->second).nCols(); + rhs[thisFieldName].reset(nrows,ncols); + } + } + } + } + + //----------------------------------------------------------------- + void ATC_Transfer::compute_flux(const Array2D & rhsMask, + const FIELDS & fields, + GRAD_FIELDS & flux, + const PhysicsModel * physicsModel) + { + if (! physicsModel) { physicsModel = physicsModel_; } + feEngine_->compute_flux(rhsMask, + fields, + physicsModel, + elementToMaterialMap_, + flux); + } + + //----------------------------------------------------------------- + void ATC_Transfer::evaluate_rhs_integral(const Array2D & rhsMask, + const FIELDS & fields, FIELDS & rhs, + const IntegrationDomainType domain, + const PhysicsModel * physicsModel) + { + if (!physicsModel) physicsModel = physicsModel_; + + // compute B and N flux NOTE linear or not + if (domain == FE_DOMAIN ) { + feEngine_->compute_rhs_vector(rhsMask, + fields, + physicsModel, + elementToMaterialMap_, + rhs, + &elementMask_); + if (nLocalMask_ > 0) { + feEngine_->compute_rhs_vector(rhsMask, + fields, + physicsModel, + atomMaterialGroups_, + atomicWeightsMask_, + shpFcnMasked_, + shpFcnDerivsMask_, + rhsAtomDomain_); + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) { + FieldName thisFieldName = field->first; + rhs[thisFieldName] -= rhsAtomDomain_[thisFieldName]; + } + } + } + else if (domain == ATOM_DOMAIN) { + // NOTE should we let this actually do a full atom quadrature integration? + if (nLocalMask_ > 0) { + feEngine_->compute_rhs_vector(rhsMask, + fields, + physicsModel, + atomMaterialGroups_, + atomicWeightsMask_, + shpFcnMasked_, + shpFcnDerivsMask_, + rhs); + } + } + else { // domain == FULL_DOMAIN + feEngine_->compute_rhs_vector(rhsMask, + fields, + physicsModel, + elementToMaterialMap_, + rhs); + } + } + + //----------------------------------------------------------------- + void ATC_Transfer::compute_rhs_vector(const Array2D & rhsMask, + const FIELDS & fields, FIELDS & rhs, + const IntegrationDomainType domain, + const PhysicsModel * physicsModel) + { + if (!physicsModel) physicsModel = physicsModel_; + // NOTE what about FE_DOMAIN & Extrinsic??? + int index; + // compute FE contributions + evaluate_rhs_integral(rhsMask,fields,rhs,domain,physicsModel); + + // NOTE change massMask to rhsMask + Array massMask; + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) + { + FieldName thisFieldName = field->first; + if (rhsMask(thisFieldName,PRESCRIBED_SOURCE)) { + if (is_intrinsic(thisFieldName)) { + rhs[thisFieldName] += fluxMaskComplement_*sources_[thisFieldName]; + } // NOTE perhaps replace fluxMask in future + else { + rhs[thisFieldName] += sources_[thisFieldName]; + } + } + + // add in sources from extrinsic models + if (rhsMask(thisFieldName,EXTRINSIC_SOURCE)) { + if (is_intrinsic(thisFieldName)) { + rhs[thisFieldName] += fluxMaskComplement_*extrinsicSources_[thisFieldName]; + } + else { + rhs[thisFieldName] += extrinsicSources_[thisFieldName]; + } + } + + + } + + } + + //----------------------------------------------------------------- + void ATC_Transfer::compute_atomic_sources(const Array2D & fieldMask, + const FIELDS & fields, + FIELDS & atomicSources) + { + + // NOTE change massMask to rhsMask + Array massMask; + for (FIELDS::const_iterator field = fields.begin(); + field != fields.end(); field++) { + FieldName thisFieldName = field->first; + if (is_intrinsic(thisFieldName)) { + atomicSources[thisFieldName] = 0.; + if (fieldMask(thisFieldName,FLUX)) { + atomicSources[thisFieldName] = boundaryFlux_[thisFieldName]; + } + if (fieldMask(thisFieldName,PRESCRIBED_SOURCE)) { + atomicSources[thisFieldName] -= fluxMask_*sources_[thisFieldName]; + } // NOTE perhaps replace fluxMask in future + + + // add in sources from extrinsic models + if (fieldMask(thisFieldName,EXTRINSIC_SOURCE)) + atomicSources[thisFieldName] -= fluxMask_*extrinsicSources_[thisFieldName]; + + } + } + } + + + + void ATC_Transfer::update_filter(MATRIX & filteredQuantity, + const MATRIX & unfilteredQuantity, + MATRIX & unfilteredQuantityOld, + const double dt) + { + double tau = timeFilterManager_.get_filter_scale(); + filteredQuantity = 1./(1./dt+1./(2*tau))*( 1./(2*tau)* + (unfilteredQuantity+unfilteredQuantityOld) + + (1./dt-1./(2*tau))*filteredQuantity); + unfilteredQuantityOld = unfilteredQuantity; + return; + } + + //-------------------------------------------------------- + void ATC_Transfer::update_filter_implicit(MATRIX & filteredQuantity, + const MATRIX & unfilteredQuantity, + const double dt) + { + double tau = timeFilterManager_.get_filter_scale(); + filteredQuantity = (1./(1.+dt/tau))*((dt/tau)*unfilteredQuantity + filteredQuantity); + return; + } +}; diff --git a/lib/atc/ATC_TypeDefs.h b/lib/atc/ATC_TypeDefs.h new file mode 100644 index 0000000000..f158a0b3b1 --- /dev/null +++ b/lib/atc/ATC_TypeDefs.h @@ -0,0 +1,207 @@ +#ifndef ATC_TYPEDEFS_H +#define ATC_TYPEDEFS_H + +#include +using std::pair; +using std::set; + +#include "Array.h" +#include "Array2D.h" +#include "XT_Function.h" +#include "MatrixLibrary.h" +#include "ATC_Error.h" + +namespace ATC +{ + /** Material types */ + enum FieldName { // Intrinsic Fields + TEMPERATURE=0, + DISPLACEMENT, + VELOCITY, + MASS_DENSITY, + CHARGE_DENSITY, + ELECTRON_DENSITY, // Extrinsic Fields + ELECTRON_VELOCITY, + ELECTRON_TEMPERATURE, + ELECTRIC_POTENTIAL, + NUM_FIELDS + }; + + /** solver types */ + enum SolverType { DIRECT=0, ITERATIVE}; + + /** physics types */ + enum PhysicsType + { + NO_PHYSICS=0, // for post-processing only + THERMAL, + ELASTIC, + THERMO_ELASTIC, + SPECIES + }; + + /** rhs types */ + enum FluxType + { + FLUX = 0, // has a source weighted by gradient of shape function + SOURCE, // has a source term weighted by the shape function + PRESCRIBED_SOURCE, // has a prescribed source term + EXTRINSIC_SOURCE, // has an extrinsic source term + NUM_FLUX + }; + + /** stiffness/ derivative of rhs types */ + enum StiffnessType + { + BB_STIFFNESS = 0, + NN_STIFFNESS, + BN_STIFFNESS, + NB_STIFFNESS, + NUM_STIFFNESS + }; + + /** typedefs for N and B integrand functions */ + typedef DenseMatrix FIELD; + typedef vector > GRAD_FIELD; + typedef map > FIELDS; + typedef map > > GRAD_FIELDS; + + /** typedefs for input/output */ + typedef map*> OUTPUT_LIST; + + /** misc typedefs */ + typedef pair PAIR; + typedef map > > SURFACE_SOURCE; + typedef map > VOLUME_SOURCE; + typedef vector > GRAD_SHPFCN; + + /** typedefs for FE_Mesh */ + typedef map > NODE_SET_MAP; + typedef map > ELEMENT_SET_MAP; + typedef map > FACE_SET_MAP; + + /** string to index */ + static bool string_to_index(const string & dim, int & index, int & sgn) + { + char dir; + if (dim.empty()) return false; + sgn = (dim[0] == '-') ? -1 : 1; + dir = dim[dim.size()-1]; // dir is last character + if (dir == 'x') index = 0; + else if (dir == 'y') index = 1; + else if (dir == 'z') index = 2; + else return false; + return true; + }; + + /** string to index */ + static string index_to_string(const int &index) + { + if (index==0) return "x"; + else if (index==1) return "y"; + else if (index==2) return "z"; + return "unknown"; + }; + + /** string to index */ + static bool string_to_index(const string &dim, int &index) + { + if (dim=="x") + index = 0; + else if (dim=="y") + index = 1; + else if (dim=="z") + index = 2; + else + return false; + + return true; + }; + + + /** field name enum to string */ + static string field_to_string(const FieldName index) + { + switch (index) { + case TEMPERATURE: + return "temperature"; + case DISPLACEMENT: + return "displacement"; + case VELOCITY: + return "velocity"; + case MASS_DENSITY: + return "mass_density"; + case CHARGE_DENSITY: + return "charge_density"; + case ELECTRON_DENSITY: + return "electron_density"; + case ELECTRON_VELOCITY: + return "electron_velocity"; + case ELECTRON_TEMPERATURE: + return "electron_temperature"; + case ELECTRIC_POTENTIAL: + return "electric_potential"; + default: + throw ATC_Error(0,"field not found in field_to_string"); + } + }; + + /** string to field enum */ + static FieldName string_to_field(const string & name) + { + if (name=="temperature") + return TEMPERATURE; + else if (name=="displacement") + return DISPLACEMENT; + else if (name=="velocity") + return VELOCITY; + else if (name=="mass_density") + return MASS_DENSITY; + else if (name=="charge_density") + return CHARGE_DENSITY; + else if (name=="electron_density") + return ELECTRON_DENSITY; + else if (name=="electron_velocity") + return ELECTRON_VELOCITY; + else if (name=="electron_temperature") + return ELECTRON_TEMPERATURE; + else if (name=="electric_potential") + return ELECTRIC_POTENTIAL; + else + throw ATC_Error(0,name + " is not a valid field"); + }; + + static bool is_intrinsic(const FieldName & field_enum) + { + if (field_enum==TEMPERATURE + || field_enum==DISPLACEMENT + || field_enum==VELOCITY + || field_enum==MASS_DENSITY + || field_enum==CHARGE_DENSITY) return true; + else return false; + }; + + + static void print_mask(const Array2D & rhsMask) + { + for (int i = 0; i < NUM_FIELDS; i++) { + FieldName field = (FieldName) i; + string name = field_to_string(field); + if (rhsMask(field,FLUX) + || rhsMask(field,SOURCE) + || rhsMask(field,PRESCRIBED_SOURCE) + || rhsMask(field,EXTRINSIC_SOURCE)) { + cout << "RHS_MASK: " << name; + if (rhsMask(field,FLUX)) cout << " flux"; + if (rhsMask(field,SOURCE)) cout << " source"; + if (rhsMask(field,PRESCRIBED_SOURCE)) cout << " prescribed_src"; + if (rhsMask(field,EXTRINSIC_SOURCE)) cout << " extrinsic_src"; + cout << "\n"; + } + } + } + + +} + +#endif diff --git a/lib/atc/Array.h b/lib/atc/Array.h new file mode 100644 index 0000000000..5b0d60d9e9 --- /dev/null +++ b/lib/atc/Array.h @@ -0,0 +1,169 @@ +#ifndef ARRAY_H +#define ARRAY_H + +//#include +//#include +#include +#include + +template +class Array { +public: + Array(); + Array(int len); + Array(const Array& A); + ~Array(); + + // Resize and reinitialize the array + void reset(int len); + // Access method to get the element i: + T& operator() (int i); + // Access method to get the element i: + const T& operator() (int i) const; + // Assignment + Array& operator= (const Array &other); + Array& operator= (const T &value); + // Get length of array + int get_length() const; + int size() const; + // Do I have this element? + bool has_member(T val) const; + // Return pointer to internal data + const T* get_data() const; + T* get_ptr() const; + // print + void print(std::string name = "") const; + // Dump templated type to disk; operation not safe for all types + void write_restart(FILE *f) const; + +private: + int len_; + T *data_; +}; + +template +Array::Array(void) { + len_ = 0; + data_ = NULL; +} + +template +Array::Array(int len) { + len_ = len; + data_ = new T[len_]; +} + +template +Array::Array(const Array& A) { + len_ = A.len_; + if (A.data_==NULL) + data_ = NULL; + else { + data_ = new T[len_]; + for(int i=0;i +void Array::reset(int len) { + if (len_ == len) { // no size change; don't realloc memory + return; + } + else { // size change, realloc memory + len_ = len; + if (data_ != NULL) + delete[] data_; + if (len_ > 0) + data_ = new T[len_]; + else { + data_ = NULL; + len_ = 0; + } + } +} + +template +T& Array::operator() (int i) { + // Array bounds checking + return data_[i]; +} + +template +Array& Array::operator= (const Array &other) { + if (data_ == NULL) { // initialize my internal storage to match LHS + len_ = other.len_; + if (other.data_==NULL) + data_ = NULL; + else + data_ = new T[len_]; + } + for(int i=0;i +Array& Array::operator= (const T &value) { + for(int i=0;i +const T& Array::operator() (int i) const { + // Array bounds checking + return data_[i]; +} + +template +int Array::get_length(void) const { + return len_; +} +template +int Array::size(void) const { + return len_; +} + +template +bool Array::has_member(T val) const { + int i; + bool retval = false; + for(i=0;i +void Array::write_restart(FILE *f) const { + fwrite(&len_,sizeof(int),1,f); + if (len_ > 0) + fwrite(data_,sizeof(T),len_,f); +} + +template +const T* Array::get_data() const { + return data_; +} +template +T* Array::get_ptr() const { + return data_; +} + +template +Array::~Array() { + if (data_ != NULL) + delete[] data_; +} + +template +void Array::print(std::string name) const { + std::cout << "------- Begin "< +#include + +template +class Array2D { +public: + Array2D(); + Array2D(int nrows, int ncols); + Array2D(const Array2D& A); // copy constructor + ~Array2D(); + + // Resize and reinitalize matrix + void reset(int nrows, int ncols); + // Access method to get the (i,j) element: + T& operator() (int i, int j); + // Access method to get the (i,j) element: + const T& operator() (int i, int j) const; + // Copy operator + Array2D& operator= (const Array2D& other); + // assignment operator + Array2D& operator= (const T other); + // Get size of Array2D + int nRows() const; + int nCols() const; + // Do I have this element? + bool has_member(T val) const; + // print + void print(std::string name ="") const; + // Dump templated type to disk; operation not safe for all types + void write_restart(FILE *f) const; + +private: + int nrows_, ncols_; + T *data_; +}; + +template +Array2D::Array2D() { + nrows_ = 0; + ncols_ = 0; + data_ = NULL; +} + +template +Array2D::Array2D(int nrows, int ncols) { + nrows_ = nrows; + ncols_ = ncols; + data_ = new T[nrows_ * ncols_]; +} + +template +Array2D::Array2D(const Array2D& A) { + nrows_ = A.nrows_; + ncols_ = A.ncols_; + if (A.data_==NULL) + data_ = NULL; + else { + data_ = new T[nrows_ * ncols_]; + for(int i=0;i +void Array2D::reset(int nrows, int ncols) { + if (nrows_ == nrows && ncols_ == ncols) { // no size change; don't realloc memory + return; + } + else { // size changed; realloc memory + nrows_ = nrows; + ncols_ = ncols; + if (data_ != NULL) + delete [] data_; + if (ncols_ > 0 && nrows_ > 0) + data_ = new T[nrows_ * ncols_]; + else { + data_ = NULL; + nrows_ = 0; + ncols_ = 0; + } + } +} + +template +T& Array2D::operator() (int row, int col) { + // Array bounds checking + return data_[col*nrows_ + row]; +} + +template +const T& Array2D::operator() (int row, int col) const { + // Array bounds checking + return data_[col*nrows_ + row]; +} + +template +Array2D& Array2D::operator= (const Array2D& other) { + if (data_ == NULL) { // initialize my internal storage to match LHS + nrows_ = other.nrows_; + ncols_ = other.ncols_; + if (other.data_==NULL) + data_ = NULL; + else + data_ = new T[nrows_ * ncols_]; + } + for(int i=0;i +Array2D& Array2D::operator= (const T other) { + for(int i=0;i +int Array2D::nRows() const { + return nrows_; +} + +template +int Array2D::nCols() const { + return ncols_; +} + +template +bool Array2D::has_member(T val) const { + int i; + bool retval = false; + for(i=0;i +void Array2D::write_restart(FILE *f) const { + fwrite(&nrows_,sizeof(int),1,f); + fwrite(&ncols_,sizeof(int),1,f); + if (nrows_*ncols_ > 0) + fwrite(data_,sizeof(T),nrows_*ncols_,f); +} + +template +Array2D::~Array2D() { + if (data_ != NULL) + delete[] data_; +} + +template +void Array2D::print(std::string name) const { + std::cout << "------- Begin "<get_nlocal(); + if (nLocal_ > 0) + lambdaForce_.reset(nLocal_,nsd_); + if (regulatorMethod_) + regulatorMethod_->reset_nlocal(); + } + + //-------------------------------------------------------- + // reset_data: + // sets up storage for all data structures + //-------------------------------------------------------- + void AtomicRegulator::reset_data() + { + nNodes_ = atcTransfer_->get_nNodes(); + nsd_ = atcTransfer_->get_nsd(); + + if (timeFilter_) + delete timeFilter_; + timeFilter_ = NULL; + + resetData_ = false; + } + + //-------------------------------------------------------- + // reset_method: + // sets up methods, if necessary + //-------------------------------------------------------- + void AtomicRegulator::reset_method() + { + // set up defaults for anything that didn't get set + if (!regulatorMethod_) + regulatorMethod_ = new RegulatorMethod(this); + if (!timeFilter_) + timeFilter_ = (atcTransfer_->get_time_filter_manager())->construct(); + + needReset_ = false; + } + + //-------------------------------------------------------- + // initialize: + // sets up methods before a run + //-------------------------------------------------------- + void AtomicRegulator::initialize() + { + // make sure consistent boundary integration is being used + atcTransfer_->set_boundary_integration_type(boundaryIntegrationType_); + + // reset data related to local atom count + reset_nlocal(); + } + + //-------------------------------------------------------- + // output: + // pass through to appropriate output methods + //-------------------------------------------------------- + void AtomicRegulator::output(double dt, OUTPUT_LIST & outputData) const + { + regulatorMethod_->output(dt,outputData); + } + + //-------------------------------------------------------- + // apply_pre_predictor: + // applies the controller in the pre-predictor + // phase of the time integrator + //-------------------------------------------------------- + void AtomicRegulator::apply_pre_predictor(double dt, int timeStep) + { + if (timeStep % howOften_==0) // apply full integration scheme, including filter + regulatorMethod_->apply_pre_predictor(dt); + } + + //-------------------------------------------------------- + // apply_mid_predictor: + // applies the controller in the mid-predictor + // phase of the time integrator + //-------------------------------------------------------- + void AtomicRegulator::apply_mid_predictor(double dt, int timeStep) + { + if (timeStep % howOften_==0) // apply full integration scheme, including filter + regulatorMethod_->apply_mid_predictor(dt); + } + + //-------------------------------------------------------- + // apply_post_predictor: + // applies the controller in the post-predictor + // phase of the time integrator + //-------------------------------------------------------- + void AtomicRegulator::apply_post_predictor(double dt, int timeStep) + { + if (timeStep % howOften_==0) // apply full integration scheme, including filter + regulatorMethod_->apply_post_predictor(dt); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // applies the controller in the pre-corrector phase + // of the time integrator + //-------------------------------------------------------- + void AtomicRegulator::apply_pre_corrector(double dt, int timeStep) + { + if (timeStep % howOften_==0) // apply full integration scheme, including filter + regulatorMethod_->apply_pre_corrector(dt); + } + + //-------------------------------------------------------- + // apply_post_corrector: + // applies the controller in the post-corrector phase + // of the time integrator + //-------------------------------------------------------- + void AtomicRegulator::apply_post_corrector(double dt, int timeStep) + { + if (timeStep % howOften_==0) // apply full integration scheme, including filter + regulatorMethod_->apply_post_corrector(dt); + } + + //-------------------------------------------------------- + // compute_boundary_flux: + // computes the boundary flux to be consistent with + // the controller + //-------------------------------------------------------- + void AtomicRegulator::compute_boundary_flux(FIELDS & fields) + { + regulatorMethod_->compute_boundary_flux(fields); + } + + //-------------------------------------------------------- + // add_to_rhs: + // adds any controller contributions to the FE rhs + //-------------------------------------------------------- + void AtomicRegulator::add_to_rhs(FIELDS & rhs) + { + regulatorMethod_->add_to_rhs(rhs); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class RegulatorMethod + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + RegulatorMethod::RegulatorMethod(AtomicRegulator * atomicRegulator) : + atomicRegulator_(atomicRegulator), + atcTransfer_(atomicRegulator->get_atc_transfer()), + fieldMask_(NUM_FIELDS,NUM_FLUX), + boundaryFlux_(atcTransfer_->get_boundary_fluxes()), + nNodes_(atomicRegulator_->get_nNodes()) + { + fieldMask_ = false; + } + + //-------------------------------------------------------- + // compute_boundary_flux + // default computation of boundary flux based on + // finite + //-------------------------------------------------------- + void RegulatorMethod::compute_boundary_flux(FIELDS & fields) + { + atcTransfer_->compute_boundary_flux(fieldMask_, + fields, + boundaryFlux_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class RegulatorShapeFunction + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + RegulatorShapeFunction::RegulatorShapeFunction(AtomicRegulator * atomicRegulator) : + RegulatorMethod(atomicRegulator), + maxIterations_(50), + tolerance_(1.e-10), + nNodeOverlap_(atcTransfer_->get_nNode_overlap()), + nsd_(atomicRegulator_->get_nsd()), + lambda_(atomicRegulator_->get_lambda()), + shapeFunctionMatrix_(atcTransfer_->get_nhat_overlap()), + glcMatrixTemplate_(atcTransfer_->get_m_t_template()), + shapeFunctionGhost_(atcTransfer_->get_shape_function_ghost_overlap()), + internalToAtom_(atcTransfer_->get_internal_to_atom_map()), + internalToOverlapMap_(atcTransfer_->get_atom_to_overlap_map()), + ghostToAtom_(atcTransfer_->get_ghost_to_atom_map()), + nLocal_(0), + nLocalLambda_(0), + nLocalGhost_(0) + { + if (atcTransfer_->use_lumped_lambda_solve()) + matrixSolver_ = new LambdaMatrixSolverLumped(glcMatrixTemplate_, + shapeFunctionMatrix_, + maxIterations_, + tolerance_); + else + matrixSolver_ = new LambdaMatrixSolverCg(glcMatrixTemplate_, + shapeFunctionMatrix_, + maxIterations_, + tolerance_); + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + RegulatorShapeFunction::~RegulatorShapeFunction() + { + if (matrixSolver_) + delete matrixSolver_; + } + + //-------------------------------------------------------- + // solve_for_lambda + // solves matrix equation for lambda using given rhs + //-------------------------------------------------------- + void RegulatorShapeFunction::solve_for_lambda(const DENS_MAT & rhs) + { + // set up weighting matrix + DIAG_MAT weights; + if (nLocalLambda_>0) + set_weights(weights); + + // solve on overlap nodes + DENS_MAT rhsOverlap(nNodeOverlap_,rhs.nCols()); + atcTransfer_->map_unique_to_overlap(rhs, rhsOverlap); + DENS_MAT lambdaOverlap(nNodeOverlap_,lambda_.nCols()); + + for (int i = 0; i < rhs.nCols(); i++) { + CLON_VEC tempRHS(rhsOverlap,CLONE_COL,i); + CLON_VEC tempLambda(lambdaOverlap,CLONE_COL,i); + matrixSolver_->execute(tempRHS,tempLambda,weights,atcTransfer_); + } + + // map solution back to all nodes + atcTransfer_->map_overlap_to_unique(lambdaOverlap,lambda_); + } + + //-------------------------------------------------------- + // reset_nlocal: + // resets data dependent on local atom count + //-------------------------------------------------------- + void RegulatorShapeFunction::reset_nlocal() + { + RegulatorMethod::reset_nlocal(); + nLocal_ = atomicRegulator_->get_nLocal(); + nLocalLambda_ = atcTransfer_->get_nlocal_lambda(); + nLocalGhost_ = atcTransfer_->get_nlocal_ghost(); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolver + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to necessary data + //-------------------------------------------------------- + LambdaMatrixSolver::LambdaMatrixSolver(SPAR_MAT & matrixTemplate, SPAR_MAT & shapeFunctionMatrix, int maxIterations, double tolerance) : + matrixTemplate_(matrixTemplate), + shapeFunctionMatrix_(shapeFunctionMatrix), + maxIterations_(maxIterations), + tolerance_(tolerance) + { + // do nothing + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolverLumped + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to necessary data + //-------------------------------------------------------- + LambdaMatrixSolverLumped::LambdaMatrixSolverLumped(SPAR_MAT & matrixTemplate, SPAR_MAT & shapeFunctionMatrix, int maxIterations, double tolerance) : + LambdaMatrixSolver(matrixTemplate,shapeFunctionMatrix,maxIterations,tolerance) + { + // do nothing + } + + void LambdaMatrixSolverLumped::execute(VECTOR & rhs, VECTOR & lambda, DIAG_MAT & weights, ATC_Transfer * atcTransfer) + { + // form matrix : sum_a N_Ia * W_a * N_Ja + SPAR_MAT myMatrixLocal(matrixTemplate_); + if (weights.nRows()>0) + myMatrixLocal.WeightedLeastSquares(shapeFunctionMatrix_,weights); + + // swap contributions + SPAR_MAT myMatrix(matrixTemplate_); + LammpsInterface::instance()->allsum(myMatrixLocal.get_ptr(), + myMatrix.get_ptr(), myMatrix.size()); + + DIAG_MAT lumpedMatrix(myMatrix.nRows(),myMatrix.nCols()); + for (int i = 0; i < myMatrix.nRows(); i++) + for (int j = 0; j < myMatrix.nCols(); j++) + lumpedMatrix(i,i) += myMatrix(i,j); + + // solve lumped equation + for (int i = 0; i < rhs.size(); i++) + lambda(i) = rhs(i)/lumpedMatrix(i,i); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolverCg + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to necessary data + //-------------------------------------------------------- + LambdaMatrixSolverCg::LambdaMatrixSolverCg(SPAR_MAT & matrixTemplate, SPAR_MAT & shapeFunctionMatrix, int maxIterations, double tolerance) : + LambdaMatrixSolver(matrixTemplate,shapeFunctionMatrix,maxIterations,tolerance) + { + // do nothing + } + + void LambdaMatrixSolverCg::execute(VECTOR & rhs, VECTOR & lambda, DIAG_MAT & weights, ATC_Transfer * atcTransfer) + { + // form matrix : sum_a N_Ia * W_a * N_Ja + SPAR_MAT myMatrixLocal(matrixTemplate_); + if (weights.nRows()>0) + myMatrixLocal.WeightedLeastSquares(shapeFunctionMatrix_,weights); + + // swap contributions + SPAR_MAT myMatrix(matrixTemplate_); + LammpsInterface::instance()->allsum(myMatrixLocal.get_ptr(), + myMatrix.get_ptr(), myMatrix.size()); + + + DIAG_MAT preConditioner = myMatrix.get_diag(); + int myMaxIt = 2*myMatrix.nRows(); // note could also use the fixed parameter + double myTol = tolerance_; + + int convergence = CG(myMatrix, lambda, rhs, preConditioner, myMaxIt, myTol); + + // error if didn't converge + if (convergence>0) + throw ATC_Error(0,"CG solver did not converge in LambdaMatrixSolverCg::execute()"); + } + +}; diff --git a/lib/atc/AtomicRegulator.h b/lib/atc/AtomicRegulator.h new file mode 100644 index 0000000000..25863b0649 --- /dev/null +++ b/lib/atc/AtomicRegulator.h @@ -0,0 +1,394 @@ +/** Atomic Regulator : a base class class for atom-continuum control */ + +#ifndef ATOMICREGULATOR_H +#define ATOMICREGULATOR_H + +// ATC_Transfer headers +#include "ATC_Transfer.h" + +// other headers +#include +#include + +namespace ATC { + + // forward declarations + class TimeFilter; + class RegulatorMethod; + class LambdaMatrixSolver; + + /** + * @class AtomicRegulator + * @brief Base class for atom-continuum control + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class AtomicRegulator + //-------------------------------------------------------- + //-------------------------------------------------------- + + class AtomicRegulator { + + public: + + // constructor + AtomicRegulator(ATC_Transfer * atcTransfer); + + // destructor + ~AtomicRegulator(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + /** add output information */ + virtual void output(double dt, OUTPUT_LIST & outputData) const; + + /** reset number of local atoms, as well as atomic data */ + virtual void reset_nlocal(); + + // application steps + /** apply the thermostat in the pre-predictor phase */ + virtual void apply_pre_predictor(double dt, int timeStep); + /** apply the thermostat in the mid-predictor phase */ + virtual void apply_mid_predictor(double dt, int timeStep); + /** apply the thermostat in the post-predictor phase */ + virtual void apply_post_predictor(double dt, int timeStep); + /** apply the thermostat in the pre-correction phase */ + virtual void apply_pre_corrector(double dt, int timeStep); + /** apply the thermostat in the post-correction phase */ + virtual void apply_post_corrector(double dt, int timeStep); + + // coupling to FE state + /** compute the thermal boundary flux, must be consistent with thermostat */ + virtual void compute_boundary_flux(FIELDS & fields); + /** add contributions (if any) to the finite element right-hand side */ + virtual void add_to_rhs(FIELDS & rhs); + + // data access, intended for method objects + /** return value of lambda */ + DENS_MAT & get_lambda() {return lambda_;}; + /** return the atomic force defined by lambda */ + DENS_MAT & get_lambda_force() {return lambdaForce_;}; + /** access for ATC transfer */ + ATC_Transfer * get_atc_transfer() {return atcTransfer_;}; + /** access for time filter */ + TimeFilter * get_time_filter() {return timeFilter_;}; + /** access for number of nodes */ + int get_nNodes() {return nNodes_;}; + /** access for number of spatial dimensions */ + int get_nsd() {return nsd_;}; + /** access for number of local atoms */ + int get_nLocal() {return nLocal_;}; + /** access for boundary integration methods */ + ATC_Transfer::BoundaryIntegrationType get_boundary_integration_type() + {return boundaryIntegrationType_;}; + /** access for boundary face sets */ + const set< pair > * get_face_sets() + { return boundaryFaceSet_;}; + + protected: + + void destroy(); + + /** point to atc_transfer object */ + ATC_Transfer * atcTransfer_; + + /** how often in number of time steps thermostat is applied */ + int howOften_; + + // reset/reinitialize flags + /** flag to see if data requires a reset */ + bool resetData_; + /** flag to reset data */ + bool needReset_; + /** resets data structures */ + void reset_data(); + /** reinitialize method */ + void reset_method(); + + // regulator data + /** control parameter */ + DENS_MAT lambda_; + /** lambda force computed by controller */ + DENS_MAT lambdaForce_; + + /** number of nodes */ + int nNodes_; + /** number of spatial dimensions */ + int nsd_; + /** number of local atoms */ + int nLocal_; + + // method pointers + /** time filtering object */ + TimeFilter * timeFilter_; + /** sets up and solves the thermostat equations */ + RegulatorMethod * regulatorMethod_; + + // boundary flux information + ATC_Transfer::BoundaryIntegrationType boundaryIntegrationType_; + const set< pair > * boundaryFaceSet_; + + private: + + // DO NOT define this + AtomicRegulator(); + + }; + + /** + * @class RegulatorMethod + * @brief Base class for implementation of control algorithms + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class RegulatorMethod + //-------------------------------------------------------- + //-------------------------------------------------------- + + class RegulatorMethod { + + public: + + RegulatorMethod(AtomicRegulator * atomicRegulator); + + ~RegulatorMethod(){}; + + /** reset number of local atoms, as well as atomic data */ + virtual void reset_nlocal(){}; + + /** applies thermostat to atoms in the pre-predictor phase */ + virtual void apply_pre_predictor(double dt){}; + + /** applies thermostat to atoms in the mid-predictor phase */ + virtual void apply_mid_predictor(double dt){}; + + /** applies thermostat to atoms in the post-predictor phase */ + virtual void apply_post_predictor(double dt){}; + + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt){}; + + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt){}; + + /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields); + + /** add contributions (if any) to the finite element right-hand side */ + virtual void add_to_rhs(FIELDS & rhs){}; + + /** get data for output */ + virtual void output(double dt, OUTPUT_LIST & outputData){}; + + protected: + + /** pointer to ATC_transfer object */ + ATC_Transfer * atcTransfer_; + + /** pointer to atomic regulator object for data */ + AtomicRegulator * atomicRegulator_; + + /** boundary flux */ + FIELDS & boundaryFlux_; + + /** field mask for specifying boundary flux */ + Array2D fieldMask_; + + /** number of nodes */ + int nNodes_; + + private: + + // DO NOT define this + RegulatorMethod(); + + }; + + /** + * @class RegulatorShapeFunction + * @brief Base class for implementation of regulation algorithms using the shape function matrices + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class RegulatorShapeFunction + // base class for all regulators of general form + // of N^T w N lambda = rhs + //-------------------------------------------------------- + //-------------------------------------------------------- + + class RegulatorShapeFunction : public RegulatorMethod { + + public: + + RegulatorShapeFunction(AtomicRegulator * atomicRegulator); + + ~RegulatorShapeFunction(); + + /** reset number of local atoms, as well as atomic data */ + virtual void reset_nlocal(); + + protected: + + // methods + /** solve matrix equation */ + void solve_for_lambda(const DENS_MAT & rhs); + + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(DIAG_MAT & weights){}; + + // member data + /** lambda coupling parameter */ + DENS_MAT & lambda_; + + /** shape function matrix for use in GLC solve */ + SPAR_MAT & shapeFunctionMatrix_; + + /** pre-templated sparsity pattern for N^T * T * N */ + SPAR_MAT & glcMatrixTemplate_; + + /** reference to ATC unity shape function on ghost atoms */ + SPAR_MAT & shapeFunctionGhost_; + + /** maximum number of iterations used in solving for lambda */ + int maxIterations_; + + /** tolerance used in solving for lambda */ + double tolerance_; + + /** matrix solver object */ + LambdaMatrixSolver * matrixSolver_; + + /** maps internal atom ids to LAMMPS atom ids */ + Array & internalToAtom_; + + /** maps internal atoms to overlap atoms */ + SPAR_MAT & internalToOverlapMap_; + + /** maps ghost atom and LAMMPS atom ids */ + Array & ghostToAtom_; + + /** number of overlapping nodes */ + int nNodeOverlap_; + + /** number of spatial dimensions */ + int nsd_; + + /** number of ATC internal atoms on this processor */ + int nLocal_; + + /** number of thermostatted ATC internal atoms on this processor */ + int nLocalLambda_; + + /** number of ATC ghost atoms on this processor */ + int nLocalGhost_; + + private: + + // DO NOT define this + RegulatorShapeFunction(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolver + //-------------------------------------------------------- + //-------------------------------------------------------- + + class LambdaMatrixSolver { + + public: + + LambdaMatrixSolver(SPAR_MAT & matrixTemplate, SPAR_MAT & shapeFunctionMatrix, int maxIterations, double tolerance); + + ~LambdaMatrixSolver(){}; + + /** execute the solver */ + virtual void execute(VECTOR & rhs, VECTOR & lambda, DIAG_MAT & weights,ATC_Transfer * atcTransfer_=NULL) = 0; + + protected: + + /** sparse template for the matrix */ + SPAR_MAT & matrixTemplate_; + + /** non-symmetric part of the matrix */ + SPAR_MAT & shapeFunctionMatrix_; + + /** maximum number of iterations */ + int maxIterations_; + + /** relative tolerance to solve to */ + double tolerance_; + + private: + + // DO NOT define this + LambdaMatrixSolver(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolverLumped + //-------------------------------------------------------- + //-------------------------------------------------------- + + class LambdaMatrixSolverLumped : public LambdaMatrixSolver { + + public: + + LambdaMatrixSolverLumped(SPAR_MAT & matrixTemplate, SPAR_MAT & shapeFunctionMatrix, int maxIterations, double tolerance); + + ~LambdaMatrixSolverLumped(){}; + + /** execute the solver */ + virtual void execute(VECTOR & rhs, VECTOR & lambda, DIAG_MAT & weights,ATC_Transfer * atcTransfer_=NULL); + + protected: + + + private: + + // DO NOT define this + LambdaMatrixSolverLumped(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class LambdaMatrixSolverCg + //-------------------------------------------------------- + //-------------------------------------------------------- + + class LambdaMatrixSolverCg : public LambdaMatrixSolver { + + public: + + LambdaMatrixSolverCg(SPAR_MAT & matrixTemplate, SPAR_MAT & shapeFunctionMatrix, int maxIterations, double tolerance); + + ~LambdaMatrixSolverCg(){}; + + /** execute the solver */ + virtual void execute(VECTOR & rhs, VECTOR & lambda, DIAG_MAT & weights,ATC_Transfer * atcTransfer_=NULL); + + protected: + + + + private: + + // DO NOT define this + LambdaMatrixSolverCg(); + + }; + +}; + +#endif diff --git a/lib/atc/CG.h b/lib/atc/CG.h new file mode 100644 index 0000000000..7ce19f37ef --- /dev/null +++ b/lib/atc/CG.h @@ -0,0 +1,79 @@ +//***************************************************************** +// Iterative template routine -- CG +// +// CG solves the symmetric positive definite linear +// system Ax=b using the Conjugate Gradient method. +// +// CG follows the algorithm described on p. 15 in the +// SIAM Templates book. +// +// The return value indicates convergence within max_iter (input) +// iterations (0), or no convergence within max_iter iterations (1). +// +// Upon successful return, output arguments have the following values: +// +// x -- approximate solution to Ax = b +// max_iter -- the number of iterations performed before the +// tolerance was reached +// tol -- the residual after the final iteration +// +//***************************************************************** + +template < class Matrix, class Vector, class DataVector, class Preconditioner, class Real > +int +CG(const Matrix &A, Vector &x, const DataVector &b, const Preconditioner &M, int &max_iter, Real &tol) { + Real resid; + DenseVector p, z, q; + Real alpha, beta, rho, rho_1; + DenseVector tmp; + tmp.reset(b.size()); + + p.reset(b.size()); + z.reset(b.size()); + q.reset(b.size()); + + Real normb = b.norm(); + DenseVector r; + tmp = A*x; + r = b - tmp; + // Implicit assumption that only diagonal matrices are being used for preconditioning + Preconditioner Minv = M.inv(); + + if (normb == 0.0) + normb = 1; + + if ((resid = r.norm() / normb) <= tol) { + tol = resid; + max_iter = 0; + return 0; + } + + for (int i = 0; i < max_iter; i++) { + z = Minv*r; + rho = r.dot(z); + + if (i == 0) + p = z; + else { + beta = rho / rho_1; + tmp = p*beta; + p = z + tmp; + } + + q = A*p; + alpha = rho / p.dot(q); + + x += p*alpha; + r -= q*alpha; + + if ((resid = r.norm() / normb) <= tol) + { + tol = resid; + max_iter = i+1; + return 0; + } + rho_1 = rho; + } + tol = resid; + return 1; +} diff --git a/lib/atc/CloneVector.h b/lib/atc/CloneVector.h new file mode 100644 index 0000000000..a92848984e --- /dev/null +++ b/lib/atc/CloneVector.h @@ -0,0 +1,243 @@ +#ifndef CLONEVECTOR_H +#define CLONEVECTOR_H + +#include "Vector.h" + + +template +class CloneVector : public Vector +{ +public: + CloneVector(); // do not implement + CloneVector(const Vector &c); + CloneVector(const Matrix &c, int dim, INDEX idx=0); + CloneVector(const DiagonalMatrix &c, INDEX idx=0); + + // overloaded virtual functions + T& operator[](INDEX i); + T operator[](INDEX i) const; + T operator()(INDEX i, INDEX j=0) const; + T& operator()(INDEX i, INDEX j=0); + INDEX nRows() const; + + CloneVector& operator=(const T &v); + CloneVector& operator=(const CloneVector &C); + CloneVector& operator=(const Matrix &C); + + virtual bool memory_contiguous() const; + T* get_ptr() const; + void resize(INDEX nRows, INDEX nCols=0, bool copy=false); + void reset(INDEX nRows, INDEX nCols=0, bool zero=true); + void copy(const T * ptr, INDEX nRows, INDEX nCols=0); + +private: + void _resize(INDEX nRows, INDEX nCols, bool copy, bool zero); + + Vector * const _baseV; // ptr to a base vector + Matrix * const _baseM; // ptr to a base matrix + int _clone_type; // what to clown (see enum CLONE_TYPE) + INDEX _idx; // index of matrix dimension to clone +}; +/////////////////////////////////////////////////////////////////////////////// +// Template definitions /////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +// Construct from another vector +//----------------------------------------------------------------------------- +template +CloneVector::CloneVector(const Vector &c) + : Vector(), _baseV(const_cast*>(&c)), _baseM(NULL) +{} +//----------------------------------------------------------------------------- +// Construct from a matrix, the const_cast isn't pretty +/* CloneVector(const Matrix &c, int dim, INDEX idx) +/ attaches to a slice of a matrix +/ Arguments: c = pointer to the matrix +/ dim = type of slice CLONE_ROW, CLONE_COL, CLONE_DIAG +/ idx = index of row or column (no effect on diag currently) +*/ +//----------------------------------------------------------------------------- +template +CloneVector::CloneVector(const Matrix &c, int dim, INDEX idx) + : Vector(), _baseV(NULL), _baseM(const_cast*>(&c)) + , _clone_type(dim), _idx(idx) +{} +//----------------------------------------------------------------------------- +// Construct from a DiagonalMatrix +//----------------------------------------------------------------------------- +template +CloneVector::CloneVector(const DiagonalMatrix &c, INDEX idx) + : Vector(), _baseV(NULL), _baseM(const_cast*>(&c)) + , _clone_type(CLONE_DIAG), _idx(0) +{} +//----------------------------------------------------------------------------- +// value (const) indexing operator +//----------------------------------------------------------------------------- +template +T CloneVector::operator()(INDEX i, INDEX j) const +{ + return (*this)[i]; +} +//----------------------------------------------------------------------------- +// reference index operator +//----------------------------------------------------------------------------- +template +T& CloneVector::operator()(INDEX i, INDEX j) +{ + return (*this)[i]; +} +//----------------------------------------------------------------------------- +// Indexes the cloned vector either from another vector or a matrix +//----------------------------------------------------------------------------- +template +T CloneVector::operator[](INDEX i) const +{ + if (_baseV) return (*_baseV)(i); + if (_clone_type == CLONE_ROW) return (*_baseM)(_idx, i); + else if (_clone_type == CLONE_COL) return (*_baseM)(i,_idx); + else if (_clone_type == CLONE_DIAG) return (*_baseM)(i,i); + return 0; +} +//----------------------------------------------------------------------------- +// Indexes the cloned vector either from another vector or a matrix +//----------------------------------------------------------------------------- +template +T& CloneVector::operator[](INDEX i) +{ + if (_baseV) return (*_baseV)(i); + if (_clone_type == CLONE_ROW) return (*_baseM)(_idx, i); + if (_clone_type == CLONE_COL) return (*_baseM)(i,_idx); + if (_clone_type == CLONE_DIAG) return (*_baseM)(i,i); + return (*_baseV)(i); +} +//----------------------------------------------------------------------------- +// Returns the size of the base vector or of the row/col of the base matrix +//----------------------------------------------------------------------------- +template +INDEX CloneVector::nRows() const +{ + using std::min; + if (_baseV) return _baseV->size(); + if (_clone_type == CLONE_ROW) return _baseM->nCols(); + if (_clone_type == CLONE_COL) return _baseM->nRows(); + if (_clone_type == CLONE_DIAG) return min(_baseM->nRows(), _baseM->nCols()); + return 0; +} +//----------------------------------------------------------------------------- +// assigns all elements to a constant +//----------------------------------------------------------------------------- +template +CloneVector& CloneVector::operator=(const T &v) +{ + this->set_all_elements_to(v); // NOTE: DO NOT do _baseX->set_elements_to() + return *this; +} +//----------------------------------------------------------------------------- +// assigns all elements to the corresponding elements in C +//----------------------------------------------------------------------------- +template +CloneVector& CloneVector::operator=(const CloneVector &C) +{ + GCK(*this, C, this->size()!=C.size(), "Error in CloneVector:operator="); + FORi VIDX(i) = C[i]; + return *this; +} +//----------------------------------------------------------------------------- +// assigns all elements to the corresponding elements in C +//----------------------------------------------------------------------------- +template +CloneVector& CloneVector::operator=(const Matrix &C) +{ + GCK(*this, C, this->size()!=C.size(), "Error in CloneVector:operator="); + FORi VIDX(i) = C[i]; + return *this; +} +//----------------------------------------------------------------------------- +// returns true only if its guaranteed memory is contiguous +//----------------------------------------------------------------------------- +template +bool CloneVector::memory_contiguous() const +{ + // drill down through clone of clones + if (_baseV) return _baseV->memory_contiguous(); + // could be okay if DiagonalMatrix, but can't guarantee this + if (_clone_type == CLONE_DIAG) return false; +#ifdef ROW_STORAGE + return _clone_type == CLONE_ROW; +#else + return _clone_type == CLONE_COL; +#endif +} +//----------------------------------------------------------------------------- +// Returns a pointer to the data unless the data is a column of a matrix +//----------------------------------------------------------------------------- +template +T* CloneVector::get_ptr() const +{ + if (_baseV) return _baseV->get_ptr(); +#ifdef ROW_STORAGE + if (_clone_type == CLONE_ROW) return _baseM->get_ptr() + this->size()*_idx; + if (_clone_type == CLONE_COL) return _baseM->get_ptr() + this->size(); + if (_clone_type == CLONE_DIAG) return _baseM->get_ptr(); +#else + if (_clone_type == CLONE_COL) return _baseM->get_ptr() + this->size()*_idx; + if (_clone_type == CLONE_ROW) return _baseM->get_ptr() + this->size(); + if (_clone_type == CLONE_DIAG) return _baseM->get_ptr(); +#endif + return 0; +} +//----------------------------------------------------------------------------- +// general resize function, can handle parents that are matrices or vectors +//----------------------------------------------------------------------------- +template +void CloneVector::_resize(INDEX nRows, INDEX nCols, bool copy, bool zero) +{ + if (_baseV) + { + if (copy) _baseV->resize(nRows, nCols, copy); + else _baseV->reset (nRows, nCols, zero); + return; + } + // parent is a matrix, need to decide what the Vector is cloning + switch (_clone_type) + { + case CLONE_ROW: // now the leading dimension is rows + nCols = nCols ? nCols : _baseM->nCols(); + break; + case CLONE_COL: // now the leading dimension is columns + nCols = nCols ? nCols : _baseM->nRows(); + Utility::Swap(nRows, nCols); + break; + case CLONE_DIAG: // lets just hope you knew what you were doing + break; + default: + return; + } + if (zero) _baseM->reset(nRows, nCols, zero); // zero overrides copy + else _baseM->resize(nRows, nCols, copy); +} +//----------------------------------------------------------------------------- +// resizes the matrix and optionally copies what fits +//----------------------------------------------------------------------------- +template +void CloneVector::resize(INDEX nRows, INDEX nCols, bool copy) +{ + _resize(nRows, nCols, copy, false); +} +//----------------------------------------------------------------------------- +// resizes the matrix and optionally zeros it out +//----------------------------------------------------------------------------- +template +void CloneVector::reset(INDEX nRows, INDEX nCols, bool zero) +{ + _resize(nRows, nCols, false, zero); +} +//----------------------------------------------------------------------------- +// resizes the matrix and copies data +//----------------------------------------------------------------------------- +template +void CloneVector::copy(const T * ptr, INDEX nRows, INDEX nCols) +{ + _resize(nRows, nCols, false, false); + memcpy(this->get_ptr(), ptr, this->size()*sizeof(T)); +} +#endif diff --git a/lib/atc/DenseMatrix.h b/lib/atc/DenseMatrix.h new file mode 100644 index 0000000000..9ecc65fa7a --- /dev/null +++ b/lib/atc/DenseMatrix.h @@ -0,0 +1,317 @@ +#ifndef DENSEMATRIX_H +#define DENSEMATRIX_H + +#include "Matrix.h" + +template +class DenseMatrix : public Matrix +{ +public: + DenseMatrix(INDEX rows=0, INDEX cols=0, bool z=1) { _create(rows, cols, z); } + DenseMatrix(const DenseMatrix& c) : _data(NULL){ _copy(c); } + DenseMatrix(const SparseMatrix& c): _data(NULL){ c.dense_copy(*this);} + DenseMatrix(const Matrix& c) : _data(NULL){ _copy(c); } +// const SparseMatrix * p = sparse_cast(&c); +// (p) ? p->dense_copy(*this) : _copy(c); } + ~DenseMatrix() { _delete();} + + void reset (INDEX rows, INDEX cols, bool zero=true); + void resize(INDEX rows, INDEX cols, bool copy=false); + void copy (const T * ptr, INDEX rows, INDEX cols); + /** returns transpose(this) * B */ + DenseMatrix transMat(const DenseMatrix& B) const; + /** returns by element multiply A_ij = this_ij * B_ij */ + DenseMatrix mult_by_element(const DenseMatrix& B) const; + + /** overloaded virtual functions */ + T& operator()(INDEX i, INDEX j) { MICK(i,j) return DATA(i,j); } + T operator()(INDEX i, INDEX j) const { MICK(i,j) return DATA(i,j); } + T operator[](INDEX i) const { VICK(i) return _data[i]; } + T& operator[](INDEX i) { VICK(i) return _data[i]; } + INDEX nRows() const { return _nRows; } + INDEX nCols() const { return _nCols; } + T * get_ptr() const { return _data; } + void write_restart(FILE *f) const; + void set_all_elements_to(const T &v); + DiagonalMatrix get_diag() const; + + DenseMatrix& operator=(const T &v); + DenseMatrix& operator=(const Matrix &c); + DenseMatrix& operator=(const DenseMatrix &c); + DenseMatrix& operator=(const SparseMatrix &c); + +private: + void _delete(); + void _create(INDEX rows, INDEX cols, bool zero=false); + void _copy(const Matrix &c); + + T *_data; + INDEX _nRows, _nCols; +}; + +//! Computes the cofactor matrix of A. +template +DenseMatrix adjugate(const Matrix &A, bool symmetric=false); + +//! Returns a the tensor product of two vectors +template +DenseMatrix tensor_product(const Vector &a, const Vector &b); + +//---------------------------------------------------------------------------- +// Returns an identity matrix, defaults to 3x3. +//---------------------------------------------------------------------------- +template +DenseMatrix eye(INDEX rows=3, INDEX cols=3) +{ + const double dij[] = {0.0, 1.0}; + DENS_MAT I(rows, cols, false); // do not need to pre-zero + for (INDEX j=0; j +void DenseMatrix::reset(INDEX rows, INDEX cols, bool zero) +{ + if (!this->is_size(rows, cols)) + { + _delete(); + _create(rows, cols); + } + if (zero) this->zero(); +} +//---------------------------------------------------------------------------- +// resizes the matrix and optionally copies over what still fits +//---------------------------------------------------------------------------- +template +void DenseMatrix::resize(INDEX rows, INDEX cols, bool copy) +{ + if (this->is_size(rows, cols)) return; // if is correct size, done + if (!copy) + { + _delete(); + _create(rows, cols); + return; + } + DenseMatrix temp(*this); + _delete(); + _create(rows, cols); + FORij MIDX(i,j) = temp.in_range(i,j) ? temp(i,j) : T(0); +} +//---------------------------------------------------------------------------- +// resizes the matrix and copies data +//---------------------------------------------------------------------------- +template +void DenseMatrix::copy(const T * ptr, INDEX rows, INDEX cols) +{ + resize(rows, cols, false); + memcpy(_data, ptr, this->size()*sizeof(T)); +} +//---------------------------------------------------------------------------- +// returns transpose(this) * B +//---------------------------------------------------------------------------- +template +DenseMatrix DenseMatrix::transMat(const DenseMatrix& B) const +{ + DenseMatrix C; + MultAB(*this, B, C, true); + return C; +} +//---------------------------------------------------------------------------- +// returns this_ij * B_ij +//---------------------------------------------------------------------------- +template +DenseMatrix DenseMatrix::mult_by_element(const DenseMatrix& B) const +{ + DenseMatrix C; + C.reset(_nRows,_nCols); + FORij C(i,j) = (*this)(i,j)*B(i,j); + return C; +} +//---------------------------------------------------------------------------- +// writes the matrix data to a file +//---------------------------------------------------------------------------- +template +void DenseMatrix::write_restart(FILE *f) const +{ + fwrite(&_nRows, sizeof(INDEX),1,f); + fwrite(&_nCols, sizeof(INDEX),1,f); + if (this->size()) fwrite(_data, sizeof(T), this->size(), f); +} +//---------------------------------------------------------------------------- +// sets all elements to a value (optimized) +//---------------------------------------------------------------------------- +template +inline void DenseMatrix::set_all_elements_to(const T &v) +{ + FORi _data[i] = v; +} +//----------------------------------------------------------------------------- +// Return a diagonal matrix containing the diagonal entries of this matrix +//----------------------------------------------------------------------------- +template +DiagonalMatrix DenseMatrix::get_diag() const +{ + DiagonalMatrix D(nRows(), true); // initialized to zero + INDEX i; + for (i=0; i +void DenseMatrix::_delete() +{ + _nRows = _nCols = 0; + if (_data) delete [] _data; +} +//---------------------------------------------------------------------------- +// allocates memory for an rows by cols DenseMatrix +//---------------------------------------------------------------------------- +template +void DenseMatrix::_create(INDEX rows, INDEX cols, bool zero) +{ + _nRows=rows; + _nCols=cols; + _data = (this->size() ? new T [_nCols*_nRows] : NULL); + if (zero) this->zero(); +} +//---------------------------------------------------------------------------- +// creates a deep memory copy from a general matrix +//---------------------------------------------------------------------------- +template +void DenseMatrix::_copy(const Matrix &c) +{ + if (!_data || this->size()!=c.size()) + { + _delete(); + _create(c.nRows(), c.nCols()); + } + else + { + _nRows = c.nRows(); + _nCols = c.nCols(); + } + memcpy(_data, c.get_ptr(), c.size()*sizeof(T)); +} +//---------------------------------------------------------------------------- +// sets all elements to a constant +//---------------------------------------------------------------------------- +template +DenseMatrix& DenseMatrix::operator=(const T &v) +{ + this->set_all_elements_to(v); + return *this; +} +//---------------------------------------------------------------------------- +// copys c with a deep copy +//---------------------------------------------------------------------------- +template +DenseMatrix& DenseMatrix::operator=(const Matrix &c) +{ + _copy(c); + return *this; +} +//---------------------------------------------------------------------------- +// copys c with a deep copy +//---------------------------------------------------------------------------- +template +DenseMatrix& DenseMatrix::operator=(const DenseMatrix &c) +{ + _copy(c); + return *this; +} +//----------------------------------------------------------------------------- +// copys c with a deep copy, including zeros +//----------------------------------------------------------------------------- +template +DenseMatrix& DenseMatrix::operator=(const SparseMatrix &c) +{ + _delete(); + _create(c.nRows(), c.nCols(), true); + SparseMatrix::compress(c); + for (INDEX i=0; i x = c.get_triplet(i); + cout << "x.i: "<< x.i << "\nx.j: "<< x.j << "\nv.j: "<< x.v << std::endl << std::endl; + MIDX(x.i, x.j) = x.v; + } + return *this; +} + +//* Returns the transpose of the cofactor matrix of A. +//* see http://en.wikipedia.org/wiki/Adjugate_matrix +//* symmetric flag only affects cases N>3 +template +DenseMatrix adjugate(const Matrix &A, bool symmetric) +{ + if (!A.is_square()) gerror("adjugate can only be computed for square matrices."); + DenseMatrix C(A.nRows(), A.nRows()); + switch (A.nRows()) { + case 1: + gerror("adjugate must be computed for matrixes of size greater than 1"); + case 2: + C(0,0) = A(1,1); C(0,1) =-A(0,1); + C(1,0) =-A(1,0); C(1,1) = A(0,0); + break; + case 3: // 3x3 case was tested vs matlab + C(0,0) = A(1,1)*A(2,2)-A(1,2)*A(2,1); + C(1,0) =-A(1,0)*A(2,2)+A(1,2)*A(2,0); // i+j is odd (reverse sign) + C(2,0) = A(1,0)*A(2,1)-A(1,1)*A(2,0); + C(0,1) =-A(0,1)*A(2,2)+A(0,2)*A(2,1); // i+j is odd + C(1,1) = A(0,0)*A(2,2)-A(0,2)*A(2,0); + C(2,1) =-A(0,0)*A(2,1)+A(0,1)*A(2,0); // i+j is odd + C(0,2) = A(0,1)*A(1,2)-A(0,2)*A(1,1); + C(1,2) =-A(0,0)*A(1,2)+A(0,2)*A(1,0); // i+j is odd + C(2,2) = A(0,0)*A(1,1)-A(0,1)*A(1,0); + break; + default: + // NOTE: - det() is only defined for type double + // this feature is neither tested nor optimal - use at your own risk!!! + DenseMatrix m(A.nRows()-1, A.nRows()-1); + double sign[] = {1.0, -1.0}; + for (unsigned j=0; j=i), mj+(mj>=j)); // skip row i and col j + } + } + if (!symmetric) C(j,i)=det(m)*sign[(i+j)&1]; + if (symmetric && i>=j) C(i,j)=C(j,i)=det(m)*sign[(i+j)&1]; + } + } + } + return C; +} + +// Returns a the tensor product of two vectors +template +DenseMatrix tensor_product(const Vector &a, const Vector &b) +{ + DenseMatrix ab(a.size(), b.size()); + for (unsigned j=0; j +DenseMatrix rand(unsigned rows, unsigned cols, int seed=1234) +{ + srand(seed); + const double rand_max_inv = 1.0 / double(RAND_MAX); + DenseMatrix R(rows, cols, false); + for (unsigned i=0; i +class DenseVector : public Vector +{ +public: + explicit DenseVector(INDEX n=0, bool z=1) { _create(n,z); } + DenseVector(const DenseVector &c) : _data(NULL) { _copy(c); } + DenseVector(const Vector &c) : _data(NULL) { _copy(c); } + virtual ~DenseVector() { _delete(); } + + //* resizes the Vector, ignores nCols, optionally copys what fits + void resize(INDEX rows, INDEX cols=1, bool copy=false); + //* resizes the Vector, ignores nCols, optionally zeros it out + void reset (INDEX rows, INDEX cols=1, bool zero=true); + //* resizes the Vector and copies data, ignores nCols + void copy(const T * ptr, INDEX rows, INDEX cols=1); + + // overloaded inline virtual functions + T operator[](INDEX i) const { VICK(i) return _data[i]; } + T& operator[](INDEX i) { VICK(i) return _data[i]; } + T operator()(INDEX i, INDEX j=0) const { VICK(i) return _data[i]; } + T& operator()(INDEX i, INDEX j=0) { VICK(i) return _data[i]; } + void set_all_elements_to(const T &v) { FORi _data[i] = v; } + INDEX nRows() const { return _size; } + + T* get_ptr() const { return _data; } + + DenseVector& operator=(const T &v); + DenseVector& operator=(const Vector &c); + DenseVector& operator=(const DenseVector &c); + + void write_restart(FILE *f) const; + +private: + void _delete(); + void _create(INDEX n, bool zero=0); + void _copy(const Vector &c); + + T *_data; + INDEX _size; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Template definitions /////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +// resizes the matrix and optionally copies over what still fits, ignores cols +//----------------------------------------------------------------------------- +template +void DenseVector::resize(INDEX rows, INDEX cols, bool copy) +{ + if (_size==rows) return; // if is correct size, done + if (!copy) + { + _delete(); + _create(rows); + return; + } + DenseVector temp(*this); + _delete(); + _create(rows); + FORi _data[i] = i +void DenseVector::reset(INDEX rows, INDEX cols, bool zero) +{ + if (_size!=rows) + { + _delete(); + _create(rows); + } + if (zero) this->zero(); +} +/////////////////////////////////////////////////////////////////////////////// +//* resizes the matrix and optionally zeros it out +template +void DenseVector::copy(const T * ptr, INDEX rows, INDEX cols) +{ + resize(rows, 1, false); + memcpy(_data, ptr, this->size()*sizeof(T)); +} +/////////////////////////////////////////////////////////////////////////////// +//* writes the matrix data to a file +template +void DenseVector::write_restart(FILE *f) const +{ + fwrite(&_size, sizeof(INDEX),1,f); + if(_size) fwrite(_data, sizeof(T), _size, f); +} +/////////////////////////////////////////////////////////////////////////////// +//* clears allocated memory +template +inline void DenseVector::_delete() +{ + if (_data) delete [] _data; + _size = 0; +} +/////////////////////////////////////////////////////////////////////////////// +//* allocates memory for an rows by cols DenseMatrix +template +inline void DenseVector::_create(INDEX n, bool zero) +{ + _size=n; + _data = _size ? new T [_size] : NULL ; + if (zero) this->zero(); +} +/////////////////////////////////////////////////////////////////////////////// +//* creates a deep memory copy from a general matrix +template +inline void DenseVector::_copy(const Vector &c) +{ + if (!_data || _size!=c.size()) + { + _delete(); + _create(c.size(), false); + } + else _size = c.size(); + memcpy(_data, c.get_ptr(), _size*sizeof(T)); +} +/////////////////////////////////////////////////////////////////////////////// +//* assigns v to all values in the vector +template +DenseVector& DenseVector::operator=(const T &v) +{ + FORi VIDX(i) = v; + return *this; +} +/////////////////////////////////////////////////////////////////////////////// +//* copys c with a deep copy +template +DenseVector& DenseVector::operator=(const Vector &c) +{ + _copy(c); + return *this; +} +/////////////////////////////////////////////////////////////////////////////// +//* copys c with a deep copy +template +DenseVector& DenseVector::operator=(const DenseVector &c) +{ + _copy(c); + return *this; +} + +#endif diff --git a/lib/atc/DiagonalMatrix.h b/lib/atc/DiagonalMatrix.h new file mode 100644 index 0000000000..fa8e9047cb --- /dev/null +++ b/lib/atc/DiagonalMatrix.h @@ -0,0 +1,462 @@ +#ifndef DIAGONALMATRIX_H +#define DIAGONALMATRIX_H + +#include "MatrixDef.h" + +template +class DiagonalMatrix : public Matrix +{ + public: + explicit DiagonalMatrix(INDEX nRows=0, bool zero=0); + DiagonalMatrix(const DiagonalMatrix& c); + DiagonalMatrix(const Vector& v); + virtual ~DiagonalMatrix(); + + //* resizes the matrix, ignores nCols, optionally zeros + void reset(INDEX rows, INDEX cols=0, bool zero=true); + //* resizes the matrix, ignores nCols, optionally copies what fits + void resize(INDEX rows, INDEX cols=0, bool copy=false); + //* resets based on full copy of vector v + void reset(const Vector& v); + //* resets based on full copy of a DiagonalMatrix + void reset(const DiagonalMatrix& v); + //* resizes the matrix, ignores nCols, optionally copies what fits + void copy(const T * ptr, INDEX rows, INDEX cols=0); + + //* resets based on a "shallow" copy of a vector + void shallowreset(const Vector &v); + //* resets based on a "shallow" copy of a DiagonalMatrix + void shallowreset(const DiagonalMatrix &c); + + T& operator()(INDEX i, INDEX j); + T operator()(INDEX i, INDEX j) const; + T& operator[](INDEX i); + T operator[](INDEX i) const; + INDEX nRows() const; + INDEX nCols() const; + T* get_ptr() const; + void write_restart(FILE *f) const; + + // Dump matrix contents to screen (not defined for all datatypes) + string tostring() const { return _data->tostring(); } + + using Matrix::matlab; + void matlab(ostream &o, const string &s="D") const; + + // overloaded operators + DiagonalMatrix& operator=(const T s); + DiagonalMatrix& operator=(const DiagonalMatrix &C); + //DiagonalMatrix& operator=(const Vector &C); + + INDEX size() const { return _data->size(); } + //* computes the inverse of this matrix + DiagonalMatrix& inv_this(); + //* returns a copy of the inverse of this matrix + DiagonalMatrix inv() const; + +protected: + void _set_equal(const Matrix &r); + DiagonalMatrix& operator=(const Vector &c) {} + DiagonalMatrix& operator=(const Matrix &c) {} + +private: + void _delete(); + Vector *_data; +}; + +//----------------------------------------------------------------------------- +// DiagonalMatrix-DiagonalMatrix multiplication +//----------------------------------------------------------------------------- +template +DiagonalMatrix operator*(const DiagonalMatrix& A, const DiagonalMatrix& B) +{ + SSCK(A, B, "DiagonalMatrix-DiagonalMatrix multiplication"); + DiagonalMatrix R(A); + for (INDEX i=0; i +DenseMatrix operator*(const DiagonalMatrix& A, const Matrix &B) +{ + GCK(A, B, A.nCols()!=B.nRows(), "DiagonalMatrix-Matrix multiplication"); + DenseMatrix R(B); // makes a copy of r to return + for (INDEX i=0; i +DenseMatrix operator*(const Matrix &B, const DiagonalMatrix& A) +{ + GCK(B, A, B.nCols()!=A.nRows(), "Matrix-DiagonalMatrix multiplication"); + DenseMatrix R(B); // makes a copy of r to return + for (INDEX j=0; j +DenseVector operator*(const DiagonalMatrix& A, const Vector &b) +{ + GCK(A, b, A.nCols()!=b.size(), "DiagonalMatrix-Vector multiplication"); + DenseVector r(b); // makes a copy of r to return + for (INDEX i=0; i +DenseVector operator*(const Vector &b, const DiagonalMatrix& A) +{ + GCK(b, A, b.size()!=A.nRows(), "Matrix-DiagonalMatrix multiplication"); + DenseVector r(b); // makes a copy of r to return + for (INDEX i=0; i +SparseMatrix operator*(const DiagonalMatrix &A, const SparseMatrix& B) +{ + GCK(A, B, A.nCols()!=B.nRows() ,"DiagonalMatrix-SparseMatrix multiplication"); + SparseMatrix R(B); + CloneVector d(A); + R.row_scale(d); + return R; +} +//----------------------------------------------------------------------------- +// DiagonalMatrix-scalar multiplication +//----------------------------------------------------------------------------- +template +DiagonalMatrix operator*(DiagonalMatrix &A, const T s) +{ + DiagonalMatrix R(A); + R *= s; + return R; +} +//----------------------------------------------------------------------------- +// Commute with DiagonalMatrix * double +//----------------------------------------------------------------------------- +template +DiagonalMatrix operator*(const T s, const DiagonalMatrix& A) +{ + DiagonalMatrix R(A); + R *= s; + return R; +} +//----------------------------------------------------------------------------- +// DiagonalMatrix addition +//----------------------------------------------------------------------------- +template +DiagonalMatrix operator+(const DiagonalMatrix &A, const DiagonalMatrix &B) +{ + DiagonalMatrix R(A); + R+=B; + return R; +} +//----------------------------------------------------------------------------- +// DiagonalMatrix subtraction +//----------------------------------------------------------------------------- +template +DiagonalMatrix operator-(const DiagonalMatrix &A, const DiagonalMatrix &B) +{ + DiagonalMatrix R(A); + return R-=B; +} +//----------------------------------------------------------------------------- +// template member definitions +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Default constructor - optionally zeros the matrix +//----------------------------------------------------------------------------- +template +DiagonalMatrix::DiagonalMatrix(INDEX rows, bool zero) + : _data(NULL) +{ + reset(rows, zero); +} +//----------------------------------------------------------------------------- +// copy constructor - makes a full copy +//----------------------------------------------------------------------------- +template +DiagonalMatrix::DiagonalMatrix(const DiagonalMatrix& c) + : _data(NULL) +{ + reset(c); +} +//----------------------------------------------------------------------------- +// copy constructor from vector +//----------------------------------------------------------------------------- +template +DiagonalMatrix::DiagonalMatrix(const Vector& v) + : _data(NULL) +{ + reset(v); +} +//----------------------------------------------------------------------------- +// destructor +//----------------------------------------------------------------------------- +template +DiagonalMatrix::~DiagonalMatrix() +{ + _delete(); +} +//----------------------------------------------------------------------------- +// deletes the data stored by this matrix +//----------------------------------------------------------------------------- +template +void DiagonalMatrix::_delete() +{ + if (_data) delete _data; +} +//----------------------------------------------------------------------------- +// resizes the matrix, ignores nCols, optionally zeros +//----------------------------------------------------------------------------- +template +void DiagonalMatrix::reset(INDEX rows, INDEX cols, bool zero) +{ + _delete(); + _data = new DenseVector(rows, zero); +} +//----------------------------------------------------------------------------- +// resizes the matrix, ignores nCols, optionally copies what fits +//----------------------------------------------------------------------------- +template +void DiagonalMatrix::resize(INDEX rows, INDEX cols, bool copy) +{ + _data->resize(rows, copy); +} +//----------------------------------------------------------------------------- +// changes the diagonal of the matrix to a vector v (makes a copy) +//----------------------------------------------------------------------------- +template +void DiagonalMatrix::reset(const Vector& v) +{ + if (&v == _data) return; // check for self-reset + _delete(); + _data = new DenseVector(v); +} +//----------------------------------------------------------------------------- +// copys from another DiagonalMatrix +//----------------------------------------------------------------------------- +template +void DiagonalMatrix::reset(const DiagonalMatrix& c) +{ + reset(*(c._data)); +} +//----------------------------------------------------------------------------- +// resizes the matrix and copies data +//----------------------------------------------------------------------------- +template +void DiagonalMatrix::copy(const T * ptr, INDEX rows, INDEX cols) +{ + if (_data) _data->reset(rows, false); + else _data = new DenseVector(rows, false); + memcpy(_data, ptr, this->size()*sizeof(T)); +} +//----------------------------------------------------------------------------- +// shallow reset from another DiagonalMatrix +//----------------------------------------------------------------------------- +template +void DiagonalMatrix::shallowreset(const DiagonalMatrix &c) +{ + _delete(); + _data = new CloneVector(*(c._data)); +} +//----------------------------------------------------------------------------- +// shallow reset from Vector +//----------------------------------------------------------------------------- +template +void DiagonalMatrix::shallowreset(const Vector &v) +{ + _delete(); + _data = new CloneVector(v); +} +//----------------------------------------------------------------------------- +// reference indexing operator - must throw an error if i!=j +//----------------------------------------------------------------------------- +template +T& DiagonalMatrix::operator()(INDEX i, INDEX j) +{ + GCK(*this,*this,i!=j,"DiagonalMatrix: tried to index off diagonal"); + return VIDX(i); +} +//----------------------------------------------------------------------------- +// value indexing operator - returns 0 if i!=j +//----------------------------------------------------------------------------- +template +T DiagonalMatrix::operator()(INDEX i, INDEX j) const +{ + return (i==j) ? (*_data)(i) : (T)0; +} +//----------------------------------------------------------------------------- +// flat reference indexing operator +//----------------------------------------------------------------------------- +template +T& DiagonalMatrix::operator[](INDEX i) +{ + return (*_data)(i); +} +//----------------------------------------------------------------------------- +// flat value indexing operator +//----------------------------------------------------------------------------- +template +T DiagonalMatrix::operator[](INDEX i) const +{ + return (*_data)(i); +} +//----------------------------------------------------------------------------- +// returns the number of rows +//----------------------------------------------------------------------------- +template +INDEX DiagonalMatrix::nRows() const +{ + return _data->size(); +} +//----------------------------------------------------------------------------- +// returns the number of columns (same as nCols()) +//----------------------------------------------------------------------------- +template +INDEX DiagonalMatrix::nCols() const +{ + return _data->size(); +} +//----------------------------------------------------------------------------- +// returns a pointer to the diagonal values, dangerous! +//----------------------------------------------------------------------------- +template +T* DiagonalMatrix::get_ptr() const +{ + return _data->get_ptr(); +} +//----------------------------------------------------------------------------- +// writes the diagonal to a binary data restart file +//----------------------------------------------------------------------------- +template +void DiagonalMatrix::write_restart(FILE *f) const +{ + _data->write_restart(f); +} +//----------------------------------------------------------------------------- +// sets the diagonal to a constant +//----------------------------------------------------------------------------- +template +DiagonalMatrix& DiagonalMatrix::operator=(const T v) +{ + set_all_elements_to(v); + return *this; +} +//----------------------------------------------------------------------------- +// assignment operator with another diagonal matrix +//----------------------------------------------------------------------------- +template +DiagonalMatrix& DiagonalMatrix::operator=(const DiagonalMatrix& C) +{ + reset(C); + return *this; +} +//----------------------------------------------------------------------------- +// writes a matlab command to duplicate this sparse matrix +//----------------------------------------------------------------------------- +template +void DiagonalMatrix::matlab(ostream &o, const string &s) const +{ + _data->matlab(o, s); + o << s <<"=diag("< +DiagonalMatrix& DiagonalMatrix::inv_this() +{ + for(INDEX i=0; iminabs() / _data->maxabs(); + if (min_max > 1e-14) return *this; + cout << "DiagonalMatrix::inv_this(): Warning: Matrix is badly scaled."; + cout << " RCOND = "< +DiagonalMatrix DiagonalMatrix::inv() const +{ + DiagonalMatrix invA(*this); // Make copy of A to invert + + for(INDEX i=0; iminabs() / _data->maxabs(); + if (min_max > 1e-14) return invA; + cout << "DiagonalMatrix::inv(): Warning: Matrix is badly scaled."; + cout << " RCOND = "< inv(const DiagonalMatrix& A) +{ + return A.inv(); +} +//----------------------------------------------------------------------------- +// general diagonalmatrix assigment +//----------------------------------------------------------------------------- +template +void DiagonalMatrix::_set_equal(const Matrix &r) +{ + this->resize(r.nRows(), r.nCols()); + const Matrix *pr = &r; + + const SparseMatrix *ps = dynamic_cast*> (pr); + const DiagonalMatrix *pd = dynamic_cast*> (pr); + const Vector *pv = dynamic_cast*> (pr); + + if (ps) this->reset(ps->get_diag()); + else if (pd) this->reset(*pd); + else if (pv) this->reset(*pv); + else + { + cout <<"Error in general sparse matrix assignment\n"; + } +} +//----------------------------------------------------------------------------- +// casts a generic matrix pointer into a DiagonalMatrix pointer - null if fail +//----------------------------------------------------------------------------- +template +const DiagonalMatrix *diag_cast(const Matrix *m) +{ + return dynamic_cast*>(m); +} + +#endif diff --git a/lib/atc/ElasticTimeIntegrator.cpp b/lib/atc/ElasticTimeIntegrator.cpp new file mode 100644 index 0000000000..57a3120513 --- /dev/null +++ b/lib/atc/ElasticTimeIntegrator.cpp @@ -0,0 +1,251 @@ +// ATC transfer headers +#include "ElasticTimeIntegrator.h" +#include "ATC_Transfer.h" +#include "TimeFilter.h" +#include "ATC_Error.h" + +namespace ATC { + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ElasticTimeIntegrator + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab data from ATC + //-------------------------------------------------------- + ElasticTimeIntegrator::ElasticTimeIntegrator(ATC_Transfer * atcTransfer, + TimeIntegrationType timeIntegrationType) : + TimeIntegrator(atcTransfer, timeIntegrationType) + { + // do nothing + } + + //-------------------------------------------------------- + // modify + // parses inputs and modifies state of the filter + //-------------------------------------------------------- + bool ElasticTimeIntegrator::modify(int narg, char **arg) + { + // currently no parsing for elastic time integration + return false; + } + + //-------------------------------------------------------- + // initialize + // sets up all the necessary data + //-------------------------------------------------------- + void ElasticTimeIntegrator::initialize() + { + if (needReset_ || timeFilterManager_->need_reset()) { + if (timeIntegrationMethod_) + delete timeIntegrationMethod_; + + if (timeFilterManager_->need_reset()) { + if (timeFilter_) + delete timeFilter_; + timeFilter_ = timeFilterManager_->construct(TimeFilterManager::IMPLICIT); + } + + if (timeFilterManager_->filter_dynamics()) { + timeIntegrationMethod_ = new ElasticTimeIntegratorVerletFiltered(this); + } + else { + timeIntegrationMethod_ = new ElasticTimeIntegratorVerlet(this); + } + } + TimeIntegrator::initialize(); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ElasticIntegrationMethod + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab data from ATC + //-------------------------------------------------------- + ElasticIntegrationMethod::ElasticIntegrationMethod(ElasticTimeIntegrator * elasticTimeIntegrator) : + TimeIntegrationMethod(elasticTimeIntegrator), + timeFilter_(timeIntegrator_->get_time_filter()), + displacement_(atcTransfer_->get_field(DISPLACEMENT)), + velocity_(atcTransfer_->get_field(VELOCITY)), + acceleration_(atcTransfer_->get_field_roc(VELOCITY)), + nodalAtomicDisplacement_(atcTransfer_->get_atomic_field(DISPLACEMENT)), + nodalAtomicVelocity_(atcTransfer_->get_atomic_field(VELOCITY)), + velocityRhs_(atcTransfer_->get_field_rhs(VELOCITY)), + nodalAtomicForce_(atcTransfer_->get_fe_atomic_field_roc(VELOCITY)), + forceFilteringData_(atcTransfer_->get_aux_storage(VELOCITY)) + { + // do nothing + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ElasticTimeIntegratorVerlet + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ElasticTimeIntegratorVerlet::ElasticTimeIntegratorVerlet(ElasticTimeIntegrator * elasticTimeIntegrator) : + ElasticIntegrationMethod(elasticTimeIntegrator) + { + TimeFilterManager * timeFilterManager = (timeIntegrator_->get_atc_transfer())->get_time_filter_manager(); + if (timeFilterManager->need_reset()) { + timeFilter_->initialize(nodalAtomicForce_); + } + // reset filtering data, if needed + if (!(timeFilterManager->end_equilibrate())) { + forceFilteringData_.reset(atcTransfer_->get_nNodes(),atcTransfer_->get_nsd()); + } + } + + //-------------------------------------------------------- + // mid_initial_integrate1 + // time integration at the mid-point of Verlet step 1 + //-------------------------------------------------------- + void ElasticTimeIntegratorVerlet::mid_initial_integrate1(double dt) + { + explicit_1(velocity_,acceleration_,.5*dt); + } + + //-------------------------------------------------------- + // post_initial_integrate1 + // time integration after Verlet step 1 + //-------------------------------------------------------- + void ElasticTimeIntegratorVerlet::post_initial_integrate1(double dt) + { + // NOTE could use explicit_2 with velocityRhs_ as the 2nd derivative + // for improved accuracy, but this would be inconsistent with + // the atomic integration scheme + explicit_1(displacement_,velocity_,dt); + } + + //-------------------------------------------------------- + // pre_final_integrate1 + // first time integration computations + // before Verlet step 2 + //-------------------------------------------------------- + void ElasticTimeIntegratorVerlet::pre_final_integrate1(double dt) + { + // Compute MD contribution to FEM equation + DENS_MAT atomicForces; + atcTransfer_->compute_atomic_force(atomicForces,atcTransfer_->get_f()); + atcTransfer_->restrict_volumetric_quantity(atomicForces,nodalAtomicForce_); + timeFilter_->apply_post_step2(forceFilteringData_,nodalAtomicForce_,dt); + } + + //-------------------------------------------------------- + // post_final_integrate1 + // second time integration computations + // after Verlet step 2 + //-------------------------------------------------------- + void ElasticTimeIntegratorVerlet::post_final_integrate1(double dt) + { + atcTransfer_->apply_inverse_mass_matrix(velocityRhs_, + acceleration_, + VELOCITY); + explicit_1(velocity_,acceleration_,.5*dt); + } + + //-------------------------------------------------------- + // post_process + // post processing of variables before output + //-------------------------------------------------------- + void ElasticTimeIntegratorVerlet::post_process(double dt) + { + DENS_MAT atomicQuantity; + atcTransfer_->compute_atomic_momentum(atomicQuantity,atcTransfer_->get_v()); + atcTransfer_->project_md_volumetric_quantity(atomicQuantity,nodalAtomicVelocity_,VELOCITY); + + atcTransfer_->compute_atomic_centerOfMass_displacement(atomicQuantity,atcTransfer_->get_x()); + atcTransfer_->project_md_volumetric_quantity(atomicQuantity,nodalAtomicDisplacement_,VELOCITY); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ElasticTimeIntegratorVerletFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ElasticTimeIntegratorVerletFiltered::ElasticTimeIntegratorVerletFiltered(ElasticTimeIntegrator * elasticTimeIntegrator) : + ElasticTimeIntegratorVerlet(elasticTimeIntegrator), + nodalAtomicAcceleration_(atcTransfer_->get_atomic_field_roc(VELOCITY)) + { + // swap filtered and unfiltered forces + if ((timeIntegrator_->get_time_filter_manager())->end_equilibrate()) { + DENS_MAT temp(nodalAtomicForce_); + nodalAtomicForce_ = forceFilteringData_; + forceFilteringData_ = temp; + } + } + + //-------------------------------------------------------- + // mid_initial_integrate1 + // time integration at the mid-point of Verlet step 1 + //-------------------------------------------------------- + void ElasticTimeIntegratorVerletFiltered::mid_initial_integrate1(double dt) + { + explicit_1(velocity_,acceleration_,.5*dt); + explicit_1(nodalAtomicVelocity_,nodalAtomicAcceleration_,.5*dt); + } + + //-------------------------------------------------------- + // post_initial_integrate1 + // time integration after Verlet step 1 + //-------------------------------------------------------- + void ElasticTimeIntegratorVerletFiltered::post_initial_integrate1(double dt) + { + // NOTE could use explicit_2 with velocityRhs_ as the 2nd derivative + // for improved accuracy, but this would be inconsistent with + // the atomic integration scheme + explicit_1(displacement_,velocity_,dt); + explicit_1(nodalAtomicDisplacement_,nodalAtomicVelocity_,dt); + } + + //-------------------------------------------------------- + // pre_final_integrate1 + // first time integration computations + // before Verlet step 2 + //-------------------------------------------------------- + void ElasticTimeIntegratorVerletFiltered::pre_final_integrate1(double dt) + { + // Compute MD contribution to FEM equation + DENS_MAT atomicForces; + atcTransfer_->compute_atomic_force(atomicForces,atcTransfer_->get_f()); + + // apply time filtering to instantaneous atomic force for FE equation + // NOTE would an explicit-implicit time filter be more accurate? + atcTransfer_->restrict_volumetric_quantity(atomicForces,forceFilteringData_); + timeFilter_->apply_post_step2(nodalAtomicForce_,forceFilteringData_,dt); + } + + //-------------------------------------------------------- + // post_final_integrate1 + // second time integration computations + // after Verlet step 2 + //-------------------------------------------------------- + void ElasticTimeIntegratorVerletFiltered::post_final_integrate1(double dt) + { + DENS_MAT velocityRoc(velocityRhs_.nRows(),velocityRhs_.nCols()); + atcTransfer_->apply_inverse_mass_matrix(velocityRhs_, + acceleration_, + VELOCITY); + explicit_1(velocity_,acceleration_,.5*dt); + + atcTransfer_->apply_inverse_md_mass_matrix(nodalAtomicForce_, + nodalAtomicAcceleration_, + VELOCITY); + explicit_1(nodalAtomicVelocity_,nodalAtomicAcceleration_,.5*dt); + } + +}; diff --git a/lib/atc/ElasticTimeIntegrator.h b/lib/atc/ElasticTimeIntegrator.h new file mode 100644 index 0000000000..b7cc00d3ef --- /dev/null +++ b/lib/atc/ElasticTimeIntegrator.h @@ -0,0 +1,209 @@ +/** ElasticTimeIntegrator : a class to implement various elasticity integrators for FE quantities */ + +#ifndef ELASTIC_TIME_INTEGRATOR_H +#define ELASTIC_TIME_INTEGRATOR_H + +// ATC_Transfer headers +#include "TimeIntegrator.h" + +using namespace std; +namespace ATC { + + // forward declarations + class ElasticIntegrationMethod; + + /** + * @class ElasticTimeIntegrator + * @brief Base class fo various time integrators for elasticity FE quantities + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ElasticTimeIntegrator + // Base class for elastic integrators which handles + // parsing and stores basic data structures + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ElasticTimeIntegrator : public TimeIntegrator { + + public: + + // constructor + ElasticTimeIntegrator(ATC_Transfer * atcTransfer, + TimeIntegrationType timeIntegrationType); + + // destructor + virtual ~ElasticTimeIntegrator(){}; + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + private: + + // DO NOT define this + ElasticTimeIntegrator(); + + }; + + /** + * @class ElasticIntegrationMethod + * @brief Base class fo various time integration methods for elasticity FE quantities + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ElasticIntegrationMethod + // Base class for elastic integration methods which + // update the FE quantities in time + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ElasticIntegrationMethod : public TimeIntegrationMethod { + + public: + + // constructor + ElasticIntegrationMethod(ElasticTimeIntegrator * elasticTimeIntegrator); + + // destructor + virtual ~ElasticIntegrationMethod(){}; + + protected: + + /** time filtering application object */ + TimeFilter * timeFilter_; + + /** finite element displacement field */ + DENS_MAT & displacement_; + /** finite element velocity field */ + DENS_MAT & velocity_; + /** finite element acceleration field */ + DENS_MAT & acceleration_; + + /** atomic nodal displacement field */ + DENS_MAT & nodalAtomicDisplacement_; + /** atomic nodal velocity field */ + DENS_MAT & nodalAtomicVelocity_; + + /** right-hand side of velocity equation */ + DENS_MAT & velocityRhs_; + + /** force at nodes from atomic quantities */ + DENS_MAT & nodalAtomicForce_; + + /** filtered power for computation during equilibration */ + DENS_MAT & forceFilteringData_; + + private: + + // DO NOT define this + ElasticIntegrationMethod(); + + }; + + /** + * @class ElasticTimeIntegratorVerlet + * @brief Verlet integration for FE elastic quantities + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ElasticTimeIntegratorVerlet + // Uses the second order Verlet integration to update + // the finite element velocity and displacement + // fields, i.e. the same integration used for the + // atomic velocities and positions. + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ElasticTimeIntegratorVerlet : public ElasticIntegrationMethod { + + public: + + // constructor + ElasticTimeIntegratorVerlet(ElasticTimeIntegrator * elasticTimeIntegrator); + + // destructor + virtual ~ElasticTimeIntegratorVerlet(){}; + + // time step methods, corresponding to ATC_Transfer + + /** first part of mid_initial_integrate */ + virtual void mid_initial_integrate1(double dt); + + /** first part of post_initial_integrate */ + virtual void post_initial_integrate1(double dt); + + /** first part of pre_final_integrate */ + virtual void pre_final_integrate1(double dt); + + /** first part of post_final_integrate */ + virtual void post_final_integrate1(double dt); + + /** post processing step before output */ + virtual void post_process(double dt); + + private: + + // DO NOT define this + ElasticTimeIntegratorVerlet(); + + }; + + /** + * @class ElasticTimeIntegratorVerlet + * @brief Verlet integration for FE elastic quantities with time filtering + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ElasticTimeIntegratorVerletFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ElasticTimeIntegratorVerletFiltered : public ElasticTimeIntegratorVerlet { + + public: + + // constructor + ElasticTimeIntegratorVerletFiltered(ElasticTimeIntegrator * elasticTimeIntegrator); + + // destructor + virtual ~ElasticTimeIntegratorVerletFiltered(){}; + + // time step methods, corresponding to ATC_Transfer + + /** first part of mid_initial_integrate */ + virtual void mid_initial_integrate1(double dt); + + /** first part of post_initial_integrate */ + virtual void post_initial_integrate1(double dt); + + /** first part of pre_final_integrate */ + virtual void pre_final_integrate1(double dt); + + /** first part of post_final_integrate */ + virtual void post_final_integrate1(double dt); + + /** post processing step before output */ + virtual void post_process(double dt){}; + + protected: + + /** atomic nodal acceleration field */ + DENS_MAT & nodalAtomicAcceleration_; + + private: + + // DO NOT define this + ElasticTimeIntegratorVerletFiltered(); + + }; + +}; + +#endif diff --git a/lib/atc/ElectronFlux.cpp b/lib/atc/ElectronFlux.cpp new file mode 100644 index 0000000000..22d22e4235 --- /dev/null +++ b/lib/atc/ElectronFlux.cpp @@ -0,0 +1,66 @@ +#include "ElectronFlux.h" +#include "StringManip.h" +#include "ATC_Error.h" + +#include +#include + +namespace ATC { +using namespace ATC_STRING; + +ElectronFluxLinear::ElectronFluxLinear( + fstream &fileId, map & parameters) + : ElectronFlux(), + electronMobility_(0), + electronDiffusivity_(0) +{ + if (!fileId.is_open()) throw ATC_Error(0,"cannot open material file"); + vector line; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") return; + double value = str2dbl(line[1]); + if (line[0] == "mobility") { + electronMobility_ = value; + parameters["electron_mobility"] = electronMobility_; + } + else if (line[0] == "diffusivity") { + electronDiffusivity_ = value; + parameters["electron_diffusivity"] = electronDiffusivity_; + } + else { + throw ATC_Error(0, "unrecognized material function "+line[0]); + } + } +} + +ElectronFluxThermopower::ElectronFluxThermopower( + fstream &fileId, map & parameters) + : ElectronFlux(), + electronMobility_(0), + seebeckCoef_(0) +{ + if (!fileId.is_open()) throw ATC_Error(0,"cannot open material file"); + vector line; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") return; + double value = str2dbl(line[1]); + if (line[0] == "mobility") { + electronMobility_ = value; + parameters["electron_mobility"] = electronMobility_; + } + else if (line[0] == "seebeck") { + seebeckCoef_ = value; + parameters["seebeck_coefficient"] = seebeckCoef_; + } + else { + throw ATC_Error(0, "unrecognized material function "+line[0]); + } + } +} + +} + diff --git a/lib/atc/ElectronFlux.h b/lib/atc/ElectronFlux.h new file mode 100644 index 0000000000..0a06c96c5e --- /dev/null +++ b/lib/atc/ElectronFlux.h @@ -0,0 +1,96 @@ +#ifndef ELECTRON_FLUX_H +#define ELECTRON_FLUX_H + +#include +#include + +using std::map; +using std::string; + +#include "MatrixLibrary.h" +#include "ATC_TypeDefs.h" + +namespace ATC { + class ElectronFlux + { + public: + ElectronFlux() {}; + virtual ~ElectronFlux() {}; + /** computes flux */ + virtual void electron_flux(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &flux)=0; + }; + //----------------------------------------------------------------------- + class ElectronFluxLinear : public ElectronFlux + { + public: + ElectronFluxLinear(fstream &matfile, map & parameters); + virtual ~ElectronFluxLinear() {}; + virtual void electron_flux(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &flux) + { + // J_n = - \mu n grad \phi - D grad n + const FIELD & n = (fields.find(ELECTRON_DENSITY))->second; + const GRAD_FIELD & dn =(gradFields.find(ELECTRON_DENSITY))->second; + const GRAD_FIELD & dphi =(gradFields.find(ELECTRIC_POTENTIAL))->second; + // NOTE use electron velocity instead + flux.push_back(-electronMobility_*dphi[0]); + flux.push_back(-electronMobility_*dphi[1]); + flux.push_back(-electronMobility_*dphi[2]); + flux[0] *= n; // scale by n to get : -n \mu grad(\phi) + flux[1] *= n; + flux[2] *= n; + flux[0] += -electronDiffusivity_* dn[0]; + flux[1] += -electronDiffusivity_* dn[1]; + flux[2] += -electronDiffusivity_* dn[2]; + }; + protected: + double electronMobility_, electronDiffusivity_; + }; + //----------------------------------------------------------------------- + class ElectronFluxThermopower : public ElectronFlux + { + public: + ElectronFluxThermopower(fstream &matfile,map & parameters); + virtual ~ElectronFluxThermopower() {}; + virtual void electron_flux(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &flux) + { + static const double kB_ = 8.617343e-5;// [eV/K] + // J_n = - \mu n grad \phi - \mu kB/e T_e grad n + // - \mu S n grad T_e - \mu kB/e n grad T_e + const FIELD & n = (fields.find(ELECTRON_DENSITY))->second; + const GRAD_FIELD & dn =(gradFields.find(ELECTRON_DENSITY))->second; + const GRAD_FIELD & dphi =(gradFields.find(ELECTRIC_POTENTIAL))->second; + const GRAD_FIELD & dT =(gradFields.find(ELECTRON_TEMPERATURE))->second; + flux.push_back(-electronMobility_*dphi[0]); + flux.push_back(-electronMobility_*dphi[1]); + flux.push_back(-electronMobility_*dphi[2]); + double coef = -electronMobility_*(seebeckCoef_ + kB_); + flux[0] += coef* dT[0]; + flux[1] += coef* dT[1]; + flux[2] += coef* dT[2]; + flux[0] *= n; // scale by n + flux[1] *= n; + flux[2] *= n; + GRAD_FIELD tmp = dn; + const FIELD & Te = (fields.find(ELECTRON_TEMPERATURE))->second; + tmp[0] *= Te; + tmp[1] *= Te; + tmp[2] *= Te; + coef = -electronMobility_*kB_; // kB: eV/K/e -> V/K + flux[0] += tmp[0]; + flux[1] += tmp[1]; + flux[2] += tmp[2]; + }; + protected: + double electronMobility_, seebeckCoef_; + }; +} + +#endif + + diff --git a/lib/atc/ElectronHeatCapacity.cpp b/lib/atc/ElectronHeatCapacity.cpp new file mode 100644 index 0000000000..b075f3af7f --- /dev/null +++ b/lib/atc/ElectronHeatCapacity.cpp @@ -0,0 +1,54 @@ +#include "ElectronHeatCapacity.h" +#include "StringManip.h" +#include "ATC_Error.h" + +#include +#include + +namespace ATC { +using namespace ATC_STRING; + +ElectronHeatCapacityConstant::ElectronHeatCapacityConstant( + fstream &fileId, map & parameters) + : ElectronHeatCapacity(), + electronHeatCapacity_(0) +{ + if (!fileId.is_open()) throw ATC_Error(0,"cannot open material file"); + vector line; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") return; + else if (line[0] == "capacity") { + electronHeatCapacity_ = str2dbl(line[1]); + parameters["electron_heat_capacity"] = electronHeatCapacity_; + } + else { + throw ATC_Error(0, "unrecognized material function:" + line[0]); + } + } +} + +ElectronHeatCapacityLinear::ElectronHeatCapacityLinear( + fstream &fileId, map & parameters) + : ElectronHeatCapacity(), + electronHeatCapacity_(0) +{ + if (!fileId.is_open()) throw ATC_Error(0,"cannot open material file"); + vector line; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") return; + else if (line[0] == "capacity") { + electronHeatCapacity_ = str2dbl(line[1]); + parameters["electron_heat_capacity"] = electronHeatCapacity_; + } + else { + throw ATC_Error(0, "unrecognized material function: " + line[0]); + } + } +} + +} + diff --git a/lib/atc/ElectronHeatCapacity.h b/lib/atc/ElectronHeatCapacity.h new file mode 100644 index 0000000000..6d4cb6061b --- /dev/null +++ b/lib/atc/ElectronHeatCapacity.h @@ -0,0 +1,76 @@ +#ifndef ELECTRON_HEAT_CAPACITY_H +#define ELECTRON_HEAT_CAPACITY_H + +#include +#include + +using std::map; +using std::string; + +#include "MatrixLibrary.h" +#include "ATC_TypeDefs.h" + +namespace ATC { + class ElectronHeatCapacity + { + public: + ElectronHeatCapacity() {}; + virtual ~ElectronHeatCapacity() {}; + /** computes heat capacity */ + virtual void electron_heat_capacity(const FIELDS &fields, + FIELD &capacity)=0; + /** computes thermal energy */ + virtual void electron_thermal_energy(const FIELDS &fields, + FIELD &energy)=0; + }; + //------------------------------------------------------------------- + class ElectronHeatCapacityConstant : public ElectronHeatCapacity + { + public: + ElectronHeatCapacityConstant(fstream &matfile, + map & parameters); + virtual ~ElectronHeatCapacityConstant() {}; + virtual void electron_heat_capacity(const FIELDS &fields, + FIELD &capacity) + { + const FIELD & T = (fields.find(ELECTRON_TEMPERATURE))->second; + capacity.reset(T.nRows(),T.nCols()); + capacity = electronHeatCapacity_; + }; + virtual void electron_thermal_energy(const FIELDS &fields, + FIELD &energy) + { + const FIELD & T = (fields.find(ELECTRON_TEMPERATURE))->second; + energy = electronHeatCapacity_ * T; + }; + protected: + double electronHeatCapacity_; + }; + //------------------------------------------------------------------- + class ElectronHeatCapacityLinear : public ElectronHeatCapacity + { + public: + ElectronHeatCapacityLinear(fstream &matfile, + map & parameters); + virtual ~ElectronHeatCapacityLinear() {}; + virtual void electron_heat_capacity(const FIELDS &fields, + FIELD &capacity) + { + const FIELD & T = (fields.find(ELECTRON_TEMPERATURE))->second; + capacity = electronHeatCapacity_*T; + }; + virtual void electron_thermal_energy(const FIELDS &fields, + FIELD &energy) + { + const FIELD & T = (fields.find(ELECTRON_TEMPERATURE))->second; + energy = 0.5 * electronHeatCapacity_ * T; + energy *= T; + }; + protected: + double electronHeatCapacity_; + }; +} + +#endif + + diff --git a/lib/atc/ElectronHeatFlux.cpp b/lib/atc/ElectronHeatFlux.cpp new file mode 100644 index 0000000000..57ca7256bb --- /dev/null +++ b/lib/atc/ElectronHeatFlux.cpp @@ -0,0 +1,80 @@ +#include "ElectronHeatFlux.h" +#include "StringManip.h" +#include "ATC_Error.h" + +#include +#include + +namespace ATC { +using namespace ATC_STRING; + +ElectronHeatFluxLinear::ElectronHeatFluxLinear( + fstream &fileId, map & parameters) + : ElectronHeatFlux(), + conductivity_(0) +{ + if (!fileId.is_open()) throw ATC_Error(0,"cannot open material file"); + vector line; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") return; + else if (line[0] == "conductivity") { + conductivity_ = str2dbl(line[1]); + parameters["electron_thermal_conductivity"] = conductivity_; + } + else { + throw ATC_Error(0, "unrecognized material function "+line[0]); + } + } +} + +ElectronHeatFluxPowerLaw::ElectronHeatFluxPowerLaw( + fstream &fileId, map & parameters) + : ElectronHeatFlux(), + conductivity_(0) +{ + if (!fileId.is_open()) throw ATC_Error(0,"cannot open material file"); + vector line; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") return; + else if (line[0] == "conductivity") { + conductivity_ = str2dbl(line[1]); + parameters["electron_thermal_conductivity"] = conductivity_; + } + else { + throw ATC_Error(0, "unrecognized material function "+line[0]); + } + } +} + +ElectronHeatFluxThermopower::ElectronHeatFluxThermopower( + fstream &fileId, map & parameters, + /*const*/ ElectronFlux * electronFlux) + : ElectronHeatFlux(), + electronFlux_(electronFlux), + conductivity_(0), + seebeckCoef_(0) +{ + if (!fileId.is_open()) throw ATC_Error(0,"cannot open material file"); + vector line; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") return; + double value = str2dbl(line[1]); + if (line[0] == "conductivity") { + conductivity_ = value; + parameters["electron_thermal_conductivity"] = conductivity_; + } + else { + throw ATC_Error(0, "unrecognized material function "+line[0]); + } + seebeckCoef_ = parameters["seebeck_coefficient"]; + } +} + +} + diff --git a/lib/atc/ElectronHeatFlux.h b/lib/atc/ElectronHeatFlux.h new file mode 100644 index 0000000000..59b3263c68 --- /dev/null +++ b/lib/atc/ElectronHeatFlux.h @@ -0,0 +1,112 @@ +#ifndef ELECTRON_HEAT_FLUX_H +#define ELECTRON_HEAT_FLUX_H + +#include +#include + +using std::map; +using std::string; + +#include "MatrixLibrary.h" +#include "ATC_TypeDefs.h" +#include "ElectronFlux.h" + +namespace ATC { + class ElectronHeatFlux + { + public: + ElectronHeatFlux() {}; + virtual ~ElectronHeatFlux() {}; + /** computes heat flux */ + virtual void electron_heat_flux(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &flux)=0; + }; + //----------------------------------------------------------------------- + class ElectronHeatFluxLinear : public ElectronHeatFlux + { + public: + ElectronHeatFluxLinear(fstream &matfile,map & parameters); + virtual ~ElectronHeatFluxLinear() {}; + virtual void electron_heat_flux(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &flux) + { + // flux = -ke dTe/dx + const GRAD_FIELD& dT = (gradFields.find(ELECTRON_TEMPERATURE))->second; + flux.push_back(-conductivity_ * dT[0]); + flux.push_back(-conductivity_ * dT[1]); + flux.push_back(-conductivity_ * dT[2]); + }; + protected: + double conductivity_; + }; + //----------------------------------------------------------------------- + class ElectronHeatFluxPowerLaw : public ElectronHeatFlux + { + public: + ElectronHeatFluxPowerLaw(fstream &matfile,map ¶meters); + virtual ~ElectronHeatFluxPowerLaw() {}; + virtual void electron_heat_flux(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &flux) + { + // flux = -ke * ( Te / T ) dT; + const GRAD_FIELD dT = (gradFields.find(ELECTRON_TEMPERATURE))->second; + const FIELD & T = (fields.find(TEMPERATURE))->second; + const FIELD & Te = (fields.find(ELECTRON_TEMPERATURE))->second; + flux.push_back(dT[0]); + flux.push_back(dT[1]); + flux.push_back(dT[2]); + FIELD k_e; + k_e = (-conductivity_* Te) / T; + flux[0] *= k_e; + flux[1] *= k_e; + flux[2] *= k_e; + }; + protected: + double conductivity_; + }; + //----------------------------------------------------------------------- + class ElectronHeatFluxThermopower : public ElectronHeatFlux + { + public: + ElectronHeatFluxThermopower(fstream &matfile, + map & parameters, + /*const*/ ElectronFlux * electronFlux = NULL); + virtual ~ElectronHeatFluxThermopower() {}; + virtual void electron_heat_flux(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &flux) + { + // flux = -ke * ( Te / T ) dT + pi J_e; + const GRAD_FIELD dT = (gradFields.find(ELECTRON_TEMPERATURE))->second; + const FIELD & T = (fields.find(TEMPERATURE))->second; + const FIELD & Te = (fields.find(ELECTRON_TEMPERATURE))->second; + flux.push_back(dT[0]); + flux.push_back(dT[1]); + flux.push_back(dT[2]); + FIELD k_e; + k_e = (-conductivity_* Te) / T; + flux[0] *= k_e; + flux[1] *= k_e; + flux[2] *= k_e; + GRAD_FIELD tmp; + electronFlux_->electron_flux(fields, gradFields, tmp); + tmp[0] *= Te; + tmp[1] *= Te; + tmp[2] *= Te; + flux[0] += seebeckCoef_*tmp[0]; + flux[1] += seebeckCoef_*tmp[1]; + flux[2] += seebeckCoef_*tmp[2]; + + }; + protected: + double conductivity_,seebeckCoef_; + ElectronFlux * electronFlux_; + }; +} + +#endif + + diff --git a/lib/atc/ElectronPhononExchange.cpp b/lib/atc/ElectronPhononExchange.cpp new file mode 100644 index 0000000000..d6b62b993a --- /dev/null +++ b/lib/atc/ElectronPhononExchange.cpp @@ -0,0 +1,58 @@ +#include "ElectronPhononExchange.h" +#include "StringManip.h" +#include "ATC_Error.h" + +#include +#include + +namespace ATC { +using namespace ATC_STRING; + +ElectronPhononExchangeLinear::ElectronPhononExchangeLinear( + fstream &fileId, map & parameters) + : ElectronPhononExchange(), + exchangeCoef_(0) +{ + if (!fileId.is_open()) throw ATC_Error(0,"cannot open material file"); + vector line; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") return; + else if (line[0] == "coefficient") { + exchangeCoef_ = str2dbl(line[1]); + parameters["electron_phonon_exchange_coefficient"] = exchangeCoef_; + } + else { + throw ATC_Error(0, "unrecognized material function "+line[0]); + } + } +} + +ElectronPhononExchangePowerLaw::ElectronPhononExchangePowerLaw( + fstream &fileId, map & parameters) + : ElectronPhononExchange(), + exchangeCoef_(0), + exponent_(1) +{ + if (!fileId.is_open()) throw ATC_Error(0,"cannot open material file"); + vector line; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") return; + else if (line[0] == "coefficient") { + exchangeCoef_ = str2dbl(line[1]); + parameters["electron_phonon_exchange_coefficient"] = exchangeCoef_; + } + else if (line[0] == "exponent") { + exponent_ = str2int(line[1]); + } + else { + throw ATC_Error(0, "unrecognized material function "+line[0]); + } + } +} + +} + diff --git a/lib/atc/ElectronPhononExchange.h b/lib/atc/ElectronPhononExchange.h new file mode 100644 index 0000000000..306a2fd145 --- /dev/null +++ b/lib/atc/ElectronPhononExchange.h @@ -0,0 +1,69 @@ +#ifndef ELECTRON_PHONON_EXCHANGE_H +#define ELECTRON_PHONON_EXCHANGE_H + +#include +#include + +using std::map; +using std::string; + +#include "MatrixLibrary.h" +#include "ATC_TypeDefs.h" + +namespace ATC { + class ElectronPhononExchange + { + public: + ElectronPhononExchange() {}; + virtual ~ElectronPhononExchange() {}; + /** computes heat capacity */ + virtual void electron_phonon_exchange(const FIELDS &fields, + DENS_MAT &flux)=0; + }; + //------------------------------------------------------------------- + class ElectronPhononExchangeLinear : public ElectronPhononExchange + { + public: + ElectronPhononExchangeLinear(fstream &matfile, + map & parameters); + virtual ~ElectronPhononExchangeLinear() {}; + virtual void electron_phonon_exchange(const FIELDS &fields, + DENS_MAT &flux) + { + // flux = g * ( T- Te) + const FIELD & T = (fields.find(TEMPERATURE))->second; + const FIELD & Te = (fields.find(ELECTRON_TEMPERATURE))->second; + flux = Te; + flux -= T; + flux *= exchangeCoef_; + }; + protected: + double exchangeCoef_; + }; + //------------------------------------------------------------------- + class ElectronPhononExchangePowerLaw : public ElectronPhononExchange + { + public: + ElectronPhononExchangePowerLaw(fstream &matfile, + map & parameters); + virtual ~ElectronPhononExchangePowerLaw() {}; + virtual void electron_phonon_exchange(const FIELDS &fields, + DENS_MAT &flux) + { + // flux = g c_e T_e (T_e - T_p)^5 / T_e + const FIELD & T = (fields.find(TEMPERATURE))->second; + const FIELD & Te = (fields.find(ELECTRON_TEMPERATURE))->second; + flux = Te; + flux -= T; + flux = flux.pow(exponent_); + flux *= exchangeCoef_; + }; + protected: + double exchangeCoef_; + int exponent_; + }; +} + +#endif + + diff --git a/lib/atc/ExtrinsicModel.cpp b/lib/atc/ExtrinsicModel.cpp new file mode 100644 index 0000000000..472d015fa4 --- /dev/null +++ b/lib/atc/ExtrinsicModel.cpp @@ -0,0 +1,290 @@ +// ATC_Transfer Headers +#include "ExtrinsicModel.h" +#include "ExtrinsicModelTwoTemperature.h" +#include "ATC_Error.h" +#include "TimeIntegrator.h" +#include "ATC_Transfer.h" +#include "LammpsInterface.h" +#include "PrescribedDataManager.h" +#include "PhysicsModel.h" + +namespace ATC { + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ExtrinsicModelManager + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ExtrinsicModelManager::ExtrinsicModelManager(ATC_Transfer * atcTransfer) : + atcTransfer_(atcTransfer) + { + // do nothing + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + ExtrinsicModelManager::~ExtrinsicModelManager() + { + vector::iterator imodel; + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + delete *(imodel); + } + + //-------------------------------------------------------- + // modify + //-------------------------------------------------------- + bool ExtrinsicModelManager::modify(int narg, char **arg) + { + FieldName thisField; + int thisIndex; + int argIndx = 0; + + bool foundMatch = false; + + // loop over models with command + vector::iterator imodel; + for(imodel=extrinsicModels_.begin(); + imodel!=extrinsicModels_.end(); imodel++) { + foundMatch = (*imodel)->modify(narg,arg); + if (foundMatch) break; + } + + return foundMatch; + } + + //-------------------------------------------------------- + // create_model + //-------------------------------------------------------- + void ExtrinsicModelManager::create_model(ExtrinsicModelType modelType, + string matFileName) + { + string typeName; + bool validModel = model_to_string(modelType,typeName); + if (!validModel) { + throw ATC_Error(0,"Could not create extrinsic model"); + return; + } + ExtrinsicModel * myModel; + if (modelType==TWO_TEMPERATURE) { + cout << "ATC: creating two_temperature extrinsic model \n"; + myModel = new ExtrinsicModelTwoTemperature + (this,modelType,matFileName); + } + extrinsicModels_.push_back(myModel); + + // add new fields to fields data + map fieldSizes; + myModel->get_num_fields(fieldSizes); + atcTransfer_->add_fields(fieldSizes); + } + + //-------------------------------------------------------- + // initialize + //-------------------------------------------------------- + void ExtrinsicModelManager::initialize() + { + vector::iterator imodel; + for(imodel=extrinsicModels_.begin(); + imodel!=extrinsicModels_.end(); imodel++) { + // initialize models + (*imodel)->initialize(); + } + } + + //-------------------------------------------------------- + // size_vector + //-------------------------------------------------------- + int ExtrinsicModelManager::size_vector(int intrinsicSize) + { + int extrinsicSize = 0; + vector::iterator imodel; + for(imodel=extrinsicModels_.begin(); + imodel!=extrinsicModels_.end(); imodel++) { + // query all models for LAMMPS display + int currentSize = intrinsicSize + extrinsicSize; + extrinsicSize += (*imodel)->size_vector(currentSize); + } + + return extrinsicSize; + } + + //-------------------------------------------------------- + // compute_vector + //-------------------------------------------------------- + double ExtrinsicModelManager::compute_vector(int n) + { + double value = 0.; + vector::iterator imodel; + for(imodel=extrinsicModels_.begin(); + imodel!=extrinsicModels_.end(); imodel++) { + // query all models for LAMMPS display + if ((*imodel)->compute_vector(n,value)) + break; + } + return value; + } + + //-------------------------------------------------------- + // finish + //-------------------------------------------------------- + void ExtrinsicModelManager::finish() + { + // do nothing + } + + //-------------------------------------------------------- + // pre_init_integrate + //-------------------------------------------------------- + void ExtrinsicModelManager::pre_init_integrate(ExtrinsicModelType modelType) + { + vector::iterator imodel; + if (modelType == NUM_MODELS) {// execute all the models + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + (*imodel)->pre_init_integrate(); + } + else { // execute only requested type of model + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + if ((*imodel)->get_model_type() == modelType) + (*imodel)->pre_init_integrate(); + } + } + + //-------------------------------------------------------- + // mid_init_integrate + //-------------------------------------------------------- + void ExtrinsicModelManager::mid_init_integrate(ExtrinsicModelType modelType) + { + vector::iterator imodel; + if (modelType == NUM_MODELS) {// execute all the models + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + (*imodel)->mid_init_integrate(); + } + else { // execute only requested type of model + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + if ((*imodel)->get_model_type() == modelType) + (*imodel)->mid_init_integrate(); + } + } + + //-------------------------------------------------------- + // post_init_integrate + //-------------------------------------------------------- + void ExtrinsicModelManager::post_init_integrate(ExtrinsicModelType modelType) + { + vector::iterator imodel; + if (modelType == NUM_MODELS) {// execute all the models + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + (*imodel)->post_init_integrate(); + } + else { // execute only requested type of model + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + if ((*imodel)->get_model_type() == modelType) + (*imodel)->post_init_integrate(); + } + } + + //-------------------------------------------------------- + // pre_final_integrate + //-------------------------------------------------------- + void ExtrinsicModelManager::pre_final_integrate(ExtrinsicModelType modelType) + { + vector::iterator imodel; + if (modelType == NUM_MODELS) {// execute all the models + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + (*imodel)->pre_final_integrate(); + } + else { // execute only requested type of model + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + if ((*imodel)->get_model_type() == modelType) + (*imodel)->pre_final_integrate(); + } + } + + //-------------------------------------------------------- + // post_final_integrate + //-------------------------------------------------------- + void ExtrinsicModelManager::post_final_integrate(ExtrinsicModelType modelType) + { + vector::iterator imodel; + if (modelType == NUM_MODELS) {// execute all the models + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + (*imodel)->post_final_integrate(); + } + else { // execute only requested type of model + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + if ((*imodel)->get_model_type() == modelType) + (*imodel)->post_final_integrate(); + } + } + + //-------------------------------------------------------- + // set_sources + //-------------------------------------------------------- + void ExtrinsicModelManager::set_sources(FIELDS & fields, FIELDS & sources, ExtrinsicModelType modelType) + { + vector::iterator imodel; + if (modelType == NUM_MODELS) {// execute all the models + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + (*imodel)->set_sources(fields,sources); + } + else { // execute only requested type of model + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + if ((*imodel)->get_model_type() == modelType) + (*imodel)->set_sources(fields,sources); + } + } + + //-------------------------------------------------------- + // output + //-------------------------------------------------------- + void ExtrinsicModelManager::output(double dt,OUTPUT_LIST & outputData) + { + vector::iterator imodel; + for(imodel=extrinsicModels_.begin(); imodel!=extrinsicModels_.end(); imodel++) + (*imodel)->output(dt,outputData); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ExtrinsicModel + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ExtrinsicModel::ExtrinsicModel(ExtrinsicModelManager * modelManager, + ExtrinsicModelType modelType, + string matFileName) : + modelManager_(modelManager), + modelType_(modelType), + atc_(modelManager->get_atc_transfer()), + physicsModel_(NULL) + { + rhsMaskIntrinsic_.reset(NUM_FIELDS,NUM_FLUX); + rhsMaskIntrinsic_ = false; + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + ExtrinsicModel::~ExtrinsicModel() + { + if (physicsModel_) delete physicsModel_; + } + + //-------------------------------------------------------- + // get_num_fields + // - sets dict of fields + //-------------------------------------------------------- + void ExtrinsicModel::get_num_fields(map & fieldSizes) + { + physicsModel_->get_num_fields(fieldSizes,atc_->fieldMask_); // NOTE clunky + } + +}; diff --git a/lib/atc/ExtrinsicModel.h b/lib/atc/ExtrinsicModel.h new file mode 100644 index 0000000000..82cdd316d6 --- /dev/null +++ b/lib/atc/ExtrinsicModel.h @@ -0,0 +1,228 @@ +#ifndef EXTRINSIC_MODEL +#define EXTRINSIC_MODEL + +// ATC_Transfer headers +#include "MatrixLibrary.h" +#include "ATC_TypeDefs.h" + +using namespace std; +namespace ATC { + + // forward declarations + class ATC_Transfer; + class ExtrinsicModel; + class PhysicsModel; + + /** enumeration for the model types avaiable */ + enum ExtrinsicModelType { + NO_MODEL=0, + TWO_TEMPERATURE, + NUM_MODELS + }; + + /** + * @class ExtrinsicModelManager + * @brief Handles parsing and parameter storage extrinsic models + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ExtrinsicModelManager + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ExtrinsicModelManager { + + public: + + // constructor + ExtrinsicModelManager(ATC_Transfer * atcTransfer); + + // destructor + ~ExtrinsicModelManager(); + + /** parser/modifier */ + bool modify(int narg, char **arg); + + /** create_model */ + void create_model(ExtrinsicModelType modelType, string matFileName); + + /** pre time integration */ + void initialize(); + + /** set up LAMMPS display variables */ + int size_vector(int intrinsicSize); + + /** get LAMMPS display variables */ + double compute_vector(int n); + + /** post integration run */ + // is this called at end of run or simulation + void finish(); + + // calls during LAMMPS Velocity-Verlet integration + /** Predictor phase, executed before Verlet */ + void pre_init_integrate(ExtrinsicModelType modelType = NUM_MODELS); + /** Predictor phase, executed between velocity and position Verlet */ + void mid_init_integrate(ExtrinsicModelType modelType = NUM_MODELS); + /** Predictor phase, executed after Verlet */ + void post_init_integrate(ExtrinsicModelType modelType = NUM_MODELS); + /** Corrector phase, executed before Verlet */ + void pre_final_integrate(ExtrinsicModelType modelType = NUM_MODELS); + /** Corrector phase, executed after Verlet*/ + void post_final_integrate(ExtrinsicModelType modelType = NUM_MODELS); + + /** get source terms for AtC equations */ + void set_sources(FIELDS & fields, FIELDS & sources, + ExtrinsicModelType modelType = NUM_MODELS); + + /** return output data to main AtC */ + void output(double dt,OUTPUT_LIST & outputData); + + /** model name enum to string */ + static bool model_to_string(const ExtrinsicModelType index, string & name) + { + switch (index) { + case NO_MODEL: + name = "no_model"; + break; + case TWO_TEMPERATURE: + name = "two_temperature"; + break; + default: + return false; + break; + } + return true; + }; + + /** string to model enum */ + static bool string_to_model(const string & name, ExtrinsicModelType & index) + { + if (name=="no_model") + index = NO_MODEL; + else if (name=="two_temperature") + index = TWO_TEMPERATURE; + else + return false; + + return true; + }; + + /** access to ATC transfer object */ + ATC_Transfer * get_atc_transfer() {return atcTransfer_;}; + + protected: + + + /** associated ATC_Transfer object */ + ATC_Transfer * atcTransfer_; + + /** equation hanlder */ + vector extrinsicModels_; + + private: + ExtrinsicModelManager(); // DO NOT define this, only use constructor above + + }; + + /** + * @class ExtrinsicModel + * @brief base class for functionality of all extrinsic models + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ExtrinsicModel + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ExtrinsicModel { + + public: + + // constructor + ExtrinsicModel(ExtrinsicModelManager * modelManager, + ExtrinsicModelType modelType, + string matFileName); + + // destructor + virtual ~ExtrinsicModel(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg) {return false;}; + + /** pre time integration */ + virtual void initialize(){}; + + /** set up LAMMPS display variables */ + virtual int size_vector(int externalSize) {return 0;}; + + /** get LAMMPS display variables */ + virtual bool compute_vector(int n, double & value) {return false;}; + + /** post integration run */ + // is this called at end of run or simulation + virtual void finish(){}; + + /** Predictor phase, executed before Verlet */ + virtual void pre_init_integrate(){}; + + /** Predictor phase, executed between velocity and position Verlet */ + virtual void mid_init_integrate(){}; + + /** Predictor phase, executed after Verlet */ + virtual void post_init_integrate(){}; + + /** Corrector phase, executed before Verlet */ + virtual void pre_final_integrate(){}; + + /** Corrector phase, Verlet second step for velocity */ + virtual void final_integrate(){}; + + /** Corrector phase, executed after Verlet*/ + virtual void post_final_integrate(){}; + + /** Set sources to AtC equation */ + virtual void set_sources(FIELDS & fields, FIELDS & sources){}; + + /** Add model-specific output data */ + virtual void output(double dt, OUTPUT_LIST & outputData){}; + + /** get the fields and their sizes */ + void get_num_fields(map & fieldSizes); + + /** return the type of model being used */ + ExtrinsicModelType get_model_type() {return modelType_;}; + + protected: + + ExtrinsicModel(){}; + + /** ATC transfer object */ + ATC_Transfer * atc_; + + /** model manager object */ + ExtrinsicModelManager * modelManager_; + + /** tag for model type */ + ExtrinsicModelType modelType_; + + /** list of model fields in this model */ + std::map fieldSizes_; + + /** definition for physics used in this model */ + PhysicsModel * physicsModel_; + + /** rhs */ + FIELDS rhs_; + + /** rhs mask for coupling with MD */ + Array2D rhsMaskIntrinsic_; + + GRAD_FIELDS fluxes_; + + }; + +}; +#endif diff --git a/lib/atc/ExtrinsicModelTwoTemperature.cpp b/lib/atc/ExtrinsicModelTwoTemperature.cpp new file mode 100644 index 0000000000..5a3b6dc2a8 --- /dev/null +++ b/lib/atc/ExtrinsicModelTwoTemperature.cpp @@ -0,0 +1,265 @@ +// ATC_Transfer Headers +#include "ExtrinsicModelTwoTemperature.h" +#include "ATC_Error.h" +#include "FieldEulerIntegrator.h" +#include "ATC_Transfer.h" +#include "LammpsInterface.h" +#include "PrescribedDataManager.h" +#include "PhysicsModelTwoTemperature.h" +#include "ImplicitSolveOperator.h" + +namespace ATC { + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ExtrinsicModelTwoTemperature + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ExtrinsicModelTwoTemperature::ExtrinsicModelTwoTemperature + (ExtrinsicModelManager * modelManager, + ExtrinsicModelType modelType, + string matFileName) : + ExtrinsicModel(modelManager,modelType,matFileName), + nsubcycle_(1), + electronTimeIntegration_(TimeIntegrator::IMPLICIT), + temperatureIntegrator_(NULL), + exchangeFlag_(true), + baseSize_(0) + { + physicsModel_ = new PhysicsModelTwoTemperature(matFileName, + modelManager->get_atc_transfer()); + + // set up correct masks for coupling + rhsMaskIntrinsic_.reset(NUM_FIELDS,NUM_FLUX); + rhsMaskIntrinsic_ = false; + rhsMaskIntrinsic_(TEMPERATURE,SOURCE) = true; + atc_->fieldMask_(TEMPERATURE,EXTRINSIC_SOURCE) = true; + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + ExtrinsicModelTwoTemperature::~ExtrinsicModelTwoTemperature() + { + } + + //-------------------------------------------------------- + // modify + //-------------------------------------------------------- + bool ExtrinsicModelTwoTemperature::modify(int narg, char **arg) + { + bool match = false; + int argIndx = 0; + + // energy exchange switch + /*! \page man_extrinsic_exchange fix_modify AtC extrinsic exchange + \section syntax + fix_modify AtC extrinsic exchange + + \section examples + fix_modify AtC extrinsic exchange on \n + + \section description + Switches energy exchange between the MD system and electron system on and off + + \section restrictions + Only valid for use with two_temperature type of AtC fix. + + \section related + see \ref man_fix_atc + + \section default + on + */ + if (strcmp(arg[argIndx],"exchange")==0) { + argIndx++; + if (strcmp(arg[argIndx],"off")==0) { + exchangeFlag_ = false; + rhsMaskIntrinsic_(TEMPERATURE,SOURCE) = false; + atc_->fieldMask_(ELECTRON_TEMPERATURE,SOURCE) = false; + atc_->fieldMask_(TEMPERATURE,EXTRINSIC_SOURCE) = false; + } + else { + exchangeFlag_ = true; + rhsMaskIntrinsic_(TEMPERATURE,SOURCE) = true; + atc_->fieldMask_(ELECTRON_TEMPERATURE,SOURCE) = true; + atc_->fieldMask_(TEMPERATURE,EXTRINSIC_SOURCE) = true; + } + match = true; + } // end "exchange" + + // electron integration type + /*! \page man_electron_integration fix_modify AtC extrinsic electron_integration + \section syntax + fix_modify AtC extrinsic electron_integration \n + - integration_type (string) = explicit | implicit | steady \n + - num_subcycle_steps (int), optional = number of subcycle steps for the electron time integration + + \section examples + fix_modify AtC extrinsic electron_integration implicit \n + fix_modify AtC extrinsic electron_integration explicit 100 \n + + \section description + Switches between integration scheme for the electron temperature. The number of subcyling steps used to integrate the electron temperature 1 LAMMPS timestep can be manually adjusted to capture fast electron dynamics. + + \section restrictions + For use only with two_temperature type of AtC fix ( see \ref man_fix_atc ) \n + \section default + implicit\n + subcycle_steps = 1 + */ + else if (strcmp(arg[argIndx],"electron_integration")==0) { + argIndx++; + nsubcycle_ = 1; + if (strcmp(arg[argIndx],"explicit")==0) { + electronTimeIntegration_ = TimeIntegrator::EXPLICIT; + match = true; + } + else if (strcmp(arg[argIndx],"implicit")==0) { + electronTimeIntegration_ = TimeIntegrator::IMPLICIT; + match = true; + } + else if (strcmp(arg[argIndx],"steady")==0) { + electronTimeIntegration_ = TimeIntegrator::STEADY; + match = true; + } + if (narg > ++argIndx) nsubcycle_ = atoi(arg[argIndx]); + } // end "electron_integration" + + if (!match) { + match = ExtrinsicModel::modify(narg, arg); + } + + return match; + } + + //-------------------------------------------------------- + // initialize + //-------------------------------------------------------- + void ExtrinsicModelTwoTemperature::initialize() + { + int nNodes = atc_->get_fe_engine()->get_nNodes(); + rhs_[TEMPERATURE].reset(nNodes,1); + rhs_[ELECTRON_TEMPERATURE].reset(nNodes,1); + + // set up electron temperature integrator + Array2D rhsMask(NUM_FIELDS,NUM_FLUX); + rhsMask = false; + for (int i = 0; i < NUM_FLUX; i++) { + rhsMask(ELECTRON_TEMPERATURE,i) = atc_->fieldMask_(ELECTRON_TEMPERATURE,i); + } + if (temperatureIntegrator_) delete temperatureIntegrator_; + if (electronTimeIntegration_ == TimeIntegrator::STEADY) { + throw ATC_Error(0,"not implemented"); + } + else if (electronTimeIntegration_ == TimeIntegrator::IMPLICIT) { + double alpha = 1; // backwards Euler + temperatureIntegrator_ = new FieldImplicitEulerIntegrator( + ELECTRON_TEMPERATURE, physicsModel_, atc_->get_fe_engine(), atc_, + rhsMask, alpha); + } + else { + temperatureIntegrator_ = new FieldExplicitEulerIntegrator( + ELECTRON_TEMPERATURE, physicsModel_, atc_->get_fe_engine(), atc_, + rhsMask); + } + + // set up mass matrix + Array massMask(1); + massMask = ELECTRON_TEMPERATURE; + (atc_->feEngine_)->compute_lumped_mass_matrix(massMask,atc_->fields_,physicsModel_,atc_->elementToMaterialMap_,atc_->massMats_); + atc_->massMatInv_[ELECTRON_TEMPERATURE] = inv(atc_->massMats_[ELECTRON_TEMPERATURE]); + } + + //-------------------------------------------------------- + // pre initial integration + //-------------------------------------------------------- + void ExtrinsicModelTwoTemperature::pre_init_integrate() + { + double dt = atc_->lammpsInterface_->dt(); + double time = atc_->simTime_; + + // integrate fast electron variable/s + // note: atc calls set_sources in pre_final_integrate + atc_->set_fixed_nodes(); + double idt = dt/nsubcycle_; + for (int i = 0; i < nsubcycle_ ; ++i) { + temperatureIntegrator_->update(idt,time,atc_->fields_,rhs_); + } + + } + + //-------------------------------------------------------- + // set coupling source terms + //-------------------------------------------------------- + void ExtrinsicModelTwoTemperature::set_sources(FIELDS & fields, FIELDS & sources) + { + // compute source term with appropriate masking and physics model + atc_->evaluate_rhs_integral(rhsMaskIntrinsic_, fields, + sources, + atc_->FULL_DOMAIN, physicsModel_); + } + + //-------------------------------------------------------- + // output + //-------------------------------------------------------- + void ExtrinsicModelTwoTemperature::output(double dt, OUTPUT_LIST & outputData) + { + // nodal data + outputData["dot_electron_temperature"] + = & rhs_[ELECTRON_TEMPERATURE]; + // global data + if (atc_->lammpsInterface_->comm_rank() == 0) { + double T_mean = atc_->fields_[ELECTRON_TEMPERATURE].col_sum(0)/atc_->nNodes_; + atc_->feEngine_->add_global("electron_temperature_mean", T_mean); + double T_stddev = atc_->fields_[ELECTRON_TEMPERATURE].col_stdev(0); + atc_->feEngine_->add_global("electron_temperature_std_dev", T_stddev); + } + } + + //-------------------------------------------------------- + // size_vector + //-------------------------------------------------------- + int ExtrinsicModelTwoTemperature::size_vector(int intrinsicSize) + { + baseSize_ = intrinsicSize; + return 2; + } + + //-------------------------------------------------------- + // compute_vector + //-------------------------------------------------------- + bool ExtrinsicModelTwoTemperature::compute_vector(int n, double & value) + { + // output[1] = total electron energy + // output[2] = average electron temperature + + if (n == baseSize_) { + Array mask(1); + FIELDS energy; + mask(0) = ELECTRON_TEMPERATURE; + (atc_->feEngine_)->compute_energy(mask, + atc_->fields_, + physicsModel_, + atc_->elementToMaterialMap_, + energy); + // convert to lammps energy units + double mvv2e = (atc_->lammpsInterface_)->mvv2e(); + double electronEnergy = mvv2e * energy[ELECTRON_TEMPERATURE].col_sum(); + value = electronEnergy; + return true; + } + else if (n == baseSize_+1) { + double electronTemperature = (atc_->fields_[ELECTRON_TEMPERATURE]).col_sum()/(atc_->nNodes_); + value = electronTemperature; + return true; + } + + return false; + } + +}; diff --git a/lib/atc/ExtrinsicModelTwoTemperature.h b/lib/atc/ExtrinsicModelTwoTemperature.h new file mode 100644 index 0000000000..d22abffb7e --- /dev/null +++ b/lib/atc/ExtrinsicModelTwoTemperature.h @@ -0,0 +1,82 @@ +#ifndef EXTRINSIC_MODEL_TWO_TEMPERATURE +#define EXTRINSIC_MODEL_TWO_TEMPERATURE + +// ATC_Transfer headers +#include "MatrixLibrary.h" +#include "ATC_TypeDefs.h" +#include "ExtrinsicModel.h" +#include "FieldEulerIntegrator.h" + +using namespace std; +namespace ATC { + + // forward declarations + class ATC_Transfer; + class PrescribedDataManager; + class ExtrinsicModel; + class PhysicsModel; + + /** + * @class ExtrinsicModelTwoTemperature + * @brief add electron temperature physics to phonon physics + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ExtrinsicModelTwoTemperature + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ExtrinsicModelTwoTemperature : public ExtrinsicModel { + + public: + + // constructor + ExtrinsicModelTwoTemperature(ExtrinsicModelManager * modelManager, + ExtrinsicModelType modelType, + string matFileName); + + // destructor + virtual ~ExtrinsicModelTwoTemperature(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + /** set up LAMMPS display variables */ + virtual int size_vector(int externalSize); + + /** get LAMMPS display variables */ + virtual bool compute_vector(int n, double & value); + + /** Predictor phase, executed before Verlet */ + virtual void pre_init_integrate(); + + /** Set sources to AtC equation */ + virtual void set_sources(FIELDS & fields, FIELDS & sources); + + /** Add model-specific output data */ + virtual void output(double dt, OUTPUT_LIST & outputData); + + protected: + /** electron time integration flag */ + TimeIntegrator::TimeIntegrationType electronTimeIntegration_; + + /** electron time integration flag */ + FieldEulerIntegrator * temperatureIntegrator_; + + /** number of electron time integration subscycles */ + int nsubcycle_; + + /** flag for turning off exchange during equilibration */ + bool exchangeFlag_; + + /** offset/size for LAMMPS display output */ + int baseSize_; + + }; + +}; +#endif diff --git a/lib/atc/FE_Element.cpp b/lib/atc/FE_Element.cpp new file mode 100644 index 0000000000..ce743e41f3 --- /dev/null +++ b/lib/atc/FE_Element.cpp @@ -0,0 +1,515 @@ +// ATC header files +#include "ATC_Error.h" +#include "FE_Element.h" +#include "FE_Mesh.h" + +// Other headers +#include "math.h" + +namespace ATC { + + // ------------------------------------------------------------- + // ------------------------------------------------------------- + // class FE_Element + // ------------------------------------------------------------- + // ------------------------------------------------------------- + + FE_Element::FE_Element(FE_Mesh * feMesh, + int nSD, + int numEltNodes, + int numIPs, + int numFaces, + int numFaceNodes, + int numFaceIPs) + : feMesh_(feMesh), + nSD_(nSD), + numEltNodes_(numEltNodes), + numIPs_(numIPs), + numFaces_(numFaces), + numFaceNodes_(numFaceNodes), + numFaceIPs_(numFaceIPs), + localCoords_(nSD,numEltNodes), + ipCoords_(nSD,numIPs), + N_(numIPs,numEltNodes), + ipWeights_(numIPs), + localFaceConn_(numFaces,numFaceNodes), + ipFace2DCoords_(nSD-1,numFaceIPs), + ipFaceCoords_(numFaces), + // localFaceCoords_(nSD-1,numFaceNodes), + NFace_(numFaces), + ipFaceWeights_(numFaceIPs) + { + for (int f = 0; f < numFaces; f++) { + ipFaceCoords_[f].reset(nSD,numFaceIPs); + NFace_[f].reset(numFaceIPs,numEltNodes); + } + } + + FE_Element::~FE_Element() + { + // Nothing else to do + } + + + // ------------------------------------------------------------- + // ------------------------------------------------------------- + // class FE_ElementHex + // ------------------------------------------------------------- + // ------------------------------------------------------------- + + FE_ElementHex::FE_ElementHex(FE_Mesh * feMesh) + : FE_Element(feMesh, 3, 8, 8, 6, 4, 4) + { + + // Matrix of local nodal coordinates + localCoords_(0,0) = -1; localCoords_(1,0) = -1; localCoords_(2,0) = -1; + localCoords_(0,1) = +1; localCoords_(1,1) = -1; localCoords_(2,1) = -1; + localCoords_(0,2) = +1; localCoords_(1,2) = +1; localCoords_(2,2) = -1; + localCoords_(0,3) = -1; localCoords_(1,3) = +1; localCoords_(2,3) = -1; + localCoords_(0,4) = -1; localCoords_(1,4) = -1; localCoords_(2,4) = +1; + localCoords_(0,5) = +1; localCoords_(1,5) = -1; localCoords_(2,5) = +1; + localCoords_(0,6) = +1; localCoords_(1,6) = +1; localCoords_(2,6) = +1; + localCoords_(0,7) = -1; localCoords_(1,7) = +1; localCoords_(2,7) = +1; + + + // 3 --- 2 + // /| /| + // / 0 --/ 1 y + // 7 --- 6 / | + // | |/ | + // 4 --- 5 ---> x + // / + // / + // z + + // Matrix of local face connectivity + // -x + localFaceConn_(0,0) = 0; + localFaceConn_(0,1) = 4; + localFaceConn_(0,2) = 7; + localFaceConn_(0,3) = 3; + + // +x + localFaceConn_(1,0) = 1; + localFaceConn_(1,1) = 2; + localFaceConn_(1,2) = 6; + localFaceConn_(1,3) = 5; + + // -y + localFaceConn_(2,0) = 0; + localFaceConn_(2,1) = 1; + localFaceConn_(2,2) = 5; + localFaceConn_(2,3) = 4; + + // +y + localFaceConn_(3,0) = 2; + localFaceConn_(3,1) = 3; + localFaceConn_(3,2) = 7; + localFaceConn_(3,3) = 6; + + // -z + localFaceConn_(4,0) = 0; + localFaceConn_(4,1) = 3; + localFaceConn_(4,2) = 2; + localFaceConn_(4,3) = 1; + + // +z + localFaceConn_(5,0) = 4; + localFaceConn_(5,1) = 5; + localFaceConn_(5,2) = 6; + localFaceConn_(5,3) = 7; + + /* + localFaceCoords_(0,0) = -1; localFaceCoords_(1,0) = -1; + localFaceCoords_(0,1) = +1; localFaceCoords_(1,1) = -1; + localFaceCoords_(0,2) = +1; localFaceCoords_(1,2) = +1; + localFaceCoords_(0,3) = -1; localFaceCoords_(1,3) = +1; + */ + + set_quadrature(GAUSSIAN_QUADRATURE); + + } + + //----------------------------------------------------------------- + void FE_ElementHex::set_quadrature(int quadrature) + { + double a = 1./sqrt(3.); + if (quadrature == NODAL_QUADRATURE) { + a = 1.0; + } + // Matrix of integration point locations (Gaussian) & follows local conn + ipCoords_(0,0) = -a; ipCoords_(1,0) = -a; ipCoords_(2,0) = -a; + ipCoords_(0,1) = +a; ipCoords_(1,1) = -a; ipCoords_(2,1) = -a; + ipCoords_(0,2) = +a; ipCoords_(1,2) = +a; ipCoords_(2,2) = -a; + ipCoords_(0,3) = -a; ipCoords_(1,3) = +a; ipCoords_(2,3) = -a; + ipCoords_(0,4) = -a; ipCoords_(1,4) = -a; ipCoords_(2,4) = +a; + ipCoords_(0,5) = +a; ipCoords_(1,5) = -a; ipCoords_(2,5) = +a; + ipCoords_(0,6) = +a; ipCoords_(1,6) = +a; ipCoords_(2,6) = +a; + ipCoords_(0,7) = -a; ipCoords_(1,7) = +a; ipCoords_(2,7) = +a; + + // Compute shape functions at ip's + for (int ip = 0; ip < numIPs_; ip++) { + double r = ipCoords_(0, ip); + double s = ipCoords_(1, ip); + double t = ipCoords_(2, ip); + for (int iNode = 0; iNode < numEltNodes_; iNode++) { + double rI = localCoords_(0, iNode); + double sI = localCoords_(1, iNode); + double tI = localCoords_(2, iNode); + N_(ip, iNode) = 0.125 * (1 + r*rI) * (1 + s*sI) * (1 + t*tI); + } + } + + // Integration point weights + ipWeights_ = 1.0; + + // integration points by face + ipFace2DCoords_(0,0)=-a; ipFace2DCoords_(1,0)=-a; + ipFace2DCoords_(0,1)= a; ipFace2DCoords_(1,1)=-a; + ipFace2DCoords_(0,2)= a; ipFace2DCoords_(1,2)= a; + ipFace2DCoords_(0,3)=-a; ipFace2DCoords_(1,3)= a; + + ipFaceCoords_[0](0,0)=-1;ipFaceCoords_[0](1,0)=-a;ipFaceCoords_[0](2,0)=-a; + ipFaceCoords_[0](0,1)=-1;ipFaceCoords_[0](1,1)= a;ipFaceCoords_[0](2,1)=-a; + ipFaceCoords_[0](0,2)=-1;ipFaceCoords_[0](1,2)= a;ipFaceCoords_[0](2,2)= a; + ipFaceCoords_[0](0,3)=-1;ipFaceCoords_[0](1,3)=-a;ipFaceCoords_[0](2,3)= a; + + ipFaceCoords_[1](0,0)= 1;ipFaceCoords_[1](1,0)=-a;ipFaceCoords_[1](2,0)=-a; + ipFaceCoords_[1](0,1)= 1;ipFaceCoords_[1](1,1)= a;ipFaceCoords_[1](2,1)=-a; + ipFaceCoords_[1](0,2)= 1;ipFaceCoords_[1](1,2)= a;ipFaceCoords_[1](2,2)= a; + ipFaceCoords_[1](0,3)= 1;ipFaceCoords_[1](1,3)=-a;ipFaceCoords_[1](2,3)= a; + + ipFaceCoords_[2](0,0)=-a;ipFaceCoords_[2](1,0)=-1;ipFaceCoords_[2](2,0)=-a; + ipFaceCoords_[2](0,1)=-a;ipFaceCoords_[2](1,1)=-1;ipFaceCoords_[2](2,1)= a; + ipFaceCoords_[2](0,2)= a;ipFaceCoords_[2](1,2)=-1;ipFaceCoords_[2](2,2)= a; + ipFaceCoords_[2](0,3)= a;ipFaceCoords_[2](1,3)=-1;ipFaceCoords_[2](2,3)=-a; + + ipFaceCoords_[3](0,0)=-a;ipFaceCoords_[3](1,0)= 1;ipFaceCoords_[3](2,0)=-a; + ipFaceCoords_[3](0,1)=-a;ipFaceCoords_[3](1,1)= 1;ipFaceCoords_[3](2,1)= a; + ipFaceCoords_[3](0,2)= a;ipFaceCoords_[3](1,2)= 1;ipFaceCoords_[3](2,2)= a; + ipFaceCoords_[3](0,3)= a;ipFaceCoords_[3](1,3)= 1;ipFaceCoords_[2](2,3)=-a; + + ipFaceCoords_[4](0,0)=-a;ipFaceCoords_[4](1,0)=-a;ipFaceCoords_[4](2,0)=-1; + ipFaceCoords_[4](0,1)= a;ipFaceCoords_[4](1,1)=-a;ipFaceCoords_[4](2,1)=-1; + ipFaceCoords_[4](0,2)= a;ipFaceCoords_[4](1,2)= a;ipFaceCoords_[4](2,2)=-1; + ipFaceCoords_[4](0,3)=-a;ipFaceCoords_[4](1,3)= a;ipFaceCoords_[4](2,3)=-1; + + ipFaceCoords_[5](0,0)=-a;ipFaceCoords_[5](1,0)=-a;ipFaceCoords_[5](2,0)= 1; + ipFaceCoords_[5](0,1)= a;ipFaceCoords_[5](1,1)=-a;ipFaceCoords_[5](2,1)= 1; + ipFaceCoords_[5](0,2)= a;ipFaceCoords_[5](1,2)= a;ipFaceCoords_[5](2,2)= 1; + ipFaceCoords_[5](0,3)=-a;ipFaceCoords_[5](1,3)= a;ipFaceCoords_[5](2,3)= 1; + + // Compute shape functions at ip's + for (int f = 0; f < numFaces_; f++) { + for (int ip = 0; ip < numFaceIPs_; ip++) { + double r = ipFaceCoords_[f](0, ip); + double s = ipFaceCoords_[f](1, ip); + double t = ipFaceCoords_[f](2, ip); + for (int iNode = 0; iNode < numEltNodes_; iNode++) { + double rI = localCoords_(0, iNode); + double sI = localCoords_(1, iNode); + double tI = localCoords_(2, iNode); + NFace_[f](ip, iNode) = 0.125 * (1 + r*rI) * (1 + s*sI) * (1 + t*tI); + } + } + } + + // integration points + ipFaceWeights_ = 1.0; + } + + FE_ElementHex::~FE_ElementHex() + { + // Nothing to do here + } + + // ------------------------------------------------------------- + // shape_function + // ------------------------------------------------------------- + void FE_ElementHex::shape_function(const int eltID, + DENS_MAT &N, + vector &dN, + DIAG_MAT &weights) + { + // Get element node coordinates from mesh + DENS_MAT xCoords; + feMesh_->element_coordinates(eltID, xCoords); + + // Transpose xCoords + DENS_MAT xCoordsT(transpose(xCoords)); + + // Shape functions are simply the canonical element values + N = N_; + + // Set sizes of matrices and vectors + if ((int)dN.size() != nSD_) dN.resize(nSD_); + for (int isd = 0; isd < nSD_; isd++) dN[isd].resize(numIPs_, numEltNodes_); + weights.resize(numIPs_,numIPs_); + + // Create some temporary matrices: + + // Shape function deriv.'s w.r.t. element coords + DENS_MAT dNdr(nSD_, numEltNodes_, false); + + // Jacobian matrix: [dx/dr dy/ds dz/dt | dx/ds ... ] + DENS_MAT dxdr, drdx, dNdx; + + // Loop over integration points + for (int ip = 0; ip < numIPs_; ip++) { + const double &r = ipCoords_(0,ip); + const double &s = ipCoords_(1,ip); + const double &t = ipCoords_(2,ip); + + // Fill dNdr + for (int inode = 0; inode < numEltNodes_; inode++) { + const double &rI = localCoords_(0,inode); + const double &sI = localCoords_(1,inode); + const double &tI = localCoords_(2,inode); + dNdr(0,inode) = 0.125 * rI * (1 + s*sI) * (1 + t*tI); + dNdr(1,inode) = 0.125 * sI * (1 + r*rI) * (1 + t*tI); + dNdr(2,inode) = 0.125 * tI * (1 + r*rI) * (1 + s*sI); + } + + // Compute dx/dxi matrix + dxdr = dNdr * xCoordsT; + drdx = inv(dxdr); + + // Compute dNdx and fill dN matrix + dNdx = drdx*dNdr; + for (int isd = 0; isd < nSD_; isd++) + for (int inode = 0; inode < numEltNodes_; inode++) + dN[isd](ip,inode) = dNdx(isd,inode); + + // Compute jacobian determinant of dxdr at this ip + double J = dxdr(0,0) * ( dxdr(1,1)*dxdr(2,2) - dxdr(2,1)*dxdr(1,2) ) + - dxdr(0,1) * ( dxdr(1,0)*dxdr(2,2) - dxdr(1,2)*dxdr(2,0) ) + + dxdr(0,2) * ( dxdr(1,0)*dxdr(2,1) - dxdr(1,1)*dxdr(2,0) ); + + // Compute ip weight + weights(ip,ip) = ipWeights_(ip)*J; + } + + } + + + //----------------------------------------------------------------- + void FE_ElementHex::shape_function(const int eltID, + const VECTOR &xi, + DENS_VEC &N) + { + N.resize(numEltNodes_); + for (int inode = 0; inode < numEltNodes_; ++inode) + { + double shp = 0.125; + for (int i = 0; i < nSD_; ++i) shp *= (1 + xi(i)*localCoords_(i, inode)); + N(inode) = shp; + } + } + + //----------------------------------------------------------------- + void FE_ElementHex::shape_function(const int eltID, + const VECTOR &xi, + DENS_VEC &N, + DENS_MAT &dNdx) + { + N.resize(numEltNodes_); + + // Get element node coordinates from mesh + DENS_MAT xCoords; + feMesh_->element_coordinates(eltID, xCoords); + DENS_MAT xCoordsT = transpose(xCoords); + DENS_MAT dNdr(3,8,false), dxdr(3,3,false), drdx; + double r = xi(0); + double s = xi(1); + double t = xi(2); + for (int inode = 0; inode < numEltNodes_; ++inode) + { + double rI = localCoords_(0,inode); + double sI = localCoords_(1,inode); + double tI = localCoords_(2,inode); + + // Shape function + N(inode) = 0.125 * (1.0 + r*rI) * (1.0 + s*sI) * (1.0 + t*tI); + + // Shape function derivative wrt (r,s,t) + dNdr(0,inode) = 0.125 * rI * (1.0 + s*sI) * (1.0 + t*tI); + dNdr(1,inode) = 0.125 * sI * (1.0 + r*rI) * (1.0 + t*tI); + dNdr(2,inode) = 0.125 * tI * (1.0 + r*rI) * (1.0 + s*sI); + } + + // Derivatives wrt (x,y,z) + dxdr = dNdr * xCoordsT; + drdx = inv(dxdr); + dNdx = drdx*dNdr; + } + + //----------------------------------------------------------------- + void FE_ElementHex::face_shape_function(const PAIR & face, + DENS_MAT &N, + vector &dN, + vector &Nn, + DIAG_MAT &weights) + { + int eltID = face.first; + int local_faceID = face.second; + // const double * face_normal = face.normal(); + // double * normal = face_normal; + DENS_VEC normal(nSD_); + + // Get element node coordinates from mesh + DENS_MAT xCoords; + feMesh_->element_coordinates(eltID, xCoords); + + // Transpose xCoords + DENS_MAT xCoordsT = transpose(xCoords); + + // Shape functions are simply the canonical element values + N.reset(numFaceIPs_, numEltNodes_); + N = NFace_[local_faceID]; + + // Set sizes of matrices and vectors + if ((int)dN.size() != nSD_) dN.resize(nSD_); + for (int isd = 0; isd < nSD_; isd++) dN[isd].reset(numFaceIPs_, numEltNodes_); + weights.reset(numFaceIPs_,numFaceIPs_); + + // Create some temporary matrices: + + // Shape function deriv.'s w.r.t. element coords + DENS_MAT dNdr(nSD_, numEltNodes_); + + // Jacobian matrix: [dx/dr dy/ds dz/dt | dx/ds ... ] + DENS_MAT dxdr, drdx, dNdx; + + // Loop over integration points + for (int ip = 0; ip < numFaceIPs_; ip++) { + double r = ipFaceCoords_[local_faceID](0,ip); + double s = ipFaceCoords_[local_faceID](1,ip); + double t = ipFaceCoords_[local_faceID](2,ip); + + // Fill dNdr + for (int inode = 0; inode < numEltNodes_; inode++) { + double rI = localCoords_(0,inode); + double sI = localCoords_(1,inode); + double tI = localCoords_(2,inode); + dNdr(0,inode) = 0.125 * rI * (1 + s*sI) * (1 + t*tI); + dNdr(1,inode) = 0.125 * sI * (1 + r*rI) * (1 + t*tI); + dNdr(2,inode) = 0.125 * tI * (1 + r*rI) * (1 + s*sI); + } + + // Compute dx/dxi matrix + dxdr = dNdr * xCoordsT; + drdx = inv(dxdr); + + // Compute 2d jacobian determinant of dxdr at this ip + double J = face_normal(face, ip, normal); + + // Compute dNdx and fill dN matrix + dNdx = drdx*dNdr; + for (int isd = 0; isd < nSD_; isd++) { + for (int inode = 0; inode < numEltNodes_; inode++) { + dN[isd](ip,inode) = dNdx(isd,inode); + Nn[isd](ip,inode) = N(ip,inode)*normal(isd); + } + } + + // Compute ip weight + weights(ip,ip) = ipFaceWeights_(ip)*J; + } + + } + + //----------------------------------------------------------------- + void FE_ElementHex::face_shape_function(const PAIR & face, + DENS_MAT &N, + DENS_MAT &n, + DIAG_MAT &weights) + { + int eltID = face.first; + int local_faceID = face.second; + // const double * face_normal = face.normal(); + // double * normal = face_normal; + DENS_VEC normal(nSD_); + + // Get element node coordinates from mesh + DENS_MAT xCoords; + feMesh_->element_coordinates(eltID, xCoords); + + // Transpose xCoords + DENS_MAT xCoordsT = transpose(xCoords); + + // Shape functions are simply the canonical element values + N.reset(numFaceIPs_, numEltNodes_); + N = NFace_[local_faceID]; + + weights.reset(numFaceIPs_,numFaceIPs_); + + // Create some temporary matrices: + + // Shape function deriv.'s w.r.t. element coords + DENS_MAT dNdr(nSD_, numEltNodes_); + + // Jacobian matrix: [dx/dr dy/ds dz/dt | dx/ds ... ] + DENS_MAT dxdr, drdx, dNdx; + + // Loop over integration points + for (int ip = 0; ip < numFaceIPs_; ip++) { + double r = ipFaceCoords_[local_faceID](0,ip); + double s = ipFaceCoords_[local_faceID](1,ip); + double t = ipFaceCoords_[local_faceID](2,ip); + + // Compute 2d jacobian determinant of dxdr at this ip + double J = face_normal(face, ip, normal); + + // Copy normal at integration point + for (int isd = 0; isd < nSD_; isd++) { + n(isd,ip) = normal(isd); + } + + // Compute ip weight + weights(ip,ip) = ipFaceWeights_(ip)*J; + } + + } + + // ------------------------------------------------------------- + // face_normal + // ------------------------------------------------------------- + double FE_ElementHex::face_normal(const PAIR & face, + int ip, + DENS_VEC &normal) + { + // Get element node coordinates from mesh + DENS_MAT xCoords; + feMesh_->face_coordinates(face, xCoords); + + // Transpose xCoords + DENS_MAT xCoordsT = transpose(xCoords); + + double r = ipFace2DCoords_(0,ip); + double s = ipFace2DCoords_(1,ip); + DENS_MAT dNdr(nSD_-1, numFaceNodes_); + for (int inode = 0; inode < numFaceNodes_; inode++) { + double rI = localCoords_(0,inode); // NOTE a little dangerous + double sI = localCoords_(1,inode); + dNdr(0,inode) = 0.25 * rI * (1 + s*sI); + dNdr(1,inode) = 0.25 * sI * (1 + r*rI); + } + DENS_MAT dxdr(2,3); + + // Compute dx/dxi matrix + dxdr = dNdr * xCoordsT; + normal(0) = dxdr(0,1)*dxdr(1,2) - dxdr(0,2)*dxdr(1,1) ; + normal(1) = dxdr(0,2)*dxdr(1,0) - dxdr(0,0)*dxdr(1,2) ; + normal(2) = dxdr(0,0)*dxdr(1,1) - dxdr(0,1)*dxdr(1,0) ; + + double J = sqrt(normal(0)*normal(0)+normal(1)*normal(1)+normal(2)*normal(2)); + // double inv_J = normal_sense(face)/J; + double inv_J = 1.0/J; + normal(0) *= inv_J; + normal(1) *= inv_J; + normal(2) *= inv_J; + return J; + } + + +}; // namespace ATC_Transfer diff --git a/lib/atc/FE_Element.h b/lib/atc/FE_Element.h new file mode 100644 index 0000000000..1ecd4b58a8 --- /dev/null +++ b/lib/atc/FE_Element.h @@ -0,0 +1,204 @@ +#ifndef FE_ELEMENT_H +#define FE_ELEMENT_H + +// ATC_Transfer headers +#include "MatrixLibrary.h" +#include "Array2D.h" +#include "ATC_TypeDefs.h" + +using namespace std; + +namespace ATC { + + // Forward declarations + class FE_Mesh; + + /** + * @class FE_Element + * @brief Base class for a finite element holding info for canonical element + */ + + class FE_Element { + + public: + FE_Element(FE_Mesh *feMesh, + int nSD, + int numEltNodes, + int numIPs, + int numFaces, + int numFaceNodes, + int numFaceIPs); + virtual ~FE_Element(); + + /** get number of element nodes */ + int num_elt_nodes() { return numEltNodes_; } + + /** get number of integration points */ + int num_ips() { return numIPs_; } + + /** get number of faces */ + int num_faces() { return numFaces_; } + + /** get number of face nodes */ + int num_face_nodes() { return numFaceNodes_; } + + /** get number of integration points */ + int num_face_ips() { return numFaceIPs_; } + + /** cannonical face numbering */ + const Array2D & local_face_conn(void) const {return localFaceConn_ ;} + + /** + * compute shape functions at all ip's: + * indexed: N(ip,node) + * dN[nsd](ip,node) + * weights(ip) + */ + virtual void shape_function(const int eltID, + DENS_MAT &N, + vector &dN, + DIAG_MAT &weights)=0; + + /** + * compute shape functions at a single point, given the local coordinates + * indexed: N(node) + * dN[nsd](node) + */ + virtual void shape_function(const int eltID, + const VECTOR &xi, + DENS_VEC &N) = 0; + + virtual void shape_function(const int eltID, + const VECTOR &xi, + DENS_VEC &N, + DENS_MAT &dN) = 0; + + /** + * compute shape functions at all face ip's: + * indexed: N(ip,node) + * dN[nsd](ip,node) + * Nn[nsd](ip,node) + * weights(ip) + */ + virtual void face_shape_function(const PAIR &face, + DENS_MAT &N, + vector &dN, + vector &Nn, + DIAG_MAT &weights) = 0; + + virtual void face_shape_function(const PAIR & face, + DENS_MAT &N, + DENS_MAT &n, + DIAG_MAT &weights) = 0; + + virtual double face_normal(const PAIR & face, + const int ip, + DENS_VEC &normal) = 0; + + enum FE_ElementQuadrature { GAUSSIAN_QUADRATURE, NODAL_QUADRATURE}; + virtual void set_quadrature(int quadrature_type) = 0; + + protected: + + // Mesh + FE_Mesh * feMesh_; + + // Number of spatial dimensions + int nSD_; + + // Number of element nodes + int numEltNodes_; + + // Number of integration points + int numIPs_; + + // Number of faces + int numFaces_; + + // Number of face nodes + int numFaceNodes_; + + // Number of face integration points + int numFaceIPs_; + + // local coords of nodes: localCoords_(isd, ip) + DENS_MAT localCoords_; + + // quadrature scheme: localCoords_(isd, ip) + DENS_MAT ipCoords_; // local coordinates + + // matrix of shape functions at ip's: N_(ip, node) + DENS_MAT N_; + + // integration point weights: ipWeights_(ip) + DENS_VEC ipWeights_; // local coordinates + + // local face numbering + Array2D localFaceConn_; + + // quadrature scheme: localCoords_(isd, ip) + vector ipFaceCoords_; + DENS_MAT ipFace2DCoords_; + + // matrix of shape functions at ip's: N_(ip, node) + vector NFace_; + + // integration point weights: ipWeights_(ip) + DENS_VEC ipFaceWeights_; + + }; + + + + /** + * @class FE_ElementHex + * @author Greg Wagner + * @brief 3D, linear 8-node hex element with 2x2x2 quadrature + */ + class FE_ElementHex : public FE_Element { + + public: + FE_ElementHex(FE_Mesh * feMesh); + // Dump state info to disk for later restart + void write_restart(FILE *); + ~FE_ElementHex(); + + protected: + virtual void shape_function(const int eltID, + DENS_MAT &N, + vector &dN, + DiagonalMatrix &weights); + + /** + * compute shape functions at a single point, given the local coordinates + * indexed: N(node) + * dN[nsd](node) + */ + virtual void shape_function(const int eltID, + const VECTOR &xi, + DENS_VEC &N); + + virtual void shape_function(const int eltID, + const VECTOR &xi, + DENS_VEC &N, + DENS_MAT &dN); + + virtual void face_shape_function(const PAIR &face, + DENS_MAT &N, + vector &dN, + vector &Nn, + DiagonalMatrix &weights); + + virtual void face_shape_function(const PAIR & face, + DENS_MAT &N, + DENS_MAT &n, + DIAG_MAT &weights); + + virtual double face_normal(const PAIR &face, const int ip, DENS_VEC &normal); + + virtual void set_quadrature(int quadrature_type); + }; + +}; // namespace ATC_Transfer + +#endif // FE_ELEMENT_H diff --git a/lib/atc/FE_Engine.cpp b/lib/atc/FE_Engine.cpp new file mode 100644 index 0000000000..b0f75cdceb --- /dev/null +++ b/lib/atc/FE_Engine.cpp @@ -0,0 +1,1722 @@ +#include "FE_Engine.h" +#include "ATC_Transfer.h" +#include "FE_Element.h" +#include "XT_Function.h" +#include "LammpsInterface.h" +#include "PhysicsModel.h" + +#include +#include + +using namespace std; + +namespace ATC{ + + //----------------------------------------------------------------- + FE_Engine::FE_Engine(ATC_Transfer * atcTransfer) + : atcTransfer_(atcTransfer), + feMesh_(NULL), + initialized_(false), + amendedMeshData_(false), + outputManager_() + { + } + + FE_Engine::~FE_Engine() + { + if (feMesh_) delete feMesh_; + if (connectivity_ && amendedMeshData_) delete connectivity_; + if (nodeMap_ && amendedMeshData_) delete nodeMap_; + if (coordinates_ && amendedMeshData_) delete coordinates_; + } + + //----------------------------------------------------------------- + void FE_Engine::initialize() + { + if (! feMesh_) + throw ATC_Error(0,"FE_Engine has no mesh"); + + if (! initialized_ ) { + // mesh data + nodeMap_ = &(feMesh_->global_to_unique_map()); + connectivity_ = &(feMesh_->connectivity()); + coordinates_ = &(feMesh_->nodal_coordinates()); + + // set up work spaces + nNodesPerElement_ = feMesh_->get_nNodesPerElement(); + nIPsPerElement_ = feMesh_->get_nIPsPerElement(); + nIPsPerFace_ = feMesh_->get_nIPsPerFace(); + nSD_ = feMesh_->get_nSpatialDimensions(); + nElems_ = feMesh_->get_nElements(); + + // remove specified elements + if (nullElements_.size() > 0) delete_elements(nullElements_); + + initialized_ = true; + } + } + + //----------------------------------------------------------------- + bool FE_Engine::modify(int narg, char **arg) + { + bool match = false; + /*! \page man_fem_mesh fix_modify AtC fem create mesh + \section syntax + fix_modify AtC fem create mesh + + - nx ny nz = number of elements in x, y, z + - region-id = id of region that is to be meshed + - f p p = perioidicity flags for x, y, z + \section examples + fix_modify AtC fem create mesh 10 1 1 feRegion p p p + \section description + Creates a uniform mesh in a rectangular region + \section restrictions + creates only uniform rectangular grids in a rectangular region + \section related + \section default + none + */ + if (strcmp(arg[0],"fem")==0) { + // create mesh + if (strcmp(arg[1],"create")==0) { + if (strcmp(arg[2],"mesh")==0) { + int nx = atoi(arg[3]); + int ny = atoi(arg[4]); + int nz = atoi(arg[5]); + int xperiodic = 0; + if (strcmp(arg[7],"p")==0) xperiodic = 1; + int yperiodic = 0; + if (strcmp(arg[8],"p")==0) yperiodic = 1; + int zperiodic = 0; + if (strcmp(arg[9],"p")==0) zperiodic = 1; + if (feMesh_) throw ATC_Error(0,"FE Engine already has a mesh"); + create_mesh(nx, ny, nz, arg[6], xperiodic, yperiodic, zperiodic ); + + // construct prescribed data manager NOTE move to ATC_Transfer? + atcTransfer_->initialize_mesh_data(); + + match = true; + } + } + } + /*! \page man_mesh_delete_elements fix_modify AtC mesh delete_elements + \section syntax + fix_modify AtC mesh delete_elements + - = name of an element set + \section examples + fix_modify AtC delete_elements gap + \section description + Deletes a group of elements from the mesh. + \section restrictions + \section related + \section default + none + */ + else if ( (strcmp(arg[0],"mesh")==0) + && (strcmp(arg[1],"delete_elements")==0) ) { + string esetName = arg[2]; + set elemSet = feMesh_->get_elementset(esetName); + nullElements_.insert(elemSet.begin(), elemSet.end()); + match = true; + } + // FE_Mesh + else { + match = feMesh_->modify(narg,arg); + } + return match; + } + + //----------------------------------------------------------------- + void FE_Engine::finish() + { + // Nothing to do + } + + //----------------------------------------------------------------- + // write geometry + //----------------------------------------------------------------- + void FE_Engine::initialize_output(int rank, + string outputPrefix, OutputType otype) + { + outputManager_.initialize(outputPrefix, otype); + if (! feMesh_) throw ATC_Error(0,"output needs mesh"); + if (! initialized_) initialize(); + if (rank == 0) outputManager_.write_geometry(*coordinates_, connectivity_); + } + + //----------------------------------------------------------------- + // write geometry + //----------------------------------------------------------------- + void FE_Engine::write_geometry(void) + { + outputManager_.write_geometry(*coordinates_, connectivity_); + } + + // ------------------------------------------------------------- + // write data + // ------------------------------------------------------------- + void FE_Engine::write_data(double time, FIELDS &soln, OUTPUT_LIST *data) + { + outputManager_.write_data(time, &soln, data, nodeMap_->get_data()); + } + + // ------------------------------------------------------------- + // write data + // ------------------------------------------------------------- + void FE_Engine::write_data(double time, OUTPUT_LIST *data) + { + outputManager_.write_data(time, data, nodeMap_->get_data()); + } + + // ------------------------------------------------------------- + // amend mesh for deleted elements + // ------------------------------------------------------------- + void FE_Engine::delete_elements(const set & elementList) + { + int nsd = feMesh_->get_nSpatialDimensions(); + set elementsNew; + feMesh_->elementset_complement(elementList,elementsNew); + int nElementsNew = elementsNew.size(); + set newToOld; + map oldToNewMap; + feMesh_->elementset_to_nodeset(elementsNew,newToOld); + int nNodesNew = newToOld.size(); + set::const_iterator itr; + + + // coordinates & node map (from nodes to data) + const DENS_MAT &coordinates = feMesh_->nodal_coordinates(); + const Array & node_map = feMesh_->global_to_unique_map(); + if (coordinates_ && amendedMeshData_) delete coordinates_; + coordinates_ = new DENS_MAT(nsd,nNodesNew); + DENS_MAT & coor = * (const_cast ( coordinates_)); + if (nodeMap_ && amendedMeshData_) delete nodeMap_; + nodeMap_ = new Array (nNodesNew); + Array & nmap = * (const_cast *> ( nodeMap_)); + int k = 0, i = 0; + for (itr = newToOld.begin(); itr != newToOld.end(); itr++) { + int node = *itr; + oldToNewMap[node]=i++; + nmap(k) = node_map(node); + for(int j = 0; j < nsd; j++) { + coor(j,k) = coordinates(j,node); + } + k++; + } + // connectivity + const Array2D & connectivity = feMesh_->connectivity(); + int nNodesPerElement = connectivity.nRows(); + int nElements = connectivity.nCols(); + if (connectivity_ && amendedMeshData_) delete connectivity_; + connectivity_ = new Array2D(nNodesPerElement,nElementsNew); + Array2D & conn = * (const_cast *> ( connectivity_)); + k = 0; + for (itr = elementsNew.begin(); itr != elementsNew.end(); itr++) { + int ielem = *itr; + for(int j = 0; j < nNodesPerElement; j++) { + int old_node = connectivity(j,ielem); + map::iterator map_itr = oldToNewMap.find(old_node); + if (map_itr == oldToNewMap.end()) { + cout << "map failure " << old_node << "\n"; + } + int node = map_itr->second; + conn(j,k) = node; + } + k++; + } + amendedMeshData_ = true; + } + + // ------------------------------------------------------------- + // compute dimensionless stiffness matrix using native quadrature + // ------------------------------------------------------------- + void FE_Engine::compute_stiffness_matrix(SPAR_MAT &matrix) const + { + //int nfields = field_mask.get_length(); + int nNodes = feMesh_->get_nNodesUnique(); + int nNodesPerElement = feMesh_->get_nNodesPerElement(); + int nIPsPerElement = feMesh_->get_nIPsPerElement(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nelems = feMesh_->get_nElements(); + + DENS_MAT N(nIPsPerElement,nNodesPerElement); + vector dN(nsd); + dN.assign(nsd, DENS_MAT(nIPsPerElement,nNodesPerElement)); + DiagonalMatrix weights(nIPsPerElement,nIPsPerElement); + Array conn(nNodesPerElement); + + + // assemble consistent mass (nnodes X nnodes) + matrix.reset(nNodes,nNodes); + DENS_MAT elementMassMatrix(nNodesPerElement,nNodesPerElement); + for (int ielem = 0; ielem < nelems; ++ielem) + { + // evaluate shape functions + feMesh_->shape_function(ielem, N, dN, weights); + // perform quadrature + elementMassMatrix = dN[0].transMat(weights*dN[0]); + for (int i = 1; i < nsd; ++i) { + elementMassMatrix += dN[i].transMat(weights*dN[i]); + } + // get connectivity + feMesh_->element_connectivity_unique(ielem, conn); + for (int i = 0; i < nNodesPerElement; ++i) + { + int inode = conn(i); + for (int j = 0; j < nNodesPerElement; ++j) + { + int jnode = conn(j); + matrix.add(inode, jnode, elementMassMatrix(i,j)); + } + } + } + } + // ------------------------------------------------------------- + // compute dimensionless consistent mass using native quadrature + // ------------------------------------------------------------- + void FE_Engine::compute_mass_matrix(SPAR_MAT &mass_matrix) const + { + int nNodes = feMesh_->get_nNodesUnique(); + int nNodesPerElement = feMesh_->get_nNodesPerElement(); + int nIPsPerElement = feMesh_->get_nIPsPerElement(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nelems = feMesh_->get_nElements(); + + DENS_MAT N(nIPsPerElement,nNodesPerElement); + DiagonalMatrix weights(nIPsPerElement,nIPsPerElement); + Array conn(nNodesPerElement); + + // assemble consistent mass (nnodes X nnodes) + mass_matrix.reset(nNodes,nNodes); + DENS_MAT elementMassMatrix(nNodesPerElement,nNodesPerElement); + for (int ielem = 0; ielem < nelems; ++ielem) + { + // evaluate shape functions + feMesh_->shape_function(ielem, N, weights); + // perform quadrature + elementMassMatrix = N.transMat(weights*N); + // get connectivity + feMesh_->element_connectivity_unique(ielem, conn); + for (int i = 0; i < nNodesPerElement; ++i) + { + int inode = conn(i); + for (int j = 0; j < nNodesPerElement; ++j) + { + int jnode = conn(j); + mass_matrix.add(inode, jnode, elementMassMatrix(i,j)); + } + } + } + } + + // ------------------------------------------------------------- + // compute consistent mass using given quadrature + // ------------------------------------------------------------- + void FE_Engine::compute_mass_matrix(const DIAG_MAT &weights, + const SPAR_MAT &N, + SPAR_MAT &mass_matrix) const + { + int nn = N.nCols(); + int nips = N.nRows(); + + DENS_MAT tmp_mass_matrix_local(nn,nn), tmp_mass_matrix(nn,nn); + if (nips>0) { tmp_mass_matrix_local = N.transMat(weights*N); } + // share information between processors + int count = nn*nn; + LammpsInterface::instance()->allsum( + tmp_mass_matrix_local.get_ptr(), + tmp_mass_matrix.get_ptr(), count); + + // create sparse from dense + mass_matrix.reset(tmp_mass_matrix); + + } + + // ------------------------------------------------------------- + // compute dimensionless lumped mass using native quadrature + // ------------------------------------------------------------- + void FE_Engine::compute_lumped_mass_matrix(DiagonalMatrix & M) const + { + // initialize + int nNodes = feMesh_->get_nNodesUnique(); + M.reset(nNodes,nNodes); + + int nNodesPerElement = feMesh_->get_nNodesPerElement(); + int nIPsPerElement = feMesh_->get_nIPsPerElement(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nelems = feMesh_->get_nElements(); + + DENS_MAT N(nNodesPerElement,nIPsPerElement); + DIAG_MAT weights(nIPsPerElement,nIPsPerElement); + Array conn(nNodesPerElement); + + // assemble lumped diagonal mass (nnodes X nnodes) + int inode = -1; + DENS_VEC weightVec(nIPsPerElement); + DENS_VEC Nvec(nNodesPerElement); + for (int ielem = 0; ielem < nelems; ++ielem) + { + // evaluate shape functions + feMesh_->shape_function(ielem, N, weights); + int nips = weights.nRows(); // different than nIPsPerElement? + weightVec = 1.; + weightVec = weights*weightVec; + // get connectivity + feMesh_->element_connectivity_unique(ielem, conn); + Nvec = N*weightVec; + for (int i = 0; i < nNodesPerElement; ++i) + { + inode = conn(i); + M(inode,inode) += Nvec(i); + } + } + } + + // ------------------------------------------------------------- + // compute physical lumped mass using native quadrature + // ------------------------------------------------------------- + void FE_Engine::compute_lumped_mass_matrix( + const Array& field_mask, + const FIELDS & fields, + const PhysicsModel * physicsModel, + const Array &elementMaterials, + map& M, // mass matrix + const Array *element_mask) const + { + int nfields = field_mask.get_length(); + // initialize + int nNodes = feMesh_->get_nNodesUnique(); + for (int j = 0; j < nfields; ++j) { + M[field_mask(j)].reset(nNodes,nNodes); + } + + int nNodesPerElement = feMesh_->get_nNodesPerElement(); + int nIPsPerElement = feMesh_->get_nIPsPerElement(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nelems = feMesh_->get_nElements(); + + DENS_MAT N(nNodesPerElement,nIPsPerElement); + DIAG_MAT weights(nIPsPerElement,nIPsPerElement); + Array conn(nNodesPerElement); + FIELDS fieldsAtIPs, localElementFields; + DENS_MAT Nmat; + + // assemble lumped diagonal mass (nnodes X nnodes) + for (int ielem = 0; ielem < nelems; ++ielem) + { + // if element is masked, skip it + if (element_mask && !(*element_mask)(ielem)) continue; + // material id + int imat = elementMaterials(ielem); + // evaluate shape functions + feMesh_->shape_function(ielem, N, weights); + int nips = weights.nRows(); + // get connectivity + feMesh_->element_connectivity_unique(ielem, conn); + + // interpolate fields at IPs + FIELDS::const_iterator field; + for (field = fields.begin(); field != fields.end(); field++) + { + // field values at all nodes + const DENS_MAT &vI = field->second; + // field values at element nodes + DENS_MAT &vIe = localElementFields[field->first]; + // field values at integration points -> to be computed + DENS_MAT &vP = fieldsAtIPs[field->first]; + + int numFieldDOF = vI.nCols(); + vIe.reset(nNodesPerElement, numFieldDOF); + // gather local field + for (int i = 0; i < nNodesPerElement; i++) { + for (int j = 0; j < numFieldDOF; j++) { + vIe(i,j) = vI(conn(i),j); + } + } + + // interpolate field at integration points + vP = N*vIe; + } + + // compute energy/momentum/mass densities + FIELDS capacities; + physicsModel->M_integrand(field_mask, fieldsAtIPs, capacities, imat); + + // integrate & assemble + for (int j = 0; j < nfields; ++j) { + FieldName thisFieldName = field_mask(j); + Nmat = N.transMat(weights*capacities[thisFieldName]); + for (int i = 0; i < nNodesPerElement; ++i) { + int inode = conn(i); + M[thisFieldName](inode,inode) += Nmat(i,0);// assume all dof same + } + } + } + } + + // ------------------------------------------------------------- + // compute physical lumped mass using given quadrature + // ------------------------------------------------------------- + void FE_Engine::compute_lumped_mass_matrix( + const Array &field_mask, + const FIELDS &fields, + const PhysicsModel * physicsModel, + const Array > & pointMaterialGroups, + const DIAG_MAT &weights, // NOTE use these as a mask + const SPAR_MAT &N, + map &M) const // mass matrices + { + int nips = weights.nCols(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nfields = field_mask.get_length(); + int nNodes = feMesh_->get_nNodesUnique(); + // initialize + FieldName thisFieldName; + map M_local; + for (int j = 0; j < nfields; ++j) { + thisFieldName = field_mask(j); + M_local[thisFieldName].reset(nNodes,nNodes); + M[thisFieldName].reset(nNodes,nNodes); + } + + if (nips>0) { + // compute fields at given ips + FIELDS fieldsAtIPs; + for (int j = 0; j < nfields; ++j) { + thisFieldName = field_mask(j); + const FIELD & field = (fields.find(thisFieldName))->second; + int numFieldDOF = field.nCols(); + fieldsAtIPs[thisFieldName].reset(nips,numFieldDOF); + fieldsAtIPs[thisFieldName] = N*field; + } + + // treat single material point sets specially + int nMatls = pointMaterialGroups.size(); + int atomMatls = 0; + for (int imat = 0; imat < nMatls; imat++) { + const set & indices = pointMaterialGroups(imat); + if ( indices.size() > 0) atomMatls++; + } + bool singleMaterial = ( atomMatls == 1 ); + + // setup data structures + FIELDS capacities; + // evaluate physics model + if (singleMaterial) { + physicsModel->M_integrand(field_mask, fieldsAtIPs, capacities); + } + else { + throw ATC_Error(0,"unimplemented function in FE_Engine::compute_mass_matrix"); + } + + // integrate & assemble + for (int j = 0; j < nfields; ++j) { + FieldName thisFieldName = field_mask(j); + M_local[thisFieldName].reset( // assume all columns same + column(N.transMat(weights*capacities[thisFieldName]),0) ); + } + } + + // Share information between processors + for (int j = 0; j < nfields; ++j) { + FieldName thisFieldName = field_mask(j); + int count = M_local[thisFieldName].size(); + LammpsInterface::instance()->allsum( + M_local[thisFieldName].get_ptr(), + M[thisFieldName].get_ptr(), count); + } + } + + //----------------------------------------------------------------- + // compute assembled average gradient evaluated at the nodes + //----------------------------------------------------------------- + void FE_Engine::compute_gradient_matrix( + GRAD_SHPFCN & grad_matrix) const + { + int nNodesUnique = feMesh_->get_nNodesUnique(); + int nNodesPerElement = feMesh_->get_nNodesPerElement(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nelems = feMesh_->get_nElements(); + int nIPsPerElement = feMesh_->get_nIPsPerElement(); + + DENS_MAT N(nIPsPerElement,nNodesPerElement); + vector< DENS_MAT > dN(nsd); + vector< DENS_MAT > tmp_grad_matrix(nsd); + for (int i = 0; i < nsd; i++) { + dN[i].reset(nNodesPerElement,nNodesPerElement); + tmp_grad_matrix[i].reset(nNodesUnique,nNodesUnique); + } + + DiagonalMatrix weights(nIPsPerElement,nIPsPerElement); + Array conn(nNodesPerElement); + + // element wise assembly + Array count(nNodesUnique); count = 0; + feMesh_->set_quadrature(FE_Element::NODAL_QUADRATURE); + for (int ielem = 0; ielem < nelems; ++ielem) { + // evaluate shape functions + feMesh_->shape_function(ielem, N, dN, weights); + int nips = weights.nRows(); + // get connectivity + feMesh_->element_connectivity_unique(ielem, conn); + + for (int j = 0; j < nIPsPerElement; ++j) { + int jnode = conn(j); // NOTE assumes ip order is node order + count(jnode) += 1; + for (int i = 0; i < nNodesPerElement; ++i) { + int inode = conn(i); + for (int k = 0; k < nsd; ++k) { + tmp_grad_matrix[k](jnode,inode) += dN[k](j,i); + } + } + } + } + feMesh_->set_quadrature(FE_Element::GAUSSIAN_QUADRATURE); // reset to default + for (int inode = 0; inode < nNodesUnique; ++inode) { + //cout << inode << " count " << count(inode) << "\n"; + for (int jnode = 0; jnode < nNodesUnique; ++jnode) { + for (int k = 0; k < nsd; ++k) { + tmp_grad_matrix[k](jnode,inode) /= count(jnode); + } + } + } + // compact dense matrices + grad_matrix.resize(nsd); + for (int k = 0; k < nsd; ++k) { + grad_matrix[k].reset(tmp_grad_matrix[k]); + } + } + + // ------------------------------------------------------------- + // compute energy per node using native quadrature + // ------------------------------------------------------------- + void FE_Engine::compute_energy(const Array &mask, + const FIELDS &fields, + const PhysicsModel * physicsModel, + const Array & elementMaterials, + FIELDS &energy, + const Array *element_mask) const + { + //NOTE move to initialization and make workspace + const int nNodesPerElement = feMesh_->get_nNodesPerElement(); + const int nIPsPerElement = feMesh_->get_nIPsPerElement(); + const int nsd = feMesh_->get_nSpatialDimensions(); + const int nelems = feMesh_->get_nElements(); + DENS_MAT Nmat; + + //NOTE move to initialization and make workspace + DENS_MAT N(nIPsPerElement,nNodesPerElement); + vector dN(nsd); + dN.assign(nsd, DENS_MAT(nIPsPerElement,nNodesPerElement)); + + FIELDS fieldsAtIPs, localElementFields; + GRAD_FIELDS grad_fieldsAtIPs; + FIELDS::const_iterator field; + FieldName thisFieldName; + int numFieldDOF; + + for (int n = 0; n < mask.get_length(); n++) { + FieldName thisFieldName = mask(n); + const FIELD & field = (fields.find(thisFieldName))->second; + energy[thisFieldName].reset(field.nRows(), field.nCols()); + } + + DIAG_MAT weights(nIPsPerElement,nIPsPerElement); + Array conn(nNodesPerElement); + + // element wise assembly + int inode = -1; + for (int ielem=0; ielem < nelems; ielem++) + { + // if element is masked, skip it + if (element_mask && !(*element_mask)(ielem)) continue; + // material id + int imat = elementMaterials(ielem); + // evaluate shape functions + feMesh_->shape_function(ielem, N, dN, weights); + int nips = weights.nRows(); + // get connectivity + feMesh_->element_connectivity_unique(ielem, conn); + // compute fields and gradients of fields ips of this element + + for (int n = 0; n < mask.get_length(); n++) { + FieldName thisFieldName = mask(n); + FIELDS::const_iterator field = (fields.find(thisFieldName)); + // field values at all nodes + const DENS_MAT &vI = field->second; + // field values at element nodes + DENS_MAT &vIe = localElementFields[field->first]; + // field values at integration points -> to be computed + DENS_MAT &vP = fieldsAtIPs[field->first]; + // gradients of field at integration points -> to be computed + vector &dvP = grad_fieldsAtIPs[field->first]; + + numFieldDOF = vI.nCols(); + vIe.reset(nNodesPerElement, numFieldDOF); + // gather local field + for (int i = 0; i < nNodesPerElement; i++) + for (int j = 0; j < numFieldDOF; j++) + vIe(i,j) = vI(conn(i),j); + + // interpolate field at integration points + vP = N*vIe; + dvP.assign(nsd, DENS_MAT(nIPsPerElement, numFieldDOF)); + for (int j = 0; j < nsd; ++j) dvP[j] = dN[j]*vIe; + } + + FIELDS energies; + physicsModel->E_integrand(mask, fieldsAtIPs, grad_fieldsAtIPs, energies, imat); + // assemble + for (int n = 0; n < mask.get_length(); n++) { + FieldName thisFieldName = mask(n); + FIELDS::const_iterator field = (fields.find(thisFieldName)); + numFieldDOF = (field->second).nCols(); + Nmat = N.transMat(weights*energies[thisFieldName]); + for (int i = 0; i < nNodesPerElement; ++i) { + inode = conn(i); + for (int k = 0; k < numFieldDOF; ++k) { + energy[thisFieldName](inode,k) += Nmat(i,k); + } + } + } + } // element loop + } // function call + + // ------------------------------------------------------------- + // compute rhs using native quadrature + // ------------------------------------------------------------- + void FE_Engine::compute_rhs_vector(const Array2D &rhs_mask, + const FIELDS &fields, + const PhysicsModel * physicsModel, + const Array & elementMaterials, + FIELDS &rhs, + const Array *element_mask) const + { + //NOTE move to initialization and make workspace + const int nNodesPerElement = feMesh_->get_nNodesPerElement(); + const int nIPsPerElement = feMesh_->get_nIPsPerElement(); + const int nsd = feMesh_->get_nSpatialDimensions(); + const int nelems = feMesh_->get_nElements(); + DENS_MAT Nmat; + + //NOTE move to initialization and make workspace + DENS_MAT N(nIPsPerElement,nNodesPerElement); + vector dN(nsd); + dN.assign(nsd, DENS_MAT(nIPsPerElement,nNodesPerElement)); + + FIELDS fieldsAtIPs, localElementFields; + GRAD_FIELDS grad_fieldsAtIPs; + FIELDS::const_iterator field; + FieldName thisFieldName; + int numFieldDOF; + + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + if (rhs_mask(thisFieldName,FLUX) || rhs_mask(thisFieldName,SOURCE)) { + int nrows = (field->second).nRows(); + int ncols = (field->second).nCols(); + rhs[thisFieldName].reset(nrows,ncols); + } + } + + DIAG_MAT weights(nIPsPerElement,nIPsPerElement); + Array conn(nNodesPerElement); + + // element wise assembly + int inode = -1; + for (int ielem=0; ielem < nelems; ielem++) + { + // if element is masked, skip it + if (element_mask && !(*element_mask)(ielem)) continue; + // material id + int imat = elementMaterials(ielem); + // evaluate shape functions + feMesh_->shape_function(ielem, N, dN, weights); + int nips = weights.nRows(); + // get connectivity + feMesh_->element_connectivity_unique(ielem, conn); + // compute fields and gradients of fields ips of this element + + for (field = fields.begin(); field != fields.end(); field++) + { + // field values at all nodes + const DENS_MAT &vI = field->second; + // field values at element nodes + DENS_MAT &vIe = localElementFields[field->first]; + // field values at integration points -> to be computed + DENS_MAT &vP = fieldsAtIPs[field->first]; + // gradients of field at integration points -> to be computed + vector &dvP = grad_fieldsAtIPs[field->first]; + + numFieldDOF = vI.nCols(); + vIe.reset(nNodesPerElement, numFieldDOF); + // gather local field + for (int i = 0; i < nNodesPerElement; i++) + for (int j = 0; j < numFieldDOF; j++) + vIe(i,j) = vI(conn(i),j); + + // interpolate field at integration points + vP = N*vIe; + dvP.assign(nsd, DENS_MAT(nIPsPerElement, numFieldDOF)); + for (int j = 0; j < nsd; ++j) dvP[j] = dN[j]*vIe; + } + + // NOTE move scaling of N by weights up here + + // evaluate physics model + // N_fluxes is a set of fields + if(physicsModel->has_N_integrand()) { + FIELDS N_fluxes; + physicsModel->N_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs, N_fluxes, imat); + // assemble + for (field = fields.begin(); field != fields.end(); field++) + { + thisFieldName = field->first; + // NOTE why is this line here? + // SOURCE should refer only to adding extrinsic coupling sources + if (!rhs_mask(thisFieldName,SOURCE)) continue; + numFieldDOF = (field->second).nCols(); + Nmat = N.transMat(weights*N_fluxes[thisFieldName]); + for (int i = 0; i < nNodesPerElement; ++i) { + inode = conn(i); + for (int k = 0; k < numFieldDOF; ++k) { + rhs[thisFieldName](inode,k) += Nmat(i,k); + } + } + } + } + + // evaluate Physics model + // B_fluxes is a set of field gradients + if (physicsModel->has_B_integrand()) { + GRAD_FIELDS B_fluxes; + physicsModel->B_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs, B_fluxes, imat); + + // assemble + for (field = fields.begin(); field != fields.end(); field++) + { + thisFieldName = field->first; + numFieldDOF = (field->second).nCols(); + if (!rhs_mask(thisFieldName,FLUX)) continue; + + for (int j = 0; j < nsd; j++) + { + Nmat = dN[j].transMat(weights*B_fluxes[thisFieldName][j]); + for (int i = 0; i < nNodesPerElement; ++i) + { + inode = conn(i); + for (int k = 0; k < numFieldDOF; ++k) + { + rhs[thisFieldName](inode,k) += Nmat(i,k); + } // scatter k + } // scatter i + } // loop over nsd + } //loop on fields + } // if B_integrand + } // element loop + } // function call + + // ------------------------------------------------------------- + // compute rhs using given quadrature + // ------------------------------------------------------------- + void FE_Engine::compute_rhs_vector(const Array2D &rhs_mask, + const FIELDS &fields, + const PhysicsModel *physicsModel, + const Array >&pointMaterialGroups, + const DIAG_MAT &weights, + const SPAR_MAT &N, + const GRAD_SHPFCN &dN, + FIELDS &rhs) const + { + int nips = weights.nCols(); + int nNodesUnique = feMesh_->get_nNodesUnique(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nelems = feMesh_->get_nElements(); + + FieldName thisFieldName; + FIELDS::const_iterator field; + FIELDS rhs_local; + for (field = rhs.begin(); field != rhs.end(); field++) { + thisFieldName = field->first; + if (rhs_mask(thisFieldName,FLUX) || rhs_mask(thisFieldName,SOURCE)) { + int nrows = (field->second).nRows(); + int ncols = (field->second).nCols(); + rhs [thisFieldName].reset(nrows,ncols); + rhs_local[thisFieldName].reset(nrows,ncols); + } + } + + if (nips>0) { + // compute fields and gradients of fields at given ips + GRAD_FIELDS grad_fieldsAtIPs; + FIELDS fieldsAtIPs; + int numFieldDOF; + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + const FIELD & field = (fields.find(thisFieldName))->second; + numFieldDOF = field.nCols(); + grad_fieldsAtIPs[thisFieldName].assign(nsd,DENS_MAT(nips,numFieldDOF)); + fieldsAtIPs[thisFieldName].reset(nips,numFieldDOF); + fieldsAtIPs[thisFieldName] = N*field; + for (int j = 0; j < nsd; ++j) { + grad_fieldsAtIPs[thisFieldName][j] = dN[j]*field; + } + } + + // treat single material point sets specially + int nMatls = pointMaterialGroups.size(); + int atomMatls = 0; + for (int imat = 0; imat < nMatls; imat++) { + const set & indices = pointMaterialGroups(imat); + if ( indices.size() > 0) atomMatls++; + } + bool singleMaterial = ( atomMatls == 1 ); + + // setup data structures + FIELDS N_fluxes; + GRAD_FIELDS B_fluxes; + if(physicsModel->has_B_integrand()) { + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + int index = (int) thisFieldName; + if ( rhs_mask(index,FLUX) ) { + B_fluxes[thisFieldName].reserve(nsd); + } + } + } + // evaluate physics model + if (singleMaterial) + { + if(physicsModel->has_N_integrand()) { + physicsModel->N_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs, + N_fluxes); + } + if(physicsModel->has_B_integrand()) { + physicsModel->B_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs, + B_fluxes); + } + } + else + { + // NOTE this copy in fields/copy out fluxes is extremely slow + // NOTE alternates: + // 1) permanent workspace with per-row mapped clones per matl + // from caller/atc + // 2) per point calls to N/B_integrand + // 3) collect internalToAtom into materials and use mem-cont clones + // what about dof for matrices and data storage: clone _matrix_ + + // for each material group: + // set up storage + FIELDS groupN_fluxes, fieldsGroup; + GRAD_FIELDS groupB_fluxes, grad_fieldsGroup; + if(physicsModel->has_B_integrand()) { + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + int index = (int) thisFieldName; + int ndof = (field->second).nCols(); + grad_fieldsGroup[thisFieldName].assign(nsd,DENS_MAT(nips,ndof)); + N_fluxes[thisFieldName].reset(nips,ndof); + B_fluxes[thisFieldName].assign(nsd,DENS_MAT(nips,ndof)); + if ( rhs_mask(index,FLUX) ) { + groupB_fluxes[thisFieldName].reserve(nsd); + } + } + } + // copy fields + for ( int imat = 0; imat < pointMaterialGroups.get_length(); imat++) + { + const set & indices = pointMaterialGroups(imat); + int npts = indices.size(); + int i = 0; + for (set::const_iterator iter=indices.begin(); + iter != indices.end(); iter++, i++ ) { + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + int ndof = (field->second).nCols(); + fieldsGroup[thisFieldName].reset(npts,ndof); + for (int j = 0; j < nsd; ++j) { + (grad_fieldsGroup[thisFieldName][j]).reset(npts,ndof); + } + for (int dof = 0; dof < ndof; ++dof) { + fieldsGroup[thisFieldName](i,dof) + = fieldsAtIPs[thisFieldName](*iter,dof); + for (int j = 0; j < nsd; ++j) { + grad_fieldsGroup[thisFieldName][j](i,dof) + = grad_fieldsAtIPs[thisFieldName][j](*iter,dof); + } + } + } + } + // calculate forces, & assemble + if(physicsModel->has_N_integrand()) { + physicsModel->N_integrand(rhs_mask, fieldsGroup, grad_fieldsGroup, + groupN_fluxes, imat); + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + int index = (int) thisFieldName; + int ndof = (field->second).nCols(); + if ( rhs_mask(index,SOURCE) ) { + int i = 0; + for (set::const_iterator iter=indices.begin(); + iter != indices.end(); iter++, i++ ) { + for (int dof = 0; dof < ndof; ++dof) { + N_fluxes[thisFieldName](*iter,dof) + = groupN_fluxes[thisFieldName](i,dof); + } + } + } + } + } + // calculate forces, & assemble + if(physicsModel->has_B_integrand()) { + physicsModel->B_integrand(rhs_mask, fieldsGroup, grad_fieldsGroup, + groupB_fluxes, imat); + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + int index = (int) thisFieldName; + int ndof = (field->second).nCols(); + if ( rhs_mask(index,FLUX) ) { + int i = 0; + for (set::const_iterator iter=indices.begin(); + iter != indices.end(); iter++, i++ ) { + for (int dof = 0; dof < ndof; ++dof) { + for (int j = 0; j < nsd; ++j) { + B_fluxes[thisFieldName][j](*iter,dof) + = groupB_fluxes[thisFieldName][j](i,dof); + } + } + } + } + } + } + } + } + + // assemble + for (field = fields.begin(); field != fields.end(); field++) + { + thisFieldName = field->first; + int index = (int) thisFieldName; + if(physicsModel->has_N_integrand()) { + if ( rhs_mask(index,SOURCE) ) { + rhs_local[thisFieldName] + += N.transMat(weights*N_fluxes[thisFieldName]); + } + } + if(physicsModel->has_B_integrand()) { + if ( rhs_mask(index,FLUX) ) { + for (int j = 0; j < nsd; ++j) { + rhs_local[thisFieldName] + += dN[j].transMat(weights*B_fluxes[thisFieldName][j]); + } + } + } + } + } + + // Share information between processors + for (field = rhs.begin(); field != rhs.end(); field++) { + thisFieldName = field->first; + if (rhs_mask(thisFieldName,FLUX) || rhs_mask(thisFieldName,SOURCE)) { + int count = rhs_local[thisFieldName].size(); + //rhs_local[thisFieldName].print("RHS LCL"); + LammpsInterface::instance()->allsum( + rhs_local[thisFieldName].get_ptr(), + rhs[thisFieldName].get_ptr(), count); + } + } + } + + // ------------------------------------------------------------- + // compute flux for post processing + // ------------------------------------------------------------- + void FE_Engine::compute_flux(const Array2D &rhs_mask, + const FIELDS &fields, + const PhysicsModel * physicsModel, + const Array & elementMaterials, + GRAD_FIELDS &flux, + const Array *element_mask) const + { + //NOTE move to initialization and make workspace + const int nNodesPerElement = feMesh_->get_nNodesPerElement(); + const int nIPsPerElement = feMesh_->get_nIPsPerElement(); + const int nsd = feMesh_->get_nSpatialDimensions(); + const int nelems = feMesh_->get_nElements(); + DENS_MAT Nmat; + + //NOTE move to initialization and make workspace + DENS_MAT N(nIPsPerElement,nNodesPerElement); + vector dN(nsd); + dN.assign(nsd, DENS_MAT(nIPsPerElement,nNodesPerElement)); + + FIELDS fieldsAtIPs, localElementFields; + GRAD_FIELDS grad_fieldsAtIPs; + FIELDS::const_iterator field; + FieldName thisFieldName; + int numFieldDOF; + + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + if (rhs_mask(thisFieldName,FLUX)) { + int nrows = (field->second).nRows(); + int ncols = (field->second).nCols(); + flux[thisFieldName].assign(nsd, DENS_MAT(nrows,ncols)); + } + } + + DIAG_MAT weights(nIPsPerElement,nIPsPerElement); + Array conn(nNodesPerElement); + + // element wise assembly + int inode = -1; + for (int ielem=0; ielem < nelems; ielem++) + { + // if element is masked, skip it + if (element_mask && !(*element_mask)(ielem)) continue; + // material id + int imat = elementMaterials(ielem); + // evaluate shape functions + feMesh_->shape_function(ielem, N, dN, weights); + int nips = weights.nRows(); + // get connectivity + feMesh_->element_connectivity_unique(ielem, conn); + // compute fields and gradients of fields ips of this element + + for (field = fields.begin(); field != fields.end(); field++) + { + // field values at all nodes + const DENS_MAT &vI = field->second; + // field values at element nodes + DENS_MAT &vIe = localElementFields[field->first]; + // field values at integration points -> to be computed + DENS_MAT &vP = fieldsAtIPs[field->first]; + // gradients of field at integration points -> to be computed + vector &dvP = grad_fieldsAtIPs[field->first]; + + numFieldDOF = vI.nCols(); + vIe.reset(nNodesPerElement, numFieldDOF); + // gather local field + for (int i = 0; i < nNodesPerElement; i++) + for (int j = 0; j < numFieldDOF; j++) + vIe(i,j) = vI(conn(i),j); + + // interpolate field at integration points + vP = N*vIe; + dvP.assign(nsd, DENS_MAT(nIPsPerElement, numFieldDOF)); + for (int j = 0; j < nsd; ++j) dvP[j] = dN[j]*vIe; + } + + // NOTE move scaling of N by weights up here + + // evaluate Physics model + // B_fluxes is a set of field gradients + if (physicsModel->has_B_integrand()) { + GRAD_FIELDS B_fluxes; + physicsModel->B_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs, B_fluxes, imat); + + // assemble + for (field = fields.begin(); field != fields.end(); field++) + { + thisFieldName = field->first; + numFieldDOF = (field->second).nCols(); + if (!rhs_mask(thisFieldName,FLUX)) continue; + + for (int j = 0; j < nsd; j++) + { + Nmat = N.transMat(weights*B_fluxes[thisFieldName][j]); + for (int i = 0; i < nNodesPerElement; ++i) + { + inode = conn(i); + for (int k = 0; k < numFieldDOF; ++k) + { + flux[thisFieldName][j](inode,k) += Nmat(i,k); + } // scatter k + } // scatter i + } // loop over nsd + } //loop on fields + } // if B_integrand + } // element loop + } // function call + + //----------------------------------------------------------------- + // boundary flux using native quadrature + //----------------------------------------------------------------- + void FE_Engine::compute_boundary_flux( + const Array2D & rhs_mask, + const FIELDS & fields, + const PhysicsModel * physicsModel, + const Array & elementMaterials, + const set< pair > & faceSet, + FIELDS & rhs) const + { + //NOTE move to initialization and make workspace + int nNodesPerElement = feMesh_->get_nNodesPerElement(); + int nIPsPerFace = feMesh_->get_nIPsPerFace(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nelems = feMesh_->get_nElements(); + DENS_MAT Nmat(nNodesPerElement,3); + + FieldName thisFieldName; + FIELDS::const_iterator field; + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + if (rhs_mask(thisFieldName,FLUX)) { + int nrows = (field->second).nRows(); + int ncols = (field->second).nCols(); + rhs [thisFieldName].reset(nrows,ncols); + } + } + + //NOTE move to initialization and make workspace + // sizing working arrays + DENS_MAT N(nIPsPerFace,nNodesPerElement); + vector< DENS_MAT > dN(nsd), nN(nsd); + for (int i = 0; i < nsd; i++) { + dN[i].reset(nIPsPerFace,nNodesPerElement); + nN[i].reset(nIPsPerFace,nNodesPerElement); + } + + GRAD_FIELDS grad_fieldsAtIPs; + FIELDS fieldsAtIPs; + FIELDS localElementFields; + int numFieldDOF; + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + numFieldDOF = field->second.nCols(); + grad_fieldsAtIPs[thisFieldName].reserve(nsd); + for (int i = 0; i < nsd; ++i) { + grad_fieldsAtIPs[thisFieldName].push_back(DENS_MAT(nIPsPerFace,numFieldDOF)); + } + fieldsAtIPs[thisFieldName].reset(nIPsPerFace,numFieldDOF); + localElementFields[thisFieldName].reset(nNodesPerElement,numFieldDOF); + } + + DiagonalMatrix weights(nIPsPerFace,nIPsPerFace); + Array conn(nNodesPerElement); + + // element wise assembly + int inode = -1; + set< pair >::iterator iter; + for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { + // evaluate shape functions at ips + feMesh_->face_shape_function(*iter, N, dN, nN, weights); + int nips = weights.nRows(); + // get connectivity + int ielem = iter->first; + int imat = elementMaterials(ielem); + feMesh_->element_connectivity_unique(ielem, conn); + + // interpolate fields and gradients of fields ips of this element + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + const DenseMatrix * thisField + = (const DENS_MAT *) &(field->second); + numFieldDOF = thisField->nCols(); + //NOTE an hopefully unnecessary copy (should alias) + for (int i = 0; i < nNodesPerElement; i++) { + for (int j = 0; j < numFieldDOF; j++) { + localElementFields[thisFieldName](i,j) = (*thisField)(conn(i),j); + } + } + // ips X dof = ips X nodes * nodes X dof + fieldsAtIPs[thisFieldName] = N*localElementFields[thisFieldName]; + for (int j = 0; j < nsd; ++j) { + grad_fieldsAtIPs[thisFieldName][j] = dN[j]*localElementFields[thisFieldName]; + } + } + + // Evaluate- physics model + // do nothing for N_integrand + // nN : precomputed and held by ATC_Transfer + if(physicsModel->has_B_integrand()) { + map > B_fluxes; + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + if ( rhs_mask(thisFieldName,FLUX) ) { + B_fluxes[thisFieldName].reserve(nsd); + } + } + physicsModel->B_integrand(rhs_mask, fieldsAtIPs, grad_fieldsAtIPs, B_fluxes,imat); + // assemble + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + int index = (int) thisFieldName; + const DenseMatrix * thisField + = (const DENS_MAT *) &(field->second); + if ( rhs_mask(index,FLUX) ) { + numFieldDOF = thisField->nCols(); + Nmat.reset(nNodesPerElement,numFieldDOF); + for (int j = 0; j < nsd; j++) { + // nodes X dof = nodes X ips * ips X dof ??? + Nmat = nN[j].transMat(weights*B_fluxes[thisFieldName][j]); + for (int i = 0; i < nNodesPerElement; ++i) { + inode = conn(i); + for (int k = 0; k < numFieldDOF; ++k) { + rhs[thisFieldName](inode,k) += Nmat(i,k); + } + } + } + } + } + } + + } + } + + // ------------------------------------------------------------- + // compute boundary flux using given quadrature and interpolation + // ------------------------------------------------------------- + void FE_Engine::compute_boundary_flux( + const Array2D & rhs_mask, + const FIELDS & fields, + const PhysicsModel * physicsModel, + const Array< int > & elementMaterials, + const Array< set > & pointMaterialGroups, + const DIAG_MAT & weights, + const SPAR_MAT & N, + const GRAD_SHPFCN & dN, + const DIAG_MAT & flux_mask, + FIELDS & flux) const + { + int nNodesUnique = feMesh_->get_nNodesUnique(); + FieldName thisFieldName; + + map rhs; + map rhsSubset; + map rhsSubset_local; + + FIELDS::const_iterator field; + for (field = fields.begin(); field != fields.end(); field++) { + thisFieldName = field->first; + if (rhs_mask(thisFieldName,FLUX)) { + int nrows = (field->second).nRows(); + int ncols = (field->second).nCols(); + rhs [thisFieldName].reset(nrows,ncols); + rhsSubset[thisFieldName].reset(nrows,ncols); + } + } + + // R_I = - int_Omega Delta N_I .q dV + compute_rhs_vector(rhs_mask, fields, physicsModel, elementMaterials, rhs); + // R_I^md = - int_Omega^md Delta N_I .q dV + compute_rhs_vector(rhs_mask, fields, physicsModel, pointMaterialGroups, + weights, N, dN, rhsSubset); + + // flux_I = 1/Delta V_I V_I^md R_I + R_I^md + // where : Delta V_I = int_Omega N_I dV + for (int n = 0; n < NUM_FIELDS; ++n) { + FieldName thisFieldName = (FieldName) n; + if ( rhs_mask(thisFieldName,FLUX) ) { + flux[thisFieldName] + = rhsSubset[thisFieldName] - flux_mask*rhs[thisFieldName]; + } + } + } + + //----------------------------------------------------------------- + // prescribed boundary flux using native quadrature + // integrate \int_delV N_I s(x,t).n dA + //----------------------------------------------------------------- + void FE_Engine::add_fluxes(const Array &fieldMask, + const double time, + const SURFACE_SOURCE & sourceFunctions, + FIELDS &nodalSources) const + { + int nNodesPerElement = feMesh_->get_nNodesPerElement(); + int nNodesPerFace = feMesh_->get_nNodesPerFace(); + int nIPsPerFace = feMesh_->get_nIPsPerFace(); + int nsd = feMesh_->get_nSpatialDimensions(); + FieldName thisField; + int nFieldDOF = 1; + XT_Function * f = NULL; + + //NOTE move arrays to initialization and make workspace + // sizing working arrays + vector dN, nN; + dN.assign(nsd, DENS_MAT(nIPsPerFace, nNodesPerElement)); + nN.assign(nsd, DENS_MAT(nIPsPerFace, nNodesPerElement)); + + DENS_MAT N(nIPsPerFace,nNodesPerElement); + DENS_MAT xCoords(nsd,nNodesPerElement); + DENS_MAT xAtIPs(nsd,nIPsPerFace); + DENS_MAT Nmat(nNodesPerElement,nsd); + DIAG_MAT weights(nIPsPerFace,nIPsPerFace); + Array conn(nNodesPerElement); + DENS_MAT faceSource(nIPsPerFace,nFieldDOF); + + // element wise assembly + SURFACE_SOURCE::const_iterator src_iter; + for (src_iter=sourceFunctions.begin(); src_iter!=sourceFunctions.end(); src_iter++) + { + FieldName thisFieldName = src_iter->first; + if (!fieldMask((int)thisFieldName)) continue; + + typedef map > FSET; + const FSET *fset = (const FSET *)&(src_iter->second); + FSET::const_iterator fset_iter; + for (fset_iter = fset->begin(); fset_iter != fset->end(); fset_iter++) + { + const PAIR &face = fset_iter->first; + const int elem = face.first; + const Array &fs = fset_iter->second; + feMesh_->element_connectivity_unique(elem, conn); + // evaluate location at ips + feMesh_->face_shape_function(face, N, dN, nN, weights); + feMesh_->element_coordinates(elem, xCoords); + MultAB(xCoords,N,xAtIPs,0,1); //xAtIPs = xCoords*(N.transpose()); + + // interpolate prescribed flux at ips of this element + // NOTE this seems totally redundant + FSET::const_iterator face_iter = fset->find(face); + if (face_iter == fset->end()) + { + cout << "face not found" << std::endl; + // NOTE: do something there + } + // NOTE why can't I know the size earlier?? + // NOTE only normal flux here + nFieldDOF = (face_iter->second).size(); + faceSource.reset(nIPsPerFace,nFieldDOF); + for (int ip = 0; ip < nIPsPerFace; ++ip) + { + for (int idof = 0; idoff(column(xAtIPs,ip).get_ptr(),time); + } + } + // assemble + Nmat = N.transMat(weights*faceSource); + for (int i = 0; i < nNodesPerElement; ++i) + { + int inode = conn(i); + for (int idof = 0; idof < nFieldDOF; ++idof) + { + nodalSources[thisFieldName](inode,idof) += Nmat(i,idof); + } // end assemble nFieldDOF + } // end assemble nNodesPerElement + } // end fset loop + } // field loop + } + + //----------------------------------------------------------------- + // prescribed volume flux using native quadrature + // integrate \int_V N_I s(x,t) dV + //----------------------------------------------------------------- + void FE_Engine::add_sources(const Array &fieldMask, + const double time, + const VOLUME_SOURCE &sourceFunctions, + FIELDS &nodalSources) const + { + int nNodes = feMesh_->get_nNodes(); + int nNodesPerElement = feMesh_->get_nNodesPerElement(); + int nIPsPerElement = feMesh_->get_nIPsPerElement(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nelems = feMesh_->get_nElements(); + int nFieldDOF = 1; + XT_Function * f = NULL; + + //NOTE move arrays to initialization and make workspace + // sizing working arrays + DENS_MAT elemSource(nIPsPerElement,nFieldDOF); + DENS_MAT N(nIPsPerElement,nNodesPerElement); + DENS_MAT xCoords(nsd,nNodesPerElement); + DENS_MAT xAtIPs(nsd,nIPsPerElement); + DENS_MAT Nwg(nNodesPerElement,1); + DENS_MAT Nmat(nNodesPerElement,nsd); + DiagonalMatrix weights(nIPsPerElement,nIPsPerElement); + Array conn(nNodesPerElement); + + // element wise assembly + for (int ielem = 0; ielem < nelems; ++ielem) { + feMesh_->element_connectivity_unique(ielem, conn); + + // evaluate location at ips + feMesh_->shape_function(ielem, N, weights); + feMesh_->element_coordinates(ielem, xCoords); + xAtIPs =xCoords*(N.transpose()); + + VOLUME_SOURCE ::const_iterator src_iter; + for (src_iter = sourceFunctions.begin(); + src_iter != sourceFunctions.end(); src_iter++) { + FieldName thisField = src_iter->first; + int index = (int) thisField; + if ( fieldMask(index) ) { + const Array2D * thisSource + = (const Array2D *) &(src_iter->second); + nFieldDOF = thisSource->nCols(); + elemSource.reset(nIPsPerElement,nFieldDOF); + + // interpolate prescribed flux at ips of this element + for (int ip = 0; ip < nIPsPerElement; ++ip) { + for (int idof = 0; idof < nFieldDOF; ++idof) { + f = (*thisSource)(ielem,idof); + if (f) { + elemSource(ip,idof) = f->f(column(xAtIPs,ip).get_ptr(),time); + } + } + } + + // assemble + Nmat.reset(nNodesPerElement,nFieldDOF); + Nmat = N.transMat(weights*elemSource); + for (int i = 0; i < nNodesPerElement; ++i) { + int inode = conn(i); + for (int idof = 0; idof < nFieldDOF; ++idof) { + nodalSources[thisField](inode,idof) += Nmat(i,idof); + } + } + } + } + } + } + + //----------------------------------------------------------------- + // boundary integral of a nodal field + //----------------------------------------------------------------- + void FE_Engine::field_surface_flux( + const DENS_MAT & field, + const set< PAIR > & faceSet, + DENS_MAT & values, + const bool contour, + const int axis) const + { + int nNodesPerElement = feMesh_->get_nNodesPerElement(); + int nIPsPerFace = feMesh_->get_nIPsPerFace(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nelems = feMesh_->get_nElements(); + int numFieldDOF = field.nCols(); + + double a[3] = {0,0,0}; + a[axis] = 1; + + // sizing working arrays + DENS_MAT N(nIPsPerFace,nNodesPerElement); + DENS_MAT n(nsd,nIPsPerFace); + DENS_MAT fieldsAtIPs(nIPsPerFace,numFieldDOF); + DENS_MAT localElementFields(nNodesPerElement,numFieldDOF); + DiagonalMatrix weights(nIPsPerFace,nIPsPerFace); + Array conn(nNodesPerElement); + + DENS_MAT Nmat(nsd,numFieldDOF); + DENS_MAT integrals(numFieldDOF,nsd); + + // element wise assembly + set< pair >::iterator iter; + for (iter = faceSet.begin(); iter != faceSet.end(); iter++) { + // evaluate shape functions at ips + feMesh_->face_shape_function(*iter, N, n, weights); + // cross n with axis to get tangent + if (contour) { + double t[3]; + for (int i = 0; i < nIPsPerFace; i++) { + t[0] = a[1]*n(2,i) - a[2]*n(1,i); + t[1] = a[2]*n(0,i) - a[0]*n(2,i); + t[2] = a[0]*n(1,i) - a[1]*n(0,i); + n(0,i) = t[0]; + n(1,i) = t[1]; + n(2,i) = t[2]; + } + } + int nips = weights.nRows(); + // get connectivity + int ielem = iter->first; + feMesh_->element_connectivity_unique(ielem, conn); + + // interpolate fields and gradients of fields ips of this element + for (int i = 0; i < nNodesPerElement; i++) { + for (int j = 0; j < numFieldDOF; j++) { + localElementFields(i,j) = field(conn(i),j); + } + } + // ips X dof = ips X nodes * nodes X dof + fieldsAtIPs = N*localElementFields; + + // assemble : integral(k,j) = sum_ip n(j,ip) wg(ip,ip) field(ip,k) + Nmat = n*weights*fieldsAtIPs; + for (int j = 0; j < nsd; j++) { + for (int k = 0; k < numFieldDOF; ++k) { + integrals(k,j) += Nmat(j,k); + } + } + } + // (S.n)_1 = S_1j n_j = S_11 n_1 + S_12 n_2 + S_13 n_3 + // (S.n)_2 = S_2j n_j = S_21 n_1 + S_22 n_2 + S_23 n_3 + // (S.n)_3 = S_3j n_j = S_31 n_1 + S_32 n_2 + S_33 n_3 + if (numFieldDOF == 9) { // tensor + values.reset(nsd,1); + values(0,0) = integrals(0,0)+integrals(1,1)+integrals(2,2); + values(1,0) = integrals(3,0)+integrals(4,1)+integrals(5,2); + values(2,0) = integrals(6,0)+integrals(7,1)+integrals(8,2); + } + else if (numFieldDOF == 6) { // sym tensor + values.reset(nsd,1); + values(0,0) = integrals(0,0)+integrals(3,1)+integrals(4,2); + values(1,0) = integrals(3,0)+integrals(1,1)+integrals(5,2); + values(2,0) = integrals(4,1)+integrals(5,1)+integrals(2,2); + } + // (v.n) = v_j n_j + else if (numFieldDOF == 3) { // vector + values.reset(1,1); + values(0,0) = integrals(0,0)+integrals(1,1)+integrals(2,2); + } + // s n = s n_j e_j + else if (numFieldDOF == 1) { // scalar + values.reset(nsd,1); + values(0,0) = integrals(0,0); + values(1,0) = integrals(0,1); + values(2,0) = integrals(0,2); + } + else { + string msg = "FE_Engine::field_surface_flux unsupported field width: "; + msg += tostring(numFieldDOF); + throw ATC_Error(0,msg); + } + } + + + //----------------------------------------------------------------- + // evaluate shape functions at given points + //----------------------------------------------------------------- + void FE_Engine::evaluate_shape_functions( + const MATRIX & pt_coords, + SPAR_MAT & N, + Array & pointToEltMap) const + { + // Get shape function and derivatives at atomic locations + int npe = feMesh_->get_nNodesPerElement(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nnodes = feMesh_->get_nNodesUnique(); + int npts = pt_coords.nCols(); + + pointToEltMap.reset(npts); + + // loop over point (atom) coordinates + DENS_VEC x(nsd); + Array node_index(npe); + DENS_VEC shp(npe); + + N.reset(npts,nnodes); + for (int i = 0; i < npts; ++i) { + for (int k = 0; k < nsd; ++k) { + x(k) = pt_coords(k,i); + } + int eltID; + feMesh_->shape_functions(x,shp,eltID,node_index); + for (int j = 0; j < npe; ++j) { + int jnode = node_index(j); + N.add(i,jnode,shp(j)); + } + pointToEltMap(i) = eltID; + } + N.compress(); + } + + //----------------------------------------------------------------- + // evaluate shape functions and their spatial derivatives at given points + //----------------------------------------------------------------- + void FE_Engine::evaluate_shape_functions( + const MATRIX & pt_coords, + SPAR_MAT & N, + GRAD_SHPFCN & dN, + Array & pointToEltMap) const + { + // Get shape function and derivatives at atomic locations + int npe = feMesh_->get_nNodesPerElement(); + int nsd = feMesh_->get_nSpatialDimensions(); + int nnodes = feMesh_->get_nNodesUnique(); + int npts = pt_coords.nCols(); + + pointToEltMap.reset(npts); + + // loop over point (atom) coordinates + DENS_VEC x(nsd); + Array node_index(npe); + DENS_VEC shp(npe); + DENS_MAT dshp(nsd,npe); + + for (int k = 0; k < nsd; ++k) { + dN[k].reset(npts,nnodes); + } + + N.reset(npts,nnodes); + for (int i = 0; i < npts; ++i) { + for (int k = 0; k < nsd; ++k) { + x(k) = pt_coords(k,i); + } + int eltID; + feMesh_->shape_functions(x,shp,dshp,eltID,node_index); + for (int j = 0; j < npe; ++j) { + int jnode = node_index(j); + N.add(i,jnode,shp(j)); + for (int k = 0; k < nsd; ++k) { + dN[k].add(i,jnode,dshp(k,j)); + } + } + pointToEltMap(i) = eltID; + } + N.compress(); + for (int k = 0; k < nsd; ++k) { + dN[k].compress(); + } + } + + //----------------------------------------------------------------- + // evaluate shape functions at given points + //----------------------------------------------------------------- + void FE_Engine::evaluate_shape_functions(const VECTOR & x, + Array& node_index, + DENS_VEC& shp, + DENS_MAT& dshp, + int & eltID) const + { + // Get shape function and derivatives at a specific point + int nsd = feMesh_->get_nSpatialDimensions(); + int npe = feMesh_->get_nNodesPerElement(); + + dshp.reset(nsd,npe); + + feMesh_->shape_functions(x,shp,dshp,eltID,node_index); + + } + + //----------------------------------------------------------------- + // create a uniform rectangular mesh on a rectangular region + //----------------------------------------------------------------- + void FE_Engine::create_mesh(int nx, int ny, int nz, char * regionName, + int xperiodic, int yperiodic, int zperiodic) + { + double xmin, xmax, ymin, ymax, zmin, zmax; + double xscale, yscale, zscale; + double boxxlo, boxxhi, boxylo, boxyhi, boxzlo, boxzhi; + + // check to see if region exists and is it a box, and if so get the bounds + bool isBox; + isBox = atcTransfer_->get_region_bounds(regionName, + xmin, xmax, + ymin, ymax, + zmin, zmax, + xscale, + yscale, + zscale); + if (!isBox) throw ATC_Error(0,"Region for FE mesh is not a box"); + + feMesh_ = new FE_Uniform3DMesh(nx,ny,nz, + xmin,xmax, + ymin,ymax, + zmin,zmax, + xscale, + yscale, + zscale, + xperiodic, + yperiodic, + zperiodic); + if (LammpsInterface::instance()->comm_rank()==0) { + printf(" ATC:: created FEM Mesh with %i Global Nodes, %i Unique Nodes, and %i Elements\n", + feMesh_->get_nNodes(),feMesh_->get_nNodesUnique(),feMesh_->get_nElements()); } + } + +}; diff --git a/lib/atc/FE_Engine.h b/lib/atc/FE_Engine.h new file mode 100644 index 0000000000..7d37ba797d --- /dev/null +++ b/lib/atc/FE_Engine.h @@ -0,0 +1,348 @@ +/** fe_engine : + * computes and assembles mass matrix, rhs vectors + * initial conditions handled in atc_transfer +*/ + +/** field structure: + a field is a dense matrix of numPoints X number of DOF in the field + a gradient is a std::vector of fields of length numSpatialDimensions + a set of fields is a map of fieldName->field of length numFields + a set of gradients is a map of fieldName->gradient of length numFields + Note: shape functions follow similar conventions with a shape function being + a field of numPoints X numNodes + and a shape function gradient being a std::vector of shape functions + of length numSpatialDimensions, although this is modified in calls when + numPoints = 1 + Note: the convention between shape function format and field format allows + for shape functions to matmat nodal fields, creating matrices of + numPoints X numElementsInField for evaluating data at atomic/quadarture points +*/ + +/** current internal limitations: + * 3 spatial dimensions + * 8 node bricks + * structured mesh + * no stiffness matrix + (i.e. no implicit integration or special treatment of linear problems) + * lumped mass +*/ + + + +/** terminology: +density rate = flux + = Grad.FLUX(f,D_x f) + + SOURCE(f,D_x f) + PRESCRIBED_SOURCE(x,t) + + EXTRINSIC_SOURCE(f,D_x f,f_e,D_x f_e) +*/ + +#ifndef FE_ENGINE_H +#define FE_ENGINE_H + +// Other headers +#include +#include + +// ATC_Transfer headers +#include "Array.h" +#include "Array2D.h" +#include "FE_Mesh.h" +#include "PhysicsModel.h" +#include "OutputManager.h" + +using namespace std; + +namespace ATC { + + // Forward declarations + class ATC_Transfer; + class FE_Element; + class XT_Function; + + class FE_Engine{ + public: + /** constructor/s */ + FE_Engine(ATC_Transfer * atcTransfer); + + /** destructor */ + ~FE_Engine(); + + /** initialize */ + void initialize(); + + /** parser/modifier */ + bool modify(int narg, char **arg); + + /** finish up */ + void finish(); + + //---------------------------------------------------------------- + /** \name output */ + //---------------------------------------------------------------- + /*@{*/ + /** these assume the caller is handling the parallel collection */ + void initialize_output(int rank, + string outputPrefix, OutputType otype = ENSIGHT); + + /** write geometry */ + void write_geometry(void); + + /** write data: data is arrayed over _unique_ nodes + & then mapped by the engine */ + void write_data(double time, FIELDS &soln, OUTPUT_LIST *data=NULL); + void write_data(double time, OUTPUT_LIST *data); + void write_restart_file(string fileName, OUTPUT_LIST *data) + {outputManager_.write_restart_file(fileName,data);}; + void read_restart_file(string fileName, OUTPUT_LIST *data) + {outputManager_.read_restart_file(fileName,data);}; + + void delete_elements(const set & elementList); + + void add_global(string name, double value) + {outputManager_.add_global(name,value);} + void reset_globals() {outputManager_.reset_globals();} + + /** pass through to access output manager */ + OutputManager* output_manager() {return &outputManager_;} + /*@}*/ + + //---------------------------------------------------------------- + /** \name assembled matrices and vectors */ + //---------------------------------------------------------------- + /*@{*/ + /** compute a dimensionless stiffness matrix */ + void compute_stiffness_matrix(SPAR_MAT &matrix) const; + + /** compute a dimensionless mass matrix */ + void compute_mass_matrix(SPAR_MAT &mass_matrix) const; + /** computes a dimensionless mass matrix for the given-quadrature */ + void compute_mass_matrix(const DIAG_MAT &weights, + const SPAR_MAT &N, + SPAR_MAT &mass_matrix) const; + /** compute a single dimensionless mass matrix */ + void compute_lumped_mass_matrix(DIAG_MAT &lumped_mass_matrix) const; + + /** compute lumped mass matrix = diag (\int \rho N_I dV) */ + void compute_lumped_mass_matrix(const Array &mask, + const FIELDS &fields, + const PhysicsModel * physicsModel, + const Array & elementMaterials, + map &mass_matrix, + const Array *element_mask=NULL) const; + /** compute dimensional lumped mass matrix using given quadrature */ + void compute_lumped_mass_matrix(const Array &mask, + const FIELDS &fields, + const PhysicsModel * physicsModel, + const Array > & pointMaterialGroups, + const DIAG_MAT &weights, + const SPAR_MAT &N, + map &mass_matrix) const; + + /** compute an approximation to a finite difference gradient from mesh */ + void compute_gradient_matrix(GRAD_SHPFCN &grad_matrix) const; + + + /** compute energy */ + void compute_energy(const Array &mask, + const FIELDS &fields, + const PhysicsModel * physicsModel, + const Array & elementMaterials, + FIELDS &energy, + const Array *element_mask=NULL) const; + + + /** compute residual or RHS of the dynamic weak eqn */ + void compute_rhs_vector(const Array2D &rhs_mask, + const FIELDS &fields, + const PhysicsModel * physicsModel, + const Array & elementMaterials, + FIELDS &rhs, + const Array *element_mask=NULL) const; + + /** compute RHS for given quadrature */ + void compute_rhs_vector(const Array2D &rhs_mask, + const FIELDS &fields, + const PhysicsModel * physicsModel, + const Array > & pointMaterialGroups, + const DIAG_MAT &weights, + const SPAR_MAT &N, + const GRAD_SHPFCN &dN, + FIELDS &rhs) const; + + /** compute flux in domain i.e. N^T B_integrand */ + void compute_flux(const Array2D & rhs_mask, + const FIELDS &fields, + const PhysicsModel * physicsModel, + const Array & elementMaterials, + GRAD_FIELDS &flux, + const Array *element_mask=NULL) const; + + /** compute the flux on the MD/FE boundary */ + void compute_boundary_flux( + const Array2D & rhs_mask, + const FIELDS & fields, + const PhysicsModel * physicsModel, + const Array & elementMaterials, + const set & faceSet, + FIELDS & rhs) const; + + /** compute the flux on using an L2 interpolation of the flux */ + void compute_boundary_flux( + const Array2D & rhs_mask, + const FIELDS & fields, + const PhysicsModel * physicsModel, + const Array & elementMaterials, + const Array > & pointMaterialGroups, + const DIAG_MAT & weights, + const SPAR_MAT & N, + const GRAD_SHPFCN & dN, + const DIAG_MAT & flux_mask, + FIELDS & rhs ) const; + + /** compute prescribed flux given an array of functions of x & t */ + void add_fluxes(const Array &fieldMask, + const double time, + const SURFACE_SOURCE & sourceFunctions, + FIELDS &nodalSources) const; + + /** compute nodal vector of volume based sources */ + void add_sources(const Array &fieldMask, + const double time, + const VOLUME_SOURCE &sourceFunctions, + FIELDS &nodalSources) const; + + /** compute surface flux of a nodal field */ + void field_surface_flux(const DENS_MAT & field, + const set &faceSet, + DENS_MAT & values, + const bool contour = false, + const int axis = 2) const; + + /*@}*/ + + //---------------------------------------------------------------- + /** \name shape functions */ + //---------------------------------------------------------------- + /*@{*/ + /** evaluate shape function at a list of points in R^3 */ + void evaluate_shape_functions(const MATRIX &coords, + SPAR_MAT &N, + Array & pointToEltMap) const; + + /** evaluate shape function & derivatives at a list of points in R^3 */ + void evaluate_shape_functions( const MATRIX &coords, + SPAR_MAT &N, + GRAD_SHPFCN &dN, + Array & pointToEltMap) const; + + /** evaluate all shape function & derivatives at a specific R^3 location */ + void evaluate_shape_functions(const VECTOR & x, + Array& node_index, + DENS_VEC& shp, + DENS_MAT& dshp, + int & eltID) const; + + /** pass through */ + void shape_functions(const VECTOR &x, + DENS_VEC& shp, + int & eltID, + Array& node_list) const + { feMesh_->shape_functions(x,shp,eltID,node_list); } + + void shape_functions(const VECTOR &x, + DENS_VEC& shp, + int & eltID, + Array& node_list, + const Array& periodicity) const + { feMesh_->shape_functions(x,shp,eltID,node_list, periodicity); } + /*@}*/ + + //---------------------------------------------------------------- + /** \name accessors */ + //---------------------------------------------------------------- + /*@{*/ + /** even though these are pass-throughs there is a necessary translation */ + /** return number of unique nodes */ + int get_nNodes() const { return feMesh_->get_nNodesUnique(); }; + + /** return number of total nodes */ + int get_nNodesTotal() const { return feMesh_->get_nNodes(); }; + + /** return number of elements */ + int get_nElements() const { return feMesh_->get_nElements(); }; + + /** return element connectivity */ + void element_connectivity(const int eltID, Array & nodes) const + { feMesh_->element_connectivity_unique(eltID, nodes); } + + /** return face connectivity */ + void face_connectivity(const PAIR &faceID, Array &nodes) const + { feMesh_->face_connectivity_unique(faceID, nodes); } + + /** in lieu of pass-throughs const accessors ... */ + // return const ptr to mesh + const FE_Mesh* get_feMesh() const { return feMesh_;} + + // return number of spatial dimensions + int get_nsd() const { return feMesh_->get_nSpatialDimensions(); } + + // return if the FE mesh has been created + int fe_mesh_exist() const { return feMesh_!=NULL; } + + // get nodal coordinates for a given element + void element_coordinates(const int eltIdx, DENS_MAT &coords) + { feMesh_->element_coordinates(eltIdx,coords); } + + // access list of elements to be deleted + set & null_elements(void) { return nullElements_; } + /*@}*/ + + private: + //---------------------------------------------------------------- + /** mesh setup commands (called from modify) */ + //---------------------------------------------------------------- + /*@{*/ + + /** initialized flag */ + bool initialized_; + + /** create a uniform, structured mesh */ + void create_mesh(int nx, int ny, int nz, char * regionName, + int xperiodic, int yperiodic, int zperiodic); + /*@}*/ + + /** ATC transfer object */ + ATC_Transfer * atcTransfer_; + + /** finite element mesh */ + FE_Mesh * feMesh_; + + /** data that can be used for a subset of original mesh */ + set nullElements_; + bool amendedMeshData_; + const Array2D * connectivity_; + const Array * nodeMap_; + const DENS_MAT * coordinates_; + + /** workspace */ + int nNodesPerElement_; + int nIPsPerElement_; + int nIPsPerFace_; + int nSD_; + int nElems_; + + /** output object */ + OutputManager outputManager_; + + /** base name for output files */ + string outputPrefix_; + + /** output frequency (NOTE will move to "Transfer") */ + int outputFrequency_; + + /** list of output timesteps */ + vector outputTimes_; + }; +}; // end namespace ATC + +#endif diff --git a/lib/atc/FE_Mesh.cpp b/lib/atc/FE_Mesh.cpp new file mode 100644 index 0000000000..d0eeffe3e4 --- /dev/null +++ b/lib/atc/FE_Mesh.cpp @@ -0,0 +1,1315 @@ +// ATC header files +#include "FE_Element.h" +#include "FE_Mesh.h" +#include "LammpsInterface.h" +#include "ATC_Error.h" +#include "OutputManager.h" +#include "StringManip.h" + +// Other headers +#include + +using namespace std; +using namespace ATC_STRING; + +namespace ATC { + + const static double tol = 1.0e-10; + + // ------------------------------------------------------------- + // ------------------------------------------------------------- + // class FE_Mesh + // ------------------------------------------------------------- + // ------------------------------------------------------------- + + FE_Mesh::FE_Mesh(): + nNodesUnique_(0), nNodes_(0) + { + feElement_ = NULL; + } + + FE_Mesh::~FE_Mesh() + { + if (feElement_) delete feElement_; + } + + // ------------------------------------------------------------- + // modify + // ------------------------------------------------------------- + bool FE_Mesh::modify(int narg, char **arg) + { + bool match = false; + + if (strcmp(arg[0],"mesh")==0) + { + /*! \page man_mesh_faceset fix_modify AtC mesh create_faceset + \section syntax + fix_modify AtC create_faceset + [units] + - = id to assign to the collection of FE faces + - = coordinates of + the bounding box that is coincident with the desired FE faces + - = "in" gives inner faces to the box, + "out" gives the outer faces to the box + - units = option to specify real as opposed to lattice units + \section examples + fix_modify AtC mesh create_faceset obndy -4.0 4.0 -12 12 -12 12 out + \section description + Command to assign an id to a set of FE faces to be used subsequently + in defining flux boundary conditions. + \section restrictions + Only viable for rectangular grids. Also "INF" is not currrently handled. + \section related + \section default + The default option is units = lattice. + */ + if (strcmp(arg[1],"create_faceset")==0) + { + int argIdx = 2; + string tag = arg[argIdx++]; + if (strcmp(arg[argIdx],"plane")==0) + { + argIdx++; + string n(arg[argIdx++]); + int idir, isgn; + string_to_index(n, idir, isgn); + double x = atof(arg[argIdx++]); + if (narg > argIdx && (strcmp(arg[argIdx++],"units") == 0)) + {} + else + { + if (idir == 0) { x *= xscale_; } + else if (idir == 1) { x *= yscale_; } + else if (idir == 2) { x *= zscale_; } + } + create_faceset(tag, x, idir, isgn); + match = true; + } + // bounding_box + else + { + if (strcmp(arg[argIdx],"box")==0) argIdx++; + double xmin = atof(arg[argIdx++]); + double xmax = atof(arg[argIdx++]); + double ymin = atof(arg[argIdx++]); + double ymax = atof(arg[argIdx++]); + double zmin = atof(arg[argIdx++]); + double zmax = atof(arg[argIdx++]); + bool outward = true; + if (narg > argIdx && (strcmp(arg[argIdx++],"in") == 0)) + outward = false; + + if (narg > argIdx && (strcmp(arg[argIdx++],"units") == 0)) + {} + else + { // scale from lattice units to physical units + xmin *= xscale_; + xmax *= xscale_; + ymin *= yscale_; + ymax *= yscale_; + zmin *= zscale_; + zmax *= zscale_; + } + + create_faceset(tag, xmin, xmax, ymin, ymax, zmin, zmax, outward); + match = true; + } + } + /*! \page man_mesh_nodeset fix_modify AtC mesh create_nodeset + \section syntax + fix_modify AtC create_nodeset + + - = id to assign to the collection of FE nodes + - = coordinates of + the bounding box that contains only the desired nodes + \section examples + fix_modify AtC mesh create_faceset left -4.1 3.9 -100 100 -100 100 + \section description + Command to assign an id to a set of FE nodes to be used subsequently + in defining boundary conditions. + \section restrictions + Only viable for rectangular grids. Also "INF" is not currrently handled. + \section related + \section default + Coordinates are assumed to be in lattice units. + */ + else if (strcmp(arg[1],"create_nodeset")==0) { + string tag = arg[2]; + double xmin = atof(arg[3]); + double xmax = atof(arg[4]); + double ymin = atof(arg[5]); + double ymax = atof(arg[6]); + double zmin = atof(arg[7]); + double zmax = atof(arg[8]); + create_nodeset(tag, xmin, xmax, ymin, ymax, zmin, zmax); + match = true; + } + /*! \page man_mesh_elemset fix_modify AtC mesh create_elementset + \section syntax + fix_modify AtC create_elementset + + - = id to assign to the collection of FE element + - = coordinates of + the bounding box that contains only the desired elements + \section examples + fix_modify AtC mesh create_elementset middle -4.1 4.1 -100 100 -100 1100 + \section description + Command to assign an id to a set of FE elements to be used subsequently + in defining material and mesh-based operations. + \section restrictions + Only viable for rectangular grids. Also "INF" is not currrently handled. + \section related + \section default + Coordinates are assumed to be in lattice units. + */ + else if (strcmp(arg[1],"create_elementset")==0) { + string tag = arg[2]; + double xmin = atof(arg[3]); + double xmax = atof(arg[4]); + double ymin = atof(arg[5]); + double ymax = atof(arg[6]); + double zmin = atof(arg[7]); + double zmax = atof(arg[8]); + create_elementset(tag, xmin, xmax, ymin, ymax, zmin, zmax); + match = true; + } + /*! \page man_mesh_output fix_modify AtC mesh output + \section syntax + fix_modify AtC mesh output + \section examples + fix_modify AtC mesh output meshData \n + \section description + Command to output mesh and associated data: nodesets, facesets, and + elementsets. This data is only output once upon initialization since + currently the mesh is static. Creates (binary, "gold" format) Ensight + output of mesh data. + \section restrictions + none + \section related + \section default + none + */ + else if (strcmp(arg[1],"output")==0) { + string outputPrefix = arg[2]; + output(outputPrefix); + match = true; + } + } + return match; + } + + // ------------------------------------------------------------- + // element_coordinates + // ------------------------------------------------------------- + void FE_Mesh::element_coordinates(const int eltID, + DENS_MAT & xCoords) const + { + const int nne = get_nNodesPerElement(); + xCoords.reset(nSD_, nne, false); + for (int inode=0; inode & local_conn = feElement_->local_face_conn(); + + xCoords.reset(nSD_, nnf, false); + + for (int inode=0; inode < nnf; inode++) + { + int id = connectivity_(local_conn(faceID,inode), eltID); + for (int isd=0; isd & FE_Mesh::get_nodeset(const string & name) const + { + NODE_SET_MAP::const_iterator iter = nodeSetMap_.find(name); + if (name == "all") return nodeSetAll_; + else if (iter == nodeSetMap_.end()) + throw ATC_Error(0, "No nodeset with name " + name + " found."); + else return iter->second; + } + + // ------------------------------------------------------------- + // get_elementset + // ------------------------------------------------------------- + const set & FE_Mesh::get_elementset(const string & name) const + { + NODE_SET_MAP::const_iterator iter = elementSetMap_.find(name); + if (name == "all") return elementSetAll_; + else if (iter == elementSetMap_.end()) + throw ATC_Error(0, "No elementset with name " + name + " found."); + else return iter->second; + } + + // ------------------------------------------------------------- + // nodeset_to_minimal_elementset + // ------------------------------------------------------------- + void FE_Mesh::nodeset_to_minimal_elementset + (const string & name, set & elemSet) const + { + if (name == "all") + for (int ielem = 0; ielem < nElts_; ielem++) + elemSet.insert(ielem); + + else + { + NODE_SET_MAP::const_iterator iter = nodeSetMap_.find(name); + if (iter == nodeSetMap_.end()) + throw ATC_Error(0, "No nodeset with name " + name + " found."); + + int npe = get_nNodesPerElement(); + const set &nodeSet = iter->second; + for (int ielem=0; ielem < nElts_; ielem++) + { + int inode = 0; + bool in = true; + while (in && inode < npe) + { + int node = connectivityUnique_(inode, ielem); + set::const_iterator iter = nodeSet.find(node); + if (iter == nodeSet.end()) { in=false; } + inode++; + } + if (in) elemSet.insert(ielem); + } + } + } + + // ------------------------------------------------------------- + // nodeset_to_maximal_elementset + // ------------------------------------------------------------- + void FE_Mesh::nodeset_to_maximal_elementset(const string &name, set &elemSet) const + { + if (name == "all") + for (int ielem = 0; ielem < nElts_; ielem++) + elemSet.insert(ielem); + + else + { + NODE_SET_MAP::const_iterator iter = nodeSetMap_.find(name); + if (iter == nodeSetMap_.end()) + throw ATC_Error(0, "No nodeset with name " + name + " found."); + + int npe = get_nNodesPerElement(); + const set & nodeSet = iter->second; + for (int ielem = 0; ielem < nElts_; ielem++) + { + int inode = 0; + bool in = false; + while (!in && inode < npe) + { + int node = connectivityUnique_(inode, ielem); + set::const_iterator iter = nodeSet.find(node); + if (iter != nodeSet.end()) { in = true; } + inode++; + } + if (in) elemSet.insert(ielem); + } + } + } + + // ------------------------------------------------------------- + // elementset_to_nodeset + // ------------------------------------------------------------- + void FE_Mesh::elementset_to_nodeset + (const set & elemSet, set & nodeSet) const + { + int npe = get_nNodesPerElement(); + set::const_iterator itr; + for (itr = elemSet.begin(); itr != elemSet.end(); itr++) + { + int ielem = *itr; + for (int inode=0; inode < npe; inode++) + { + int node = connectivity_(inode, ielem); + nodeSet.insert(node); + } + } + } + + // ------------------------------------------------------------- + // elementset_to_nodeset + // ------------------------------------------------------------- + void FE_Mesh::elementset_to_nodeset + (const string & name, set & nodeSet) const + { + if (name == "all") + for (int ielem = 0; ielem < nElts_; ielem++) + nodeSet.insert(ielem); + + else + { + ELEMENT_SET_MAP::const_iterator iter = elementSetMap_.find(name); + if (iter == elementSetMap_.end()) + throw ATC_Error(0, "No elementset with name " + name + " found."); + + int npe = get_nNodesPerElement(); + const set &elemSet = iter->second; + set::const_iterator itr; + for (itr = elemSet.begin(); itr != elemSet.end(); itr++) + { + int ielem = *itr; + for (int inode=0; inode < npe; inode++) + { + int node = connectivityUnique_(inode, ielem); + nodeSet.insert(node); + inode++; + } + } + } + } + + // ------------------------------------------------------------- + // elementset_to_minimal_nodeset + // ------------------------------------------------------------- + void FE_Mesh::elementset_to_minimal_nodeset + (const string & name, set & nodeSet) const + { + // return: set - complement_of_set + if (name == "all") { return;} + else + { + elementset_to_nodeset(name,nodeSet); + set compElemSet; + elementset_complement(name,compElemSet); + int npe = get_nNodesPerElement(); + set::const_iterator itr; + for (itr = compElemSet.begin(); itr != compElemSet.end(); itr++) + { + int ielem = *itr; + for (int inode=0; inode < npe; inode++) + { + int node = connectivityUnique_(inode, ielem); + nodeSet.erase(node); + inode++; + } + } + } + } + + // ------------------------------------------------------------- + // elementset_complement + // ------------------------------------------------------------- + void FE_Mesh::elementset_complement + (const string & name, set & cElemSet) const + { + // return: set - complement_of_set + if (name == "all") { return;} + else + { + ELEMENT_SET_MAP::const_iterator iter = elementSetMap_.find(name); + if (iter == elementSetMap_.end()) + throw ATC_Error(0, "No elementset with name " + name + " found."); + + int npe = get_nNodesPerElement(); + const set &elemSet = iter->second; + for (int ielem = 0; ielem < nElts_; ielem++) + { + if(elemSet.find(ielem) == elemSet.end() ) cElemSet.insert(ielem); + } + } + } + + // ------------------------------------------------------------- + // elementset_complement + // ------------------------------------------------------------- + void FE_Mesh::elementset_complement + (const set & elemSet, set & cElemSet) const + { + int npe = get_nNodesPerElement(); + for (int ielem = 0; ielem < nElts_; ielem++) + { + if(elemSet.find(ielem) == elemSet.end() ) cElemSet.insert(ielem); + } + } + // ------------------------------------------------------------- + // faceset_to_nodeset + // ------------------------------------------------------------- + void FE_Mesh::faceset_to_nodeset(const string &name, set &nodeSet) const + { + if (name == "all") { + for (int inode = 0; inode < nNodesUnique_; inode++) + nodeSet.insert(inode); + } + else + { + FACE_SET_MAP::const_iterator faceset = faceSetMap_.find(name); + if (faceset == faceSetMap_.end()) + throw ATC_Error(0, "No faceset with name " + name + " found."); + const set & faceSet = faceset->second; + set::const_iterator iter; + Array conn; + for (iter = faceSet.begin(); iter != faceSet.end(); iter++) + { + pair face = *iter; + face_connectivity_unique(face,conn); + for (int i = 0; i < conn.get_length() ; ++i) { + int inode = conn(i); + nodeSet.insert(inode); + } + } + } + } + // ------------------------------------------------------------- + // faceset_to_nodeset + // ------------------------------------------------------------- + void FE_Mesh::faceset_to_nodeset_global(const string &name, set &nodeSet) const + { + if (name == "all") { + for (int inode = 0; inode < nNodes_; inode++) + nodeSet.insert(inode); + } + else + { + FACE_SET_MAP::const_iterator faceset = faceSetMap_.find(name); + if (faceset == faceSetMap_.end()) + throw ATC_Error(0, "No faceset with name " + name + " found."); + const set & faceSet = faceset->second; + set::const_iterator iter; + Array conn; + for (iter = faceSet.begin(); iter != faceSet.end(); iter++) + { + pair face = *iter; + face_connectivity(face,conn); + for (int i = 0; i < conn.get_length() ; ++i) { + int inode = conn(i); + nodeSet.insert(inode); + } + } + } + } + + + // ------------------------------------------------------------- + // get_faceset + // ------------------------------------------------------------- + const set &FE_Mesh::get_faceset(const string & name) const + { + FACE_SET_MAP::const_iterator iter = faceSetMap_.find(name); + if (iter == faceSetMap_.end()) + { + throw ATC_Error(0, "No faceset with name " + name + " found."); + } + return iter->second; + } + + // ------------------------------------------------------------- + // create_nodeset + // ------------------------------------------------------------- + void FE_Mesh::create_nodeset(const string & name, + double xmin, + double xmax, + double ymin, + double ymax, + double zmin, + double zmax) + { + // Make sure we don't already have a nodeset with this name + NODE_SET_MAP::iterator iter = nodeSetMap_.find(name); + if (iter != nodeSetMap_.end()) { + string message("A nodeset with name " + name + " is already defined."); + throw ATC_Error(0, message); + } + + xmin *= xscale_; + xmax *= xscale_; + ymin *= yscale_; + ymax *= yscale_; + zmin *= zscale_; + zmax *= zscale_; + + set nodeSet; + + // Loop over nodes and add their unique id's to the set if they're + // in the correct range + for (int inode = 0; inode < nNodes_; inode++) { + double x = nodalCoords_(0,inode); + double y = nodalCoords_(1,inode); + double z = nodalCoords_(2,inode); + if ( (xmin <= x) && (x <= xmax) && + (ymin <= y) && (y <= ymax) && + (zmin <= z) && (z <= zmax) ) { + int uid = globalToUniqueMap_(inode); + nodeSet.insert(uid); + } + } + if (nodeSet.size() == 0) { + string message("nodeset " + name + " has zero size."); + throw ATC_Error(0, message); + } + + nodeSetMap_[name] = nodeSet; + + if (ATC::LammpsInterface::instance()->comm_rank() == 0) { + cout << " ATC:: created nodeset " << name + << " with " << nodeSet.size() << " nodes\n"; + } + } + + // ------------------------------------------------------------- + // create_faceset + // ------------------------------------------------------------- + void FE_Mesh::create_faceset(const string & name, + double xmin, + double xmax, + double ymin, + double ymax, + double zmin, + double zmax, + bool outward) + { + // Make sure we don't already have a nodeset with this name + FACE_SET_MAP::iterator iter = faceSetMap_.find(name); + if (iter != faceSetMap_.end()) + throw ATC_Error(0, "A faceset with name " + name + " is already defined."); + + set faceSet; + // Loop over face and add their unique id's to the set if they concide + // with region + const int nf = get_nFacesPerElement(); + const int npf = get_nNodesPerFace(); + const Array2D & face_conn = local_face_connectivity(); + for (int ielem = 0; ielem < nElts_; ielem++) + { + for (int iface = 0; iface < nf; iface++) + { + bool in = true; + bool on_xmin = true, on_xmax = true; + bool on_ymin = true, on_ymax = true; + bool on_zmin = true, on_zmax = true; + bool x_neg = false, x_pos = false; + bool y_neg = false, y_pos = false; + bool z_neg = false, z_pos = false; + double x,y,z; + for (int inode = 0; inode < npf; inode++) + { + x = nodalCoords_(0,connectivity_(face_conn(iface,inode),ielem)); + y = nodalCoords_(1,connectivity_(face_conn(iface,inode),ielem)); + z = nodalCoords_(2,connectivity_(face_conn(iface,inode),ielem)); + + if ( x + tol < xmin) { in = false; break; } + if ( x - tol > xmax) { in = false; break; } + if ( y + tol < ymin) { in = false; break; } + if ( y - tol > ymax) { in = false; break; } + if ( z + tol < zmin) { in = false; break; } + if ( z - tol > zmax) { in = false; break; } + + on_xmin = on_xmin && fabs(x-xmin) <= tol; + on_xmax = on_xmax && fabs(x-xmax) <= tol; + on_ymin = on_ymin && fabs(y-ymin) <= tol; + on_ymax = on_ymax && fabs(y-ymax) <= tol; + on_zmin = on_zmin && fabs(z-zmin) <= tol; + on_zmax = on_zmax && fabs(z-zmax) <= tol; + } + if (in) { + // NOTE this is based on structured grid + if (outward) + { + if (on_xmin && iface==0) { x_neg = true;} + if (on_xmax && iface==1) { x_pos = true;} + if (on_ymin && iface==2) { y_neg = true;} + if (on_ymax && iface==3) { y_pos = true;} + if (on_zmin && iface==4) { z_neg = true;} + if (on_zmax && iface==5) { z_pos = true;} + } + else + { + if (on_xmin && iface==1) { x_pos = true;} + if (on_xmax && iface==0) { x_neg = true;} + if (on_ymin && iface==3) { y_pos = true;} + if (on_ymax && iface==2) { y_neg = true;} + if (on_zmin && iface==5) { z_pos = true;} + if (on_zmax && iface==4) { z_neg = true;} + } + + if ( (x_neg || x_pos) || (y_neg || y_pos) || (z_neg || z_pos) ) { + PAIR face(ielem,iface); + faceSet.insert(face); + } + } + } + } + if (faceSet.empty()) throw ATC_Error(0, "faceset "+name+" is empty."); + + faceSetMap_[name] = faceSet; + if (ATC::LammpsInterface::instance()->comm_rank() == 0) { + cout << " ATC:: created faceset " << name + << " with " << faceSet.size() << " faces\n"; + } + } + + void FE_Mesh::create_faceset(const string & name, + double xRef, + int nIdx, int nSgn) + { + // Make sure we don't already have a nodeset with this name + FACE_SET_MAP::iterator iter = faceSetMap_.find(name); + // NOTE change this to add to a faceset + if (iter != faceSetMap_.end()) + throw ATC_Error(0, "A faceset with name "+name+" is already defined."); + if (periodicFlag_[nIdx]==1) + throw ATC_Error(0,"Faceset definition via plane in periodic direction."); + + set faceSet; + // Loop over faces i& add unique id's to the set if concide w/ plane + int nf = get_nFacesPerElement(); + int npf = get_nNodesPerFace(); + const Array2D & face_conn = local_face_connectivity(); + for (int ielem = 0; ielem < nElts_; ielem++) + { + for (int iface = 0; iface < nf; iface++) + { + bool in = true; + bool neg = false, pos = false; + // all nodes must be on the plane + for (int inode = 0; inode < npf; inode++) { + double x + = nodalCoords_(nIdx,connectivity_(face_conn(iface,inode),ielem)); + if ( fabs(x-xRef) > tol){ in = false;} + } + // check correct orientation + bool add = false; + if (in) + { + if ( (nIdx == 0 && iface==0 && nSgn == -1) + || (nIdx == 0 && iface==1 && nSgn == 1) + || (nIdx == 1 && iface==2 && nSgn == -1) + || (nIdx == 1 && iface==3 && nSgn == 1) + || (nIdx == 2 && iface==4 && nSgn == -1) + || (nIdx == 3 && iface==5 && nSgn == 1) ) + { + PAIR face(ielem,iface); + faceSet.insert(face); + } + } + } + } + + if (faceSet.empty()) + throw ATC_Error(0, "faceset "+name+" is empty."); + + faceSetMap_[name] = faceSet; + if (ATC::LammpsInterface::instance()->comm_rank() == 0) { + cout << " ATC:: created faceset " << name + << " with " << faceSet.size() << " faces\n"; + } + } + // ------------------------------------------------------------- + // create_elementset + // ------------------------------------------------------------- + void FE_Mesh::create_elementset(const string & name, + double xmin, + double xmax, + double ymin, + double ymax, + double zmin, + double zmax) + { + // Make sure we don't already have a elementset with this name + ELEMENT_SET_MAP::iterator iter = elementSetMap_.find(name); + if (iter != elementSetMap_.end()) { + string message("An elementset with name "+name+" is already defined."); + throw ATC_Error(0, message); + } + + xmin *= xscale_; + xmax *= xscale_; + ymin *= yscale_; + ymax *= yscale_; + zmin *= zscale_; + zmax *= zscale_; + + set nodeSet; + + // Loop over nodes and add their unique id's to the set if they're + // in the correct range + for (int inode = 0; inode < nNodes_; inode++) { + double x = nodalCoords_(0,inode); + double y = nodalCoords_(1,inode); + double z = nodalCoords_(2,inode); + if ( (xmin <= x) && (x <= xmax) && + (ymin <= y) && (y <= ymax) && + (zmin <= z) && (z <= zmax) ) { + int uid = globalToUniqueMap_(inode); + nodeSet.insert(uid); + } + } + if (nodeSet.size() == 0) { + string message("elementset " + name + " has zero size."); + throw ATC_Error(0, message); + } + + // create a minimal element set from all the nodes included in the region + set elemSet; + int npe = get_nNodesPerElement(); + for (int ielem=0; ielem < nElts_; ielem++) + { + int inode = 0; + bool in = true; + while (in && inode < npe) + { + int node = connectivityUnique_(inode, ielem); + set::const_iterator iter = nodeSet.find(node); + if (iter == nodeSet.end()) { in=false; } + inode++; + } + if (in) elemSet.insert(ielem); + } + if (elemSet.size() == 0) { + string message("element set " + name + " has zero size."); + throw ATC_Error(0, message); + } + elementSetMap_[name] = elemSet; + + if (ATC::LammpsInterface::instance()->comm_rank() == 0) { + cout << " ATC:: created elementset " << name + << " with " << elemSet.size() << " elements\n"; + } + } + // ------------------------------------------------------------- + // get_nIPsPerElement() + // ------------------------------------------------------------- + int FE_Mesh::get_nIPsPerElement() const + { + return feElement_->num_ips(); + } + // ------------------------------------------------------------- + // get_nNodesPerElement() + // ------------------------------------------------------------- + int FE_Mesh::get_nNodesPerElement() const + { + return feElement_->num_elt_nodes(); + } + // ------------------------------------------------------------- + // get_nFacesPerElement() + // ------------------------------------------------------------- + int FE_Mesh::get_nFacesPerElement() const + { + return feElement_->num_faces(); + } + // ------------------------------------------------------------- + // get_nIPsPerFace() + // ------------------------------------------------------------- + int FE_Mesh::get_nIPsPerFace() const + { + return feElement_->num_face_ips(); + } + // ------------------------------------------------------------- + // get_nNodesPerFace() + // ------------------------------------------------------------- + int FE_Mesh::get_nNodesPerFace() const + { + return feElement_->num_face_nodes(); + } + // ------------------------------------------------------------- + // mappings from element id to associated nodes + // ------------------------------------------------------------- + void FE_Mesh::element_connectivity_global(const int eltID, + Array & nodes) const + { + const int npe = get_nNodesPerElement(); + nodes.reset(npe); + + // use connectivity arrays + for (int inode = 0; inode < npe; inode++) + nodes(inode) = connectivity_(inode, eltID); + } + // ------------------------------------------------------------- + // + // ------------------------------------------------------------- + void FE_Mesh::element_connectivity_unique(const int eltID, + Array & nodes) const + { + const int npe = get_nNodesPerElement(); + nodes.reset(npe); + + // use connectivity arrays + for (int inode = 0; inode < npe; inode++) + nodes(inode) = connectivityUnique_(inode, eltID); + } + + // ------------------------------------------------------------- + // local_face_connectivity() + // ------------------------------------------------------------- + const Array2D & FE_Mesh::local_face_connectivity() const + { + return feElement_->local_face_conn(); + } + + // ------------------------------------------------------------- + // shape function evaluation + // ------------------------------------------------------------- + void FE_Mesh::shape_functions(const VECTOR &x, + DENS_VEC &shp, + int & eltID, + Array &node_list) const + { + // get element id and local coordinates from global coordinates + DENS_VEC xi; + eltID = map_to_element(x, xi); + + // call FE_Engine shape function routines + feElement_->shape_function(eltID,xi,shp); + + // determine nodes which correspond to shp function indices + element_connectivity_unique(eltID,node_list); + + return; + } + + void FE_Mesh::shape_functions(const VECTOR &x, + DENS_VEC &shp, + int & eltID, + Array &node_list, + const Array &periodicity) const + { + // get element id and local coordinates from global coordinates + DENS_VEC xi; + eltID = map_to_element(x, xi, periodicity); + + // call FE_Engine shape function routines + feElement_->shape_function(eltID,xi,shp); + + // determine nodes which correspond to shp function indices + element_connectivity_unique(eltID,node_list); + + return; + } + + void FE_Mesh::shape_functions(const VECTOR &x, + DENS_VEC &shp, + DENS_MAT &dshp, + int & eltID, + Array& node_list) const + { + // get element id and local coordinates from global coordinates + DENS_VEC xi; + eltID = map_to_element(x,xi); + + // call FE_Engine shape function routines + feElement_->shape_function(eltID, xi, shp, dshp); + + // determine nodes which correspond to shp function indices + element_connectivity_unique(eltID,node_list); + return; + } + + void FE_Mesh::shape_function(int eltID, DENS_MAT &N, DIAG_MAT &weights) const + { + vector dN; // NOTE dummy/placeholder + // pass through call + feElement_->shape_function(eltID, N, dN, weights); + } + + void FE_Mesh::shape_function(int eltID, DENS_MAT &N, vector &dN, + DIAG_MAT &weights) const + { + // pass through call + feElement_->shape_function(eltID,N,dN,weights); + } + + void FE_Mesh::face_shape_function(const PAIR &face, + DenseMatrix &N, + vector< DenseMatrix > &dN, + vector< DenseMatrix > &Nn, + DiagonalMatrix &weights) const + { + // pass through call + feElement_->face_shape_function(face,N,dN,Nn,weights); + } + + void FE_Mesh::face_shape_function(const PAIR &face, + DenseMatrix &N, + DenseMatrix &n, + DiagonalMatrix &weights) const + { + // pass through call + feElement_->face_shape_function(face,N,n,weights); + } + + void FE_Mesh::set_quadrature(int type) {feElement_->set_quadrature(type);} + + //----------------------------------------------------------------------- + void FE_Mesh::output(string prefix) const + { + OutputManager meshSets(prefix,GNUPLOT); + meshSets.write_geometry(nodalCoords_, & connectivity_); + OUTPUT_LIST subsetData; + int size = nNodesUnique_; + // material +// DENS_MAT material(nNodes_,1); +// material = 1; +// subsetData["material"] = &material; + // nodesets + NODE_SET_MAP::const_iterator niter; + map nodesets; + for (niter = nodeSetMap_.begin(); niter != nodeSetMap_.end(); niter++) { + string name = niter->first; + const set & nset = niter->second; + string nodeset = "nodeset_"+name; + nodesets[nodeset].reset(size,1); + set::const_iterator iter; + for (iter = nset.begin(); iter != nset.end(); iter++) { + (nodesets[nodeset])(*iter,0) = 1; + } + subsetData[nodeset] = & nodesets[nodeset]; + } + // facesets + FACE_SET_MAP::const_iterator fiter; + map facesets; + for (fiter = faceSetMap_.begin(); fiter != faceSetMap_.end(); fiter++) { + string name = fiter->first; + string faceset = "faceset_"+name; + facesets[faceset].reset(size,1); + set nset; + faceset_to_nodeset(name,nset); + set::const_iterator iter; + for (iter = nset.begin(); iter != nset.end(); iter++) { + (facesets[faceset])(*iter,0) = 1; + } + subsetData[faceset] = & facesets[faceset]; + } + // elementsets + ELEMENT_SET_MAP::const_iterator eiter; + map elemsets; + for (eiter = elementSetMap_.begin();eiter != elementSetMap_.end();eiter++) { + string name = eiter->first; + const set & eset = eiter->second; + string elemset = "elementset_"+name; + elemsets[elemset].reset(size,1); + set::const_iterator iter; + for (iter = eset.begin(); iter != eset.end(); iter++) { + Array nodes; + element_connectivity_unique(*iter, nodes); + for(int i = 0; i < nodes.get_length(); ++i) { + (elemsets[elemset])(nodes(i),0) = 1; + } + } + subsetData[elemset] = & elemsets[elemset]; + } + // output + const int * map = globalToUniqueMap_.get_data(); + if (subsetData.size() > 0 ) + meshSets.write_data(0.0,&subsetData,map); + else + if (LammpsInterface::instance()->comm_rank() == 0) + cout << "ATC: Warning mesh output requested without any mesh entities, output suppressed"; + } + + // ------------------------------------------------------------- + // ------------------------------------------------------------- + // class FE_Uniform3DMesh + // ------------------------------------------------------------- + // ------------------------------------------------------------- + + FE_Uniform3DMesh::FE_Uniform3DMesh(const int nx, + const int ny, + const int nz, + double xmin, double xmax, + double ymin, double ymax, + double zmin, double zmax, + double xscale, + double yscale, + double zscale, + int xperiodic, + int yperiodic, + int zperiodic) + { + nx_[0] = nx; + nx_[1] = ny; + nx_[2] = nz; + + borders_[0][0] = xmin; + borders_[1][0] = xmax; + borders_[0][1] = ymin; + borders_[1][1] = ymax; + borders_[0][2] = zmin; + borders_[1][2] = zmax; + + xscale_ = xscale; + yscale_ = yscale; + zscale_ = zscale; + + // Compute region size and element size + for (int i = 0; i < 3; i++) { + Lx_[i] = borders_[1][i] - borders_[0][i]; + dx_[i] = Lx_[i]/nx_[i]; + } + + // Member data setup + nSD_ = 3; + nElts_ = nx_[0] * nx_[1] * nx_[2]; + nNodes_ = (nx_[0]+1) * (nx_[1]+1) * (nx_[2]+1); + + periodicFlag_[0] = xperiodic; + periodicFlag_[1] = yperiodic; + periodicFlag_[2] = zperiodic; + + feElement_ = new FE_ElementHex(this); + + nodalCoords_.reset(3, nNodes_); + connectivity_.reset(feElement_->num_elt_nodes(), nElts_); + + // Fill nodal coordinates + double ix[3]; + int inode = 0; + for (int k = 0; k <= nx_[2]; ++k) { + ix[2] = borders_[0][2] + k*dx_[2]; + for (int j = 0; j <= nx_[1]; ++j) { + ix[1] = borders_[0][1] + j*dx_[1]; + for (int i = 0; i <= nx_[0]; ++i) { + ix[0] = borders_[0][0] + i*dx_[0]; + for (int m = 0; m < 3; ++m) { + nodalCoords_(m,inode) = ix[m]; + } + ++inode; + } + } + } + + // Compute element connectivities + int ielt = 0; + int noffx = 1; + int noffy = nx_[0] + 1; + int noffz = (nx_[0]+1) * (nx_[1]+1); + for (int k = 0; k < nx_[2]; ++k) { + for (int j = 0; j < nx_[1]; ++j) { + for (int i = 0; i < nx_[0]; ++i) { + int i1 = i + j*noffy + k*noffz; + connectivity_(0,ielt) = i1; + connectivity_(1,ielt) = i1 + noffx; + connectivity_(2,ielt) = i1 + noffx + noffy; + connectivity_(3,ielt) = i1 + noffy; + connectivity_(4,ielt) = i1 + noffz; + connectivity_(5,ielt) = i1 + noffx + noffz; + connectivity_(6,ielt) = i1 + noffx + noffy + noffz; + connectivity_(7,ielt) = i1 + noffy + noffz; + ++ielt; + } + } + } + + // Setup periodicity + setup_periodicity(); + } + + FE_Uniform3DMesh::~FE_Uniform3DMesh() + { + // Nothing to do here + } + + + // ------------------------------------------------------------- + // setup_periodicity + // ------------------------------------------------------------- + void FE_Uniform3DMesh::setup_periodicity() + { + // int to flag whether fem mesh is periodic in each direction + int perfem[3]; + for (int i = 0; i < 3; i++) { + perfem[i] = periodicFlag_[i]; //1; + } + + // determine number of unique nodes + nNodesUnique_ = 1; + for (int i = 0; i < 3; i++) { + nNodesUnique_ *= (nx_[i] + 1 - perfem[i]); + } + + // form maximal nodeset + for (int i = 0; i < nNodesUnique_; i++) { + nodeSetAll_.insert(i); + } + + // Create global-to-unique map: globalToUniqueMap_(ig) = iu + globalToUniqueMap_.reset(nNodes_); + uniqueToGlobalMap_.reset(nNodesUnique_); + for (int k = 0; k <= nx_[2]; ++k) { + int kper = (k == nx_[2] && perfem[2]) ? 0 : k; + for (int j = 0; j <= nx_[1]; ++j) { + int jper = (j == nx_[1] && perfem[1]) ? 0 : j; + for (int i = 0; i <= nx_[0]; ++i) { + int iper = (i == nx_[0] && perfem[0]) ? 0 : i; + int id = i + j*(nx_[0]+1) + k*(nx_[0]+1)*(nx_[1]+1); + int uid = iper + jper*(nx_[0]+1-perfem[0]) + + kper*(nx_[0]+1-perfem[0])*(nx_[1]+1-perfem[1]); + globalToUniqueMap_(id) = uid; + uniqueToGlobalMap_(uid) = id; + } + } + } + + // Map connectivity list + int numEltNodes = feElement_->num_elt_nodes(); + connectivityUnique_.reset(numEltNodes, nElts_); + for (int inode = 0; inode < numEltNodes; inode++) { + for (int ielt = 0; ielt < nElts_; ielt++) { + connectivityUnique_(inode,ielt) = + globalToUniqueMap_(connectivity_(inode,ielt)); + } + } + + // form maximal elementset + for (int i = 0; i < nElts_; i++) { + elementSetAll_.insert(i); + } + + } + + // ------------------------------------------------------------- + // map_to_element + // ------------------------------------------------------------- + + int FE_Uniform3DMesh::map_to_element(const DENS_VEC & x) const + { + + // ix[i] is the element in the ith direction + int ix[3]; + for (int i = 0; i < 3; i++) + { + // handle points on upper boundary + if (fabs(x(i)-borders_[1][i]) < tol) { + ix[i] = nx_[i] - 1; + } + // find bin + else { + ix[i] = (int)floor((x(i)-borders_[0][i])/dx_[i]); + } + // handle points out-of-range [0:nx-1] w/ periodicity + if (ix[i] < 0 || ix[i] > nx_[i]-1) { + if (periodicFlag_[i]) { + ix[i] = (ix[i] +nx_[i]) % nx_[i] ; // assume within a box length + } + else { + string msg = "FE_Uniform3DMesh:: point maps outside of mesh, coordinate " + + index_to_string(i) + " " + tostring(x(i)) + + " not in " + tostring(borders_[0][i]) + " : " + tostring(borders_[1][i]); + throw ATC_Error(0, msg); + } + } + } + int elt = ix[2]*(nx_[0]*nx_[1]) + ix[1]*nx_[0] + ix[0]; + return elt; + } + + int FE_Uniform3DMesh::map_to_element(const DENS_VEC & x, + DENS_VEC & xi) const + { + + xi.reset(3); + // ix[i] is the element in the ith direction + int ix[3]; + for (int i = 0; i < 3; i++) { + // handle points on upper boundary + if (fabs(x(i)-borders_[1][i]) < tol) { + ix[i] = nx_[i] - 1; } + // find bin + else { + ix[i] = (int)floor((x(i)-borders_[0][i])/dx_[i]); } + + // handle points out-of-range [0:nx-1] w/ periodicity + int shift = 0; + if (ix[i] < 0 || ix[i] > nx_[i]-1) { + if (periodicFlag_[i]) { + if (ix[i] < 0) shift = 1; + else shift = -1; + ix[i] = (ix[i]+shift*nx_[i]) % nx_[i] ; // assume within a box length + } + else { + string msg = "FE_Uniform3DMesh:: point maps outside of mesh, coordinate " + + index_to_string(i) + " " + tostring(x(i)) + + " not in " + tostring(borders_[0][i]) + " : " + tostring(borders_[1][i]); + throw ATC_Error(0, msg); + } + } + double x_lower = borders_[0][i] + (ix[i] - shift*nx_[i])*dx_[i]; + xi(i) = 2.0*(x(i) - x_lower)/dx_[i] - 1.0; + } + int elt = ix[2]*(nx_[0]*nx_[1]) + ix[1]*nx_[0] + ix[0]; + return elt; + } + + int FE_Uniform3DMesh::map_to_element(const DENS_VEC & x, + DENS_VEC & xi, + const Array & periodicFlag) const + { + + xi.reset(3); + // ix[i] is the element in the ith direction + int ix[3]; + for (int i = 0; i < 3; i++) { + // handle points on upper boundary + if (fabs(x(i)-borders_[1][i]) < tol) { + ix[i] = nx_[i] - 1; } + // find bin + else { + ix[i] = (int)floor((x(i)-borders_[0][i])/dx_[i]); } + + // handle points out-of-range [0:nx-1] w/ periodicity + int shift = 0; + if (ix[i] < 0 || ix[i] > nx_[i]-1) { + if (periodicFlag(i)) { + if (ix[i] < 0) shift = 1; + else shift = -1; + ix[i] = (ix[i]+shift*nx_[i]) % nx_[i] ; // assume within a box length + } + else { + string msg = "FE_Uniform3DMesh:: point maps outside of mesh, coordinate " + + index_to_string(i) + " " + tostring(x(i)) + + " not in " + tostring(borders_[0][i]) + " : " + tostring(borders_[1][i]); + throw ATC_Error(0, msg); + } + } + double x_lower = borders_[0][i] + (ix[i] - shift*nx_[i])*dx_[i]; + xi(i) = 2.0*(x(i) - x_lower)/dx_[i] - 1.0; + } + int elt = ix[2]*(nx_[0]*nx_[1]) + ix[1]*nx_[0] + ix[0]; + return elt; + } + +} // namespace ATC diff --git a/lib/atc/FE_Mesh.h b/lib/atc/FE_Mesh.h new file mode 100644 index 0000000000..c35565bb09 --- /dev/null +++ b/lib/atc/FE_Mesh.h @@ -0,0 +1,387 @@ +#ifndef FE_MESH_H +#define FE_MESH_H + +// ATC_Transfer headers +#include "Array.h" +#include "Array2D.h" +#include "MatrixLibrary.h" +#include "ATC_TypeDefs.h" + +// Other headers +#include +#include +#include +#include + +using namespace std; + +namespace ATC { + // Forward declarations + class FE_Element; + + /** + * @class FE_Mesh + * @brief Base class for a finite element mesh + */ + class FE_Mesh { + + public: + + /** constructor */ + FE_Mesh(); + + /** destructor */ + virtual ~FE_Mesh(); + + /** parser/modifier */ + bool modify(int narg, char **arg); + + /** evaluate shape function at real coordinates */ + void shape_functions(const VECTOR &x, + DENS_VEC& shp, + int & eltID, + Array& node_list) const; + + /** evaluate shape function at real coordinates */ + void shape_functions(const VECTOR &x, + DENS_VEC& shp, + int & eltID, + Array& node_list, + const Array& periodicity) const; + + /** evaluate shape function at real coordinates */ + void shape_functions(const VECTOR &x, + DENS_VEC & shp, + DENS_MAT & dshp, + int & eltID, + Array& node_list) const; + + /** evaluate shape functions for all ip's on an element */ + // N is numIPsInElement X numNodesInElement + void shape_function(const int eltID, + DENS_MAT &N, + DIAG_MAT &weights) const; + + /** evaluate shape functions for all ip's on an element */ + // N is numIPsInElement X numNodesInElement + void shape_function(const int eltID, + DENS_MAT &N, + vector &dN, + DIAG_MAT &weights) const; + + /** evaluate shape functions for all ip's on an face*/ + // N is numIPsInFace X numNodesInElement + void face_shape_function(const PAIR & face, + DENS_MAT &N, + vector &dN, + vector &Nn, + DIAG_MAT &weights) const; + + void face_shape_function(const PAIR & face, + DENS_MAT &N, + DENS_MAT &n, + DIAG_MAT &weights) const; + + /** return connectivity (global ID numbers) for element eltID */ + void element_connectivity_global(const int eltID, + Array & nodes) const; + + void element_connectivity_unique(const int eltID, + Array & nodes) const; + + void face_connectivity(const pair & faceID, + Array & nodes) const + { int nNodesPerFace = get_nNodesPerFace(); + nodes.reset(nNodesPerFace); + int eltID = faceID.first; + int localFace = faceID.second; + const Array2D & localFaceConn = local_face_connectivity(); + for(int i = 0; i < nNodesPerFace; ++i) { + nodes(i) = connectivity_(localFaceConn(localFace,i),eltID); + } + } + void face_connectivity_unique(const pair & faceID, + Array & nodes) const + { int nNodesPerFace = get_nNodesPerFace(); + nodes.reset(nNodesPerFace); + int eltID = faceID.first; + int localFace = faceID.second; + const Array2D & localFaceConn = local_face_connectivity(); + for(int i = 0; i < nNodesPerFace; ++i) { + nodes(i) = connectivityUnique_(localFaceConn(localFace,i),eltID); + } + } + + + + /** + * return spatial coordinates for element nodes on eltID, + * indexed xCoords(isd,inode) + */ + void element_coordinates(const int eltID, + DENS_MAT & xCoords) const; + + void face_coordinates(const pair face, + DENS_MAT & xCoords) const; + + + /** access to the nodal coordinate values */ + const DENS_MAT & nodal_coordinates(void) {return nodalCoords_ ;} + + /** access to nodal coordinates of a unique node */ + DENS_VEC nodal_coordinates(const int nodeID) const; + + /** access to nodal coordinates of a node */ + DENS_VEC global_coordinates(const int nodeID) const; + + /** access to the element connectivity values */ + const Array2D & connectivity(void) {return connectivity_ ;} + + /** map spatial location to element */ + virtual int map_to_element(const DENS_VEC & x) const = 0; + + /** map spatial location to element and local coordinates */ + virtual int map_to_element(const DENS_VEC & x, + DENS_VEC & xi) const = 0; + + /** map spatial location to element and local coordinates */ + virtual int map_to_element(const DENS_VEC & x, + DENS_VEC & xi, + const Array& periodicity) const = 0; + + /** map global node numbering to unique node numbering */ + int map_global_to_unique(const int global_id) const {return globalToUniqueMap_(global_id);} + inline const int* global_to_unique_map_pointer(void) {return globalToUniqueMap_.get_data();} + inline const Array& global_to_unique_map(void) {return globalToUniqueMap_;} + + /** map unique node numbering a global node numbering */ + int map_unique_to_global(const int unique_id) + {return uniqueToGlobalMap_(unique_id);} + inline const int* unique_to_global_map(void) {return uniqueToGlobalMap_.get_data();} + + /** query whether a nodeset with the given name exists */ + bool query_nodeset(const string & name) const; + + /** get node set (unique ID's) from the string name assigned to the set */ + const set & get_nodeset(const string & name) const; + + /** create node set with tag "name" from nodes in given spatial range */ + void create_nodeset(const string & name, + double xmin, double xmax, + double ymin, double ymax, + double zmin, double zmax); + + /** get element set from the string name assigned to the set */ + const set & get_elementset(const string & name) const; + + /** create element set with tag "name" from nodes in given spatial range */ + void create_elementset(const string & name, + double xmin, double xmax, + double ymin, double ymax, + double zmin, double zmax); + + /** get the minimal element set from a nodeset by name */ + void nodeset_to_minimal_elementset + (const string & name, set & elemSet) const; + /** get the maximal element set from a nodeset by name */ + void nodeset_to_maximal_elementset + (const string & name, set & elemSet) const; + /** get complement of element set by name */ + void elementset_complement(const string & name, set & elemSet) const; + void elementset_complement(const set & elemSet, set & elemSetComplement) const; + /** get the node set from an element set by name */ + void elementset_to_minimal_nodeset + (const string & name, set & nodeSet) const; + void elementset_to_nodeset(const string & name, set & nodeSet) const; + void elementset_to_nodeset(const set & elemSet, set & nodeSet) const; + /** convert faceset to nodeset in _unique_node numbering */ + void faceset_to_nodeset(const string &name, set &nodeSet) const; + void faceset_to_nodeset_global(const string &name, set &nodeSet) const; + + /** get face set from the string name assigned to the set */ + const set< pair > & get_faceset(const string & name) const; + + /** create face set with tag "name" from faces aligned with box */ + void create_faceset(const string & name, + double xmin, double xmax, + double ymin, double ymax, + double zmin, double zmax, + bool outward); + void create_faceset(const string & name, double x, int idir, int isgn); + + /** return number of spatial dimensions */ + int get_nSpatialDimensions() const { return nSD_; }; + + /** return total number of nodes */ + int get_nNodes() const { return nNodes_; }; + + /** return number of unique nodes */ + int get_nNodesUnique() const { return nNodesUnique_; }; + + /** return number of elements */ + int get_nElements() const { return nElts_; }; + + /** return number of integration points per element */ + int get_nIPsPerElement() const; + + /** return number of nodes per element */ + int get_nNodesPerElement() const; + + /** return number of faces per element */ + int get_nFacesPerElement() const; + + /** return number of nodes per face */ + int get_nNodesPerFace() const; + + /** return number of integration points per face */ + int get_nIPsPerFace() const; + + /** return scale in x */ + double get_xscale() const {return xscale_;} + + /** return scale in y */ + double get_yscale() const {return yscale_;} + + /** return scale in z */ + double get_zscale() const {return zscale_;} + + /** local face connectivity */ + const Array2D & local_face_connectivity() const; + + /** element size in each direction */ + virtual void element_size(const int ielem, + double hx, double hy, double hz) = 0; + + /** element size in each direction */ + virtual double min_element_size(void) const = 0; + + /** set quadrature on element : a pass-through */ + void set_quadrature(int type); + + /** output mesh subsets */ + void output(string prefix) const; + + + protected: + + /** number of spatial dimensions */ + int nSD_; + + /** number of elements */ + int nElts_; + + /** number of nodes */ + int nNodes_; + int nNodesUnique_; + + /** periodicity flags */ + int periodicFlag_[3]; + + /** element type for this mesh */ + FE_Element *feElement_; + + /** Nodal coordinates: nodalCoords_(nsd, numnode) */ + DENS_MAT nodalCoords_; + + /** Element connectivity: connectivity_(neltnode, nelt) */ + Array2D connectivity_; + Array2D connectivityUnique_; + + /** map of global to unique node ID's */ + Array globalToUniqueMap_; + + /** map of unique to global node ID's */ + Array uniqueToGlobalMap_; + + /** map of string names to node sets */ + NODE_SET_MAP nodeSetMap_; + + /** maximal nodeset */ + set nodeSetAll_; + + /** map of string names to node sets */ + FACE_SET_MAP faceSetMap_; + + /** map of string names to element sets */ + ELEMENT_SET_MAP elementSetMap_; + + /** maximal elementset */ + set elementSetAll_; + + /** length scaling used by lammps */ + double xscale_, yscale_, zscale_; + + /** scratch memory for setting face connectivities */ + Array connScratch_; + + }; + + /** + * @class FE_Uniform3DMesh + * @brief Derived class for a uniform mesh, a structured mesh with + * fixed element sizes in x, y, and z directions + */ + + class FE_Uniform3DMesh : public FE_Mesh { + + public: + + /** constructor */ + FE_Uniform3DMesh(const int nx, + const int ny, + const int nz, + double xmin, double xmax, + double ymin, double ymax, + double zmin, double zmax, + double xscale, + double yscale, + double zscale, + int xperiodic, + int yperiodic, + int zperiodic); + + /** destructor */ + ~FE_Uniform3DMesh(); + + virtual void element_size(const int ielem, + double hx, double hy, double hz) + { hx = Lx_[0]/nx_[0]; hy = Lx_[1]/nx_[1]; hz = Lx_[2]/nx_[2]; } + + virtual double min_element_size(void) const + {return min(Lx_[0]/nx_[0], min(Lx_[1]/nx_[1], Lx_[2]/nx_[2])); } + + /** map spatial location to element */ + virtual int map_to_element(const DENS_VEC & x) const; + + /** map spatial location to element and local coordinates */ + virtual int map_to_element(const DENS_VEC & x, + DENS_VEC & xi) const; + + /** map spatial location to element and local coordinates */ + virtual int map_to_element(const DENS_VEC & x, + DENS_VEC & xi, + const Array & periodicity) const; + + + protected: + + // Number of elements in each spatial direction + int nx_[3]; + + // Bounds of region on which mesh is defined + double borders_[2][3]; + + // Region size in each direction + double Lx_[3]; + + // Element size in each direction + double dx_[3]; + + /** create global-to-unique node mapping */ + void setup_periodicity(); + + }; + +}; // namespace ATC_Transfer + +#endif // FE_MESH_H diff --git a/lib/atc/FieldEulerIntegrator.cpp b/lib/atc/FieldEulerIntegrator.cpp new file mode 100644 index 0000000000..5afb0cd117 --- /dev/null +++ b/lib/atc/FieldEulerIntegrator.cpp @@ -0,0 +1,101 @@ +// Header file for this class +#include "FieldEulerIntegrator.h" + +// Other ATC includes +#include "ATC_Transfer.h" +#include "FE_Engine.h" +#include "PhysicsModel.h" +#include "GMRES.h" +#include "ImplicitSolveOperator.h" + +namespace ATC { + +// ==================================================================== +// FieldEulerIntegrator +// ==================================================================== +FieldEulerIntegrator::FieldEulerIntegrator( + const FieldName fieldName, + const PhysicsModel * physicsModel, + /*const*/ FE_Engine * feEngine, + /*const*/ ATC_Transfer * atcTransfer, + const Array2D< bool > & rhsMask // copy +) + : atc_(atcTransfer), + feEngine_(feEngine), + physicsModel_(physicsModel), + fieldName_(fieldName), + rhsMask_(rhsMask) +{ + nNodes_ = feEngine->get_nNodes(); +} + +// ==================================================================== +// FieldImplicitIntegrator +// ==================================================================== +FieldExplicitEulerIntegrator::FieldExplicitEulerIntegrator( + const FieldName fieldName, + const PhysicsModel * physicsModel, + /*const*/ FE_Engine * feEngine, + /*const*/ ATC_Transfer * atcTransfer, + const Array2D< bool > & rhsMask // copy +) : FieldEulerIntegrator(fieldName,physicsModel,feEngine,atcTransfer,rhsMask) +{ +} + +// -------------------------------------------------------------------- +// update +// -------------------------------------------------------------------- +void FieldExplicitEulerIntegrator::update(const double dt, double time, + FIELDS & fields, FIELDS & rhs) +{ // NOTE time is not used + atc_->compute_rhs_vector(rhsMask_, fields, rhs, + atc_->FULL_DOMAIN, physicsModel_); + atc_->apply_inverse_mass_matrix(rhs[fieldName_],fieldName_); + fields[fieldName_] += dt*rhs[fieldName_]; +} + +// ==================================================================== +// FieldImplicitEulerIntegrator +// ==================================================================== +FieldImplicitEulerIntegrator::FieldImplicitEulerIntegrator( + const FieldName fieldName, + const PhysicsModel * physicsModel, + /*const*/ FE_Engine * feEngine, + /*const*/ ATC_Transfer * atcTransfer, + const Array2D< bool > & rhsMask, // copy + const double alpha +) : FieldEulerIntegrator(fieldName,physicsModel,feEngine,atcTransfer,rhsMask), + alpha_(alpha), + dT_(1.0e-6), + maxRestarts_(50), + maxIterations_(200), + tol_(1.0e-8) +{ +} + +// -------------------------------------------------------------------- +// update +// -------------------------------------------------------------------- +void FieldImplicitEulerIntegrator::update(const double dt, double time, + FIELDS & fields, FIELDS & rhs) +{ // solver handles bcs + FieldImplicitSolveOperator solver(atc_, feEngine_, // NOTE make persistent + fields, fieldName_, rhsMask_, physicsModel_, + time, dt, alpha_); + DiagonalMatrix preconditioner = solver.get_preconditioner(fields); + DENS_VEC myRhs = solver.get_rhs(); + DENS_VEC dT(nNodes_); dT = dT_; + DENS_MAT H(maxRestarts_+1, maxRestarts_); + double tol = tol_; // tol returns the residual + int iterations = maxIterations_; // iterations returns number of iterations + int restarts = maxRestarts_; + int convergence = GMRES(solver, + dT, myRhs, preconditioner, H, restarts, iterations, tol); + if (convergence != 0) { + throw ATC_Error(0,field_to_string(fieldName_) + " evolution did not converge"); + } + fields[fieldName_] += dT; + rhs[fieldName_] = myRhs; +} + +} // namespace ATC diff --git a/lib/atc/FieldEulerIntegrator.h b/lib/atc/FieldEulerIntegrator.h new file mode 100644 index 0000000000..455b0ce1a1 --- /dev/null +++ b/lib/atc/FieldEulerIntegrator.h @@ -0,0 +1,138 @@ +#ifndef FIELD_EULER_INTEGRATOR_H +#define FIELD_EULER_INTEGRATOR_H + +// ATC includes +#include "Array2D.h" +#include "MatrixLibrary.h" +#include "PhysicsModel.h" +#include "TimeIntegrator.h" +#include "ImplicitSolveOperator.h" + +// other includes +#include +#include + +namespace ATC { + +// Forward class declarations +class ATC_Transfer; +class FE_Engine; + +/** + * @class FieldEulerIntegrator + * @brief method for integrating fast fields + */ +//class FieldEulerIntegrator : public TimeIntegrator { // NOTE need to construct +class FieldEulerIntegrator { + + public: + + /** Constructor */ + FieldEulerIntegrator( + const FieldName fieldName, + const PhysicsModel * physicsModel, + /*const*/ FE_Engine * feEngine, + /*const*/ ATC_Transfer * atcTransfer, + const Array2D< bool > & rhsMask // copy + ); + + + /** Destructor */ + virtual ~FieldEulerIntegrator() {}; + + /** update */ + virtual void update(const double dt, const double time, + FIELDS & fields, FIELDS & rhs) = 0; + + protected: + + /** Pointer to ATC_Tranfer */ + ATC_Transfer * atc_; + + /** Pointer to FE_Engine */ + /*const*/ FE_Engine * feEngine_; + + /** Pointer to PhysicsModel */ + const PhysicsModel * physicsModel_; + + /** field name */ + FieldName fieldName_; + + /** rhs mask */ + Array2D rhsMask_; + + /** number of nodes */ + int nNodes_; +}; + +/** + * @class FieldExplicitEulerIntegrator + * @brief explicit Euler method for integrating fast electron fields + */ +class FieldExplicitEulerIntegrator : public FieldEulerIntegrator { + + public: + + /** Constructor */ + FieldExplicitEulerIntegrator( + const FieldName fieldName, + const PhysicsModel * physicsModel, + /*const*/ FE_Engine * feEngine, + /*const*/ ATC_Transfer * atcTransfer, + const Array2D< bool > & rhsMask // copy + ); + + /** Destructor */ + virtual ~FieldExplicitEulerIntegrator() {}; + + /** update */ + void update(const double dt, const double time, + FIELDS & fields, FIELDS & rhs); + +}; + +/** + * @class FieldImplicitEulerIntegrator + * @brief explicit Euler method for integrating fast electron fields + */ +class FieldImplicitEulerIntegrator : public FieldEulerIntegrator { + + public: + + /** Constructor */ + FieldImplicitEulerIntegrator( + const FieldName fieldName, + const PhysicsModel * physicsModel, + /*const*/ FE_Engine * feEngine, + /*const*/ ATC_Transfer * atcTransfer, + const Array2D< bool > & rhsMask, // copy + const double alpha = 0.5 // default to trap/midpt + ); + + /** Destructor */ + virtual ~FieldImplicitEulerIntegrator() {}; + + /** update */ + void update(const double dt, const double time, + FIELDS & fields, FIELDS & rhs); + + protected: + /** euler update factor */ + double alpha_; + + /** perturbation */ + double dT_; + + /** max number of restarts = size of basis */ + int maxRestarts_; + + /** max number of iterations */ + int maxIterations_; + + /** convergence tolerance */ + double tol_; +}; + +} // namespace ATC + +#endif diff --git a/lib/atc/GMRES.h b/lib/atc/GMRES.h new file mode 100644 index 0000000000..3233445434 --- /dev/null +++ b/lib/atc/GMRES.h @@ -0,0 +1,150 @@ +//***************************************************************** +// Iterative template routine -- GMRES +// +// GMRES solves the unsymmetric linear system Ax = b using the +// Generalized Minimum Residual method +// +// GMRES follows the algorithm described on p. 20 of the +// SIAM Templates book. +// +// The return value indicates convergence within max_iter (input) +// iterations (0), or no convergence within max_iter iterations (1). +// +// Upon successful return, output arguments have the following values: +// +// x -- approximate solution to Ax = b +// max_iter -- the number of iterations performed before the +// tolerance was reached +// tol -- the residual after the final iteration +// +//***************************************************************** + + +template < class Matrix, class Vector > +void +Update(Vector &x, int k, Matrix &h, Vector &s, Vector v[]) +{ + Vector y(s); + + // Backsolve: + for (int i = k; i >= 0; i--) { + y(i) /= h(i,i); + for (int j = i - 1; j >= 0; j--) + y(j) -= h(j,i) * y(i); + } + + for (int j = 0; j <= k; j++) + x += v[j] * y(j); +} + + +template < class Real > +Real +abs(Real x) +{ + return (x > 0 ? x : -x); +} + + +template < class Operator, class Vector, class Preconditioner, + class Matrix, class Real > +int +GMRES(const Operator &A, Vector &x, const Vector &b, + const Preconditioner &M, Matrix &H, int &m, int &max_iter, + Real &tol) +{ + Real resid; + int i, j = 1, k; + Vector s(m+1), cs(m+1), sn(m+1), w; + + Vector p = inv(M)*b; + Real normb = p.norm(); + Vector r = inv(M) * (b - A * x); + Real beta = r.norm(); + + if (normb == 0.0) + normb = 1; + + if ((resid = r.norm() / normb) <= tol) { + tol = resid; + max_iter = 0; + return 0; + } + + Vector *v = new Vector[m+1]; + + while (j <= max_iter) { + v[0] = r * (1.0 / beta); // ??? r / beta + s = 0.0; + s(0) = beta; + + for (i = 0; i < m && j <= max_iter; i++, j++) { + w = inv(M) * (A * v[i]); + for (k = 0; k <= i; k++) { + H(k, i) = w.dot(v[k]); + w -= H(k, i) * v[k]; + } + H(i+1, i) = w.norm(); + v[i+1] = w * (1.0 / H(i+1, i)); // ??? w / H(i+1, i) + + for (k = 0; k < i; k++) + ApplyPlaneRotation(H(k,i), H(k+1,i), cs(k), sn(k)); + + GeneratePlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i)); + ApplyPlaneRotation(H(i,i), H(i+1,i), cs(i), sn(i)); + ApplyPlaneRotation(s(i), s(i+1), cs(i), sn(i)); + + if ((resid = abs(s(i+1)) / normb) < tol) { + Update(x, i, H, s, v); + tol = resid; + max_iter = j; + delete [] v; + return 0; + } + } + Update(x, m - 1, H, s, v); + r = inv(M) * (b - A * x); + beta = r.norm(); + if ((resid = beta / normb) < tol) { + tol = resid; + max_iter = j; + delete [] v; + return 0; + } + } + + tol = resid; + delete [] v; + return 1; +} + + +#include + + +template +void GeneratePlaneRotation(Real &dx, Real &dy, Real &cs, Real &sn) +{ + if (dy == 0.0) { + cs = 1.0; + sn = 0.0; + } else if (abs(dy) > abs(dx)) { + Real temp = dx / dy; + sn = 1.0 / sqrt( 1.0 + temp*temp ); + cs = temp * sn; + } else { + Real temp = dy / dx; + cs = 1.0 / sqrt( 1.0 + temp*temp ); + sn = temp * cs; + } +} + + +template +void ApplyPlaneRotation(Real &dx, Real &dy, Real &cs, Real &sn) +{ + Real temp = cs * dx + sn * dy; + dy = -sn * dx + cs * dy; + dx = temp; +} + diff --git a/lib/atc/ImplicitSolveOperator.cpp b/lib/atc/ImplicitSolveOperator.cpp new file mode 100644 index 0000000000..afef546504 --- /dev/null +++ b/lib/atc/ImplicitSolveOperator.cpp @@ -0,0 +1,160 @@ +// Header file for this class +#include "ImplicitSolveOperator.h" + +// Other ATC includes +#include "ATC_Transfer.h" +#include "FE_Engine.h" +#include "PhysicsModel.h" +#include "PrescribedDataManager.h" + +namespace ATC { + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// ImplicitSolveOperator +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +ImplicitSolveOperator:: +ImplicitSolveOperator(ATC_Transfer * atcTransfer, + /*const*/ FE_Engine * feEngine, + const PhysicsModel * physicsModel) + : atcTransfer_(atcTransfer), + feEngine_(feEngine), + physicsModel_(physicsModel) +{ + // Nothing else to do here +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// FieldImplicitSolveOperator +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +FieldImplicitSolveOperator:: +FieldImplicitSolveOperator(ATC_Transfer * atcTransfer, + /*const*/ FE_Engine * feEngine, + FIELDS & fields, + const FieldName fieldName, + const Array2D< bool > & rhsMask, + const PhysicsModel * physicsModel, + double simTime, + double dt, + double alpha) + : ImplicitSolveOperator(atcTransfer, feEngine, physicsModel), + fields_(fields), // ref to fields + fieldName_(fieldName), + simTime_(simTime), + dt_(dt), + alpha_(alpha), + epsilon0_(1.0e-8) +{ + // find field associated with ODE + rhsMask_.reset(NUM_FIELDS,NUM_FLUX); + rhsMask_ = false; + for (int i = 0; i < rhsMask.nCols(); i++) { + rhsMask_(fieldName_,i) = rhsMask(fieldName_,i); + } + massMask_.reset(1); + massMask_(0) = fieldName_; + + // Save off current field + TnVect_ = column(fields_[fieldName_],0); // NOTE assuming 1 dof ? + + // Allocate vectors for fields and rhs + int nNodes = atcTransfer_->get_nNodes(); + // copy fields + fieldsNp1_ = fields_; + // size rhs + int dof = fields_[fieldName_].nCols(); + RnMap_ [fieldName_].reset(nNodes,dof); + RnpMap_[fieldName_].reset(nNodes,dof); + + // Compute the RHS vector R(T^n) + // Set BCs on Rn, multiply by inverse mass and then extract its vector + atcTransfer_->compute_rhs_vector(rhsMask_, fields_, RnMap_, + atcTransfer_->FULL_DOMAIN, physicsModel_); + DENS_MAT & Rn = RnMap_[fieldName_]; + atcTransfer_->get_prescribed_data_manager() + ->set_fixed_dfield(simTime_, fieldName_, Rn); + atcTransfer_->apply_inverse_mass_matrix(Rn,fieldName_); + RnVect_ = column(Rn,0); +} + + +// -------------------------------------------------------------------- +// operator *(Vector) +// -------------------------------------------------------------------- +DENS_VEC +FieldImplicitSolveOperator::operator * (DENS_VEC x) const +{ + // This method uses a matrix-free approach to approximate the + // multiplication by matrix A in the matrix equation Ax=b, where the + // matrix equation results from an implicit treatment of the + // fast field solve for the Two Temperature Model. In + // brief, if the ODE for the fast field can be written: + // + // dT/dt = R(T) + // + // A generalized discretization can be written: + // + // 1/dt * (T^n+1 - T^n) = alpha * R(T^n+1) + (1-alpha) * R(T^n) + // + // Taylor expanding the R(T^n+1) term and rearranging gives the + // equation to be solved for dT at each timestep: + // + // [1 - dt * alpha * dR/dT] * dT = dt * R(T^n) + // + // The operator defined in this method computes the left-hand side, + // given a vector dT. It uses a finite difference, matrix-free + // approximation of dR/dT * dT, giving: + // + // [1 - dt * alpha * dR/dT] * dT = dt * R(T^n) + // ~= dT - dt*alpha/epsilon * ( R(T^n + epsilon*dT) - R(T^n) ) + // + + + // Compute epsilon + double epsilon = (x.norm() > 0.0) ? epsilon0_ * TnVect_.norm()/x.norm() : epsilon0_; + + // Compute incremented vector = T + epsilon*dT + fieldsNp1_[fieldName_] = TnVect_ + epsilon * x; + + // Evaluate R(b) + atcTransfer_->compute_rhs_vector(rhsMask_, fieldsNp1_, RnpMap_, + atcTransfer_->FULL_DOMAIN, physicsModel_); + DENS_MAT & Rnp = RnpMap_[fieldName_]; + atcTransfer_->get_prescribed_data_manager() + ->set_fixed_dfield(simTime_, fieldName_, Rnp); + atcTransfer_->apply_inverse_mass_matrix(Rnp,fieldName_); + RnpVect_ = column(Rnp,0); + + // Compute full left hand side and return it + DENS_VEC Ax = x - dt_ * alpha_ / epsilon * (RnpVect_ - RnVect_); + return Ax; +} + +// -------------------------------------------------------------------- +// get_rhs +// -------------------------------------------------------------------- +DENS_VEC +FieldImplicitSolveOperator::get_rhs() +{ + // Return dt * R(T^n) + return dt_ * RnVect_; +} + +// -------------------------------------------------------------------- +// get_preconditioner +// -------------------------------------------------------------------- +DIAG_MAT +FieldImplicitSolveOperator::get_preconditioner(FIELDS & fields) +{ + // Just create and return identity matrix + int nNodes = atcTransfer_->get_nNodes(); + DENS_VEC ones(nNodes); + ones = 1.0; + DIAG_MAT identity(ones); + return identity; +} + +} // namespace ATC diff --git a/lib/atc/ImplicitSolveOperator.h b/lib/atc/ImplicitSolveOperator.h new file mode 100644 index 0000000000..d1177d4da8 --- /dev/null +++ b/lib/atc/ImplicitSolveOperator.h @@ -0,0 +1,129 @@ +#ifndef IMPLICIT_SOLVE_OPERATOR_H +#define IMPLICIT_SOLVE_OPERATOR_H + +// ATC includes +#include "Array2D.h" +#include "MatrixLibrary.h" +#include "PhysicsModel.h" + +// other includes +#include +#include + +namespace ATC { + +// Forward class declarations +class ATC_Transfer; +class FE_Engine; + +/** + * @class ImplicitSolveOperator + * @brief Helper class to compute matrix-free product for use with IML++ solvers + */ +class ImplicitSolveOperator { + + public: + + /** Constructor */ + ImplicitSolveOperator(ATC_Transfer * atcTransfer, + /*const*/ FE_Engine * feEngine, + const PhysicsModel * physicsModel); + + /** Destructor */ + virtual ~ImplicitSolveOperator() {}; + + /** pure virtual operator to compute Ax, for equation Ax=b */ + virtual DENS_VEC operator * (DENS_VEC x) const = 0; + + /** pure virtual method to return the rhs vector b */ + virtual DENS_VEC get_rhs() = 0; + + /** pure virtual method to return preconditioner */ + virtual DiagonalMatrix get_preconditioner(FIELDS & fields) = 0; + + protected: + + /** Pointer to ATC_Tranfer */ + ATC_Transfer * atcTransfer_; + + /** Pointer to FE_Engine */ + /*const*/ FE_Engine * feEngine_; + + /** Pointer to PhysicsModel */ + const PhysicsModel * physicsModel_; + +}; + +/** + * @class FieldImplicitSolveOperator + * @brief Class to perform A*x operation for electron temperature solution + */ +class FieldImplicitSolveOperator : public ImplicitSolveOperator { + + public: + + /** Constructor */ + FieldImplicitSolveOperator(ATC_Transfer * atc_Transfer, + /*const*/ FE_Engine * fe_Engine, + FIELDS & fields, + const FieldName electronField, + const Array2D< bool > & rhsMask, + const PhysicsModel * physicsModel, + double simTime, + double dt, + double alpha); + + /** Destructor */ + virtual ~FieldImplicitSolveOperator() {}; + + /** operator to compute A*x for the electron temperature equation */ + virtual DENS_VEC operator * (DENS_VEC x) const; + + /** method to return the rhs vector b */ + virtual DENS_VEC get_rhs(); + + /** method to return preconditioner (identity matrix) */ + virtual DIAG_MAT get_preconditioner(FIELDS & fields); + + protected: + // field name of ODE to solve + FieldName fieldName_; + + // Reference to current fields (passed in constructor) + FIELDS & fields_; + + // Local fields + mutable FIELDS fieldsNp1_; + + // Vector to hold current temperature + DENS_VEC TnVect_; + + // Old and new RHS maps (not including inverse mass) + FIELDS RnMap_; + mutable FIELDS RnpMap_; + + // Matrices/vectors to hold electron temperature components of RHS + // vectors (including inverse mass) + DENS_VEC RnVect_; + mutable DENS_VEC RnpVect_; + + Array2D rhsMask_; + Array massMask_; + + // simulation time + double simTime_; + + // timestep + double dt_; + + // implicit/explicit parameter (0 -> explicit, else implicit) + double alpha_; + + // small parameter to compute increment + double epsilon0_; + +}; + +} // namespace ATC + +#endif diff --git a/lib/atc/Kinetostat.cpp b/lib/atc/Kinetostat.cpp new file mode 100644 index 0000000000..2dee91fecd --- /dev/null +++ b/lib/atc/Kinetostat.cpp @@ -0,0 +1,1234 @@ +// ATC_Transfer Headers +#include "ATC_Error.h" +#include "Kinetostat.h" +#include "CG.h" +#include "ATC_Transfer.h" +#include "LammpsInterface.h" + +namespace ATC { + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class Kinetostat + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + Kinetostat::Kinetostat(ATC_Transfer * atcTransfer) : + AtomicRegulator(atcTransfer), + kinetostatType_(NONE), + couplingMode_(UNCOUPLED) + { + // do nothing + } + + //-------------------------------------------------------- + // modify: + // parses and adjusts kinetostat state based on + // user input, in the style of LAMMPS user input + //-------------------------------------------------------- + bool Kinetostat::modify(int narg, char **arg) + { + bool foundMatch = false; + + // fluxstat type + /*! \page man_disp_control fix_modify AtC transfer momentum control + \section syntax + fix_modify AtC transfer momentum control none \n + + fix_modify AtC transfer momentum control rescale \n + - frequency (int) = time step frequency for applying displacement and velocity rescaling \n + + fix_modify AtC transfer momentum control glc_displacement \n + + fix_modify AtC transfer momentum control glc_velocity \n + + fix_modify AtC transfer momentum control flux [faceset face_set_id, interpolate] + - face_set_id (string) = id of boundary face set, if not specified + (or not possible when the atomic domain does not line up with + mesh boundaries) defaults to an atomic-quadrature approximate + evaulation\n + \section examples + fix_modify AtC transfer momentum control glc_velocity \n + fix_modify AtC transfer momentum control stress_flux faceset bndy_faces \n + \section description + \section restrictions + only for be used with specific transfers : + elastic \n + rescale not valid with time filtering activated + \section related + \section default + none + */ + + int argIndex = 0; + if (strcmp(arg[argIndex],"none")==0) { // restore defaults + kinetostatType_ = NONE; + couplingMode_ = UNCOUPLED; + howOften_ = 1; + boundaryIntegrationType_ = ATC_Transfer::NO_QUADRATURE; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"glc_displacement")==0) { + kinetostatType_ = GLC_DISPLACEMENT; + couplingMode_ = FIXED; + howOften_ = 1; + boundaryIntegrationType_ = ATC_Transfer::NO_QUADRATURE; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"glc_velocity")==0) { + kinetostatType_ = GLC_VELOCITY; + couplingMode_ = FIXED; + howOften_ = 1; + boundaryIntegrationType_ = ATC_Transfer::NO_QUADRATURE; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"hoover")==0) { + kinetostatType_ = FORCE; + couplingMode_ = FIXED; + howOften_ = 1; + boundaryIntegrationType_ = ATC_Transfer::NO_QUADRATURE; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"flux")==0) { + kinetostatType_ = FORCE; + couplingMode_ = FLUX; + howOften_ = 1; + argIndex++; + boundaryIntegrationType_ = atcTransfer_->parse_boundary_integration(narg-argIndex,&arg[argIndex],boundaryFaceSet_); + foundMatch = true; + } + + if (!foundMatch) + foundMatch = AtomicRegulator::modify(narg,arg); + if (foundMatch) + needReset_ = true; + return foundMatch; + } + + //-------------------------------------------------------- + // reset_nodal_lambda_force: + // resets the kinetostat generated force to a + // prescribed value + //-------------------------------------------------------- + void Kinetostat::reset_lambda_force(DENS_MAT & target) + { + for (int i = 0; i < nNodes_; ++i) + for (int j = 0; j < nsd_; ++j) + lambdaForceFiltered_(i,j) = target(i,j); + } + + //-------------------------------------------------------- + // initialize: + // sets up methods before a run + // NOTE we currently only include time filter + // dependence, but in general there is also a + // time integrator dependence. In general the + // precedence order is: + // time filter -> time integrator -> kinetostat + // In the future this may need to be added if + // different types of time integrators can be + // specified. + //-------------------------------------------------------- + void Kinetostat::initialize() + { + // NOTE: only support basic time integration + TimeFilterManager * timeFilterManager = atcTransfer_->get_time_filter_manager(); + + // reset data if needed and perform any error/conflict checking + if (resetData_) { + AtomicRegulator::reset_data(); + + // set up storage + lambda_.reset(nNodes_,nsd_); + nodalAtomicLambdaForce_.reset(nNodes_,nsd_); + lambdaForceFiltered_.reset(nNodes_,nsd_); + } + + if (needReset_ || timeFilterManager->need_reset() || timeFilterManager->end_equilibrate()) { + // eliminate existing methods + destroy(); + + DENS_MAT nodalGhostForceFiltered; + if (timeFilterManager->end_equilibrate() && kinetostatType_==FORCE) { + StressFlux * myMethod; + myMethod = dynamic_cast(regulatorMethod_); + nodalGhostForceFiltered = myMethod->get_filtered_ghost_force(); + } + + if (timeFilterManager->need_reset()) { + if (timeFilter_) + delete timeFilter_; + timeFilter_ = timeFilterManager->construct(TimeFilterManager::IMPLICIT_UPDATE); + } + + if (timeFilterManager->filter_dynamics()) { + switch (kinetostatType_) { + case NONE: { + break; + } + case GLC_DISPLACEMENT: { + regulatorMethod_ = new DisplacementGlcFiltered(this); + break; + } + case GLC_VELOCITY: { + regulatorMethod_ = new VelocityGlcFiltered(this); + break; + } + case FORCE: { + regulatorMethod_ = new StressFluxFiltered(this); + if (timeFilterManager->end_equilibrate()) { + StressFlux * myMethod; + myMethod = dynamic_cast(regulatorMethod_); + myMethod->reset_filtered_ghost_force(nodalGhostForceFiltered); + } + break; + } + default: + throw ATC_Error(0,"Unknown kinetostat type in Kinetostat::initialize"); + } + } + else { + switch (kinetostatType_) { + case NONE: { + break; + } + case GLC_DISPLACEMENT: { + regulatorMethod_ = new DisplacementGlc(this); + break; + } + case GLC_VELOCITY: { + regulatorMethod_ = new VelocityGlc(this); + break; + } + case FORCE: { + regulatorMethod_ = new StressFlux(this); + break; + } + default: + throw ATC_Error(0,"Unknown kinetostat type in Kinetostat::initialize"); + } + } + + AtomicRegulator::reset_method(); + } + + AtomicRegulator::initialize(); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class GlcKinetostat + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and kinetostat data + //-------------------------------------------------------- + GlcKinetostat::GlcKinetostat(Kinetostat *kinetostat) + : + RegulatorShapeFunction(kinetostat), + kinetostat_(kinetostat), + timeFilter_(atomicRegulator_->get_time_filter()), + nodalAtomicLambdaForce_(kinetostat_->get_nodal_atomic_lambda_force()), + lambdaForceFiltered_(kinetostat_->get_lambda_force_filtered()), + lambdaForce_(atomicRegulator_->get_lambda_force()), + mdMassMatrix_(atcTransfer_->get_mass_mat_md(VELOCITY)), + nodeToOverlapMap_(atcTransfer_->get_node_to_overlap_map()) + { + // set up list of nodes using Hoover coupling + // (a) nodes with prescribed values + Array2D isFixedNode = atcTransfer_->get_fixed_node_flags(VELOCITY); + for (int i = 0; i < nNodes_; ++i) + for (int j = 0; j < isFixedNode.nCols(); ++j) + if (isFixedNode(i,j)) + hooverNodes_.insert(pair(i,j)); + + // (b) AtC coupling nodes + if (kinetostat_->get_coupling_mode()==Kinetostat::FIXED) { + Array & nodeType(atcTransfer_->get_node_type()); + if (atcTransfer_->use_localized_lambda()) { + for (int i = 0; i < nNodes_; ++i) + if (nodeType(i)==ATC_Transfer::BOUNDARY) + for (int j = 0; j < nsd_; ++j) + hooverNodes_.insert(pair(i,j)); + } + else { + for (int i = 0; i < nNodes_; ++i) + if (nodeType(i)==ATC_Transfer::BOUNDARY || nodeType(i)==ATC_Transfer::MD_ONLY) + for (int j = 0; j < nsd_; ++j) + hooverNodes_.insert(pair(i,j)); + } + } + } + + //-------------------------------------------------------- + // apply_lambda_to_atoms + // uses existing lambda to modify given + // atomic quantity + //-------------------------------------------------------- + void GlcKinetostat::apply_to_atoms(double ** atomicQuantity, + const DENS_MAT & lambdaAtom, + double dt) + { + if (nLocal_>0) { + for (int i = 0; i < nLocal_; i++) { + for (int j = 0; j < nsd_; j++) { + atomicQuantity[internalToAtom_(i)][j] -= lambdaAtom(i,j); + } + } + } + } + + //-------------------------------------------------------- + // set_weights + // sets diagonal weighting matrix used in + // solve_for_lambda + //-------------------------------------------------------- + void GlcKinetostat::set_weights(DIAG_MAT & weights) + { + if (nLocalLambda_>0) { + DENS_VEC weightVector(nLocal_); + weightVector = 1.; + DENS_VEC maskedWeightVector(nLocalLambda_); + maskedWeightVector = internalToOverlapMap_*weightVector; + weights.reset(maskedWeightVector); + } + } + + //-------------------------------------------------------- + // reset_nlocal: + // resets data dependent on local atom count + //-------------------------------------------------------- + void GlcKinetostat::reset_nlocal() + { + RegulatorShapeFunction::reset_nlocal(); + if (nLocal_ > 0) { + atcTransfer_->compute_atomic_mass(atomicMass_); + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class DisplacementGlc + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and kinetostat data + //-------------------------------------------------------- + DisplacementGlc::DisplacementGlc(Kinetostat * kinetostat) : + GlcKinetostat(kinetostat), + nodalDisplacements_(atcTransfer_->get_field(DISPLACEMENT)), + x_(atcTransfer_->get_x()) + { + // sets up time filter for cases where variables temporally filtered + TimeFilterManager * timeFilterManager = atcTransfer_->get_time_filter_manager(); + if (!timeFilterManager->end_equilibrate()) { + nodalAtomicLambdaForce_ = 0.; + lambdaForceFiltered_ = 0.; + timeFilter_->initialize(lambdaForceFiltered_); + } + } + + //-------------------------------------------------------- + // apply: + // apply the kinetostat to the atoms + //-------------------------------------------------------- + void DisplacementGlc::apply_post_predictor(double dt) + { + compute_kinetostat(dt); + } + + //-------------------------------------------------------- + // compute_kinetostat + // manages the solution and application of the + // kinetostat equations and variables + //-------------------------------------------------------- + void DisplacementGlc::compute_kinetostat(double dt) + { + // initial filtering update + apply_pre_filtering(dt); + + // set up rhs + DENS_MAT rhs(nNodes_,nsd_); + set_kinetostat_rhs(rhs,dt); + + // solve linear system for lambda + solve_for_lambda(rhs); + + // compute force applied by lambda + DENS_MAT lambdaAtom; + compute_lambda_force(lambdaForce_,lambdaAtom,dt); + + // compute nodal atomic power + compute_nodal_lambda_force(dt); + + // apply kinetostat to atoms + apply_to_atoms(x_,lambdaAtom); + } + + //-------------------------------------------------------- + // set_kinetostat_rhs + // sets up the right-hand side of the + // kinetostat equations + //-------------------------------------------------------- + void DisplacementGlc::set_kinetostat_rhs(DENS_MAT & rhs, double dt) + { + // form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii + DENS_MAT atomicDisplacement; + atcTransfer_->compute_atomic_centerOfMass_displacement(atomicDisplacement,x_); + rhs.reset(nNodes_,nsd_); + atcTransfer_->restrict_volumetric_quantity(atomicDisplacement,rhs); + rhs -= (atcTransfer_->get_mass_mat_md(VELOCITY))*nodalDisplacements_; + } + + //-------------------------------------------------------- + // compute_lambda_force + // compute the equivalent force on the atoms + // induced by lambda + //-------------------------------------------------------- + void DisplacementGlc::compute_lambda_force(DENS_MAT & lambdaForce, + DENS_MAT & lambdaAtom, + double dt) + { + if (nLocal_>0) { + // prolongation to (unique) nodes + lambdaAtom.reset(nLocal_,nsd_); + atcTransfer_->prolong(lambda_,lambdaAtom); + + for (int i = 0; i < nLocal_; i++) + for (int j = 0; j < nsd_; j++) + lambdaForce(i,j) = (-1./(dt*dt))*lambdaAtom(i,j)*atomicMass_(i); + } + } + + //-------------------------------------------------------- + // compute_nodal_lambda_force + // compute the effective FE force applied + // by the kinetostat + //-------------------------------------------------------- + void DisplacementGlc::compute_nodal_lambda_force(double dt) + { + atcTransfer_->restrict_volumetric_quantity(lambdaForce_,nodalAtomicLambdaForce_); + timeFilter_->apply_post_step1(lambdaForceFiltered_,nodalAtomicLambdaForce_,dt); + + // update FE displacements for localized thermostats + apply_localization_correction(nodalAtomicLambdaForce_, + nodalDisplacements_, + dt*dt); + } + + //-------------------------------------------------------- + // apply_pre_filtering + // applies first step of filtering to + // relevant variables + //-------------------------------------------------------- + void DisplacementGlc::apply_pre_filtering(double dt) + { + // apply time filtered lambda force + DENS_MAT lambdaZero(nNodes_,nsd_); + timeFilter_->apply_pre_step1(lambdaForceFiltered_,(-1./dt/dt)*lambdaZero,dt); + } + + //-------------------------------------------------------- + // set_weights + // sets diagonal weighting matrix used in + // solve_for_lambda + //-------------------------------------------------------- + void DisplacementGlc::set_weights(DIAG_MAT & weights) + { + if (nLocalLambda_>0) { + DENS_VEC maskedWeightVector(nLocalLambda_); + maskedWeightVector = internalToOverlapMap_*atomicMass_; + weights.reset(maskedWeightVector); + } + } + + //-------------------------------------------------------- + // apply_localization_correction + // corrects for localized kinetostats only + // solving kinetostat equations on a subset + // of the MD region + //-------------------------------------------------------- + void DisplacementGlc::apply_localization_correction(const DENS_MAT & source, + DENS_MAT & nodalField, + double weight) + { + // NOTE can add check to see if set is empty for faster performance + DENS_MAT nodalLambdaRoc(nNodes_,nsd_); + atcTransfer_->apply_inverse_mass_matrix(source, + nodalLambdaRoc, + VELOCITY); + set >::const_iterator iter; + for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) { + nodalLambdaRoc(iter->first,iter->second) = 0.; + } + + nodalField += weight*nodalLambdaRoc; + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void DisplacementGlc::output(double dt, OUTPUT_LIST & outputData) + { + outputData["lambda"] = &(lambda_); + outputData["nodalLambdaForce"] = &(nodalAtomicLambdaForce_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class DisplacementGlcFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and kinetostat data + //-------------------------------------------------------- + DisplacementGlcFiltered::DisplacementGlcFiltered(Kinetostat * kinetostat) : + DisplacementGlc(kinetostat), + nodalAtomicDisplacements_(atcTransfer_->get_atomic_field(DISPLACEMENT)) + { + // do nothing + } + + //-------------------------------------------------------- + // apply_pre_filtering + // applies first step of filtering to + // relevant variables + //-------------------------------------------------------- + void DisplacementGlcFiltered::apply_pre_filtering(double dt) + { + // apply time filtered lambda to atomic fields + DisplacementGlc::apply_pre_filtering(dt); + DENS_MAT nodalAcceleration(nNodes_,nsd_); + atcTransfer_->apply_inverse_md_mass_matrix(lambdaForceFiltered_, + nodalAcceleration, + VELOCITY); + nodalAtomicDisplacements_ += dt*dt*nodalAcceleration; + } + + //-------------------------------------------------------- + // set_kinetostat_rhs + // sets up the right-hand side of the + // kinetostat equations + //-------------------------------------------------------- + void DisplacementGlcFiltered::set_kinetostat_rhs(DENS_MAT & rhs, double dt) + { + // form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii + double coef = 1./(timeFilter_->get_unfiltered_coefficient_pre_s1(dt)); + rhs = coef*(atcTransfer_->get_mass_mat_md(VELOCITY))*(nodalAtomicDisplacements_ - nodalDisplacements_); + } + + //-------------------------------------------------------- + // compute_nodal_lambda_force + // compute the effective FE force applied + // by the kinetostat + //-------------------------------------------------------- + void DisplacementGlcFiltered::compute_nodal_lambda_force(double dt) + { + atcTransfer_->restrict_volumetric_quantity(lambdaForce_,nodalAtomicLambdaForce_); + timeFilter_->apply_post_step1(lambdaForceFiltered_,nodalAtomicLambdaForce_,dt); + + // update filtered atomic displacements + DENS_MAT nodalLambdaRoc(nodalAtomicLambdaForce_.nRows(),nodalAtomicLambdaForce_.nCols()); + atcTransfer_->apply_inverse_md_mass_matrix(nodalAtomicLambdaForce_, + nodalLambdaRoc, + VELOCITY); + timeFilter_->apply_post_step1(nodalAtomicDisplacements_,dt*dt*nodalLambdaRoc,dt); + + // update FE displacements for localized thermostats + apply_localization_correction(lambdaForceFiltered_, + nodalDisplacements_, + dt*dt); + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void DisplacementGlcFiltered::output(double dt, OUTPUT_LIST & outputData) + { + outputData["lambda"] = &(lambda_); + outputData["nodalLambdaForce"] = &(lambdaForceFiltered_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class VelocityGlc + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and kinetostat data + //-------------------------------------------------------- + VelocityGlc::VelocityGlc(Kinetostat * kinetostat) : + GlcKinetostat(kinetostat), + nodalVelocities_(atcTransfer_->get_field(VELOCITY)), + v_(atcTransfer_->get_v()) + { + // sets up time filter for cases where variables temporally filtered + TimeFilterManager * timeFilterManager = atcTransfer_->get_time_filter_manager(); + if (!timeFilterManager->end_equilibrate()) { + nodalAtomicLambdaForce_ = 0.; + lambdaForceFiltered_ = 0.; + timeFilter_->initialize(lambdaForceFiltered_); + } + } + + //-------------------------------------------------------- + // apply_mid_corrector: + // apply the kinetostat during the middle of the + // predictor phase + //-------------------------------------------------------- + void VelocityGlc::apply_mid_predictor(double dt) + { + double dtLambda = 0.5*dt; + compute_kinetostat(dtLambda); + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the kinetostat after the corrector phase + //-------------------------------------------------------- + void VelocityGlc::apply_post_corrector(double dt) + { + double dtLambda = 0.5*dt; + compute_kinetostat(dt); + } + + //-------------------------------------------------------- + // apply_pre_filtering + // applies first step of filtering to + // relevant variables + //-------------------------------------------------------- + void VelocityGlc::apply_pre_filtering(double dt) + { + // apply time filtered lambda to atomic fields + DENS_MAT lambdaZero(nNodes_,nsd_); + timeFilter_->apply_pre_step1(lambdaForceFiltered_,(-1./dt)*lambdaZero,dt); + } + + //-------------------------------------------------------- + // compute_kinetostat + // manages the solution and application of the + // kinetostat equations and variables + //-------------------------------------------------------- + void VelocityGlc::compute_kinetostat(double dt) + { + // initial filtering update + apply_pre_filtering(dt); + + // set up rhs + DENS_MAT rhs(nNodes_,nsd_); + set_kinetostat_rhs(rhs,dt); + + // solve linear system for lambda + solve_for_lambda(rhs); + + // compute force applied by lambda + DENS_MAT lambdaAtom; + compute_lambda_force(lambdaForce_,lambdaAtom,dt); + + // compute nodal atomic power + compute_nodal_lambda_force(dt); + + // apply kinetostat to atoms + apply_to_atoms(v_,lambdaAtom); + } + + //-------------------------------------------------------- + // set_kinetostat_rhs + // sets up the right-hand side of the + // kinetostat equations + //-------------------------------------------------------- + void VelocityGlc::set_kinetostat_rhs(DENS_MAT & rhs, double dt) + { + // form rhs : sum_a (hatN_Ia * x_ai) - (\dot{Upsilon})_Ii + DENS_MAT atomicMomentum; + atcTransfer_->compute_atomic_momentum(atomicMomentum,v_); + rhs.reset(nNodes_,nsd_); + atcTransfer_->restrict_volumetric_quantity(atomicMomentum,rhs); + rhs -= (atcTransfer_->get_mass_mat_md(VELOCITY))*nodalVelocities_; + } + + //-------------------------------------------------------- + // compute_lambda_force + // compute the equivalent force on the atoms + // induced by lambda + //-------------------------------------------------------- + void VelocityGlc::compute_lambda_force(DENS_MAT & lambdaForce, + DENS_MAT & lambdaAtom, + double dt) + { + if (nLocal_>0) { + // prolongation to (unique) nodes + lambdaAtom.reset(nLocal_,nsd_); + atcTransfer_->prolong(lambda_,lambdaAtom); + + for (int i = 0; i < nLocal_; i++) + for (int j = 0; j < nsd_; j++) + lambdaForce(i,j) = (-1./(dt))*lambdaAtom(i,j)*atomicMass_(i); + } + } + + //-------------------------------------------------------- + // compute_nodal_lambda_force + // compute the effective FE force applied + // by the kinetostat + //-------------------------------------------------------- + void VelocityGlc::compute_nodal_lambda_force(double dt) + { + atcTransfer_->restrict_volumetric_quantity(lambdaForce_,nodalAtomicLambdaForce_); + timeFilter_->apply_pre_step1(lambdaForceFiltered_,nodalAtomicLambdaForce_,dt); + + // update FE displacements for localized thermostats + apply_localization_correction(nodalAtomicLambdaForce_, + nodalVelocities_, + dt); + } + + //-------------------------------------------------------- + // set_weights + // sets diagonal weighting matrix used in + // solve_for_lambda + //-------------------------------------------------------- + void VelocityGlc::set_weights(DIAG_MAT & weights) + { + if (nLocalLambda_>0) { + DENS_VEC maskedWeightVector = internalToOverlapMap_*atomicMass_; + weights.reset(maskedWeightVector); + } + } + + //-------------------------------------------------------- + // apply_localization_correction + // corrects for localized kinetostats only + // solving kinetostat equations on a subset + // of the MD region + //-------------------------------------------------------- + void VelocityGlc::apply_localization_correction(const DENS_MAT & source, + DENS_MAT & nodalField, + double weight) + { + // NOTE can add check to see if set is empty for faster performance + DENS_MAT nodalLambdaRoc(nNodes_,nsd_); + atcTransfer_->apply_inverse_mass_matrix(source, + nodalLambdaRoc, + VELOCITY); + set >::const_iterator iter; + for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) { + nodalLambdaRoc(iter->first,iter->second) = 0.; + } + + nodalField += weight*nodalLambdaRoc; + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void VelocityGlc::output(double dt, OUTPUT_LIST & outputData) + { + outputData["lambda"] = &(lambda_); + outputData["nodalLambdaForce"] = &(nodalAtomicLambdaForce_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class VelocityGlcFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and kinetostat data + //-------------------------------------------------------- + VelocityGlcFiltered::VelocityGlcFiltered(Kinetostat *kinetostat) + : VelocityGlc(kinetostat), + nodalAtomicVelocities_(atcTransfer_->get_atomic_field(VELOCITY)) + { + // do nothing + } + + //-------------------------------------------------------- + // apply_pre_filtering + // applies first step of filtering to + // relevant variables + //-------------------------------------------------------- + void VelocityGlcFiltered::apply_pre_filtering(double dt) + { + // apply time filtered lambda to atomic fields + VelocityGlc::apply_pre_filtering(dt); + DENS_MAT nodalAcceleration(nNodes_,nsd_); + atcTransfer_->apply_inverse_md_mass_matrix(lambdaForceFiltered_, + nodalAcceleration, + VELOCITY); + nodalAtomicVelocities_ += dt*nodalAcceleration; + } + + //-------------------------------------------------------- + // set_kinetostat_rhs + // sets up the right-hand side of the + // kinetostat equations + //-------------------------------------------------------- + void VelocityGlcFiltered::set_kinetostat_rhs(DENS_MAT & rhs, double dt) + { + // form rhs : sum_a (hatN_Ia * x_ai) - (Upsilon)_Ii + double coef = 1./(timeFilter_->get_unfiltered_coefficient_pre_s1(dt)); + rhs = coef*(atcTransfer_->get_mass_mat_md(VELOCITY))*(nodalAtomicVelocities_ - nodalVelocities_); + } + + //-------------------------------------------------------- + // compute_nodal_lambda_force + // compute the effective FE force applied + // by the kinetostat + //-------------------------------------------------------- + void VelocityGlcFiltered::compute_nodal_lambda_force(double dt) + { + atcTransfer_->restrict_volumetric_quantity(lambdaForce_,nodalAtomicLambdaForce_); + timeFilter_->apply_post_step1(lambdaForceFiltered_,nodalAtomicLambdaForce_,dt); + + // update filtered atomic displacements + DENS_MAT nodalLambdaRoc(nodalAtomicLambdaForce_.nRows(),nodalAtomicLambdaForce_.nCols()); + atcTransfer_->apply_inverse_md_mass_matrix(nodalAtomicLambdaForce_, + nodalLambdaRoc, + VELOCITY); + timeFilter_->apply_post_step1(nodalAtomicVelocities_,dt*nodalLambdaRoc,dt); + + // update FE displacements for localized thermostats + apply_localization_correction(lambdaForceFiltered_, + nodalVelocities_, + dt); + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void VelocityGlcFiltered::output(double dt, OUTPUT_LIST & outputData) + { + outputData["lambda"] = &(lambda_); + outputData["nodalLambdaForce"] = &(lambdaForceFiltered_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class StressFlux + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and kinetostat data + //-------------------------------------------------------- + StressFlux::StressFlux(Kinetostat * kinetostat) : + GlcKinetostat(kinetostat), + nodalForce_(atcTransfer_->get_field_rhs(VELOCITY)), + nodalAtomicForce_(atcTransfer_->get_fe_atomic_field_roc(VELOCITY)), + momentumSource_(atcTransfer_->get_atomic_source(VELOCITY)), + v_(atcTransfer_->get_v()), + f_(atcTransfer_->get_f()) + { + // flag for performing boundary flux calculation + if (kinetostat_->get_coupling_mode()==Kinetostat::FLUX) + fieldMask_(VELOCITY,FLUX) = true; + + // sets up space for ghost force related variables + nodalGhostForce_.reset(nNodes_,nsd_); + nodalGhostForceFiltered_.reset(nNodes_,nsd_); + + // NOTE ifdefs are for using reference lammps force as base pressure +#if false + int nLocalTotal = atcTransfer->get_nlocal_total(); + int nsd = atcTransfer->get_nsd(); + f0_ = new double*[nLocalTotal]; + for (int i = 0; i < nLocalTotal; ++i) { + f0_[i] = new double[nsd]; + for (int j = 0; j < nsd; ++j) + f0_[i][j] = f_[i][j]; + } +#endif + } + + StressFlux::~StressFlux() + { +#if false + if (f0_) { + ATC_Transfer * atcTransfer = kinetostat_->get_atc_transfer(); + int nLocalTotal = atcTransfer->get_nlocal_total(); + for (int i = 0; i < nLocalTotal; ++i) + delete [] f0_[i]; + delete [] f0_; + } +#endif + } + + //-------------------------------------------------------- + // apply_pre_predictor: + // apply the kinetostat to the atoms in the + // mid-predictor integration phase + //-------------------------------------------------------- + void StressFlux::apply_mid_predictor(double dt) + { + double dtLambda = 0.5*dt; + // apply lambda force to atoms + apply_to_atoms(v_,lambdaForce_,dtLambda); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the kinetostat to the atoms in the + // pre-corrector integration phase + //-------------------------------------------------------- + void StressFlux::apply_pre_corrector(double dt) + { + // compute the kinetostat force + compute_kinetostat(dt); + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the kinetostat to the atoms in the + // post-corrector integration phase + //-------------------------------------------------------- + void StressFlux::apply_post_corrector(double dt) + { + double dtLambda = 0.5*dt; + // apply lambda force to atoms + apply_to_atoms(v_,lambdaForce_,dtLambda); + } + + //-------------------------------------------------------- + // compute_kinetostat + // manages the solution and application of the + // kinetostat equations and variables + //-------------------------------------------------------- + void StressFlux::compute_kinetostat(double dt) + { + // set up ghost force + compute_ghost_force(nodalGhostForce_); + + // initial filtering update + apply_pre_filtering(dt); + + // set up rhs + DENS_MAT rhs(nNodes_,nsd_); + set_kinetostat_rhs(rhs,dt); + + // solve linear system for lambda + solve_for_lambda(rhs); + + // compute force applied by lambda + compute_lambda_force(lambdaForce_); + + // compute nodal atomic power + compute_nodal_lambda_force(dt); + } + + //-------------------------------------------------------- + // apply_pre_filtering + // applies first step of filtering to + // relevant variables + //-------------------------------------------------------- + void StressFlux::apply_pre_filtering(double dt) + { + // apply time filtered lambda force + DENS_MAT lambdaZero(nNodes_,nsd_); + timeFilter_->apply_pre_step1(lambdaForceFiltered_,lambdaZero,dt); + timeFilter_->apply_pre_step1(nodalGhostForceFiltered_,nodalGhostForce_,dt); + } + + //-------------------------------------------------------- + // set_kinetostat_rhs + // sets up the RHS of the kinetostat equations + // for the coupling parameter lambda + //-------------------------------------------------------- + void StressFlux::set_kinetostat_rhs(DENS_MAT & rhs, double dt) + { + // (a) for flux based : + // form rhs : \int N_I r dV - \sum_g N_Ig^* f_g + // sources are set in ATC transfer + rhs.reset(nNodes_,nsd_); + rhs = momentumSource_ - nodalGhostForce_; + + // (b) for ess. bcs + // NOTE can add check to see if set is empty for faster performance + // form rhs : {sum_a (N_Ia * f_ia) - M_md * (ddupsilon/dt)_I} + DENS_MAT rhsPrescribed = -1.*nodalForce_; + atcTransfer_->apply_inverse_mass_matrix(rhsPrescribed,VELOCITY); + rhsPrescribed = mdMassMatrix_*rhsPrescribed; + rhsPrescribed += nodalAtomicForce_; + + set >::const_iterator iter; + for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) { + rhs(iter->first,iter->second) = rhsPrescribed(iter->first,iter->second); + } + } + + //-------------------------------------------------------- + // compute_lambda_force + // computes the force induced by lambda + // on the atoms + //-------------------------------------------------------- + void StressFlux::compute_lambda_force(DENS_MAT & lambdaForce) + { + if (nLocal_>0) { + // prolongation to (unique) nodes + lambdaForce.reset(nLocal_,nsd_); + atcTransfer_->prolong(lambda_,lambdaForce); + lambdaForce *= -1.; + } + } + + //-------------------------------------------------------- + // compute_nodal_lambda_force + // computes the force induced on the FE + // by applying lambdaForce on the atoms + //-------------------------------------------------------- + void StressFlux::compute_nodal_lambda_force(double dt) + { + atcTransfer_->restrict_volumetric_quantity(lambdaForce_,nodalAtomicLambdaForce_); + set >::const_iterator iter; + for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) { + nodalAtomicLambdaForce_(iter->first,iter->second) = 0.; + } + + timeFilter_->apply_post_step1(lambdaForceFiltered_,nodalAtomicLambdaForce_,dt); + } + + //-------------------------------------------------------- + // add_to_rhs: + // determines what if any contributions to the + // finite element equations are needed for + // consistency with the kinetostat + //-------------------------------------------------------- + void StressFlux::add_to_rhs(FIELDS & rhs) + { + rhs[VELOCITY] += nodalAtomicLambdaForce_ + boundaryFlux_[VELOCITY]; + } + + //-------------------------------------------------------- + // compute_ghost_force + // computes computes the restricted force on + // the ghost atoms + //-------------------------------------------------------- + void StressFlux::compute_ghost_force(DENS_MAT & nodalGhostForce) + { + DENS_MAT nodalGhostForceLocal(nNodes_,nsd_); + nodalGhostForce.reset(nNodes_,nsd_); + + if (nLocalGhost_ > 0) { + DENS_MAT ghostForce(nLocalGhost_,nsd_); + for (int i = 0; i < nLocalGhost_; i++) { + int atomIndex = ghostToAtom_(i); + for (int j = 0; j < nsd_; j++) { + ghostForce(i,j) = f_[atomIndex][j]; +#if false + ghostForce(i,j) -= f0_[atomIndex][j]; +#endif + } + } + nodalGhostForceLocal = shapeFunctionGhost_.transMat(ghostForce); + } + + LammpsInterface::instance()->allsum(nodalGhostForceLocal.get_ptr(), + nodalGhostForce.get_ptr(), nodalGhostForce.size()); + // convert from Lammps force units to ATC force units + nodalGhostForce *= LammpsInterface::instance()->ftm2v(); + } + + //-------------------------------------------------------- + // apply_lambda_to_atoms + // uses existing lambda to modify given + // atomic quantity + //-------------------------------------------------------- + void StressFlux::apply_to_atoms(double ** atomicVelocity, + const DENS_MAT & lambdaForce, + double dt) + { + if (nLocal_>0) { + // explicit update + for (int i = 0; i < nLocal_; i++) { + for (int j = 0; j < nsd_; j++) { + atomicVelocity[internalToAtom_(i)][j] += dt*lambdaForce(i,j)/atomicMass_(i); + } + } + } + } + + //-------------------------------------------------------- + // reset_filtered_ghost_force: + // resets the kinetostat generated ghost force to a + // prescribed value + //-------------------------------------------------------- + void StressFlux::reset_filtered_ghost_force(DENS_MAT & target) + { + for (int i = 0; i < nNodes_; ++i) + for (int j = 0; j < nsd_; ++j) + nodalGhostForceFiltered_(i,j) = target(i,j); + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void StressFlux::output(double dt, OUTPUT_LIST & outputData) + { + outputData["lambda"] = &(lambda_); + outputData["nodalLambdaForce"] = &(nodalAtomicLambdaForce_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class StressFluxFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + StressFluxFiltered::StressFluxFiltered(Kinetostat * kinetostat) : + StressFlux(kinetostat), + nodalAtomicVelocity_(atcTransfer_->get_atomic_field(VELOCITY)) + { + // do nothing + } + +#if false + //-------------------------------------------------------- + // apply: + // apply the kinetostat to the atoms + //-------------------------------------------------------- + void StressFluxFiltered::apply_pre_corrector(double dt) + { + // NOTE currently not implemented, below based on GlcVelocityFiltered + + // get neccesary data + double coef = 1./(timeFilter_->get_unfiltered_coefficient_pre_s1(dt)); + + // form rhs : sum_a (hatN_Ia * x_ai) - (\dot{Upsilon})_Ii + DENS_MAT rhs(nNodes_,nsd_); + for (int i = 0; i < nNodes_; i++) + for (int j = 0; j < nsd_; j++) + rhs(i,j) += coef*(nodalAtomicVelocities_(i,j) - nodalVelocities_(i,j)); + DENS_MAT rhsOverlap(nNodeOverlap_,nsd_); + atcTransfer_->map_unique_to_overlap(rhs, rhsOverlap); + + // solve matrix equation and map back to all nodes + DENS_MAT lambdaOverlap(nNodeOverlap_,nsd_); + DIAG_MAT weights; + set_weights(weights); + solve_for_lambda(rhsOverlap, weights, lambdaOverlap); + atcTransfer_->map_overlap_to_unique(lambdaOverlap,lambda_); + + // apply lambda to the atoms and nodal atomic fields + DENS_MAT lambdaRestricted(nNodes_,nsd_); + apply_lambda_to_atoms(v_,lambdaRestricted); + timeFilter_->apply_post_step1(nodalAtomicVelocities_,-1.*lambdaRestricted,dt); + timeFilter_->apply_post_step1(nodalLambdaForce_,(-1./dt)*lambdaRestricted,dt); + DENS_MAT nodalLambdaRoc(nodalLambdaForce_.nRows,nodalLambdaForce_.nCols()); + atcTransfer_->apply_inverse_mass_matrix(nodalLambdaForce_, + nodalLambdaRoc, + VELOCITY); + atcTransfer_->apply_internal_atomic_source(nodalVelocities_, + nodalLambdaRoc, + dt); + + } +#endif +// //-------------------------------------------------------- +// // apply_pre_filtering +// // applies first step of filtering to +// // relevant variables +// //-------------------------------------------------------- +// void StressFluxFiltered::apply_pre_filtering(double dt) +// { +// // apply time filtered lambda to atomic fields +// StressFlux::apply_pre_filtering(dt); +// nodalAtomicForce_ += lambdaForceFiltered; +// } + + //-------------------------------------------------------- + // set_kinetostat_rhs + // sets up the RHS of the kinetostat equations + // for the coupling parameter lambda + //-------------------------------------------------------- + void StressFluxFiltered::set_kinetostat_rhs(DENS_MAT & rhs, double dt) + { + // set basic terms + // (a) for flux based : + // form rhs : \int N_I r dV - \sum_g N_Ig^* f_g + // sources are set in ATC transfer + rhs.reset(nNodes_,nsd_); + rhs = momentumSource_ - nodalGhostForceFiltered_; + + // (b) for ess. bcs + // NOTE can add check to see if set is empty for faster performance + // form rhs : {sum_a (N_Ia * f_ia) - M_md * (ddupsilon/dt)_I} + DENS_MAT rhsPrescribed = -1.*nodalForce_; + atcTransfer_->apply_inverse_mass_matrix(rhsPrescribed,VELOCITY); + rhsPrescribed = mdMassMatrix_*rhsPrescribed; + rhsPrescribed += nodalAtomicForce_; + + set >::const_iterator iter; + for (iter = hooverNodes_.begin(); iter != hooverNodes_.end(); ++iter) { + rhs(iter->first,iter->second) = rhsPrescribed(iter->first,iter->second); + } + + // adjust for application of current lambda force + rhs += lambdaForceFiltered_; + + // correct for time filtering + rhs *= 1./(timeFilter_->get_unfiltered_coefficient_pre_s1(dt)); + } + + //-------------------------------------------------------- + // apply_lambda_to_atoms + // uses existing lambda to modify given + // atomic quantity + //-------------------------------------------------------- + void StressFluxFiltered::apply_to_atoms(double ** atomicVelocity, + const DENS_MAT & lambdaForce, + double dt) + { + StressFlux::apply_to_atoms(atomicVelocity,lambdaForce,dt); + + // add in corrections to filtered nodal atomice velocity + DENS_MAT velocityRoc(nNodes_,nsd_); + atcTransfer_->apply_inverse_md_mass_matrix(lambdaForceFiltered_, + velocityRoc, + VELOCITY); + nodalAtomicVelocity_ += dt*velocityRoc; + } + + //-------------------------------------------------------- + // add_to_rhs: + // determines what if any contributions to the + // finite element equations are needed for + // consistency with the kinetostat + //-------------------------------------------------------- + void StressFluxFiltered::add_to_rhs(FIELDS & rhs) + { + rhs[VELOCITY] += lambdaForceFiltered_ + boundaryFlux_[VELOCITY]; + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void StressFluxFiltered::output(double dt, OUTPUT_LIST & outputData) + { + outputData["lambda"] = &(lambda_); + outputData["nodalLambdaForce"] = &(lambdaForceFiltered_); + } + +}; diff --git a/lib/atc/Kinetostat.h b/lib/atc/Kinetostat.h new file mode 100644 index 0000000000..45688967c7 --- /dev/null +++ b/lib/atc/Kinetostat.h @@ -0,0 +1,488 @@ +#ifndef KINETOSTAT_H +#define KINETOSTAT_H + +// ATC_Transfer headers +#include "AtomicRegulator.h" + +// other headers +#include +#include + +namespace ATC { + + /** + * @class Kinetostat + * @brief Manager class for atom-continuum control of momentum and position + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class Kinetostat + //-------------------------------------------------------- + //-------------------------------------------------------- + + class Kinetostat : public AtomicRegulator { + + public: + + /** kinetostat types */ + enum KinetostatType { + NONE=0, + GLC_DISPLACEMENT, + GLC_VELOCITY, + FORCE + }; + + enum KinetostatCouplingType { + UNCOUPLED=0, + FLUX, + FIXED + }; + + // constructor + Kinetostat(ATC_Transfer *atcTransfer); + + // destructor + ~Kinetostat(){}; + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + // data access, intended for method objects + /** reset the nodal force to a prescribed value */ + void reset_lambda_force(DENS_MAT & target); + /** return the nodal force induced by lambda */ + DENS_MAT & get_nodal_atomic_lambda_force() { return nodalAtomicLambdaForce_;} + /** return value of filtered lambda */ + DENS_MAT & get_lambda_force_filtered() { return lambdaForceFiltered_;} + /** access to kinetostat type */ + KinetostatType get_kinetostat_type() const + { return kinetostatType_;}; + KinetostatCouplingType get_coupling_mode() const + { return couplingMode_;}; + + protected: + + /** kinetostat type flag */ + KinetostatType kinetostatType_; + /** kinetostat copuling type flag */ + KinetostatCouplingType couplingMode_; + + // kinetostat data + /** lambda force applied to atoms */ + DENS_MAT nodalAtomicLambdaForce_; + /** filtered lambda force */ + DENS_MAT lambdaForceFiltered_; + + private: + + // DO NOT define this + Kinetostat(); + + }; + + /** + * @class GlcKinetostat + * @brief Base class for implementation of kinetostat algorithms based on Gaussian least constraints (GLC) + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class GlcKinetostat + // base class for all thermostats of general form of a + // Gaussian least constraint (GLC) + //-------------------------------------------------------- + //-------------------------------------------------------- + + class GlcKinetostat : public RegulatorShapeFunction { + + public: + + GlcKinetostat(Kinetostat *kinetostat); + + ~GlcKinetostat(){}; + + /** reset number of local atoms, as well as atomic data */ + virtual void reset_nlocal(); + + protected: + + // methods + /** apply forces to atoms */ + virtual void apply_to_atoms(double ** atomicQuantity, + const DENS_MAT & lambdaAtom, + double dt=0.); + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(DIAG_MAT & weights); + /** apply any required corrections for localized kinetostats */ + virtual void apply_localization_correction(const DENS_MAT & source, + DENS_MAT & nodalField, + double weight = 1.){}; + + // member data + /** pointer to thermostat object for data */ + Kinetostat * kinetostat_; + /** pointer to a time filtering object */ + TimeFilter * timeFilter_; + /** stress induced by lambda */ + DENS_MAT & nodalAtomicLambdaForce_; + /** filtered lambda force */ + DENS_MAT & lambdaForceFiltered_; + /** atomic force induced by lambda */ + DENS_MAT & lambdaForce_; + /** MD mass matrix */ + MATRIX & mdMassMatrix_; + /** mass of ATC internal atoms on this processor */ + DENS_VEC atomicMass_; + /** reference to ATC map from global nodes to overlap nodes */ + Array & nodeToOverlapMap_; + /** nodeset corresponding to Hoover coupling */ + set > hooverNodes_; + + private: + + // DO NOT define this + GlcKinetostat(); + + }; + + /** + * @class DisplacementGlc + * @brief Enforces GLC on atomic position based on FE displacement + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class DisplacementGlc + //-------------------------------------------------------- + //-------------------------------------------------------- + + class DisplacementGlc : public GlcKinetostat { + + public: + + DisplacementGlc(Kinetostat * kinetostat); + + ~DisplacementGlc(){}; + + /** applies kinetostat to atoms */ + virtual void apply_post_predictor(double dt); + + /** get data for output */ + virtual void output(double dt, OUTPUT_LIST & outputData); + + protected: + + // methods + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(DIAG_MAT & weights); + /** does initial filtering operations before main computation */ + virtual void apply_pre_filtering(double dt); + /** compute force induced by lambda */ + virtual void compute_lambda_force(DENS_MAT & lambdaForce, DENS_MAT & lambdaAtom, double dt); + /** sets up and solves kinetostat equations */ + virtual void compute_kinetostat(double dt); + /** sets up appropriate rhs for kinetostat equations */ + virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); + /** computes the nodal FE force applied by the kinetostat */ + virtual void compute_nodal_lambda_force(double dt); + /** apply any required corrections for localized kinetostats */ + virtual void apply_localization_correction(const DENS_MAT & source, + DENS_MAT & nodalField, + double weight = 1.); + + // data + /** clone of FE displacement field */ + DENS_MAT & nodalDisplacements_; + /** pointer to lammps atomic positions */ + double ** x_; + + private: + + // DO NOT define this + DisplacementGlc(); + + }; + + /** + * @class DisplacementGlcFiltered + * @brief Enforces GLC on time filtered atomic position based on FE displacement + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class DisplacementGlcFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + class DisplacementGlcFiltered : public DisplacementGlc { + + public: + + DisplacementGlcFiltered(Kinetostat * kinetostat); + + ~DisplacementGlcFiltered(){}; + + /** get data for output */ + virtual void output(double dt, OUTPUT_LIST & outputData); + + protected: + + // methods + /** does initial filtering operations before main computation */ + virtual void apply_pre_filtering(double dt); + /** sets up appropriate rhs for kinetostat equations */ + virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); + /** computes the nodal FE force applied by the kinetostat */ + virtual void compute_nodal_lambda_force(double dt); + + // data + /** clone of FE nodal atomic displacement field */ + DENS_MAT & nodalAtomicDisplacements_; + + private: + + // DO NOT define this + DisplacementGlcFiltered(); + + }; + + /** + * @class VelocityGlc + * @brief Enforces GLC on atomic velocity based on FE velocity + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class VelocityGlc + //-------------------------------------------------------- + //-------------------------------------------------------- + + class VelocityGlc : public GlcKinetostat { + + public: + + VelocityGlc(Kinetostat * kinetostat); + + ~VelocityGlc(){}; + + /** applies kinetostat to atoms */ + virtual void apply_mid_predictor(double dt); + + /** applies kinetostat to atoms */ + virtual void apply_post_corrector(double dt); + + /** get data for output */ + virtual void output(double dt, OUTPUT_LIST & outputData); + + protected: + + // methods + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(DIAG_MAT & weights); + /** compute force induced by lambda */ + virtual void compute_lambda_force(DENS_MAT & lambdaForce, DENS_MAT & lambdaAtom, double dt); + /** does initial filtering operations before main computation */ + virtual void apply_pre_filtering(double dt); + /** sets up and solves kinetostat equations */ + virtual void compute_kinetostat(double dt); + /** sets up appropriate rhs for kinetostat equations */ + virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); + /** computes the nodal FE force applied by the kinetostat */ + virtual void compute_nodal_lambda_force(double dt); + /** apply any required corrections for localized kinetostats */ + virtual void apply_localization_correction(const DENS_MAT & source, + DENS_MAT & nodalField, + double weight = 1.); + + // data + /** clone of FE velocity field */ + DENS_MAT & nodalVelocities_; + /** pointer to lammps atomic velocities */ + double ** v_; + + private: + + // DO NOT define this + VelocityGlc(); + + }; + + /** + * @class VelocityGlcFiltered + * @brief Enforces GLC on time filtered atomic velocity based on FE velocity + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class VelocityGlcFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + class VelocityGlcFiltered : public VelocityGlc { + + public: + + VelocityGlcFiltered(Kinetostat * kinetostat); + + ~VelocityGlcFiltered(){}; + + /** get data for output */ + virtual void output(double dt, OUTPUT_LIST & outputData); + + protected: + + // methods + /** does initial filtering operations before main computation */ + virtual void apply_pre_filtering(double dt); + /** sets up appropriate rhs for kinetostat equations */ + virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); + /** computes the nodal FE force applied by the kinetostat */ + virtual void compute_nodal_lambda_force(double dt); + + // data + /** clone of FE nodal atomic velocity field */ + DENS_MAT & nodalAtomicVelocities_; + + private: + + // DO NOT define this + VelocityGlcFiltered(); + + }; + + /** + * @class StressFlux + * @brief Enforces GLC on atomic forces based on FE stresses or accelerations + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class StressFlux + //-------------------------------------------------------- + //-------------------------------------------------------- + + class StressFlux : public GlcKinetostat { + + public: + + StressFlux(Kinetostat * kinetostat); + + ~StressFlux(); + + /** applies kinetostat to atoms in the mid-predictor phase */ + virtual void apply_mid_predictor(double dt); + + /** applies kinetostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); + + /** applies kinetostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt); + + /** get data for output */ + virtual void output(double dt, OUTPUT_LIST & outputData); + + /** sets filtered ghost force to prescribed value */ + void reset_filtered_ghost_force(DENS_MAT & targetForce); + /** returns reference to filtered ghost force */ + DENS_MAT & get_filtered_ghost_force() {return nodalGhostForceFiltered_;}; + + protected: + + // data + /** nodal force */ + DENS_MAT & nodalForce_; + /** nodal force due to atoms */ + DENS_MAT & nodalAtomicForce_; + /** nodal ghost force */ + DENS_MAT nodalGhostForce_; + /** filtered ghost force */ + DENS_MAT nodalGhostForceFiltered_; + /** reference to ATC sources coming from prescribed data, AtC coupling, and extrinsic coupling */ + DENS_MAT & momentumSource_; + /** pointer to lammps atomic velocities */ + double ** v_; + /** pointer to lammps atomic forces */ + double ** f_; +#if false + /** initial lammps atomic forces */ + double ** f0_; +#endif + + // methods + /** compute force induced by lambda */ + virtual void compute_lambda_force(DENS_MAT & lambdaForce); + /** does initial filtering operations before main computation */ + virtual void apply_pre_filtering(double dt); + /** sets up and solves kinetostat equations */ + virtual void compute_kinetostat(double dt); + /** sets up appropriate rhs for kinetostat equations */ + virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); + /** computes the nodal FE force applied by the kinetostat */ + virtual void compute_nodal_lambda_force(double dt); + /** apply forces to atoms */ + virtual void apply_to_atoms(double ** atomicVelocity, + const DENS_MAT & lambdaForce, + double dt); + /** adds in finite element rhs contributions */ + virtual void add_to_rhs(FIELDS & rhs); + /** computes restricted force on ghost atoms */ + void compute_ghost_force(DENS_MAT & nodalGhostForce); + + private: + + // DO NOT define this + StressFlux(); + + }; + + /** + * @class StressFluxFiltered + * @brief Enforces GLC on time filtered atomic forces based on FE stresses or accelerations + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class StressFluxFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + class StressFluxFiltered : public StressFlux { + + public: + + StressFluxFiltered(Kinetostat * kinetostat); + + ~StressFluxFiltered(){}; + + /** get data for output */ + virtual void output(double dt, OUTPUT_LIST & outputData); + + protected: + + // data + DENS_MAT & nodalAtomicVelocity_; + + // methods + /** sets up appropriate rhs for kinetostat equations */ + virtual void set_kinetostat_rhs(DENS_MAT & rhs, double dt); + /** adds in finite element rhs contributions */ + virtual void add_to_rhs(FIELDS & rhs); + /** apply forces to atoms */ + virtual void apply_to_atoms(double ** atomicVelocity, + const DENS_MAT & lambdaForce, + double dt); + + private: + + // DO NOT define this + StressFluxFiltered(); + + }; + +}; + +#endif diff --git a/lib/atc/LammpsInterface.cpp b/lib/atc/LammpsInterface.cpp new file mode 100644 index 0000000000..2965aa7eee --- /dev/null +++ b/lib/atc/LammpsInterface.cpp @@ -0,0 +1,667 @@ +// Header file for this class +#include "LammpsInterface.h" + +// LAMMPS includes +#include "lammps.h" +#include "atom.h" // x, v, f +#include "domain.h" // for basing locations on regions +#include "region.h" // region bounding box and style +#include "force.h" // boltzman constant +#include "group.h" // atom masks +#include "memory.h" // grow atom information +#include "compute.h" // computes +#include "modify.h" // +#include "neighbor.h" // neighbors +#include "neigh_list.h" // neighbor list +#include "update.h" // timestepping information +#include "pair.h" // pair potentials +#include "lattice.h" // lattice parameters +#include "comm.h" // + +// ATC includes +#include "ATC_Error.h" +#include "MatrixLibrary.h" + +// Other include files +#include "mpi.h" +#include + +namespace ATC { + + +LammpsInterface * LammpsInterface::myInstance_ = NULL; + +// ----------------------------------------------------------------- +// instance() +// ----------------------------------------------------------------- +LammpsInterface * LammpsInterface::instance() +{ + if (myInstance_ == NULL) { + myInstance_ = new LammpsInterface(); + } + return myInstance_; +} + +// ----------------------------------------------------------------- +// constructor +// ----------------------------------------------------------------- +LammpsInterface::LammpsInterface() + : lammps_(NULL), + atomPE_(NULL), + commRank_(0) +{ +} + +// ----------------------------------------------------------------- +// general interface methods +// ----------------------------------------------------------------- + +MPI_Comm LammpsInterface::world() { return lammps_->world; } + +// ----------------------------------------------------------------- +// atom interface methods +// ----------------------------------------------------------------- + +int LammpsInterface::nlocal() { return lammps_->atom->nlocal; } + +int LammpsInterface::nghost() { return lammps_->atom->nghost; } + +double LammpsInterface::natoms() { return lammps_->atom->natoms; } + +int LammpsInterface::nmax() { return lammps_->atom->nmax; } + +int LammpsInterface::ntypes() { return lammps_->atom->ntypes; } + +double ** LammpsInterface::xatom() { return lammps_->atom->x; } + +const double ** LammpsInterface::xatom() const { return (const double**)(lammps_->atom->x); } + +double ** LammpsInterface::vatom() { return lammps_->atom->v; } + +double ** LammpsInterface::fatom() { return lammps_->atom->f; } + +int * LammpsInterface::atom_mask() { return lammps_->atom->mask; } + +int * LammpsInterface::atom_type() { return lammps_->atom->type; } + +int * LammpsInterface::atom_tag() { return lammps_->atom->tag; } + +double * LammpsInterface::atom_mass() { return lammps_->atom->mass; } + +double LammpsInterface::atom_mass(int iType) { return lammps_->atom->mass[iType]; } + +double * LammpsInterface::atom_rmass() { return lammps_->atom->rmass; } + +double * LammpsInterface::atom_charge() { return lammps_->atom->q; } + +// ----------------------------------------------------------------- +// domain interface methods +// ----------------------------------------------------------------- + +int LammpsInterface::dimension() { return lammps_->domain->dimension; } + +int LammpsInterface::nregion() { return lammps_->domain->nregion; } + +void LammpsInterface::get_box_bounds(double & boxxlo, double & boxxhi, + double & boxylo, double & boxyhi, + double & boxzlo, double &boxzhi) +{ + if (lammps_->domain->triclinic == 0) { + boxxlo = lammps_->domain->boxlo[0]; + boxxhi = lammps_->domain->boxhi[0]; + boxylo = lammps_->domain->boxlo[1]; + boxyhi = lammps_->domain->boxhi[1]; + boxzlo = lammps_->domain->boxlo[2]; + boxzhi = lammps_->domain->boxhi[2]; + } + else { + boxxlo = lammps_->domain->boxlo_bound[0]; + boxxhi = lammps_->domain->boxhi_bound[0]; + boxylo = lammps_->domain->boxlo_bound[1]; + boxyhi = lammps_->domain->boxhi_bound[1]; + boxzlo = lammps_->domain->boxlo_bound[2]; + boxzhi = lammps_->domain->boxhi_bound[2]; + } +} + +int LammpsInterface::xperiodic() { return lammps_->domain->xperiodic; } + +int LammpsInterface::yperiodic() { return lammps_->domain->yperiodic; } + +int LammpsInterface::zperiodic() { return lammps_->domain->zperiodic; } + +int LammpsInterface::nperiodic() +{ + int nprd = 0; + if ( lammps_->domain->xperiodic > 0 ) { nprd++ ; } + if ( lammps_->domain->yperiodic > 0 ) { nprd++ ; } + if ( lammps_->domain->zperiodic > 0 ) { nprd++ ; } + return nprd; +} + +double LammpsInterface::domain_xprd() { return lammps_->domain->xprd; } + +double LammpsInterface::domain_yprd() { return lammps_->domain->yprd; } + +double LammpsInterface::domain_zprd() { return lammps_->domain->zprd; } + +double LammpsInterface::domain_xy() { return lammps_->domain->xy; } + +double LammpsInterface::domain_xz() { return lammps_->domain->xz; } + +double LammpsInterface::domain_yz() { return lammps_->domain->yz; } + +int LammpsInterface::domain_triclinic() { return lammps_->domain->triclinic; } + +void LammpsInterface::get_box_periodicity(int & xperiodic, + int & yperiodic, + int & zperiodic) +{ + xperiodic = lammps_->domain->xperiodic; + yperiodic = lammps_->domain->yperiodic; + zperiodic = lammps_->domain->zperiodic; +} + +int LammpsInterface::get_region_id(const char * regionName) { + int nregion = this->nregion(); + for (int iregion = 0; iregion < nregion; iregion++) { + if (strcmp(regionName, region_name(iregion)) == 0) { + return iregion; + } + } + throw ATC_Error(0,"Region has not been defined"); + return -1; +} +// ----------------------------------------------------------------- +// update interface methods +// ----------------------------------------------------------------- +LammpsInterface::UnitsType LammpsInterface::units_style(void) +{ + if (strcmp(lammps_->update->unit_style,"lj") == 0) return LJ; + else if (strcmp(lammps_->update->unit_style,"real") == 0) return REAL; + else if (strcmp(lammps_->update->unit_style,"metal") == 0) return METAL; + else return UNKNOWN; +} + + +// ----------------------------------------------------------------- +// lattice interface methods +// ----------------------------------------------------------------- + +double LammpsInterface::xlattice() { return lammps_->domain->lattice->xlattice; } + +double LammpsInterface::ylattice() { return lammps_->domain->lattice->ylattice; } + +double LammpsInterface::zlattice() { return lammps_->domain->lattice->zlattice; } + +LammpsInterface::LatticeType LammpsInterface::lattice_style() +{ + if (lammps_->domain->lattice) + return (LammpsInterface::LatticeType)lammps_->domain->lattice->style; + else + throw ATC_Error(0,"Lattice has not been defined"); +} + +//* retuns the number of basis vectors +int LammpsInterface::get_n_basis() +{ + return lammps_->domain->lattice->nbasis; +} + +//* returns the basis vectors, transformed to the box coords +void LammpsInterface::get_basis(double **basis) +{ + LAMMPS_NS::Lattice *lattice = lammps_->domain->lattice; + int i,j; + double origin[3] = {0.0, 0.0, 0.0}; + lattice->lattice2box(origin[0], origin[1], origin[2]); + for (i=0; ibasis[i],3*sizeof(double)); + lattice->lattice2box(basis[i][0], basis[i][1], basis[i][2]); + for (j=0; j<3; j++) basis[i][j] -= origin[j]; + } +} + +//* gets the unit cell vectors +void LammpsInterface::get_unit_cell(double *a1, double *a2, double *a3) +{ + int i, j; + double *a[3] = {a1,a2,a3}; + double origin[3] = {0.0,0.0,0.0}; + LAMMPS_NS::Lattice *lattice = lammps_->domain->lattice; + // transform origin + lattice->lattice2box(origin[0], origin[1], origin[2]); + + // copy reference lattice vectors + memcpy(a[0], lattice->a1, 3*sizeof(double)); + memcpy(a[1], lattice->a2, 3*sizeof(double)); + memcpy(a[2], lattice->a3, 3*sizeof(double)); + + for (i=0; i<3; i++) + { + lattice->lattice2box(a[i][0], a[i][1], a[i][2]); + for (j=0; j<3; j++) a[i][j] -= origin[j]; + } +} + +//* gets number of atoms in a unit cell +int LammpsInterface::num_atoms_per_cell(void) +{ + int naCell = 0; + LatticeType type = lattice_style(); + if (type == LammpsInterface::SC) naCell = 1; + else if (type == LammpsInterface::BCC) naCell = 2; + else if (type == LammpsInterface::FCC) naCell = 4; + else if (type == LammpsInterface::DIAMOND) naCell = 8; + else if (comm_rank()==0) { + //{throw ATC_Error(0,"lattice style not currently supported by ATC");} + cout << "ATC WARNING: Cannot get number of atoms per cell from lattice\n"; + naCell = 1; //HACK to enable us to keep going since this is only used to compute volume per atom + // ATC modes with a user specified atomic volume or using only volumetric quantities are fine + } + return naCell; +} + +//* gets tributary volume for an atom +double LammpsInterface::volume_per_atom(void) +{ + double naCell = num_atoms_per_cell(); + double volPerAtom = + xlattice() * ylattice() * zlattice() / naCell; + return volPerAtom; +} + +//* gets lattice basis +void LammpsInterface::get_lattice(MATRIX &N, MATRIX &B) +{ + int nbasis = get_n_basis(); + double **basis = new double*[nbasis]; + N.reset(3,3); + B.reset(3,nbasis); + for (int i=0; iforce->boltz; } + +double LammpsInterface::mvv2e() { return lammps_->force->mvv2e; } + +double LammpsInterface::ftm2v() { return lammps_->force->ftm2v; } + +double LammpsInterface::nktv2p() { return lammps_->force->nktv2p; } + +double LammpsInterface::qqr2e() { return lammps_->force->qqr2e; } + +double LammpsInterface::qe2f() { return lammps_->force->qe2f; } + +double LammpsInterface::dielectric() { return lammps_->force->dielectric; } + +double LammpsInterface::qqrd2e() { return lammps_->force->qqrd2e; } + +double LammpsInterface::pair_force(int i, int j, double rsq, + double & fmag_over_rmag) +{ + int itype = (lammps_->atom->type)[i]; + int jtype = (lammps_->atom->type)[j]; + // return value is the energy + if (rsq < (lammps_->force->pair->cutsq)[itype][jtype]) { + return lammps_->force->pair->single(i,j,itype,jtype, + rsq,1.0,1.0,fmag_over_rmag); + } + return 0.0; +} + +double LammpsInterface::pair_cutoff() +{ + return lammps_->force->pair->cutforce; +} + +int LammpsInterface::single_enable() +{ + return lammps_->force->pair->single_enable; +} + +//* Boltzmann's constant in M,L,T,t units +double LammpsInterface::kBoltzmann() { + return (lammps_->force->boltz)/(lammps_->force->mvv2e); +} + +//* Dulong-Petit heat capacity +double LammpsInterface::heat_capacity() { + double rhoCp = dimension()*kBoltzmann()/volume_per_atom(); + return rhoCp; +} + +//* reference mass density +double LammpsInterface::mass_density() +{ + const int ntypes = lammps_->atom->ntypes; + const int *mass_setflag = lammps_->atom->mass_setflag; + const int *type = lammps_->atom->type; + const double *mass = lammps_->atom->mass; + const double *rmass = lammps_->atom->rmass; + // NOTE currently assumes all atoms have same mass and volume + // in the future, mass and volume will be different but density should be + // an atom indepedent quantity + if (mass) { + if (type) return mass[type[0]]/volume_per_atom(); + // no type array - just use first type that has a set mass + for (int i=1; i<=ntypes; i++) { + if (mass_setflag[i]) return mass[i]/volume_per_atom(); + } + // NOTE: no masses specified in input file should we warn the user of this? + return 0.0; + } + // NOTE is this valid - lammps likes to not use 0 index + if (rmass) return rmass[0]/volume_per_atom(); + return 0.0; +} + +// ----------------------------------------------------------------- +// group interface methods +// ----------------------------------------------------------------- + +int LammpsInterface::ngroup() { return lammps_->group->ngroup; } + +int LammpsInterface::group_bit(int iGroup) { return lammps_->group->bitmask[iGroup]; } + +int LammpsInterface::find_group(const char * c) { return lammps_->group->find(c); } + +int LammpsInterface::group_inverse_mask(int iGroup) +{ + return lammps_->group->inversemask[iGroup]; +} + +char * LammpsInterface::group_name(int iGroup) +{ + return lammps_->group->names[iGroup]; +} + +void LammpsInterface::group_bounds(int iGroup, double * b) +{ + lammps_->group->bounds(iGroup, b); +} + + +// ----------------------------------------------------------------- +// memory interface methods +// ----------------------------------------------------------------- + +double * LammpsInterface::create_1d_double_array(int nlo, int nhi, const char *name) { + return lammps_->memory->create_1d_double_array(nlo, nhi, name); +} + +void LammpsInterface::destroy_1d_double_array(double * d, int i) { + lammps_->memory->destroy_1d_double_array(d, i); +} + +double ** LammpsInterface::create_2d_double_array(int n1, int n2, const char *name) { + return lammps_->memory->create_2d_double_array(n1, n2, name); +} + +void LammpsInterface::destroy_2d_double_array(double **d) { + lammps_->memory->destroy_2d_double_array(d); +} + +double **LammpsInterface::grow_2d_double_array(double **array, + int n1, + int n2, + const char *name) +{ + return lammps_->memory->grow_2d_double_array(array, n1, n2, name); +} + +int ** LammpsInterface::create_2d_int_array(int n1, int n2, const char *name) { + return lammps_->memory->create_2d_int_array(n1, n2, name); +} + +void LammpsInterface::destroy_2d_int_array(int **i) { + lammps_->memory->destroy_2d_int_array(i); +} + +int ** LammpsInterface::grow_2d_int_array(int **array, int n1, int n2, const char *name) { + return lammps_->memory->grow_2d_int_array(array, n1, n2, name); +} + + +// ----------------------------------------------------------------- +// update interface methods +// ----------------------------------------------------------------- + +double LammpsInterface::dt() { return lammps_->update->dt; } + +int LammpsInterface::ntimestep() { return lammps_->update->ntimestep; } + +int LammpsInterface::nsteps() { return lammps_->update->nsteps; } + + +// ----------------------------------------------------------------- +// neighbor list interface methods +// ----------------------------------------------------------------- + +void LammpsInterface::init_list(int id, LAMMPS_NS::NeighList *ptr) { list_ = ptr; } + +int LammpsInterface::neighbor_list_inum() { return list_->inum; } + +int * LammpsInterface::neighbor_list_numneigh() { return list_->numneigh; } + +int * LammpsInterface::neighbor_list_ilist() { return list_->ilist; } + +int ** LammpsInterface::neighbor_list_firstneigh() { return list_->firstneigh; } + +int LammpsInterface::neighbor_ago() { return lammps_->neighbor->ago; } + +// ----------------------------------------------------------------- +// region interface methods +// ----------------------------------------------------------------- + +char * LammpsInterface::region_name(int iRegion) +{ + return lammps_->domain->regions[iRegion]->id; +} + +char * LammpsInterface::region_style(int iRegion) +{ + return lammps_->domain->regions[iRegion]->style; +} + +double LammpsInterface::region_xlo(int iRegion) +{ + return lammps_->domain->regions[iRegion]->extent_xlo; +} + +double LammpsInterface::region_xhi(int iRegion) +{ + return lammps_->domain->regions[iRegion]->extent_xhi; +} + +double LammpsInterface::region_ylo(int iRegion) +{ + return lammps_->domain->regions[iRegion]->extent_ylo; +} + +double LammpsInterface::region_yhi(int iRegion) +{ + return lammps_->domain->regions[iRegion]->extent_yhi; +} + +double LammpsInterface::region_zlo(int iRegion) +{ + return lammps_->domain->regions[iRegion]->extent_zlo; +} + +double LammpsInterface::region_zhi(int iRegion) +{ + return lammps_->domain->regions[iRegion]->extent_zhi; +} + +double LammpsInterface::region_xscale(int iRegion) +{ + return lammps_->domain->regions[iRegion]->xscale; +} + +double LammpsInterface::region_yscale(int iRegion) +{ + return lammps_->domain->regions[iRegion]->yscale; +} + +double LammpsInterface::region_zscale(int iRegion) +{ + return lammps_->domain->regions[iRegion]->zscale; +} + +int LammpsInterface::region_match(int iRegion, double x, double y, double z) { + return lammps_->domain->regions[iRegion]->match(x,y,z); +} + +// ----------------------------------------------------------------- +// compute methods +// ----------------------------------------------------------------- +int LammpsInterface::find_compute(const char* tag) +{ + // a clunky way to safely get rid of the const + int n = strlen(tag) + 1; + char* tag_copy = new char[n]; + strcpy(tag_copy,tag); + + int icompute = lammps_->modify->find_compute(tag_copy); + if (icompute < 0) { + string msg("Could not find compute "); + msg += tag; + throw ATC_Error(0,msg); + } + return icompute; +} + +LAMMPS_NS::Compute* LammpsInterface::get_compute(const char* tag) +{ + int icompute = find_compute(tag); + LAMMPS_NS::Compute* cmpt = lammps_->modify->compute[icompute]; + return cmpt; +} + +double** LammpsInterface::compute_vector_data(const char* tag) +{ + LAMMPS_NS::Compute* cmpt = get_compute(tag); + if (!(cmpt->invoked_flag & INVOKED_PERATOM)) { + cmpt->compute_peratom(); + cmpt->invoked_flag |= INVOKED_PERATOM; + } + return cmpt->vector_atom; +} + +double* LammpsInterface::compute_scalar_data(const char* tag) +{ + LAMMPS_NS::Compute* cmpt = get_compute(tag); + if (!(cmpt->invoked_flag & INVOKED_PERATOM)) { + cmpt->compute_peratom(); + cmpt->invoked_flag |= INVOKED_PERATOM; + } + return cmpt->scalar_atom; +} + +int LammpsInterface::compute_ncols(const char* tag) +{ + int icompute = find_compute(tag); + int ncols = lammps_->modify->compute[icompute]->size_peratom; + if (ncols == 0) ncols = 1; // oddity of lammps, used as flag for scalar + return ncols; +} + +// ----------------------------------------------------------------- +// compute pe/atom interface methods +// ----------------------------------------------------------------- +int LammpsInterface::atomPE_create(void) +{ + //char * list[3] = {"atcPE","all","pe/atom"}; + char * list[4] = {"atcPE","all","pe/atom","pair"}; + int icompute = lammps_->modify->find_compute(list[0]); + if (icompute < 0) { + lammps_->modify->add_compute(3,list); + icompute = lammps_->modify->find_compute(list[0]); + } + if (! atomPE_ ) { + atomPE_ = lammps_->modify->compute[icompute]; + } + return icompute; +} + +void LammpsInterface::atomPE_init(void) +{ + if (atomPE_) { + atomPE_->init(); + } + else { + throw ATC_Error(0,"no atomPE compute"); + } +} + +void LammpsInterface::atomPE_addstep(int step) +{ + atomPE_->addstep(step); +} + +double * LammpsInterface::atomPE_compute(void) +{ + if (atomPE_) { + atomPE_->compute_peratom(); + return atomPE_->scalar_atom; + } + else { + return NULL; + } +} + +/* ---------------------------------------------------------------------- */ + +void LammpsInterface::unwrap_coordinates(int iatom, double* xatom) +{ + double **x = lammps_->atom->x; + int *mask = lammps_->atom->mask; + int *image = lammps_->atom->image; + int nlocal = lammps_->atom->nlocal; + + double *h = lammps_->domain->h; + double xprd = lammps_->domain->xprd; + double yprd = lammps_->domain->yprd; + double zprd = lammps_->domain->zprd; + int xbox,ybox,zbox; + + // for triclinic, need to unwrap current atom coord via h matrix + // NOTE: Using current box dimensions. + if (lammps_->domain->triclinic == 0) { + xbox = (image[iatom] & 1023) - 512; + ybox = (image[iatom] >> 10 & 1023) - 512; + zbox = (image[iatom] >> 20) - 512; + xatom[0] = x[iatom][0] + xbox*xprd; + xatom[1] = x[iatom][1] + ybox*yprd; + xatom[2] = x[iatom][2] + zbox*zprd; + } else { + xbox = (image[iatom] & 1023) - 512; + ybox = (image[iatom] >> 10 & 1023) - 512; + zbox = (image[iatom] >> 20) - 512; + xatom[0] = x[iatom][0] + h[0]*xbox + h[5]*ybox + h[4]*zbox; + xatom[1] = x[iatom][1] + h[1]*ybox + h[3]*zbox; + xatom[2] = x[iatom][2] + h[2]*zbox; + } +} + + +// ----------------------------------------------------------------- +// other methods +// ----------------------------------------------------------------- + +/** Return lammps pointer -- only as a last resort! */ +LAMMPS_NS::LAMMPS * LammpsInterface::get_lammps_ptr() { return lammps_; } + +} diff --git a/lib/atc/LammpsInterface.h b/lib/atc/LammpsInterface.h new file mode 100644 index 0000000000..0d4027f046 --- /dev/null +++ b/lib/atc/LammpsInterface.h @@ -0,0 +1,294 @@ +#ifndef LAMMPS_INTERFACE_H +#define LAMMPS_INTERFACE_H + +#include +#include +#include "mpi.h" +#include "../../src/lammps.h" + +#include "ATC_TypeDefs.h" + +// Forward class declarations for LAMMPS_NS namespace +namespace LAMMPS_NS { + class LAMMPS; + class NeighList; + class Compute; +} + +namespace ATC { + +/** + * @class LammpsInterface + * @brief Singleton class that handles all interfacing with the lammps code + */ + +class LammpsInterface { + + public: + + // Enumeration for lattice type. this MUST match the enum in src/lattice.cpp + enum LatticeType { + NONE=0, + SC, + BCC, + FCC, + HCP, + DIAMOND, + SQ, + SQ2, + HEX, + CUSTOM + }; + + // Enumeration for units type. this is internal to ATC + enum UnitsType { + UNKNOWN=0, + LJ, + REAL, + METAL + }; + + /** Static instance of this class */ + static LammpsInterface * instance(); + + /** Set lammps pointer */ + void set_lammps(LAMMPS_NS::LAMMPS * lammps) + { + lammps_ = lammps; + MPI_Comm_rank(lammps_->world, & commRank_); + } + + /** \name Methods that interface with Lammps base class */ + /*@{*/ + MPI_Comm world(); + + void allsum(double * send_buf, double * rec_buf, int count = 1) + { + MPI_Allreduce(send_buf, rec_buf, count, MPI_DOUBLE, MPI_SUM, + lammps_->world); + MPI_Barrier(lammps_->world); + } + + void int_allsum(int * send_buf, int * rec_buf, int count = 1) + { + MPI_Allreduce(send_buf, rec_buf, count, MPI_INT, MPI_SUM, + lammps_->world); + MPI_Barrier(lammps_->world); + } + + void allmax(double * send_buf, double * rec_buf, int count = 1) + { + MPI_Allreduce(send_buf, rec_buf, count, MPI_DOUBLE, MPI_MAX, + lammps_->world); + MPI_Barrier(lammps_->world); + } + + void int_allmax(int * send_buf, int * rec_buf, int count = 1) + { + MPI_Allreduce(send_buf, rec_buf, count, MPI_INT, MPI_MAX, + lammps_->world); + MPI_Barrier(lammps_->world); + } + + void logical_or(int * send_buf, int * rec_buf, int count = 1) + { + MPI_Allreduce(send_buf, rec_buf, count, MPI_INT, MPI_LOR, + lammps_->world); + MPI_Barrier(lammps_->world); + } + + int comm_rank(void) { return commRank_;} + /*@}*/ + + /** \name Methods that interface with Atom class */ + /*@{*/ + int nlocal(); + int nghost(); + int nmax(); + double natoms(); + double ** xatom(); + int ntypes(); + const double ** xatom() const; + double ** vatom(); + double ** fatom(); + int * atom_mask(); + int * atom_type(); + int * atom_tag(); + double * atom_mass(); + double atom_mass(int iType); + double * atom_rmass(); + double * atom_charge(); + void unwrap_coordinates(int iatom, double* xatom); + /*@}*/ + + /** \name Methods that interface with Domain class */ + /*@{*/ + int dimension(); + int nregion(); + void get_box_bounds(double & boxxlo, double & boxxhi, + double & boxylo, double & boxyhi, + double & boxzlo, double &boxzhi); + int xperiodic(); + int yperiodic(); + int zperiodic(); + int nperiodic(); + void get_box_periodicity(int & xperiodic, + int & yperiodic, + int & zperiodic); + int get_region_id(const char * regionName); + double domain_xprd(); + double domain_yprd(); + double domain_zprd(); + double domain_xy(); + double domain_xz(); + double domain_yz(); + int domain_triclinic(); + /*@}*/ + + /** \name Methods that interface with Update class */ + UnitsType units_style(); + /*@}*/ + + /** \name Methods that interface with Lattice class */ + /*@{*/ + double xlattice(); + double ylattice(); + double zlattice(); + LatticeType lattice_style(); + int get_n_basis(); + void get_basis(double **basis); + void get_unit_cell(double *a1, double *a2, double *a3); + /** these functions are more than just simple pass throughs */ + int num_atoms_per_cell(void); + double volume_per_atom(void); + void get_lattice(MATRIX &N, MATRIX &B); + /*@}*/ + + /** \name Methods that interface with Force class */ + /*@{*/ + double boltz(); + double mvv2e(); + double ftm2v(); + double nktv2p(); + double qqr2e(); + double qe2f(); + double dielectric(); + double qqrd2e(); + double pair_force(int i, int j, double rsq, double& fmag_over_rmag); + double pair_cutoff(); + int single_enable(); + /** these functions are more than just simple pass throughs */ + /** Boltzmann's constant in M,L,T,t units */ + double kBoltzmann(void); + /** Dulong-Petit heat capacity per volume in M,L,T,t units */ + double heat_capacity(void); + /** mass per volume in reference configuraturation in M,L units */ + double mass_density(void); + /** ratio of permittivity of free space over elemental charge in V, L units */ + double epsilon0(void) {return 0.00552635; } // [V A]^-1 + // NOTE this won't work for LJ/SI/CGS units where [L] != A + /*@}*/ + + /** \name Methods that interface with Group class */ + /*@{*/ + int ngroup(); + int group_bit(int iGroup); + int find_group(const char * c); + int group_inverse_mask(int iGroup); + char * group_name(int iGroup); + void group_bounds(int iGroup, double * b); + /*@}*/ + + /** \name Methods that interface with Memory class */ + /*@{*/ + double * create_1d_double_array(int nlo, int nhi, const char *name); + void destroy_1d_double_array(double * d, int i); + double ** create_2d_double_array(int n1, int n2, const char *name); + void destroy_2d_double_array(double **d); + double **grow_2d_double_array(double **array, int n1, int n2, const char *name); + int ** create_2d_int_array(int n1, int n2, const char *name); + void destroy_2d_int_array(int **i); + int ** grow_2d_int_array(int **array, int n1, int n2, const char *name); + /*@}*/ + + /** \name Methods that interface with Update class */ + /*@{*/ + double dt(); + int ntimestep(); + int nsteps(); + /*@}*/ + + /** \name Methods that interface with neighbor list */ + /*@{*/ + void init_list(int id, LAMMPS_NS::NeighList *ptr); + int neighbor_list_inum(); + int * neighbor_list_numneigh(); + int * neighbor_list_ilist(); + int ** neighbor_list_firstneigh(); + int neighbor_ago(); + /*@}*/ + + /** \name Methods that interface with Region class */ + /*@{*/ + char * region_name(int iRegion); + char * region_style(int iRegion); + double region_xlo(int iRegion); + double region_xhi(int iRegion); + double region_ylo(int iRegion); + double region_yhi(int iRegion); + double region_zlo(int iRegion); + double region_zhi(int iRegion); + double region_xscale(int iRegion); + double region_yscale(int iRegion); + double region_zscale(int iRegion); + int region_match(int iRegion, double x, double y, double z); + /*@}*/ + + /** \name Methods that interface with compute class */ + enum COMPUTE_INVOKED + {DUMMY0,INVOKED_SCALAR,INVOKED_VECTOR,DUMMMY3,INVOKED_PERATOM}; + int find_compute(const char* tag); + LAMMPS_NS::Compute* get_compute(const char* tag); + double* compute_scalar_data(const char* tag); + double** compute_vector_data(const char* tag); + int compute_ncols(const char* tag); + /*@}*/ + + /** \name Methods that interface with compute pe/atom class */ + /*@{*/ + int atomPE_create(void); + void atomPE_init(void); + void atomPE_addstep(int step); + double * atomPE_compute(void); + /*@}*/ + + /** Return lammps pointer -- only as a last resort! */ + LAMMPS_NS::LAMMPS * get_lammps_ptr(); + + protected: + + LAMMPS_NS::LAMMPS * lammps_; + + /** access to neighbor list */ + LAMMPS_NS::NeighList *list_; + + /** constructor */ + LammpsInterface(); + + /** comm rank */ + int commRank_; + + /** compute pe/atom */ + LAMMPS_NS::Compute * atomPE_; + + private: + + static LammpsInterface * myInstance_; + +}; + +} // end namespace ATC + + + +#endif diff --git a/lib/atc/Makefile.g++ b/lib/atc/Makefile.g++ new file mode 100644 index 0000000000..17ddd9d24d --- /dev/null +++ b/lib/atc/Makefile.g++ @@ -0,0 +1,129 @@ +SHELL = /bin/sh + +# ------ FILES ------ + +SRC = ATC_HardyKernel.cpp \ +ATC_Transfer.cpp \ +ATC_TransferHardy.cpp \ +ATC_TransferThermal.cpp \ +ATC_TransferUtility.cpp \ +AtomicRegulator.cpp \ +ElasticTimeIntegrator.cpp \ +ElectronFlux.cpp \ +ElectronHeatCapacity.cpp \ +ElectronHeatFlux.cpp \ +ElectronPhononExchange.cpp \ +ExtrinsicModel.cpp \ +ExtrinsicModelTwoTemperature.cpp \ +FE_Element.cpp \ +FE_Engine.cpp \ +FE_Mesh.cpp \ +FieldEulerIntegrator.cpp \ +ImplicitSolveOperator.cpp \ +Kinetostat.cpp \ +LammpsInterface.cpp \ +Material.cpp \ +Matrix.cpp \ +OutputManager.cpp \ +PhysicsModelThermal.cpp \ +PhysicsModelTwoTemperature.cpp \ +PrescribedDataManager.cpp \ +Solver.cpp \ +Thermostat.cpp \ +TimeFilter.cpp \ +TimeIntegrator.cpp \ +Vector.cpp \ +XT_Function.cpp + +INC = Array2D.h \ +Array.h \ +ATC_Error.h \ +ATC_HardyKernel.h \ +ATC_Transfer.h \ +ATC_TransferHardy.h \ +ATC_TransferThermal.h \ +ATC_TypeDefs.h \ \ +AtomicRegulator.h \ +CG.h \ +CloneVector.h \ +DenseMatrix.h \ +DenseVector.h \ +DiagonalMatrix.h \ +ElasticTimeIntegrator.h \ +ElectronFlux.h \ +ElectronHeatCapacity.h \ +ElectronHeatFlux.h \ +ElectronPhononExchange.h \ +ExtrinsicModel.h \ +ExtrinsicModelTwoTemperature.h \ +FE_Element.h \ +FE_Engine.h \ +FE_Mesh.h \ +FieldEulerIntegrator.h \ +GMRES.h \ +ImplicitSolveOperator.h \ +Kinetostat.h \ +LammpsInterface.h \ +Material.h \ +MatrixDef.h \ +Matrix.h \ +MatrixLibrary.h \ +OutputManager.h \ +PhysicsModel.h \ +PhysicsModelThermal.h \ +PhysicsModelTwoTemperature.h \ +PrescribedDataManager.h \ +Quadrature.h \ +Solver.h \ +SparseMatrix.h \ +SparseMatrix-inl.h \ +SparseVector.h \ +SparseVector-inl.h \ +StringManip.h \ +Thermostat.h \ +TimeFilter.h \ +TimeIntegrator.h \ +Utility.h \ +Vector.h \ +XT_Function.h + +# ------ DEFINITIONS ------ + +LIB = libatc.a +OBJ = $(SRC:.cpp=.o) + +# ------ SETTINGS ------ + +# include any MPI settings needed for the ATC library to build with +# the same MPI library that LAMMPS is built with + +CC = g++ +CCFLAGS = -O -g -I../../src -DMPICH_IGNORE_CXX_SEEK +ARCHIVE = ar +ARCHFLAG = -rc +DEPFLAGS = -M +LINK = g++ +LINKFLAGS = -O +USRLIB = +SYSLIB = + +# ------ MAKE PROCEDURE ------ + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(LIB) $(OBJ) + +# ------ COMPILE RULES ------ + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# ------ DEPENDENCIES ------ + +DEPENDS = $(OBJ:.o=.d) + +# ------ CLEAN ------ + +clean: + rm *.o *.d *~ $(LIB) diff --git a/lib/atc/Makefile.icc b/lib/atc/Makefile.icc new file mode 100644 index 0000000000..43425acab7 --- /dev/null +++ b/lib/atc/Makefile.icc @@ -0,0 +1,129 @@ +SHELL = /bin/sh + +# ------ FILES ------ + +SRC = ATC_HardyKernel.cpp \ +ATC_Transfer.cpp \ +ATC_TransferHardy.cpp \ +ATC_TransferThermal.cpp \ +ATC_TransferUtility.cpp \ +AtomicRegulator.cpp \ +ElasticTimeIntegrator.cpp \ +ElectronFlux.cpp \ +ElectronHeatCapacity.cpp \ +ElectronHeatFlux.cpp \ +ElectronPhononExchange.cpp \ +ExtrinsicModel.cpp \ +ExtrinsicModelTwoTemperature.cpp \ +FE_Element.cpp \ +FE_Engine.cpp \ +FE_Mesh.cpp \ +FieldEulerIntegrator.cpp \ +ImplicitSolveOperator.cpp \ +Kinetostat.cpp \ +LammpsInterface.cpp \ +Material.cpp \ +Matrix.cpp \ +OutputManager.cpp \ +PhysicsModelThermal.cpp \ +PhysicsModelTwoTemperature.cpp \ +PrescribedDataManager.cpp \ +Solver.cpp \ +Thermostat.cpp \ +TimeFilter.cpp \ +TimeIntegrator.cpp \ +Vector.cpp \ +XT_Function.cpp + +INC = Array2D.h \ +Array.h \ +ATC_Error.h \ +ATC_HardyKernel.h \ +ATC_Transfer.h \ +ATC_TransferHardy.h \ +ATC_TransferThermal.h \ +ATC_TypeDefs.h \ \ +AtomicRegulator.h \ +CG.h \ +CloneVector.h \ +DenseMatrix.h \ +DenseVector.h \ +DiagonalMatrix.h \ +ElasticTimeIntegrator.h \ +ElectronFlux.h \ +ElectronHeatCapacity.h \ +ElectronHeatFlux.h \ +ElectronPhononExchange.h \ +ExtrinsicModel.h \ +ExtrinsicModelTwoTemperature.h \ +FE_Element.h \ +FE_Engine.h \ +FE_Mesh.h \ +FieldEulerIntegrator.h \ +GMRES.h \ +ImplicitSolveOperator.h \ +Kinetostat.h \ +LammpsInterface.h \ +Material.h \ +MatrixDef.h \ +Matrix.h \ +MatrixLibrary.h \ +OutputManager.h \ +PhysicsModel.h \ +PhysicsModelThermal.h \ +PhysicsModelTwoTemperature.h \ +PrescribedDataManager.h \ +Quadrature.h \ +Solver.h \ +SparseMatrix.h \ +SparseMatrix-inl.h \ +SparseVector.h \ +SparseVector-inl.h \ +StringManip.h \ +Thermostat.h \ +TimeFilter.h \ +TimeIntegrator.h \ +Utility.h \ +Vector.h \ +XT_Function.h + +# ------ DEFINITIONS ------ + +LIB = libatc.a +OBJ = $(SRC:.cpp=.o) + +# ------ SETTINGS ------ + +# include any MPI settings needed for the ATC library to build with +# the same MPI library that LAMMPS is built with + +CC = icc +CCFLAGS = -O -g -I../../src -DMPICH_IGNORE_CXX_SEEK +ARCHIVE = ar +ARCHFLAG = -rc +DEPFLAGS = -M +LINK = icc +LINKFLAGS = -O +USRLIB = +SYSLIB = + +# ------ MAKE PROCEDURE ------ + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(LIB) $(OBJ) + +# ------ COMPILE RULES ------ + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# ------ DEPENDENCIES ------ + +DEPENDS = $(OBJ:.o=.d) + +# ------ CLEAN ------ + +clean: + rm *.o *.d *~ $(LIB) diff --git a/lib/atc/Makefile.serial b/lib/atc/Makefile.serial new file mode 100644 index 0000000000..04c10cebb0 --- /dev/null +++ b/lib/atc/Makefile.serial @@ -0,0 +1,129 @@ +SHELL = /bin/sh + +# ------ FILES ------ + +SRC = ATC_HardyKernel.cpp \ +ATC_Transfer.cpp \ +ATC_TransferHardy.cpp \ +ATC_TransferThermal.cpp \ +ATC_TransferUtility.cpp \ +AtomicRegulator.cpp \ +ElasticTimeIntegrator.cpp \ +ElectronFlux.cpp \ +ElectronHeatCapacity.cpp \ +ElectronHeatFlux.cpp \ +ElectronPhononExchange.cpp \ +ExtrinsicModel.cpp \ +ExtrinsicModelTwoTemperature.cpp \ +FE_Element.cpp \ +FE_Engine.cpp \ +FE_Mesh.cpp \ +FieldEulerIntegrator.cpp \ +ImplicitSolveOperator.cpp \ +Kinetostat.cpp \ +LammpsInterface.cpp \ +Material.cpp \ +Matrix.cpp \ +OutputManager.cpp \ +PhysicsModelThermal.cpp \ +PhysicsModelTwoTemperature.cpp \ +PrescribedDataManager.cpp \ +Solver.cpp \ +Thermostat.cpp \ +TimeFilter.cpp \ +TimeIntegrator.cpp \ +Vector.cpp \ +XT_Function.cpp + +INC = Array2D.h \ +Array.h \ +ATC_Error.h \ +ATC_HardyKernel.h \ +ATC_Transfer.h \ +ATC_TransferHardy.h \ +ATC_TransferThermal.h \ +ATC_TypeDefs.h \ \ +AtomicRegulator.h \ +CG.h \ +CloneVector.h \ +DenseMatrix.h \ +DenseVector.h \ +DiagonalMatrix.h \ +ElasticTimeIntegrator.h \ +ElectronFlux.h \ +ElectronHeatCapacity.h \ +ElectronHeatFlux.h \ +ElectronPhononExchange.h \ +ExtrinsicModel.h \ +ExtrinsicModelTwoTemperature.h \ +FE_Element.h \ +FE_Engine.h \ +FE_Mesh.h \ +FieldEulerIntegrator.h \ +GMRES.h \ +ImplicitSolveOperator.h \ +Kinetostat.h \ +LammpsInterface.h \ +Material.h \ +MatrixDef.h \ +Matrix.h \ +MatrixLibrary.h \ +OutputManager.h \ +PhysicsModel.h \ +PhysicsModelThermal.h \ +PhysicsModelTwoTemperature.h \ +PrescribedDataManager.h \ +Quadrature.h \ +Solver.h \ +SparseMatrix.h \ +SparseMatrix-inl.h \ +SparseVector.h \ +SparseVector-inl.h \ +StringManip.h \ +Thermostat.h \ +TimeFilter.h \ +TimeIntegrator.h \ +Utility.h \ +Vector.h \ +XT_Function.h + +# ------ DEFINITIONS ------ + +LIB = libatc.a +OBJ = $(SRC:.cpp=.o) + +# ------ SETTINGS ------ + +# include any MPI settings needed for the ATC library to build with +# the same MPI library that LAMMPS is built with + +CC = g++ +CCFLAGS = -O -g -I../../src -I../../src/STUBS +ARCHIVE = ar +ARCHFLAG = -rc +DEPFLAGS = -M +LINK = g++ +LINKFLAGS = -O +USRLIB = +SYSLIB = + +# ------ MAKE PROCEDURE ------ + +lib: $(OBJ) + $(ARCHIVE) $(ARFLAGS) $(LIB) $(OBJ) + +# ------ COMPILE RULES ------ + +%.o:%.cpp + $(CC) $(CCFLAGS) -c $< +%.d:%.cpp + $(CC) $(CCFLAGS) $(DEPFLAGS) $< > $@ + +# ------ DEPENDENCIES ------ + +DEPENDS = $(OBJ:.o=.d) + +# ------ CLEAN ------ + +clean: + rm *.o *.d *~ $(LIB) diff --git a/lib/atc/Material.cpp b/lib/atc/Material.cpp new file mode 100644 index 0000000000..f82d7980dc --- /dev/null +++ b/lib/atc/Material.cpp @@ -0,0 +1,349 @@ +#include "Material.h" +#include "ATC_Transfer.h" +#include "LammpsInterface.h" +#include "ElectronHeatCapacity.h" +#include "ElectronHeatFlux.h" +#include "ElectronPhononExchange.h" + +namespace ATC { +using namespace ATC_STRING; + + Material::Material() + : rhoCp_(0), + rho_(0), + heatCapacity_(0), + massDensity_(0), + chargeDensity_(0), + electronHeatCapacity_(NULL), + heatConductivity_(0), + electronHeatFlux_(NULL), + stress_(NULL), + electronPhononExchange_(NULL), + permittivity_(1.), + electronEquilibriumDensity_(0), + electronRecombinationInvTau_(0), + donorConcentration_(0) + { + } + //-------------------------------------------------------------- + // Constructor (parser) + //-------------------------------------------------------------- + // Example: + // material Cu + // heat_capacity constant + // capacity 1.0 + // end + // heat_flux linear + // conductivity 1.0 + // end + // electron_heat_flux linear + // conductivity 1.0 + // end + // electron_heat_capacity linear + // capacity 1.0 + // end + // electron_phonon_exchange linear + // coefficient 0.1 + // end + // end + Material::Material(string & tag, fstream &fileId) + : tag_(tag), + rhoCp_(0), + rho_(0), + heatCapacity_(0), + massDensity_(0), + chargeDensity_(0), + electronHeatCapacity_(NULL), + heatConductivity_(0), + electronHeatFlux_(NULL), + stress_(NULL), + electronPhononExchange_(NULL), + permittivity_(1.), + electronEquilibriumDensity_(0), + electronRecombinationInvTau_(0), + donorConcentration_(0) + { + linearFlux_.reset(NUM_FIELDS); + linearFlux_ = false; + linearSource_.reset(NUM_FIELDS); + linearSource_ = true; + constantDensity_.reset(NUM_FIELDS); + constantDensity_ = false; + + // NOTE these next two rely on many assumptions + rhoCp_ = ATC::LammpsInterface::instance()->heat_capacity(); + parameters_["heat_capacity"] = rhoCp_; + heatCapacity_ = rhoCp_; + registry_.insert("heat_capacity"); + constantDensity_(TEMPERATURE) = true; + + rho_ = ATC::LammpsInterface::instance()->mass_density(); + parameters_["mass_density"] = rho_; + massDensity_ = rho_; + registry_.insert("mass_density"); + constantDensity_(DISPLACEMENT) = true; + constantDensity_(VELOCITY) = true; + + + vector line; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line.size() == 1) { + if (line[0] == "end") { + return; + } + } + if (line[0] == "heat_capacity") { // over-ride default + registry_. insert("heat_capacity"); + registry_. insert("thermal_energy"); + if (line[1] == "constant") { + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") break; + double value = str2dbl(line[1]); + if (line[0] == "capacity") { + heatCapacity_ = value; + parameters_["heat_capacity"] = heatCapacity_; + } + } + } + } + else if (line[0] == "heat_flux") { + registry_. insert("heat_flux"); + if (line[1] == "linear") { + linearFlux_(TEMPERATURE) = true; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") break; + double value = str2dbl(line[1]); + if (line[0] == "conductivity") { + heatConductivity_ = value; + } + } + } + } + else if (line[0] == "electron_heat_flux") { + registry_. insert("electron_heat_flux"); + if (line[1] == "linear") { + linearFlux_(ELECTRON_TEMPERATURE) = true; + electronHeatFlux_ = new ElectronHeatFluxLinear(fileId, parameters_); + } + else if (line[1] == "power_law") { + electronHeatFlux_ = new ElectronHeatFluxPowerLaw(fileId, parameters_); + } + } + else if (line[0] == "electron_heat_capacity") { + registry_. insert("electron_heat_capacity"); + registry_. insert("electron_thermal_energy"); + if (line[1] == "constant") { + constantDensity_(ELECTRON_TEMPERATURE) = true; + electronHeatCapacity_ = new ElectronHeatCapacityConstant(fileId, + parameters_); + } + else if (line[1] == "linear") { + electronHeatCapacity_ = new ElectronHeatCapacityLinear(fileId, + parameters_); + } + } + else if (line[0] == "electron_phonon_exchange") { + registry_. insert("electron_phonon_exchange"); + if (line[1] == "linear") { + electronPhononExchange_ = new ElectronPhononExchangeLinear(fileId, + parameters_); + } + else if (line[1] == "power_law") { + linearSource_(TEMPERATURE) = false; + linearSource_(ELECTRON_TEMPERATURE) = false; + electronPhononExchange_ = new ElectronPhononExchangePowerLaw(fileId, + parameters_); + } + } + else if (line[0] == "mass_density") { // over-ride default + registry_. insert("mass_density"); + registry_. insert("kinetic_energy"); + if (line[1] == "constant") { + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") break; + double value = str2dbl(line[1]); + if (line[0] == "density") { + massDensity_ = value; + parameters_["mass_density"] = massDensity_; + } + } + } + } + else if (line[0] == "electron_recombination") { + registry_. insert("electron_recombination"); + if (line[1] == "linear") { + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") break; + double value = str2dbl(line[1]); + if (line[0] == "inv_relaxation_time") { + electronRecombinationInvTau_ = value; + parameters_["inv_relaxation_time"] = electronRecombinationInvTau_; + } + else if (line[0] == "equilibrium_carrier_density") { + electronEquilibriumDensity_ = value; + parameters_["equilibrium_carrier_density"] + = electronEquilibriumDensity_; + } + } + } + } + else if (line[0] == "charge_density") { + registry_. insert("charge_density"); + if (line[1] == "constant") { + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0) continue; + if (line[0] == "end") break; + double value = str2dbl(line[1]); + if (line[0] == "donor_concentration") { + donorConcentration_ = value; + parameters_["donor_concentration"] = donorConcentration_; + } + else if (line[0] == "density") { + chargeDensity_ = value; + parameters_["charge_density"] = chargeDensity_; + } + } + } + } + else { + throw ATC_Error(0, "unrecognized material function: "+line[0]); + } + } + } +//--------------------------------------------------------------------- +void Material::heat_capacity( + const FIELDS & fields, + FIELD & capacity) +{ + const FIELD & T = (fields.find(TEMPERATURE))->second; + int nNodes = T.nRows(); + capacity.reset(nNodes,1); + capacity = heatCapacity_; +}; +//--------------------------------------------------------------------- +void Material::thermal_energy( + const FIELDS &fields, + FIELD &energy) +{ + const FIELD & T = (fields.find(TEMPERATURE))->second; + energy = heatCapacity_ * T; +}; +//--------------------------------------------------------------------- +void Material::electron_heat_capacity( + const FIELDS & fields, + FIELD & capacity) +{ + electronHeatCapacity_->electron_heat_capacity(fields,capacity); +}; +//--------------------------------------------------------------------- +void Material::electron_thermal_energy( + const FIELDS &fields, + FIELD &energy) +{ + electronHeatCapacity_->electron_thermal_energy(fields,energy); +}; +//--------------------------------------------------------------------- +void Material::mass_density( + const FIELDS &fields, + FIELD &density) +{ + int nNodes = 0; + FIELDS::const_iterator field = fields.find(MASS_DENSITY); + if (field != fields.end()) { + const FIELD & d = field->second; + nNodes = d.nRows(); + } + else { + FIELDS::const_iterator field = fields.find(VELOCITY); + if (field != fields.end()) { + const FIELD & v = field->second; + nNodes = v.nRows(); + } + } + density.reset(nNodes,1); + density = massDensity_; +}; +//--------------------------------------------------------------------- +void Material::kinetic_energy( + const FIELDS &fields, + FIELD &energy) +{ + FIELDS::const_iterator field = fields.find(VELOCITY); + if (field != fields.end()) { + const FIELD & v = field->second; + energy = 0.5*massDensity_*v; + energy *= v; + } + else { + energy = 0.; + } +}; +//--------------------------------------------------------------------- +void Material::heat_flux( + const FIELDS & fields, + const GRAD_FIELDS & gradFields, + GRAD_FIELD & flux) +{ + const GRAD_FIELD & dT = (gradFields.find(TEMPERATURE))->second; + flux.push_back(-heatConductivity_* dT[0]); + flux.push_back(-heatConductivity_* dT[1]); + flux.push_back(-heatConductivity_* dT[2]); +} +//--------------------------------------------------------------------- +void Material::electron_heat_flux( + const FIELDS & fields, + const GRAD_FIELDS & gradFields, + GRAD_FIELD & flux) +{ + electronHeatFlux_->electron_heat_flux(fields,gradFields,flux); +} +//--------------------------------------------------------------------- +void Material::electron_phonon_exchange( + const FIELDS & fields, + FIELD & flux) +{ + electronPhononExchange_->electron_phonon_exchange(fields,flux); +} +//--------------------------------------------------------------------- +void Material::electron_recombination( + const FIELDS &fields, + const GRAD_FIELDS &gradFields, + FIELD & flux) +{ + // 1/tau (n - n0) + const FIELD & n = (fields.find(ELECTRON_DENSITY))->second; + flux = n; + flux -= electronEquilibriumDensity_; + flux *= -electronRecombinationInvTau_; +} + +//--------------------------------------------------------------------- +void Material::charge_density( + const FIELDS &fields, + const GRAD_FIELDS &gradFields, + FIELD & flux) +{ + // Extrinsic/electron charge density + FIELDS::const_iterator field = fields.find(ELECTRON_DENSITY); + if (field != fields.end()) { + // (n - nD) , where n > 0 + const FIELD & n = field->second; + flux = n; + flux -= donorConcentration_;// NOTE uniform + flux *= -1.0; // account for negative charge + } +}; + +} // end namespace + diff --git a/lib/atc/Material.h b/lib/atc/Material.h new file mode 100644 index 0000000000..efd5a9662e --- /dev/null +++ b/lib/atc/Material.h @@ -0,0 +1,185 @@ +#ifndef MATERIAL_H +#define MATERIAL_H + +#include +#include +#include +#include "MatrixLibrary.h" +#include "ATC_TypeDefs.h" +#include "ATC_Error.h" +#include "LammpsInterface.h" + +namespace ATC +{ + class ATC_Transfer; + class Stress; + class ElectronHeatCapacity; + class ElectronHeatFlux; + class ElectronFlux; + class ElectronPhononExchange; + + class Material + { + public: + Material(); + virtual ~Material() {}; + /** this constructor parses material file */ + Material(string & tag, fstream & fileId); + + /** return label */ + string label(void) {return tag_;} + + /** check material has required interfaces */ + bool check_registry(const set functionList) const + { + set::const_iterator itr; + for (itr=functionList.begin(); itr!=functionList.end(); itr++) { + if (registry_.find(*itr) == registry_.end()) return false; + } + return true; + } + + /** access to material parameters */ + bool get_parameter(const string name, double & value) const + { + map::const_iterator iter = parameters_.find(name); + if ( iter == parameters_.end()) { + value = 0.0; + return false; + } + value = iter->second; + return true; + } + /** true if rhs flux is linear (per field) */ + bool linear_flux(FieldName name) { + return linearFlux_(name); + }; + + /** true if rhs source is linear (per field) */ + bool linear_source(FieldName name) { + return linearSource_(name); + }; + + /** true if lhs density is constant (per field) */ + bool constant_density(FieldName name) { + return constantDensity_(name); + }; + + /** each of these is a field function computed at a set of points */ + /** if there is only one function it is in the base class + ** otherwise, a subsidary class is setup */ + /* -----------------------------------------------------------------*/ + /** densitities */ + /* -----------------------------------------------------------------*/ + /** thermal energy */ + void thermal_energy(const FIELDS & fields, + FIELD & energy); + /** heat capacity */ + void heat_capacity(const FIELDS & fields, + FIELD & capacity); + /** thermal energy */ + void electron_thermal_energy(const FIELDS & fields, + FIELD & energy); + /** electron heat capacity */ + void electron_heat_capacity(const FIELDS &fields, + FIELD &capacity); + /** kinetic energy */ + void kinetic_energy(const FIELDS & fields, + FIELD & energy); + /** mass density */ + void mass_density(const FIELDS &fields, + FIELD &density); + /** elastic energy */ + void elastic_energy(const FIELDS & fields, + const GRAD_FIELDS & gradFields, + FIELD & energy); + /* -----------------------------------------------------------------*/ + /** fluxes */ + /* -----------------------------------------------------------------*/ + /** heat_flux */ + void heat_flux(const FIELDS & fields, + const GRAD_FIELDS & gradFields, + GRAD_FIELD & heatFlux); + /** electron conduction flux */ + void electron_heat_flux(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &flux); + /** stress */ + void stress(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &stress); + /** computes electron flux */ + void electron_flux(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &flux); + /** computes electric displacement */ + void electric_displacement(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &flux); + /** computes electric field */ + void electric_field(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + GRAD_FIELD &flux); + /* -----------------------------------------------------------------*/ + /** sources */ + /* -----------------------------------------------------------------*/ + /** electron-phonon exchange flux */ + void electron_phonon_exchange(const FIELDS &fields, + FIELD &flux); + /** computes net generation */ + virtual void electron_recombination(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + FIELD &flux); + /** computes drift diffusion charge density */ + virtual void charge_density(const FIELDS &fields, + const GRAD_FIELDS &gradFields, + FIELD &flux); + + + protected: + /** material's label */ + string tag_; + /** dictionary of material parameters */ + map parameters_; + /** dictionary of instantiated functions */ + set registry_; + /** per eqn flag of constant density */ + Array constantDensity_; + /** per eqn flag of linearity/nonlinearity */ + Array linearFlux_, linearSource_; + /** default heat capacity */ + double rhoCp_; + /** default mass density */ + double rho_; + /** heat capacity */ + double heatCapacity_; + /** electron heat capacity */ + ElectronHeatCapacity * electronHeatCapacity_; + /** mass density */ + double massDensity_; + /** charge density */ + double chargeDensity_; + /** thermal conductivity */ + double heatConductivity_; + /** electron heat flux */ + ElectronHeatFlux * electronHeatFlux_; + /** stress */ + Stress * stress_; + /** electron-phonon exchange */ + ElectronPhononExchange * electronPhononExchange_; + /** electron heat flux */ + ElectronFlux * electronFlux_; + /** electric permittivity */ + double permittivity_; + /** equilibrium carrier density */ + double electronEquilibriumDensity_; + /** relaxation time */ + double electronRecombinationInvTau_; + /** uniform donor concentration */ + double donorConcentration_; // NOTE only for uniform + }; + +} +#endif // Material.h + + diff --git a/lib/atc/Matrix.cpp b/lib/atc/Matrix.cpp new file mode 100644 index 0000000000..ebf6d36793 --- /dev/null +++ b/lib/atc/Matrix.cpp @@ -0,0 +1,146 @@ +#include "DenseMatrix.h" +#include "Solver.h" + +//----------------------------------------------------------------------------- +//* performs a matrix-matrix multiply with optional transposes BLAS version +// C = b*C + a*A*B +//----------------------------------------------------------------------------- +void MultAB(const MATRIX &A, const MATRIX &B, DENS_MAT &C, + const bool At, const bool Bt, double a, double b) +{ + static char t[2] = {'N','T'}; + char *ta=t+At, *tb=t+Bt; + int sA[2] = {A.nRows(), A.nCols()}; // sizes of A + int sB[2] = {B.nRows(), B.nCols()}; // sizes of B + + GCK(A, B, sA[!At]!=sB[Bt], "MultAB: matrix-matrix multiply"); + if (!C.is_size(sA[At],sB[!Bt])) + { + C.resize(sA[At],sB[!Bt]); + if (b != 0.0) C.zero(); + } + // get pointers to the matrix sizes needed by BLAS + int *M = sA+At; // # of rows in op[A] (op[A] = A' if At='T' else A) + int *N = sB+!Bt; // # of cols in op[B] + int *K = sA+!At; // # of cols in op[A] or # of rows in op[B] + + double *pa=A.get_ptr(), *pb=B.get_ptr(), *pc=C.get_ptr(); + +#ifdef COL_STORAGE + dgemm_(ta, tb, M, N, K, &a, pa, sA, pb, sB, &b, pc, M); +#else + dgemm_(tb, ta, N, M, K, &a, pb, sB+1, pa, sA+1, &b, pc, N); +#endif + +} + +//----------------------------------------------------------------------------- +//* returns the inverse of a double precision matrix +//----------------------------------------------------------------------------- +DenseMatrix inv(const MATRIX& A) +{ + SQCK(A, "DenseMatrix::inv(), matrix not square"); // check matrix is square + DENS_MAT invA(A); // Make copy of A to invert + + // setup for call to BLAS + int m, info, lwork=-1; + m = invA.nRows(); + + int *ipiv = new int[m<<1]; // need 2m storage + int *iwork=ipiv+m; + + dgetrf_(&m,&m,invA.get_ptr(),&m,ipiv,&info); // compute LU factorization + GCK(A,A,info<0,"DenseMatrix::inv() dgetrf error: Argument had bad value."); + GCK(A,A,info>0,"DenseMatrix::inv() dgetrf error: Matrix not invertable."); + + // LU factorization succeeded + // Compute 1-norm of original matrix for use with dgecon + char norm = '1'; // Use 1-norm + double rcond, anorm, *workc = new double[4*m]; + anorm = dlange_(&norm,&m,&m,A.get_ptr(),&m,workc); + + // Condition number estimation (warn if bad) + dgecon_(&norm,&m,invA.get_ptr(),&m,&anorm,&rcond,workc,iwork,&info); + GCK(A,A,info<0, "DenseMatrix::inv(): dgecon error: Argument had bad value."); + GCK(A,A,rcond<1e-14,"DenseMatrix::inv(): Matrix nearly singular, RCOND0,"DenseMatrix::inv() dgetri error: Matrix not invertable."); + + // Work size query succeded + lwork = (int)work_dummy[0]; + double *work = new double[lwork]; // Allocate vector of appropriate size + + // Compute and store matrix inverse + dgetri_(&m,invA.get_ptr(),&m,ipiv,work,&lwork,&info); + GCK(A,A,info<0,"DenseMatrix::inv() dgetri error: Argument had bad value."); + GCK(A,A,info>0,"DenseMatrix::inv() dgetri error: Matrix not invertable."); + + // Clean-up + delete [] ipiv; + delete [] workc; + delete [] work; + return invA; +} +//----------------------------------------------------------------------------- +//* computes the determinant of a square matrix by LU decomposition (if n>3) +//----------------------------------------------------------------------------- +double det(const MATRIX& A) +{ + static const double sign[2] = {1.0, -1.0}; + SQCK(A, "Matrix::det(), matrix not square"); // check matrix is square + int m = A.nRows(); + switch (m) // explicit determinant for small matrix sizes + { + case 1: return A(0,0); + case 2: return A(0,0)*A(1,1)-A(0,1)*A(1,0); + case 3: + return A(0,0)*(A(1,1)*A(2,2)-A(1,2)*A(2,1)) + + A(0,1)*(A(1,2)*A(2,0)-A(1,0)*A(2,2)) + + A(0,2)*(A(1,0)*A(2,1)-A(1,1)*A(2,0)); + default: break; + } + // First compute LU factorization + int info, *ipiv = new int[m]; + double det = 1.0; + DENS_MAT PLUA(A); + dgetrf_(&m,&m,PLUA.get_ptr(),&m,ipiv,&info); + + GCK(A,A,info>0,"Matrix::det() dgetrf error: Bad argument value."); + if (!info) // matrix is non-singular + { + // Compute det(A) = det(P)*det(L)*det(U) = +/-1 * det(U) + int i, OddNumPivots; + + det = PLUA(0,0); + OddNumPivots = ipiv[0]!=(1); + for(i=1; i& A) +{ + GCK(A,A,!is_size(3,3), "max_eigenvalue only implimented for 3x3"); + const double c0 = det(A); + const double c1 = A(1,0)*A(0,1) + A(2,0)*A(0,2) + A(1,2)*A(2,1) + - A(0,0)*A(1,1) - A(0,0)*A(2,2) - A(1,1)*A(2,2); + const double c2 = trace(A); + double c[4] = {c0, c1, c2, -1.0}, x[3]; + int num_roots = ATC::solve_cubic(c, x); + double max_root = 0.0; + for (int i=0; i +class Matrix +{ +protected: + Matrix(const Matrix &c); +public: + Matrix() {} + virtual ~Matrix() {} + + //* stream output functions + void print(ostream &o) const { o << tostring(); } + void print(ostream &o, const string &name) const; + friend ostream& operator<<(ostream &o, const Matrix &m){m.print(o); return o;} + void print() const; + virtual void print(const string &name) const; + virtual string tostring() const; + + // element by element operations + DenseMatrix operator/(const Matrix& B) const; + DenseMatrix pow(int n) const; + DenseMatrix pow(double n) const; + + // functions that return a copy + DenseMatrix transpose() const; + + // matrix to scalar functions + T sum() const; + T stdev() const; + T max() const; + T min() const; + T maxabs() const; + T minabs() const; + T norm() const; + T mean() const; + T dot(const Matrix &r) const; + T trace() const; + + // row and column operations + T row_sum (INDEX i=0) const { return row(*this,i).sum(); } + T row_mean (INDEX i=0) const { return row(*this,i).mean(); } + T row_norm (INDEX i=0) const { return row(*this,i).norm(); } + T row_stdev(INDEX i=0) const { return row(*this,i).stdev(); } + T col_sum (INDEX i=0) const { return column(*this,i).sum(); } + T col_mean (INDEX i=0) const { return column(*this,i).mean(); } + T col_norm (INDEX i=0) const { return column(*this,i).norm(); } + T col_stdev(INDEX i=0) const { return column(*this,i).stdev(); } + + // pure virtual functions (required to implement these) --------------------- + //* reference index operator + virtual T& operator()(INDEX i, INDEX j)=0; + //* value index operator + virtual T operator()(INDEX i, INDEX j)const=0; + //* value flat index operator + virtual T& operator [](INDEX i)=0; + //* reference flat index operator + virtual T operator [](INDEX i) const=0; + //* returns the # of rows + virtual INDEX nRows() const=0; + //* returns the # of columns + virtual INDEX nCols() const=0; + //* returns a pointer to the data (dangerous) + virtual T * get_ptr() const=0; + //* resizes the matrix, copy what fits default to OFF + virtual void resize(INDEX nRows, INDEX nCols=1, bool copy=false)=0; + //* resizes the matrix, zero it out default to ON + virtual void reset(INDEX nRows, INDEX nCols=1, bool zero=true)=0; + //* resizes and copies data + virtual void copy(const T * ptr, INDEX nRows, INDEX nCols=1)=0; + //* create restart file + virtual void write_restart(FILE *f) const=0; + //* writes a matlab command to recreate this in a variable named s + virtual void matlab(ostream &o, const string &s="M") const; + + // output to matlab, with variable name s + void matlab(const string &s="M") const; + + Matrix& operator+=(const Matrix &r); + Matrix& operator-=(const Matrix &r); + //* NOTE these two functions are scaling operations not A.B + Matrix& operator*=(const Matrix& R); + Matrix& operator/=(const Matrix& R); + Matrix& operator+=(const T v); + Matrix& operator-=(const T v); + Matrix& operator*=(const T v); + Matrix& operator/=(T v); + + Matrix& operator=(const T &v); + Matrix& operator=(const Matrix &c); + virtual void set_all_elements_to(const T &v); + //* adds a matrix scaled by factor s to this one. + void add_scaled(const Matrix &A, const T& s); + + //* sets all elements to zero + Matrix& zero(); + //* returns the total number of elements + virtual INDEX size() const; + //* returns true if (i,j) is within the range of the matrix + bool in_range(INDEX i, INDEX j) const; + //* returns true if the matrix size is rs x cs + bool is_size(INDEX rs, INDEX cs) const; + //* returns true if the matrix is square and not empty + bool is_square() const; + //* returns true if Matrix, m, is the same size as this + bool same_size(const Matrix &m) const; + //* returns true if Matrix a and Matrix b are the same size + static bool same_size(const Matrix &a, const Matrix &b); + //* checks if memory is contiguous, only can be false for clone vector + virtual bool memory_contiguous() const { return true; } + +protected: + virtual void _set_equal(const Matrix &r); +}; + +//* Matrix operations +//@{ +//* Sets C as b*C + a*A[tranpose?]*B[transpose?] +template +void MultAB(const Matrix &A, const Matrix &B, DenseMatrix &C, + bool At=0, bool Bt=0, T a=1, T b=0); +//* performs a matrix-vector multiply +template +void MultMv(const Matrix &A, const Vector &v, DenseVector &c, + const bool At, T a, T b); +// returns the inverse of a double precision matrix +DenseMatrix inv(const Matrix& A); +//* returns the trace of a matrix +template +T trace(const Matrix& A) { return A.trace(); } +//* computes the determinant of a square matrix +double det(const Matrix& A); +//* Returns the maximum eigenvalue of a matrix. +double max_eigenvalue(const Matrix& A); +//@} + +//----------------------------------------------------------------------------- +// computes the sum of the difference squared of each element. +//----------------------------------------------------------------------------- +template +double sum_difference_squared(const Matrix& A, const Matrix &B) +{ + SSCK(A, B, "sum_difference_squared"); + double v=0.0; + for (unsigned i=0; i +DenseMatrix operator*(const Matrix &A, const Matrix &B) +{ + DenseMatrix C(0,0,false); + MultAB(A,B,C); + return C; +} +//----------------------------------------------------------------------------- +//* Multiply a Matrix by a scalar +//----------------------------------------------------------------------------- +template +DenseMatrix operator*(const Matrix &M, const T s) +{ + DenseMatrix R(M); + return R*=s; +} +//----------------------------------------------------------------------------- +//* Multiply a Matrix by a scalar +template +DenseMatrix operator*(const T s, const Matrix &M) +{ + DenseMatrix R(M); + return R*=s; +} +//----------------------------------------------------------------------------- +//* inverse scaling operator - must always create memory +template +DenseMatrix operator/(const Matrix &M, const T s) +{ + DenseMatrix R(M); + return R*=(1.0/s); // for integer types this may be worthless +} +//----------------------------------------------------------------------------- +//* Operator for Matrix-matrix sum +template +DenseMatrix operator+(const Matrix &A, const Matrix &B) +{ + DenseMatrix C(A); + return C+=B; +} +//----------------------------------------------------------------------------- +//* Operator for Matrix-matrix subtraction +template +DenseMatrix operator-(const Matrix &A, const Matrix &B) +{ + DenseMatrix C(A); + return C-=B; +} +/****************************************************************************** +* Template definitions for class Matrix +******************************************************************************/ + +//----------------------------------------------------------------------------- +//* performs a matrix-matrix multiply with general type implementation +template +void MultAB(const Matrix &A, const Matrix &B, DenseMatrix &C, + const bool At, const bool Bt, T a, T b) +{ + const INDEX sA[2] = {A.nRows(), A.nCols()}; // m is sA[At] k is sA[!At] + const INDEX sB[2] = {B.nRows(), B.nCols()}; // k is sB[Bt] n is sB[!Bt] + const INDEX M=sA[At], K=sB[Bt], N=sB[!Bt]; + GCK(A, B, sA[!At]!=K, "MultAB shared index not equal size"); + if (!C.is_size(M,N)) + { + C.resize(M,N); // set size of C + C.zero(); + } + else C *= b; + for (INDEX p=0; p +string Matrix::tostring() const +{ + string s; + for (INDEX i=0; i +void Matrix::print(ostream &o, const string &name) const +{ + o << "------- Begin "<print(o); + o << "\n------- End "< +void Matrix::print() const +{ + print(cout); +} +//----------------------------------------------------------------------------- +//* named print operator, use cout by default +template +void Matrix::print(const string &name) const +{ + print(cout, name); +} +//----------------------------------------------------------------------------- +//* element by element division +template +DenseMatrix Matrix::operator/ (const Matrix& B) const +{ + SSCK(*this, B, "Matrix::Operator/"); + DenseMatrix R(*this); + R /= B; + return R; +} +//----------------------------------------------------------------------------- +//* element-wise raise to a power +template +DenseMatrix Matrix::pow(int n) const +{ + DenseMatrix R(*this); + FORi + { + double val = R[i]; + for (int k=1; k +DenseMatrix Matrix::pow(double n) const +{ + DenseMatrix R(*this); + FORi + { + double val = R[i]; + R[i] = pow(val,n); + } + return R; +} +//----------------------------------------------------------------------------- +//* returns the transpose of this matrix (makes a copy) +template +DenseMatrix Matrix::transpose() const +{ + DenseMatrix t(this->nCols(), this->nRows()); + FORij t(j,i) = (*this)(i,j); + return t; +} +//----------------------------------------------------------------------------- +//* returns the transpose of a matrix (makes a copy) +template +DenseMatrix transpose(const Matrix &A) +{ + return A.transpose(); +} +//----------------------------------------------------------------------------- +//* Returns the sum of all matrix elements +template +T Matrix::sum() const +{ + if (!size()) return T(0); + T v = VIDX(0); + for (INDEX i=1; isize(); i++) v += VIDX(i); + return v; +} +//----------------------------------------------------------------------------- +//* Returns the standard deviation of the matrix +template +T Matrix::stdev() const +{ + GCHK(this->size()<2, "Matrix::stdev() size must be > 1"); + T mean = this->mean(); + T diff = VIDX(0)-mean; + T stdev = diff*diff; + for (INDEX i=1; isize(); i++) + { + diff = VIDX(i)-mean; + stdev += diff*diff; + } + return sqrt(stdev/T(this->size()-1)); +} +//----------------------------------------------------------------------------- +//* Returns the maximum of the matrix +template +T Matrix::max() const +{ + GCHK(!this->size(), "Matrix::max() size must be > 0"); + T v = VIDX(0); + for (INDEX i=1; isize(); i++) v = std::max(v, VIDX(i)); + return v; +} +//----------------------------------------------------------------------------- +//* Returns the minimum of the matrix +template +T Matrix::min() const +{ + GCHK(!this->size(), "Matrix::min() size must be > 0"); + T v = VIDX(0); + for (INDEX i=1; isize(); i++) v = std::min(v, VIDX(i)); + return v; +} +//----------------------------------------------------------------------------- +//* Returns the maximum absolute value of the matrix +template +T Matrix::maxabs() const +{ + GCHK(!this->size(), "Matrix::maxabs() size must be > 0"); + T v = VIDX(0); + for (INDEX i=1; isize(); i++) v = Utility::MaxAbs(v, VIDX(i)); + return v; +} +//----------------------------------------------------------------------------- +//* Returns the minimum absoute value of the matrix +template +T Matrix::minabs() const +{ + GCHK(!this->size(), "Matrix::minabs() size must be > 0"); + T v = VIDX(0); + for (INDEX i=1; isize(); i++) v = Utility::MinAbs(v, VIDX(i)); + return v; +} +//----------------------------------------------------------------------------- +//* returns the L2 norm of the matrix +template +T Matrix::norm() const +{ + GCHK(!this->size(), "Matrix::norm() size must be > 0"); + return sqrt(dot(*this)); +} +//----------------------------------------------------------------------------- +//* returns the average of the matrix +template +T Matrix::mean() const +{ + GCHK(!this->size(), "Matrix::mean() size must be > 0"); + return sum()/T(this->size()); +} +//----------------------------------------------------------------------------- +//* Returns the dot product of two vectors +template +T Matrix::dot(const Matrix& r) const +{ + SSCK(*this, r, "Matrix::dot"); + if (!this->size()) return T(0); + T v = r[0]*VIDX(0); + for (INDEX i=1; isize(); i++) v += r[i]*VIDX(i); + return v; +} +//----------------------------------------------------------------------------- +// returns the sum of the matrix diagonal +//----------------------------------------------------------------------------- +template +T Matrix::trace() const +{ + const INDEX N = std::min(nRows(),nCols()); + if (!N) return T(0); + T r = MIDX(0,0); + for (INDEX i=0; i +Matrix& Matrix::operator+=(const Matrix &r) +{ + SSCK(*this, r, "operator+= or operator +"); + FORi VIDX(i)+=r[i]; + return *this; +} +//----------------------------------------------------------------------------- +// subtracts a matrix from this one +//----------------------------------------------------------------------------- +template +Matrix& Matrix::operator-=(const Matrix &r) +{ + SSCK(*this, r, "operator-= or operator -"); + FORi VIDX(i)-=r[i]; + return *this; +} +//----------------------------------------------------------------------------- +// multiplies each element in this by the corresponding element in R +//----------------------------------------------------------------------------- +template +Matrix& Matrix::operator*=(const Matrix& R) +{ + SSCK(*this, R, "operator*="); + FORi + { + VIDX(i) *= R[i]; + } + return *this; +} +//----------------------------------------------------------------------------- +// divides each element in this by the corresponding element in R +//----------------------------------------------------------------------------- +template +Matrix& Matrix::operator/=(const Matrix& R) +{ + SSCK(*this, R, "operator/= or operator/"); + FORi + { + GCHK(fabs(R[i])>0,"Operator/: division by zero"); + VIDX(i) /= R[i]; + } + return *this; +} +//----------------------------------------------------------------------------- +// scales this matrix by a constant +//----------------------------------------------------------------------------- +template +Matrix& Matrix::operator*=(const T v) +{ + FORi VIDX(i)*=v; + return *this; +} +//----------------------------------------------------------------------------- +// adds a constant to this matrix +//----------------------------------------------------------------------------- +template +Matrix& Matrix::operator+=(const T v) +{ + FORi VIDX(i)+=v; + return *this; +} +//----------------------------------------------------------------------------- +// substracts a constant to this matrix +//----------------------------------------------------------------------------- +template +Matrix& Matrix::operator-=(const T v) +{ + FORi VIDX(i)-=v; + return *this; +} +//----------------------------------------------------------------------------- +//* scales this matrix by the inverse of a constant +template +Matrix& Matrix::operator/=(T v) +{ + return (*this)*=(1.0/v); +} + +//---------------------------------------------------------------------------- +// Assigns one matrix to another +//---------------------------------------------------------------------------- +template +Matrix& Matrix::operator=(const Matrix &r) +{ + this->_set_equal(r); + return *this; +} +//---------------------------------------------------------------------------- +// general matrix assignment (for densely packed matrices) +//---------------------------------------------------------------------------- +template +void Matrix::_set_equal(const Matrix &r) +{ + this->resize(r.nRows(), r.nCols()); + const Matrix *pr = &r; + if (const SparseMatrix *ps = sparse_cast(pr)) + copy_sparse_to_matrix(ps, *this); + + else if (diag_cast(pr)) // r is Diagonal? + { + this->zero(); + for (INDEX i=0; iget_ptr(), r.get_ptr(), r.size()*sizeof(T)); +} +//----------------------------------------------------------------------------- +//* sets all elements to a constant +template +inline Matrix& Matrix::operator=(const T &v) +{ + set_all_elements_to(v); + return *this; +} +//----------------------------------------------------------------------------- +//* sets all elements to a constant +template +void Matrix::set_all_elements_to(const T &v) +{ + FORi VIDX(i) = v; +} +//---------------------------------------------------------------------------- +// adds a matrix scaled by factor s to this one. +//---------------------------------------------------------------------------- +template +void Matrix::add_scaled(const Matrix &A, const T& s) +{ + SSCK(A, *this, "Matrix::add_scaled"); + FORi VIDX(i) += A[i]*s; +} +//----------------------------------------------------------------------------- +//* writes a matlab command to the console +template +void Matrix::matlab(const string &s) const +{ + this->matlab(cout, s); +} +//----------------------------------------------------------------------------- +//* Writes a matlab script defining the vector to the stream +template +void Matrix::matlab(ostream &o, const string &s) const +{ + o << s <<"=zeros(" << nRows() << ","< +inline Matrix& Matrix::zero() +{ + set_all_elements_to(T(0)); + return *this; +} +//----------------------------------------------------------------------------- +//* returns the total number of elements +template +inline INDEX Matrix::size() const +{ + return nRows()*nCols(); +} +//----------------------------------------------------------------------------- +//* returns true if (i,j) is within the range of the matrix +template +inline bool Matrix::in_range(INDEX i, INDEX j) const +{ + return i +inline bool Matrix::is_size(INDEX rs, INDEX cs) const +{ + return nRows()==rs && nCols()==cs; +} +//----------------------------------------------------------------------------- +//* returns true if the matrix is square and not empty +template +inline bool Matrix::is_square() const +{ + return nRows()==nCols() && nRows(); +} +//----------------------------------------------------------------------------- +//* returns true if Matrix, m, is the same size as this +template +inline bool Matrix::same_size(const Matrix &m) const +{ + return is_size(m.nRows(), m.nCols()); +} +//----------------------------------------------------------------------------- +//* returns true if Matrix a and Matrix b are the same size +template +inline bool Matrix::same_size(const Matrix &a, const Matrix &b) +{ + return a.same_size(b); +} +//----------------------------------------------------------------------------- +//* Displays indexing error message and quits +template +void ierror(const Matrix &a, const char *FILE, int LINE, INDEX i, INDEX j) +{ + cout << "Error: Matrix indexing failure "; + cout << "in file: " << FILE << ", line: "<< LINE <<"\n"; + cout << "Tried accessing index (" << i << ", " << j <<")\n"; + cout << "Matrix size was "<< a.nRows() << "x" << a.nCols() << "\n"; + exit(EXIT_FAILURE); +} +//----------------------------------------------------------------------------- +//* Displays custom message and indexing error and quits +template +void ierror(const Matrix &a, INDEX i, INDEX j, const string m) +{ + cout << m << "\n"; + cout << "Tried accessing index (" << i << ", " << j <<")\n"; + cout << "Matrix size was "<< a.nRows() << "x" << a.nCols() << "\n"; + exit(EXIT_FAILURE); +} +//----------------------------------------------------------------------------- +//* Displays matrix compatibility error message +template +void merror(const Matrix &a, const Matrix &b, const string m) +{ + cout << "Error: " << m << "\n"; + cout << "Matrix sizes were " << a.nRows() << "x" << a.nCols(); + if (&a != &b) cout << ", and "<< b.nRows() << "x" << b.nCols(); + cout << "\n"; + exit(EXIT_FAILURE); +} + +#endif diff --git a/lib/atc/MatrixDef.h b/lib/atc/MatrixDef.h new file mode 100644 index 0000000000..3d1c3767be --- /dev/null +++ b/lib/atc/MatrixDef.h @@ -0,0 +1,185 @@ +#ifndef MATRIXDEF_H +#define MATRIXDEF_H + +/****************************************************************************** +* Common definitions for Matrix and Vector classes +* This header file contains macros and inline functions needed for the matrix +* classes. All error checking should be defined here as a macro so that it is +* neatly disabled when EXTENDED_ERROR_CHECKING is not defined +******************************************************************************/ + +/****************************************************************************** +* Headers and namespaces used by Matrix and Vector classes +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include "StringManip.h" +#include "Utility.h" + +using std::cout; +using std::ostream; +using std::fstream; +using std::map; +using std::vector; +using std::string; +using std::scientific; +using std::showbase; + +/****************************************************************************** +* Typedefs used by Matrix and Vector classes +******************************************************************************/ +//* @typedef INDEX +//* @brief indexing type (default: unsigned) for matrix classes +typedef unsigned INDEX; +typedef vector List; +//* @typedef CLONE_TYPE +//* @brief dimension of matrix to clone +enum CLONE_TYPE { CLONE_ROW=0, CLONE_COL=1, CLONE_DIAG=2 }; +//* @struct TRIPLET +//* @brief Triplet output entity +template +struct TRIPLET { TRIPLET(int _i=0, int _j=0, T _v=T(0)) : i(_i), j(_j), v(_v) {} + INDEX i, j; T v; }; + +/****************************************************************************** +* Definitions for row/column major storage +******************************************************************************/ +#define COL_STORAGE /* <--- comment out this line for row-major storage*/ +#ifdef COL_STORAGE +#define DATA(i,j) _data[(i)+_nRows*(j)] +#else +#define ROW_STORAGE +#define DATA(i,j) _data[(i)*_nCols+(j)] +#endif + +/****************************************************************************** +* error checking macros +* MICK: checks if index (i,j) is in range MATRIX ONLY +* VICK: checks if index (i) is in range VECTOR ONLY +* MICM: checks if index (i,j) is in range, displays message MATRIX ONLY +* VICM: checks if index (i) is in range, displays message VECTOR ONLY +* SQCK: checks if matrix is square, displays message MATRIX ONLY +* SSCK: checks if a has the same size as b VECTOR/MATRIX +* GCK: generic two object check, checks if c is true VECTOR/MATRIX +* GCHK: generic check, checks if c is true ANYTHING +******************************************************************************/ +#define MICK(i,j) /**/ +#define VICK(i) /**/ +#define MICM(i,j,m) /**/ +#define VICM(i,m) /**/ +#define SQCK(a,m) /**/ +#define SSCK(a,b,m) /**/ +#define SSCK(a,b,m) /**/ +#define GCK(a,b,c,m) /**/ +#define GCHK(c,m) /**/ + +// the following two convert __LINE__ to a string +#define STRING2(x) #x +#define STRING(x) STRING2(x) +// prints file and line number for error messages +#define ERROR(x) __FILE__":"STRING(__LINE__)" "x + +/****************************************************************************** +* Shortcuts for Vector and Matrix indexing +******************************************************************************/ +#define MIDX(i,j) (*this)(i,j) +#define VIDX(i) (*this)[i] +/****************************************************************************** +* Shortcuts for Vector and Matrix loops over all elements +******************************************************************************/ +#define FORi for(INDEX i=0; isize(); i++) +#define FORij for(INDEX i=0; i class Matrix; +template class DenseMatrix; +template class SparseMatrix; +template class SparseVector; +template class DiagonalMatrix; +template class Vector; +template class DenseVector; +template class CloneVector; + +//* forward declaration of operations +//@{ +template DenseVector operator*(const Matrix &M, const SparseVector &v); +template DenseVector operator*(const SparseVector &v, const Matrix &M); +template SparseVector operator*(const SparseMatrix &M, const SparseVector &v); +template SparseVector operator*(const SparseVector &v, const SparseMatrix &M); +template DenseVector operator*(const SparseMatrix &A, const Vector& x); +template DenseVector operator*(const Vector &A, const SparseMatrix& x); +template DenseMatrix operator*(const SparseMatrix &A, const Matrix& D); +template SparseMatrix operator*(const SparseMatrix &A, const DiagonalMatrix& D); +template SparseMatrix operator*(const SparseMatrix &A, const SparseMatrix &B); +template T dot(const SparseVector &a, const SparseVector &b); +//@} + +template CloneVector column(Matrix &c, INDEX i) { + return CloneVector(c, CLONE_COL, i); +} +template CloneVector row(Matrix &c, INDEX i) { + return CloneVector(c, CLONE_ROW, i); +} +template CloneVector diagonal(Matrix &c) { + return CloneVector(c, CLONE_DIAG); +} +template const CloneVector column(const Matrix &c, INDEX i) { + return CloneVector(c, CLONE_COL, i); +} +template const CloneVector row(const Matrix &c, INDEX i) { + return CloneVector(c, CLONE_ROW, i); +} +template const CloneVector diagonal(const Matrix &c) { + return CloneVector(c, CLONE_DIAG); +} +template const SparseMatrix *sparse_cast(const Matrix *m); +template const DiagonalMatrix *diag_cast(const Matrix *m); +template void copy_sparse_to_matrix(const SparseMatrix *s, Matrix &m); + +// double precision shortcuts +typedef Matrix MATRIX; // matrix of double +typedef Vector VECTOR; // vector of double +typedef DenseMatrix DENS_MAT; // dense matrix of double type +typedef CloneVector CLON_VEC; // cloned vector of double type +typedef DenseVector DENS_VEC; // dense vector of double type +typedef DiagonalMatrix DIAG_MAT; // diagonal matrix of double type +typedef SparseMatrix SPAR_MAT; // sparse matrix of double type +typedef SparseVector SPAR_VEC; // sparse matrix of double type +typedef Vector IVECTOR; // general Vector of INDEX type + +// forward declaration of error messages +template void ierror(const Matrix &a, const char *FILE, int LINE, INDEX i, INDEX j=0); +template void ierror(const Matrix &a, INDEX i, INDEX j, const string m); +template void merror(const Matrix &a, const Matrix &b, const string m); +inline void gerror(const string m) { cout<<"Error: "< +const SparseMatrix *sparse_cast(const Matrix *m) +{ + return dynamic_cast*>(m); +} + +template +void copy_sparse_to_matrix(const SparseMatrix *s, Matrix &m) +{ + m.zero(); + TRIPLET triplet; + for (INDEX i=0; isize(); i++) + { + triplet = s->get_triplet(i); + m(triplet.i, triplet.j) = triplet.v; + } +} + +#endif diff --git a/lib/atc/OutputManager.cpp b/lib/atc/OutputManager.cpp new file mode 100644 index 0000000000..ee171eb636 --- /dev/null +++ b/lib/atc/OutputManager.cpp @@ -0,0 +1,706 @@ +#include +#include +#include +#include +#include "OutputManager.h" +#include "ATC_Error.h" + +//#define EXTENDED_ERROR_CHECKING + +namespace ATC { + +static const int kFieldPrecison = 12; +static const int kFieldWidth = kFieldPrecison + 6; + +static const int kFileNameSize = 26; +static string tensor_component_names[9] = {"11","12","13", + "21","22","23", + "31","32","33"}; +static string sym_tensor_component_names[6] = {"11","22","33","12","13","23"}; +static string vector_component_names[3] = {"_X","_Y","_Z"}; +static string list_component_names[26] = {"_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","_o","_p","_q","_r","_s","_t","_u","_v","_w","_x","_y","_z"}; + +string* get_component_names(int type) { + string* componentNames = list_component_names; + if (type==VECTOR_OUTPUT) + componentNames = vector_component_names; + else if (type == SYM_TENSOR_OUTPUT) + componentNames = sym_tensor_component_names; + else if (type == TENSOR_OUTPUT) + componentNames = tensor_component_names; + return componentNames; +} + + +//----------------------------------------------------------------------------- +//* +//----------------------------------------------------------------------------- +OutputManager::OutputManager(string outputPrefix, OutputType otype) + : outputPrefix_(outputPrefix), + dataType_(POINT), + outputType_(otype), + firstStep_(true), + writeGlobalsHeader_(true), + firstGlobalsWrite_(true), + tensorToComponents_(false), // paraview does not support tensors + vectorToComponents_(false), + initialized_(false) +{} +//----------------------------------------------------------------------------- +//* +//----------------------------------------------------------------------------- +OutputManager::OutputManager() + : outputPrefix_("NULL"), + dataType_(POINT), + outputType_(ENSIGHT), + firstStep_(true), + writeGlobalsHeader_(true), + firstGlobalsWrite_(true), + tensorToComponents_(false), // paraview does not support tensors + vectorToComponents_(false), + initialized_(false) +{} +//----------------------------------------------------------------------------- +//* +//----------------------------------------------------------------------------- +OutputManager::~OutputManager() {} +//----------------------------------------------------------------------------- +//* +//----------------------------------------------------------------------------- +void OutputManager::set_option(OutputOption option, bool value) { + if (option == OUTPUT_VECTOR_COMPONENTS) vectorToComponents_ = value; + else if (option == OUTPUT_TENSOR_COMPONENTS) tensorToComponents_ = value; + else throw ATC_Error(0,"unsupported output option"); +}; + +//----------------------------------------------------------------------------- +//* +//----------------------------------------------------------------------------- +void OutputManager::initialize(string outputPrefix, OutputType otype) +{ + if (outputPrefix_ != outputPrefix ) { // new stream with existing object + outputPrefix_ = outputPrefix; + initialized_ = false; + } + outputTimes_.clear(); + outputType_ = otype; + firstStep_ = true; + firstGlobalsWrite_ = true; + writeGlobalsHeader_ = true; +} +//----------------------------------------------------------------------------- +//* +//----------------------------------------------------------------------------- +// Dump text-based fields to disk for later restart +void OutputManager::write_restart_file(string fileName, OUTPUT_LIST *data) +{ + FILE * fp=NULL; + fp=fopen(fileName.c_str(),"wb"); // open + OUTPUT_LIST::iterator iter; + for (iter = data->begin(); iter != data->end(); iter++) { + const MATRIX* field_data = iter->second; + for (int i = 0; i < field_data->nRows(); ++i) { + for (int j = 0; j < field_data->nCols(); ++j) { + double x = (*field_data)(i,j); + fwrite(&x,sizeof(double),1,fp); + } + } + } + fclose(fp); +} +//----------------------------------------------------------------------------- +//* +//----------------------------------------------------------------------------- +// Read a file corresponding to a write by write_restart_file +void OutputManager::read_restart_file(string fileName, OUTPUT_LIST *data) +{ + FILE * fp=NULL; + fp=fopen(fileName.c_str(),"rb"); // open + OUTPUT_LIST::iterator iter; + for (iter = data->begin(); iter != data->end(); iter++) { + MATRIX* field_data = iter->second; + for (int i = 0; i < field_data->nRows(); ++i) { + for (int j = 0; j < field_data->nCols(); ++j) { + double myVal; + fread(&myVal,sizeof(double),1,fp); + (*field_data)(i,j) = myVal; + } + } + } + fclose(fp); +} +//----------------------------------------------------------------------------- +//* +//----------------------------------------------------------------------------- +void OutputManager::write_globals(void) +{ + if ( outputPrefix_ == "NULL") return; + string file = outputPrefix_ + ".GLOBALS"; + ofstream text; + if ( firstGlobalsWrite_ ) text.open(file.c_str(),ios_base::out); + else text.open(file.c_str(),ios_base::app); + firstGlobalsWrite_ = false; + + map::iterator iter; + // header + if ( firstStep_ || writeGlobalsHeader_) { + text << "# time:1 "; + int index = 2; + for (iter = globalData_.begin(); iter != globalData_.end(); iter++) + { + string name = iter->first; + string str; stringstream out; out << ":" << index++; + str = out.str(); + name.append(str); + text.width(kFieldWidth); text << name << " "; + } + text << '\n'; + } + writeGlobalsHeader_ = false; + // data + text.width(kFieldWidth); text << outputTimes_[outputTimes_.size()-1] << " "; + for (iter = globalData_.begin(); + iter != globalData_.end(); iter++) { + double value = iter->second; + text << setw(kFieldWidth) << std::scientific << std::setprecision(kFieldPrecison) << value << " "; + } + text << "\n"; +} +//----------------------------------------------------------------------------- +//* +//----------------------------------------------------------------------------- +void OutputManager::write_geometry(const MATRIX &coordinates, + const Array2D *connectivities) +{ + if ( outputPrefix_ == "NULL") return; + // geometry based on a reference configuration + string geom_file_name = outputPrefix_ + ".geo"; + string geom_file_text = outputPrefix_ + ".XYZ"; + + // open file + FILE * fp=NULL; + ofstream text; + char buffer[80]; + if ( ! initialized_ ) { + fp=fopen(geom_file_name.c_str(),"wb"); // open + strcpy(buffer,"C Binary"); + fwrite(buffer,sizeof(char),80,fp); + + // write geometry once + if (outputType_ == GNUPLOT) text.open(geom_file_text.c_str(),ios_base::out); + } + else { + fp=fopen(geom_file_name.c_str(),"ab"); // append + +// if (outputType_ == GNUPLOT) text.open(geom_file_text.c_str(),ios_base::app); + } + if (fp == NULL) { + throw ATC_Error(0,"can not create Ensight geometry file"); + } + + // write preamble + strcpy(buffer,"BEGIN TIME STEP"); + fwrite(buffer,sizeof(char),80,fp); + strcpy(buffer,"Ensight geometry file"); + fwrite(buffer,sizeof(char),80,fp); + strcpy(buffer,"description"); + fwrite(buffer,sizeof(char),80,fp); + strcpy(buffer,"node id assign"); + fwrite(buffer,sizeof(char),80,fp); + strcpy(buffer,"element id assign"); + fwrite(buffer,sizeof(char),80,fp); + + // per part + strcpy(buffer,"part"); + fwrite(buffer,sizeof(char),80,fp); + int part_number=1; + fwrite(&part_number,sizeof(int),1,fp); + strcpy(buffer,"description"); + fwrite(buffer,sizeof(char),80,fp); + + // write coordinates + strcpy(buffer,"coordinates"); + fwrite(buffer,sizeof(char),80,fp); + number_of_nodes_ = coordinates.nCols(); + fwrite(&number_of_nodes_,sizeof(int),1,fp); + int number_of_spatial_dimensions = coordinates.nRows(); + if (number_of_spatial_dimensions != 3) + throw ATC_Error(0,"Ensight writer needs a 3D geometry"); + + for (int i = 0; i < number_of_spatial_dimensions; ++i) + { + for (int j = 0; j < number_of_nodes_; ++j) + { + float x = (float) coordinates(i,j); + fwrite(&x,sizeof(float),1,fp); + } + } + + // write mesh connectivities or point "connectivities" + if (connectivities) + { + dataType_ = MESH; + strcpy(buffer,"hexa8"); + fwrite(buffer,sizeof(char),80,fp); + int number_of_elements = connectivities->nCols(); + fwrite(&number_of_elements,sizeof(int),1,fp); + int number_of_nodes_per_element = connectivities->nRows(); + for (int j = 0; j < number_of_elements; ++j) + { + for (int i = 0; i < number_of_nodes_per_element; ++i) + { + int inode = (*connectivities)(i,j) +1; // 1 based numbering + fwrite(&inode,sizeof(int),1,fp); + } + } + } + else + { + strcpy(buffer,"point"); + fwrite(buffer,sizeof(char),80,fp); + int number_of_elements = number_of_nodes_; + fwrite(&number_of_elements,sizeof(int),1,fp); + int number_of_nodes_per_element = 1; + for (int j = 0; j < number_of_elements; ++j) + { + int inode = j +1; // 1 based numbering + fwrite(&inode,sizeof(int),1,fp); + } + } + + // end per part + strcpy(buffer,"END TIME STEP"); + fwrite(buffer,sizeof(char),80,fp); + fclose(fp); + + // text output + if (outputType_ == GNUPLOT ) + { + for (int j = 0; j < number_of_nodes_; ++j) + { + for (int i = 0; i < number_of_spatial_dimensions; ++i) + { + text << setw(kFieldWidth) << std::scientific << std::setprecision(kFieldPrecison) << coordinates(i,j) << " "; + } + text << "\n"; + } + text << "\n"; + } + + if (!initialized_) initialized_ = true; +} +//----------------------------------------------------------------------------- +//* +//----------------------------------------------------------------------------- +void OutputManager::write_geometry(OUTPUT_LIST &part_coordinates) +{ + if ( outputPrefix_ == "NULL") return; + // geometry based on a reference configuration + string geom_file_name = outputPrefix_ + ".geo"; + string geom_file_text = outputPrefix_ + ".XYZ"; + + // open file + FILE * fp =NULL; + ofstream text; + char buffer[80]; + if ( ! initialized_ ) + { + fp=fopen(geom_file_name.c_str(),"wb"); // open + strcpy(buffer,"C Binary"); + fwrite(buffer,sizeof(char),80,fp); + + if (outputType_ == GNUPLOT) text.open(geom_file_text.c_str(),ios_base::out); + } + else + { + fp=fopen(geom_file_name.c_str(),"ab"); // append + if (outputType_ == GNUPLOT) text.open(geom_file_text.c_str(),ios_base::app); + } + if (fp == NULL) { + throw ATC_Error(0,"can not create Ensight geometry file"); + } + + // write preamble + strcpy(buffer,"BEGIN TIME STEP"); + fwrite(buffer,sizeof(char),80,fp); + strcpy(buffer,"Ensight geometry file"); + fwrite(buffer,sizeof(char),80,fp); + strcpy(buffer,"description"); + fwrite(buffer,sizeof(char),80,fp); + strcpy(buffer,"node id assign"); + fwrite(buffer,sizeof(char),80,fp); + strcpy(buffer,"element id assign"); + fwrite(buffer,sizeof(char),80,fp); + + // per part + int part_number=1; + OUTPUT_LIST::iterator iter; + for (iter = part_coordinates.begin(); iter != part_coordinates.end(); iter++) + { + string part_name = iter->first; + DenseMatrix coordinates = *(iter->second); + strcpy(buffer,"part"); + fwrite(buffer,sizeof(char),80,fp); + fwrite(&part_number,sizeof(int),1,fp); + strcpy(buffer,part_name.c_str()); + fwrite(buffer,sizeof(char),80,fp); + + // write coordinates + strcpy(buffer,"coordinates"); + fwrite(buffer,sizeof(char),80,fp); + number_of_nodes_ = coordinates.nCols(); + fwrite(&number_of_nodes_,sizeof(int),1,fp); + int number_of_spatial_dimensions = coordinates.nRows(); + if (number_of_spatial_dimensions != 3) + { + throw ATC_Error(0,"Ensight writer needs a 3D geometry"); + } + for (int i=0; ibegin(); iter != soln->end(); iter++) + { + FieldName field_index = iter->first; + MATRIX* field_data = &(iter->second); + string field_name = field_to_string(field_index); + combined_data[field_name] = field_data; + } + } + if (data) + { + OUTPUT_LIST::iterator iter; + for (iter = data->begin(); iter != data->end(); iter++) + { + string field_name = iter->first; + MATRIX* field_data = iter->second; + combined_data[field_name] = field_data; + } + } + write_data(time, &(combined_data), node_map); +}; +//----------------------------------------------------------------------------- +/** write (ensight gold format "C" binary) data */ +//----------------------------------------------------------------------------- +void OutputManager::write_data(double time, OUTPUT_LIST *data, const int *node_map) +{ + if (! initialized_) throw ATC_Error(0,"must write geometry before data"); + + // write data + OUTPUT_LIST::iterator iter; + for (iter = data->begin(); iter != data->end(); iter++) + { + string field_name = iter->first; + const MATRIX* field_data = iter->second; + write_data(field_name, field_data, node_map); + } + + // write dictionary + write_dictionary(time,data); + + // write text dump + if (outputType_ == GNUPLOT) { + write_text(data); + if (firstStep_ && node_map) { + string map_file_text = outputPrefix_ + ".MAP"; + ofstream text; + text.open(map_file_text.c_str(),ios_base::out); + for (int i=0; i< number_of_nodes_ ; i++) { + text << node_map[i] << "\n"; + } + text.close(); + } + } + + // global variables + if (! globalData_.empty()) write_globals(); + + if (firstStep_) firstStep_ = false; +} +//----------------------------------------------------------------------------- +/** write (ensight gold format "C" binary) data */ +//----------------------------------------------------------------------------- +void OutputManager::write_data(string field_name, const MATRIX *field_data, const int *node_map) +{ + int ndof = field_data->nCols(); + int col_start = 0; + int col_end = ndof; + string filenames[kFileNameSize]; + int nfiles = 1; + filenames[0] = outputPrefix_ + "." + field_name; + int type = data_type(ndof); + if (use_component_names(type)){ + nfiles = ndof; + string* component_names = get_component_names(type); + for (int ifile = 0; ifile < nfiles; ++ifile) + { + string comp_name = field_name + component_names[ifile]; + filenames[ifile] = outputPrefix_ + "." + comp_name; + } + } + + for (int ifile = 0; ifile < nfiles; ++ifile) + { + // for vector/tensor to components + if ( nfiles > 1 ) + { + col_start = ifile; + col_end = ifile+1; + } + + // open or append data file + string data_file_name = filenames[ifile]; + FILE * fp; + if ( outputTimes_.size() == 0 ) { + fp=fopen(data_file_name.c_str(),"wb"); // open + } + else { + fp=fopen(data_file_name.c_str(),"ab"); // append + } + if (fp == NULL) { + throw ATC_Error(0,"can not create Ensight data file: "+data_file_name); + } + + // write data + char buffer[80]; + strcpy(buffer,"BEGIN TIME STEP"); + fwrite(buffer,sizeof(char),80,fp); + strcpy(buffer,"field name"); // NOTE could be the field name + fwrite(buffer,sizeof(char),80,fp); + + // per part + strcpy(buffer,"part"); + fwrite(buffer,sizeof(char),80,fp); + int part_number = 1; + fwrite(&part_number,sizeof(int),1,fp); + strcpy(buffer,"coordinates"); + fwrite(buffer,sizeof(char),80,fp); + if (node_map) + { + for (int j = col_start; j < col_end; ++j) + { + for (int i = 0; i < number_of_nodes_; ++i) + { + int inode = node_map[i]; + float x = (float) (*field_data)(inode,j); + fwrite(&x,sizeof(float),1,fp); + } + } + } + else + { + for (int j = col_start; j < col_end; ++j) + { + for (int i = 0; i < field_data->nRows(); ++i) + { + float x = (float) (*field_data)(i,j); + fwrite(&x,sizeof(float),1,fp); + } + } + } + // end per part + + strcpy(buffer,"END TIME STEP"); + fwrite(buffer,sizeof(char),80,fp); + fclose(fp); + } +} + +void OutputManager::write_text(OUTPUT_LIST *data) +{ + string data_file_text = outputPrefix_ + ".DATA"; + ofstream text; + if (firstStep_) text.open(data_file_text.c_str(),ios_base::out); + else text.open(data_file_text.c_str(),ios_base::app); + + // write data label header + if (firstStep_) + { + text.width(6); text << "# index:1" << " "; // give an ordinate for gnuplot + text.width(10); text << " step:2" << " "; + int k =3; + if (data) + { + OUTPUT_LIST::iterator iter; + for (iter = data->begin(); iter != data->end(); iter++) + { + int ncols = iter->second->nCols(); + string field_name = iter->first; + if (ncols == 1) { + string str; stringstream out; out << ":" << k; str = out.str(); + field_name.append(str); + text.width(kFieldWidth); text << field_name << " "; + k++; + } + else { + for (int i = 1; i <= ncols; i++) { + string str; stringstream out; out <<"_"<begin(); + const MATRIX* field_data = iter->second; + nrows = field_data->nRows(); + + for (int i = 0; i < nrows; ++i) + { + text.width(6); text << i << " "; // give an ordinate for gnuplot + text.width(10); text << outputTimes_.size() << " "; + OUTPUT_LIST::iterator iter; + for (iter = data->begin(); iter != data->end(); iter++) + { + const MATRIX* field_data = iter->second; + for (int j = 0; j < field_data->nCols(); ++j) + { + text.width(kFieldWidth); + text << setw(kFieldWidth) << std::scientific << std::setprecision(kFieldPrecison) << (*field_data)(i,j) << " "; + } + } + text <<"\n"; + } + text <<"\n"; +} + +/** write (ensight gold : ASCII "C" format) dictionary */ +void OutputManager::write_dictionary(double time, OUTPUT_LIST *data) +{ + // store the time step value + outputTimes_.push_back(time); + + // file names + string dict_file_name = outputPrefix_ + ".case"; + string geom_file_name = outputPrefix_ + ".geo"; + + // open file + FILE * fp; + if ((fp=fopen(dict_file_name.c_str(),"w")) == NULL) + { + throw ATC_Error(0,"can not create Ensight case file"); + } + + // write file + fprintf(fp,"FORMAT\n"); + fprintf(fp,"type: ensight gold\n"); + fprintf(fp,"GEOMETRY\n"); + if ( dataType_ == POINT) { + fprintf(fp,"model: 1 1 %s change_coords_only\n", geom_file_name.c_str()); + } else { + fprintf(fp,"model: %s\n", geom_file_name.c_str()); + } + fprintf(fp,"VARIABLE\n"); + + // data types + if (!data) throw ATC_Error(0,"no data for output"); + OUTPUT_LIST::iterator iter; + int ncols = 0; + for (iter = data->begin(); iter != data->end(); iter++) { + string field_name = iter->first; + string field_file = outputPrefix_ + "." + field_name; + const MATRIX* field_data = iter->second; + int fieldCols = field_data->nCols(); + ncols += fieldCols; + int type = data_type(fieldCols); + if (use_component_names(type)){ + string* component_names = get_component_names(type); + for (int j = 0; j < fieldCols; ++j) + { + string comp_name = field_name + component_names[j]; + string comp_file = outputPrefix_ + "." + comp_name; + fprintf(fp,"scalar per node: 1 1 %s %s\n", + comp_name.c_str(),comp_file.c_str()); + } + } + else if (type == VECTOR_OUTPUT) { + fprintf(fp,"vector per node: 1 1 %s %s\n", + field_name.c_str(),field_file.c_str()); + } + else if (type == SYM_TENSOR_OUTPUT) { + fprintf(fp,"tensor symm per node: 1 1 %s %s\n", + field_name.c_str(),field_file.c_str()); + } + else if (type == TENSOR_OUTPUT) { + fprintf(fp,"tensor asymm per node: 1 1 %s %s\n", + field_name.c_str(),field_file.c_str()); + } + else { + fprintf(fp,"scalar per node: 1 1 %s %s\n", + field_name.c_str(),field_file.c_str()); + } + } + + if (!firstStep_ && ncols != nDataCols_) { + throw ATC_Error(0,"number of columns of data has changed: start new output"); + } + nDataCols_ = ncols; + + int nsteps = outputTimes_.size(); + fprintf(fp,"TIME\n"); + fprintf(fp,"time set: 1\n"); + fprintf(fp,"number of steps: %10d\n",nsteps); + if ( dataType_ == POINT) { + fprintf(fp,"filename start number: 0\n"); + fprintf(fp,"filename increment: 1\n"); + } + fprintf(fp,"time values:\n"); + for (int j = 0; j < nsteps; ++j) { + double t = outputTimes_[j]; + fprintf(fp,"%12.5e",t); + if ((j+1)%6 == 0) fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + fprintf(fp,"FILE\n"); + fprintf(fp,"file set: 1\n"); + fprintf(fp,"number of steps: %10d\n",nsteps); + fclose(fp); +}; + +} // end ATC namespace + + diff --git a/lib/atc/OutputManager.h b/lib/atc/OutputManager.h new file mode 100644 index 0000000000..f8bfc81eb0 --- /dev/null +++ b/lib/atc/OutputManager.h @@ -0,0 +1,126 @@ +#ifndef OUTPUT_MANAGER_H +#define OUTPUT_MANAGER_H + +#include "ATC_TypeDefs.h" + +#include +#include + +// NOTE : number of rows to data type: +// 1 -> scalar +// 3 -> vector x,y,z +// NOT 6 -> tensor xx,xy,xz,yy,yz,zz +// 6 -> tensor xx,yy,zz,xy,zx,yz +// 9 -> tensor xx,xy,xz,yx,yy,yz,zx,zy,zz + +using namespace std; + +namespace ATC { + + enum OutputType { ENSIGHT=0, GNUPLOT }; + enum OutputDataType { POINT=0, MESH }; + enum OutputDataCardinality { SCALAR_OUTPUT=0, VECTOR_OUTPUT, TENSOR_OUTPUT, + SYM_TENSOR_OUTPUT, LIST_OUTPUT }; + enum OutputOption { OUTPUT_VECTOR_COMPONENTS=0, OUTPUT_TENSOR_COMPONENTS}; + + class OutputManager{ + + public: + OutputManager(void); + OutputManager(string outputPrefix, OutputType otype = ENSIGHT); + ~OutputManager(void); + + /** initialize output */ + void initialize(string outputPrefix, OutputType otype = ENSIGHT); + + /** set output options */ + void set_option(OutputOption option, bool value); + + // Dump text-based field info to disk for later restart + void write_restart_file(string fileName, OUTPUT_LIST *data); + + // Read text-based field file written from write_restart_file + void read_restart_file(string fileName, OUTPUT_LIST *data); + + /** write initial/reference geometry + default is to write point data, + if connectivities are given then mesh data will be output + coordinates : num _total_ points/nodes X num spatial dim + connectivities : num elements X num nodes per element*/ + void write_geometry(const MATRIX &coordinates, + const Array2D *connectivity=NULL); + void write_geometry(OUTPUT_LIST &part_coordinates); + + /** write data from a time step + specify node_map to handle periodic soln & data */ + void write_data(double time, OUTPUT_LIST *data, const int *node_map=NULL); + + void write_data(double time, FIELDS *soln, OUTPUT_LIST *data, + const int *node_map=NULL); + +/* + void write_data (double time, + map * > * > parts_data, + const int * node_map = NULL); +*/ + /** add a scalar to a text output file */ + void add_global(const string& name, const double& value) { + globalData_[name] = value; } + + /** delete a scalar from the output */ + void delete_global(const string& name) { globalData_.erase(name); } + + /** reset the stored output scalars */ + void reset_globals() { globalData_.clear(); writeGlobalsHeader_=true; } + + /** return data type: scalar, vector, tensor, list */ + int data_type(const DENS_MAT & data) const { + return data_type(data.nCols()); + } + int data_type(int cols) const { + if (cols == 1) return SCALAR_OUTPUT; + else if (cols == 3) return VECTOR_OUTPUT; + else if (cols == 6) return SYM_TENSOR_OUTPUT; + else if (cols == 9) return TENSOR_OUTPUT; + else return LIST_OUTPUT; + } + bool use_component_names(int type) { + if ( (type==LIST_OUTPUT) || + ((type==SYM_TENSOR_OUTPUT || type==TENSOR_OUTPUT) && tensorToComponents_) + || (type==VECTOR_OUTPUT && vectorToComponents_) ) + return true; + else + return false; + } + + private: + + void write_data(string name, const MATRIX *data, const int *node_map=NULL); + void write_text(OUTPUT_LIST *data); + void write_dictionary(double time, OUTPUT_LIST *data); + void write_globals(); + + //* status booleans + bool initialized_, firstStep_, firstGlobalsWrite_, writeGlobalsHeader_; + + /** number of columns of data */ + int nDataCols_; + /** number of nodes */ + int number_of_nodes_; // NOTE it would be nicer if node_map came with a size + /** data type */ + int dataType_; + /** base name for output files */ + string outputPrefix_; + /** list of output timesteps */ + vector outputTimes_; + /** output type */ + int outputType_; + /** output tensor as its components */ + bool tensorToComponents_; + /** output vector as its components */ + bool vectorToComponents_; + /** global variables */ + map globalData_; + }; +} +#endif diff --git a/lib/atc/PhysicsModel.h b/lib/atc/PhysicsModel.h new file mode 100644 index 0000000000..5ab847b579 --- /dev/null +++ b/lib/atc/PhysicsModel.h @@ -0,0 +1,224 @@ +/** + * @class PhysicsModel + * @brief An adaptor for the FE_Engine of the specific weak form of + * the continuum PDE for the FE_Engine. + * It is assumed that the PDE fits this template: + * DENSITY(FIELDS) FIELD_RATE + * = DIV FLUX(FIELDS, GRAD_FIELDS) + SOURCE(FIELDS,GRAD_FIELDS) + * + PRESCRIBED_SOURCE(X,t) + EXTRINSIC_SOURCE(FIELDS,GRAD_FIELDS) + */ + + +#ifndef PHYSICS_MODEL_H +#define PHYSICS_MODEL_H + + +// included headers +#include +#include "Array2D.h" +#include "MatrixLibrary.h" +#include "ATC_Error.h" +#include "Material.h" +#include "ATC_TypeDefs.h" +#include "StringManip.h" + +using namespace std; +using namespace ATC_STRING; + +namespace ATC +{ + + // Forward declarations + class ATC_Transfer; + + class PhysicsModel + { + + public: + + // constructor + PhysicsModel(string fileName, + ATC_Transfer * atcTransfer) + : atcTransfer_(atcTransfer) + { + parse_material_file(fileName); + } + + // destructor + virtual ~PhysicsModel() + { + vector< Material* >::iterator iter; + for (iter = materials_.begin(); iter != materials_.end(); iter++) { + Material * mat = *iter; + if (mat) delete mat; + } + } + + /** parse material file */ + void parse_material_file(string fileName) + { + vector< Material* >::iterator iter; + for (iter = materials_.begin(); iter != materials_.end(); iter++) { + Material * mat = *iter; + if (mat) delete mat; + } + fstream fileId(fileName.c_str(), std::ios::in); + if (!fileId.is_open()) throw ATC_Error(0,"cannot open material file"); + vector line; + int index = 0; + while(fileId.good()) { + get_command_line(fileId, line); + if (line.size() == 0 || line[0] == "#") continue; + if (line[0] == "material") { + string tag = line[1]; + Material * mat = new Material(tag,fileId); + materials_.push_back(mat); + materialNameToIndexMap_[tag] = index++; + } + } + if (int(materials_.size()) == 0) { + throw ATC_Error(0,"No materials were defined"); } + cout << " ATC:: " << int(materials_.size()) << " materials defined\n"; + fileId.close(); + } + + /** initialize */ + virtual void initialize(void) = 0; + + // set timescale parameters based on a given lengthscale + virtual void set_timescales(const double lengthscale) {}; + + /** access number of materials */ + int get_nMaterials(void) const + { + return materials_.size(); + } + + /** access material index from name */ + int material_index(const string & name) const + { + string tag = name; + to_lower(tag); // this is an artifact of StringManip parsing + map::const_iterator iter; + iter = materialNameToIndexMap_.find(tag); + if (iter == materialNameToIndexMap_.end()) { + throw ATC_Error(0,"No material named "+name+" found"); + } + int index = iter->second; + return index; + } + + /** access to parameter values */ + bool parameter_value(const string& name, double& value, + const int imat = 0) const + { + // search owned parameters + value = 0.0; + map::const_iterator it = parameterValues_.find(name); + if (it != parameterValues_.end()) { + value = it->second; + return true; + } + // interogate material models + bool found = materials_[imat]->get_parameter(name,value); + return found; + } + + /** return fields ids and length */ + virtual void get_num_fields(map & fieldSizes, + Array2D & fieldMask) const = 0; + + /** is the material model linear */ + virtual bool is_linear(FieldName name) { + vector< Material* >::iterator iter; + for (iter = materials_.begin(); iter != materials_.end(); iter++) { + Material * mat = *iter; + bool linear = mat->linear_flux(name) + && mat->linear_source(name) + && mat->constant_density(name); + if (! linear) return linear; + } + return true; + } + + /** is rhs linear */ + virtual bool has_linear_rhs(FieldName name) { + vector< Material* >::iterator iter; + for (iter = materials_.begin(); iter != materials_.end(); iter++) { + Material * mat = *iter; + bool constant = mat->linear_flux(name) && mat->linear_source(name); + if (! constant) return constant; + } + return true; + } + + /** is mass matrix constant */ + virtual bool has_constant_mass(FieldName name) { + vector< Material* >::iterator iter; + for (iter = materials_.begin(); iter != materials_.end(); iter++) { + Material * mat = *iter; + bool constant = mat->constant_density(name); + if (! constant) return constant; + } + return true; + } + + /** energy or other preserved quantity */ + virtual void E_integrand(const Array &mask, + const FIELDS &fields, + const GRAD_FIELDS &grad_fields, + FIELDS &capacity, + const int matIndex = 0) const + { + throw ATC_Error(0,"E_integrand not implemented for this PhysicsModel"); + } + + /** heat/momentum/energy/mass capacity used in the LHS mass matrix */ + virtual void M_integrand(const Array &mask, + const FIELDS &fields, + FIELDS &capacity, + const int matIndex = 0) const + { + throw ATC_Error(0,"M_integrand not implemented for this PhysicsModel"); + } + // flux that is integrated with N as its weight + virtual void N_integrand(const Array2D &mask, + const FIELDS &fields, + const GRAD_FIELDS &grad_fields, + FIELDS &flux, + const int matIndex = 0) const + { + throw ATC_Error(0,"N_integrand not implemented for this PhysicsModel"); + } + + /** flux that is integrated with Grad N as its weight */ + virtual void B_integrand(const Array2D & mask, + const FIELDS &fields, + const GRAD_FIELDS &grad_fields, + GRAD_FIELDS &flux, + const int matIndex = 0) const + { + throw ATC_Error(0,"B_integrand not implemented for this PhysicsModel"); + } + + /** has a integrand for the N weighted integral */ + virtual bool has_N_integrand() const { return false; } + + /** has a integrand for the B=grad_x N weighted integral */ + virtual bool has_B_integrand() const { return false; } + + protected: + + /** associated ATC Transfer object */ + ATC_Transfer * atcTransfer_; + + // parameter values + map parameterValues_; + + // material models + vector materials_; + map materialNameToIndexMap_; + }; + +}; +#endif diff --git a/lib/atc/PhysicsModelThermal.cpp b/lib/atc/PhysicsModelThermal.cpp new file mode 100644 index 0000000000..d679da4b2f --- /dev/null +++ b/lib/atc/PhysicsModelThermal.cpp @@ -0,0 +1,84 @@ +#include "ATC_Transfer.h" +#include "PhysicsModelThermal.h" +#include "Material.h" +#include +#include +#include + +namespace ATC { + + PhysicsModelThermal::PhysicsModelThermal(string matFileName, + ATC_Transfer * atcTransfer) + : PhysicsModel(matFileName,atcTransfer) + { + initialize(); + } + + PhysicsModelThermal::~PhysicsModelThermal(void) + { + } + + //-------------------------------------------------------------- + // initialize + //-------------------------------------------------------------- + void PhysicsModelThermal::initialize(void) + { + string list[2] = {"heat_capacity", + "heat_flux"}; + set needs(list,list+2); + vector< Material* >::iterator iter; + for (iter = materials_.begin(); iter != materials_.end(); iter++) { + Material * mat = *iter; + if (! (mat->check_registry(needs)) ) { + throw ATC_Error(0,"material does not provide all interfaces for physics"); + } + } + } + //--------------------------------------------------------------------- + // compute heat capacity + //--------------------------------------------------------------------- + void PhysicsModelThermal::M_integrand(const Array &mask, + const FIELDS &fields, + FIELDS &capacity, + const int matIndex) const + { + Material* material = materials_[matIndex]; + for (int n = 0; n < mask.get_length(); n++) { + if (mask(n) == TEMPERATURE) { + material->heat_capacity(fields, capacity[TEMPERATURE]); + } + } + } + + //--------------------------------------------------------------------- + // compute total energy + //--------------------------------------------------------------------- + void PhysicsModelThermal::E_integrand(const Array &mask, + const FIELDS &fields, + const GRAD_FIELDS &grad_fields, + FIELDS &energy, + const int matIndex) const + { + Material* material = materials_[matIndex]; + for (int n = 0; n < mask.get_length(); n++) { + if (mask(n) == TEMPERATURE) { + material->thermal_energy(fields, energy[TEMPERATURE]); + } + } + } + + //-------------------------------------------------------------- + // compute heat flux + //-------------------------------------------------------------- + void PhysicsModelThermal::B_integrand(const Array2D & mask, + const FIELDS & fields, + const GRAD_FIELDS & gradFields, + GRAD_FIELDS & flux, + const int matIndex) const + { + Material *material = materials_[matIndex]; + material->heat_flux(fields, gradFields, flux[TEMPERATURE]); + } +}; + + diff --git a/lib/atc/PhysicsModelThermal.h b/lib/atc/PhysicsModelThermal.h new file mode 100644 index 0000000000..8276dee26b --- /dev/null +++ b/lib/atc/PhysicsModelThermal.h @@ -0,0 +1,53 @@ +#ifndef PHYSICS_MODEL_THERMAL_H +#define PHYSICS_MODEL_THERMAL_H + +#include "PhysicsModel.h" + +namespace ATC{ + + class PhysicsModelThermal : public PhysicsModel { + + public: + + // constructor (take material parameter/s) + PhysicsModelThermal(std::string matFileName, + ATC_Transfer * atcTransfer); + + // destructor + virtual ~PhysicsModelThermal(); + + /** checks materials for necessary interfaces */ + virtual void initialize(void); + + virtual void get_num_fields(map & fieldSizes, Array2D & fieldMask) const + { + fieldSizes[TEMPERATURE] = 1; + fieldMask(TEMPERATURE,FLUX) = true; + } + + /** heat capacity */ + virtual void M_integrand(const Array & mask, + const FIELDS &fields, + FIELDS &capacity, + const int matIndex = 0) const; + /** energy */ + virtual void E_integrand(const Array & mask, + const FIELDS &fields, + const GRAD_FIELDS &grad_fields, + FIELDS &energy, + const int matIndex = 0) const; + + /** this model has a B weighted integrand */ + virtual bool has_B_integrand() const {return true;}; + + /** flux that is integrated with Grad N as its weight */ + virtual void B_integrand(const Array2D & mask, + const FIELDS & fields, + const GRAD_FIELDS & grad_fields, + GRAD_FIELDS & flux, + const int matIndex = 0) const; + + }; + +}; +#endif diff --git a/lib/atc/PhysicsModelTwoTemperature.cpp b/lib/atc/PhysicsModelTwoTemperature.cpp new file mode 100644 index 0000000000..cb21b3ebf5 --- /dev/null +++ b/lib/atc/PhysicsModelTwoTemperature.cpp @@ -0,0 +1,123 @@ +#include "PhysicsModelTwoTemperature.h" +#include "Material.h" +#include +#include +#include + +namespace ATC { +//============================================================== +// Class PhysicsModelTwoTemperature +//============================================================== + +//-------------------------------------------------------------- +// Constructor +//-------------------------------------------------------------- +PhysicsModelTwoTemperature::PhysicsModelTwoTemperature(string matFileName, + ATC_Transfer * atcTransfer) + : PhysicsModel(matFileName,atcTransfer) +{ + initialize(); +} +//-------------------------------------------------------------- +// Destructor +//--------------------------------------------------------------------- +PhysicsModelTwoTemperature::~PhysicsModelTwoTemperature(void) +{} + +//--------------------------------------------------------------------- +// intialization +//--------------------------------------------------------------------- +void PhysicsModelTwoTemperature::initialize(void) +{ + string list[5] = {"heat_capacity", + "electron_heat_capacity", + "heat_flux", + "electron_heat_flux", + "electron_phonon_exchange"}; + set needs(list,list+5); + vector< Material* >::iterator iter; + for (iter = materials_.begin(); iter != materials_.end(); iter++) { + Material * mat = *iter; + if (! (mat->check_registry(needs)) ) { + throw ATC_Error(0,"material " + mat->label() + " does not provide all interfaces for physics"); + } + } +} + +//--------------------------------------------------------------------- +// compute energy +//--------------------------------------------------------------------- +void PhysicsModelTwoTemperature::E_integrand(const Array &mask, + const FIELDS &fields, + const GRAD_FIELDS &grad_fields, + FIELDS &energy, + const int matIndex) const +{ + Material* material = materials_[matIndex]; + for (int n = 0; n < mask.get_length(); n++) { + if (mask(n) == TEMPERATURE) { + material->thermal_energy(fields, energy[TEMPERATURE]); + } + else if (mask(n) == ELECTRON_TEMPERATURE) { + material->electron_thermal_energy(fields, energy[ELECTRON_TEMPERATURE]); + } + } +} + +//--------------------------------------------------------------------- +// compute heat capacities +//--------------------------------------------------------------------- +void PhysicsModelTwoTemperature::M_integrand(const Array &mask, + const FIELDS &fields, + FIELDS &capacity, + const int matIndex) const +{ + Material* material = materials_[matIndex]; + for (int n = 0; n < mask.get_length(); n++) { + if (mask(n) == TEMPERATURE) { + material->heat_capacity(fields, capacity[TEMPERATURE]); + } + else if (mask(n) == ELECTRON_TEMPERATURE) { + material->electron_heat_capacity(fields, capacity[ELECTRON_TEMPERATURE]); + } + } +} + +//--------------------------------------------------------------------- +// compute heat fluxes +//--------------------------------------------------------------------- +void PhysicsModelTwoTemperature::B_integrand(const Array2D & mask, + const FIELDS &fields, + const GRAD_FIELDS &grad_fields, + GRAD_FIELDS &flux, + const int matIndex) const +{ + Material * material = materials_[matIndex]; + if (mask(TEMPERATURE,FLUX)) { + material->heat_flux(fields, grad_fields, flux[TEMPERATURE]); + } + if (mask(ELECTRON_TEMPERATURE,FLUX)) { + material->electron_heat_flux(fields, grad_fields, flux[ELECTRON_TEMPERATURE]); + } +} + +//--------------------------------------------------------------------- +// compute exchange fluxes +//--------------------------------------------------------------------- +void PhysicsModelTwoTemperature::N_integrand(const Array2D &mask, + const FIELDS &fields, + const GRAD_FIELDS &grad_fields, + FIELDS &flux, + const int matIndex) const +{ + Material * material = materials_[matIndex]; + FIELD exchange_flux; + material->electron_phonon_exchange(fields, exchange_flux); + if (mask(TEMPERATURE,SOURCE)) { + flux[TEMPERATURE] = exchange_flux; + } + if (mask(ELECTRON_TEMPERATURE,SOURCE)) { + flux[ELECTRON_TEMPERATURE] = -1.*exchange_flux; + } +} +}// end namespace diff --git a/lib/atc/PhysicsModelTwoTemperature.h b/lib/atc/PhysicsModelTwoTemperature.h new file mode 100644 index 0000000000..079fc5d543 --- /dev/null +++ b/lib/atc/PhysicsModelTwoTemperature.h @@ -0,0 +1,71 @@ +#ifndef PHYSICS_MODEL_TWO_TEMPERATURE_H +#define PHYSICS_MODEL_TWO_TEMPERATURE_H + +// included headers +#include "PhysicsModel.h" + +namespace ATC{ + +class PhysicsModelTwoTemperature : public PhysicsModel { + + /** system: + \dot T_e = diffusion_e + exchange_e + \dot T_p = diffusion_p + exchange_p + */ + + + public: + + // constructor (take material parameter/s) + PhysicsModelTwoTemperature(string matFileName, + ATC_Transfer * atcTransfer); + + // destructor + virtual ~PhysicsModelTwoTemperature(); + + /** checks materials for necessary interfaces */ + virtual void initialize(void); + + virtual void get_num_fields(map & fieldSizes, + Array2D & fieldMask) const + { + fieldSizes[TEMPERATURE] = 1; fieldSizes[ELECTRON_TEMPERATURE] = 1; + fieldMask(ELECTRON_TEMPERATURE,FLUX) = true; + fieldMask(ELECTRON_TEMPERATURE,SOURCE) = true; + } + + /** energy */ + virtual void E_integrand(const Array & mask, + const FIELDS &fields, + const GRAD_FIELDS &grad_fields, + FIELDS &energy, + const int matIndex = 0) const; + + /** capacity that used to form the mass matrix */ + virtual void M_integrand(const Array & mask, + const FIELDS &fields, + FIELDS &flux, + const int matIndex = 0) const; + + /** this model has a B weighted integrand */ + virtual bool has_B_integrand() const {return true;} + + /** flux that is integrated with Grad N as its weight */ + virtual void B_integrand(const Array2D & mask, + const FIELDS &fields, + const GRAD_FIELDS &grad_fields, + GRAD_FIELDS &flux, + const int matIndex = 0) const; + + /** this model has a N weighted integrand */ + virtual bool has_N_integrand() const {return true;} + + /** flux that is integrated with N as its weight */ + virtual void N_integrand(const Array2D &mask, + const FIELDS &fields, + const GRAD_FIELDS &grad_fields, + FIELDS &flux, + const int matIndex = 0) const; +}; +}; // end namespace +#endif diff --git a/lib/atc/PrescribedDataManager.cpp b/lib/atc/PrescribedDataManager.cpp new file mode 100644 index 0000000000..105787b7b2 --- /dev/null +++ b/lib/atc/PrescribedDataManager.cpp @@ -0,0 +1,442 @@ +#include "PrescribedDataManager.h" +#include "FE_Engine.h" +#include "ATC_Error.h" + +#include + +namespace ATC { + +//------------------------------------------------------------------------- +// PrescribedDataManager +//------------------------------------------------------------------------- + PrescribedDataManager::PrescribedDataManager + (FE_Engine * feEngine, + const map & fieldSize) : + feEngine_(feEngine), fieldSizes_(fieldSize) + { + // construct & initialize internal data + nNodes_ = feEngine_->get_nNodes(); + nElems_ = feEngine_->get_nElements(); + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + // nodal ics & essential bcs + ics_[thisField].reset(nNodes_,thisSize); + bcs_[thisField].reset(nNodes_,thisSize); + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int idof = 0; idof < thisSize ; ++idof) { + ics_[thisField](inode,idof) = NULL; + bcs_[thisField](inode,idof) = NULL; + } + } + // element based sources + elementSources_[thisField].reset(nElems_,thisSize); + for (int ielem = 0; ielem < nElems_ ; ++ielem) { + for (int idof = 0; idof < thisSize ; ++idof) { + elementSources_[thisField](ielem,idof) = NULL; + } + } + } + } + +//------------------------------------------------------------------------- +// ~PrescribedDataManager +//------------------------------------------------------------------------- + PrescribedDataManager::~PrescribedDataManager() + { + } + +//------------------------------------------------------------------------- +// add_field +//------------------------------------------------------------------------- + void PrescribedDataManager::add_field(FieldName fieldName, int size) + { + // check to see if field exists + if (fieldSizes_.find(fieldName) == fieldSizes_.end()) return; + + // construct & initialize internal data + nNodes_ = feEngine_->get_nNodes(); + nElems_ = feEngine_->get_nElements(); + + // nodal ics & essential bcs + ics_[fieldName].reset(nNodes_,size); + bcs_[fieldName].reset(nNodes_,size); + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int idof = 0; idof < size ; ++idof) { + ics_[fieldName](inode,idof) = NULL; + bcs_[fieldName](inode,idof) = NULL; + } + } + + // element based sources + elementSources_[fieldName].reset(nElems_,size); + for (int ielem = 0; ielem < nElems_ ; ++ielem) { + for (int idof = 0; idof < size ; ++idof) { + elementSources_[fieldName](ielem,idof) = NULL; + } + } + } + +//------------------------------------------------------------------------- +// remove_field +//------------------------------------------------------------------------- + void PrescribedDataManager::remove_field(FieldName fieldName) + { + // check to see if field exists + if (fieldSizes_.find(fieldName) == fieldSizes_.end()) + return; + + // delete field in maps + fieldSizes_.erase(fieldName); + ics_.erase(fieldName); + bcs_.erase(fieldName); + elementSources_.erase(fieldName); + } + +//------------------------------------------------------------------------- +// fix_initial_field +//------------------------------------------------------------------------- + void PrescribedDataManager::fix_initial_field + (const string nodesetName, + const FieldName thisField, + const int thisIndex, + const XT_Function * f) + { + using std::set; + set nodeSet = (feEngine_->get_feMesh())->get_nodeset(nodesetName); + set::const_iterator iset; + for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { + int inode = *iset; + ics_[thisField](inode,thisIndex) = (XT_Function*) f; // NOTE const cast? + } + } +//------------------------------------------------------------------------- +// fix_field +//------------------------------------------------------------------------- + void PrescribedDataManager::fix_field + (const string nodesetName, + const FieldName thisField, + const int thisIndex, + const XT_Function * f) + { + using std::set; + // fix fields + set nodeSet = (feEngine_->get_feMesh())->get_nodeset(nodesetName); + set::const_iterator iset; + for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { + int inode = *iset; + bcs_[thisField](inode,thisIndex) = (XT_Function*) f; + } + } +//------------------------------------------------------------------------- +// unfix_field +//------------------------------------------------------------------------- + void PrescribedDataManager::unfix_field + (const string nodesetName, + const FieldName thisField, + const int thisIndex) + { + using std::set; + set nodeSet = (feEngine_->get_feMesh())->get_nodeset(nodesetName); + set::const_iterator iset; + for (iset = nodeSet.begin(); iset != nodeSet.end(); iset++) { + int inode = *iset; + bcs_[thisField](inode,thisIndex) = NULL; + } + } +//------------------------------------------------------------------------- +// fix_flux +//------------------------------------------------------------------------- + void PrescribedDataManager::fix_flux + (const string facesetName, + const FieldName thisField, + const int thisIndex, + const XT_Function * f) + { + const set< pair > * fset + = & ( (feEngine_->get_feMesh())->get_faceset(facesetName)); + set< pair >::const_iterator iset; + for (iset = fset->begin(); iset != fset->end(); iset++) { + pair face = *iset; + // allocate, if necessary + Array < XT_Function * > & dof = faceSources_[thisField][face]; + if (dof.get_length() == 0) { + int ndof = (fieldSizes_.find(thisField))->second; + dof.reset(ndof); + for(int i = 0; i < ndof; i++) dof(i) = NULL; + } + dof(thisIndex) = (XT_Function*) f; + } + } +//------------------------------------------------------------------------- +// unfix_flux +//------------------------------------------------------------------------- + void PrescribedDataManager::unfix_flux + (const string facesetName, + const FieldName thisField, + const int thisIndex) + { + const set< pair > * fset + = & ( (feEngine_->get_feMesh())->get_faceset(facesetName)); + set< pair >::const_iterator iset; + for (iset = fset->begin(); iset != fset->end(); iset++) { + pair face = *iset; + Array < XT_Function * > & dof = faceSources_[thisField][face]; + dof(thisIndex) = NULL; + } + } +//------------------------------------------------------------------------- +// fix_source +//------------------------------------------------------------------------- + void PrescribedDataManager::fix_source + (const string elemsetName, + const FieldName thisField, + const int thisIndex, + const XT_Function *f) + { + using std::set; + set elemSet = (feEngine_->get_feMesh())->get_elementset(elemsetName); + set::const_iterator iset; + for (iset = elemSet.begin(); iset != elemSet.end(); iset++) { + int ielem = *iset; + // fix source + elementSources_[thisField](ielem,thisIndex) = (XT_Function*) f; + } + } +//------------------------------------------------------------------------- +// unfix_source +//------------------------------------------------------------------------- + void PrescribedDataManager::unfix_source + (const string elemsetName, + const FieldName thisField, + const int thisIndex) + { + using std::set; + set elemSet = (feEngine_->get_feMesh())->get_elementset(elemsetName); + set::const_iterator iset; + for (iset = elemSet.begin(); iset != elemSet.end(); iset++) { + int ielem = *iset; + elementSources_[thisField](ielem,thisIndex) = NULL; + } + } +//------------------------------------------------------------------------- +// set_initial_conditions +//------------------------------------------------------------------------- + void PrescribedDataManager::set_initial_conditions(const double t, + FIELDS &fields, + FIELDS &dot_fields, + FIELDS &ddot_fields, + FIELDS &dddot_fields) + { + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + XT_Function *f = ics_[thisField](inode,thisIndex); + if (!f) f = bcs_[thisField](inode,thisIndex); + if (f) + { + DENS_VEC coords(3); + coords = (feEngine_->get_feMesh())->nodal_coordinates(inode); + double *x = coords.get_ptr(); + fields [thisField](inode,thisIndex) = f->f(x,t); + dot_fields [thisField](inode,thisIndex) = f->dfdt(x,t); + ddot_fields [thisField](inode,thisIndex) = f->ddfdt(x,t); + dddot_fields[thisField](inode,thisIndex) = f->dddfdt(x,t); + } + else throw ATC_Error(0,"all initial conditions have not been defined"); + } + } + } + } +//------------------------------------------------------------------------- +// set_fixed_fields +//------------------------------------------------------------------------- + void PrescribedDataManager::set_fixed_fields(const double t, + FIELDS &fields, + FIELDS &dot_fields, + FIELDS &ddot_fields, + FIELDS &dddot_fields) + { + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + XT_Function * f = bcs_[thisField](inode,thisIndex); + if (f) { + DENS_VEC coords(3); + coords = (feEngine_->get_feMesh())->nodal_coordinates(inode); + double * x = coords.get_ptr(); + FIELD * myField = & fields[thisField]; + fields [thisField](inode,thisIndex) = f->f(x,t); + dot_fields [thisField](inode,thisIndex) = f->dfdt(x,t); + ddot_fields [thisField](inode,thisIndex) = f->ddfdt(x,t); + dddot_fields[thisField](inode,thisIndex) = f->dddfdt(x,t); + } + } + } + } + } +//------------------------------------------------------------------------- +// set_fixed_field +//------------------------------------------------------------------------- + void PrescribedDataManager::set_fixed_field( + const double t, + const FieldName & fieldName, + DENS_MAT & fieldMatrix) + { + map::iterator fieldSizeIter = fieldSizes_.find(fieldName); + if (fieldSizeIter == fieldSizes_.end()) { + throw ATC_Error(0, "Unrecognized FieldName in PrescribedDataManager::set_fixed_field()"); + } + int thisSize = fieldSizeIter->second; + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + XT_Function * f = bcs_[fieldName](inode,thisIndex); + if (f) { + DENS_VEC coords(3); + coords = (feEngine_->get_feMesh())->nodal_coordinates(inode); + fieldMatrix(inode,thisIndex) = f->f(coords.get_ptr(),t); + } + } + } + } +//------------------------------------------------------------------------- +// set_fixed_dfield +//------------------------------------------------------------------------- + void PrescribedDataManager::set_fixed_dfield( + const double t, + const FieldName & fieldName, + DENS_MAT & dfieldMatrix) + { + map::iterator fieldSizeIter = fieldSizes_.find(fieldName); + if (fieldSizeIter == fieldSizes_.end()) { + throw ATC_Error(0, "Unrecognized FieldName in PrescribedDataManager::set_fixed_dfield()"); + } + int thisSize = fieldSizeIter->second; + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + XT_Function * f = bcs_[fieldName](inode,thisIndex); + if (f) { + DENS_VEC coords(3); + coords = (feEngine_->get_feMesh())->nodal_coordinates(inode); + dfieldMatrix(inode,thisIndex) = f->dfdt(coords.get_ptr(),t); + } + } + } + } +//------------------------------------------------------------------------- +// set_sources +//------------------------------------------------------------------------- + void PrescribedDataManager::set_sources + (double t, + FIELDS & sources) + { + // zero + Array fieldMask(NUM_FIELDS); + fieldMask = false; + map::const_iterator field; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + fieldMask(thisField) = true; + int thisSize = field->second; + sources[thisField].reset(nNodes_,thisSize); + } + // compute boundary fluxes + feEngine_->add_fluxes(fieldMask,t,faceSources_,sources); + + // compute internal sources + feEngine_->add_sources(fieldMask,t,elementSources_,sources); + + // mask out nodes with essential bcs + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisSize = field->second; + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + XT_Function * f = bcs_[thisField](inode,thisIndex); + if (f) { + sources[thisField](inode,thisIndex) = 0.0; + } + } + } + } + + } + +//------------------------------------------------------------------------- +// print +//------------------------------------------------------------------------- + void PrescribedDataManager::print(void) + { + // print and check consistency + enum dataType {FREE=0,FIELD,SOURCE}; + Array2D < int > bcTypes; + Array conn; + map::const_iterator field; + XT_Function * f; + for (field = fieldSizes_.begin(); field!=fieldSizes_.end(); field++) { + FieldName thisField = field->first; + int thisFieldSize = field->second; + string fieldName = field_to_string(thisField); + int thisSize = field->second; + bcTypes.reset(nNodes_,thisSize); + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + f = bcs_[thisField](inode,thisIndex); + if (f) { bcTypes(inode,thisIndex) = FIELD; } + else { bcTypes(inode,thisIndex) = FREE; } + } + } + // FIXED has higher precidence than SOURCE + for (int ielem = 0; ielem < nElems_ ; ++ielem) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + f = elementSources_[thisField](ielem,thisIndex); + if (f) { + feEngine_->element_connectivity(ielem,conn); + for (int i = 0; i < conn.get_length() ; ++i) { + int inode = conn(i); + if (bcTypes(inode,thisIndex) != FIELD) + { bcTypes(inode,thisIndex) = SOURCE; } + } + } + } + } + map < pair, Array < XT_Function * > > & fset + = faceSources_[thisField]; + map < pair, Array < XT_Function * > > ::const_iterator iset; + for (iset = fset.begin(); iset != fset.end(); iset++) { + pair face = iset->first; + Array < XT_Function * > fs = iset->second; + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + f = fs(thisIndex); + if (f) { + feEngine_->face_connectivity(face,conn); + for (int i = 0; i < conn.get_length() ; ++i) { + int inode = conn(i); + if (bcTypes(inode,thisIndex) != FIELD) + { bcTypes(inode,thisIndex) = SOURCE; } + } + } + } + } + for (int inode = 0; inode < nNodes_ ; ++inode) { + for (int thisIndex = 0; thisIndex < thisSize ; ++thisIndex) { + cout << "node: " << inode << " " << fieldName; + if (thisFieldSize > 1) { cout << " " << thisIndex; } + f = ics_[thisField](inode,thisIndex); + if (f) { cout << " IC"; } + if (bcTypes(inode,thisIndex) == FIELD ) {cout << " FIXED"; } + else if (bcTypes(inode,thisIndex) == SOURCE) {cout << " SOURCE"; } + cout << "\n"; + } + } + } + } + +} // end namespace diff --git a/lib/atc/PrescribedDataManager.h b/lib/atc/PrescribedDataManager.h new file mode 100644 index 0000000000..ff784452be --- /dev/null +++ b/lib/atc/PrescribedDataManager.h @@ -0,0 +1,157 @@ +#ifndef PRESCRIBED_DATA_MANAGER_H +#define PRESCRIBED_DATA_MANAGER_H + +// manager for initial conditions, essential/natural "boundary" conditions +// and sources + +// to do: +// handle no sources, time-independent sources +// prescribed surface sources + +#include +#include +#include + +#include "XT_Function.h" +#include "PhysicsModel.h" +#include "FE_Element.h" +#include "Array.h" +#include "Array2D.h" +#include "FE_Engine.h" + +//clase FE_Engine + + +namespace ATC { + using std::vector; + using std::pair; + using std::map; + + class PrescribedDataManager { + + public: + + /** exclusive conditions: free | fixed field | flux or domain source */ + //enum Bc_Type {FREE=0,FIELD,SOURCE}; + + PrescribedDataManager(FE_Engine * feEngine, + const map & fieldSize); + ~PrescribedDataManager(); + + /** add/remove a field */ + void add_field(FieldName fieldName, int size); + void remove_field(FieldName fieldName); + + /** direct access to ics */ + map < FieldName, Array2D < XT_Function * > > * + get_ics(void) { return & ics_; } + const Array2D < XT_Function * > * + get_ics(FieldName fieldName) { return & ics_[fieldName]; } + /** direct access to bcs */ + map < FieldName, Array2D < XT_Function * > > * + get_bcs(void) { return & bcs_; } + const Array2D < XT_Function * > * + get_bcs(FieldName fieldName) { return & bcs_[fieldName]; } + + /** query initial state */ + bool is_initially_fixed(const int node, + const FieldName thisField, + const int thisIndex=0) const + { + return ((ics_.find(thisField)->second))(node,thisIndex) ? true : false ; + } + /** query state */ + bool is_fixed(const int node, + const FieldName thisField, + const int thisIndex=0) const + { + return ((bcs_.find(thisField)->second))(node,thisIndex) ? true : false ; + } + + /** set initial field values */ + void fix_initial_field (const string nodesetName, + const FieldName thisField, + const int thisIndex, + const XT_Function * f); + /** un/set field values at fixed nodes */ + void fix_field (const string nodesetName, + const FieldName thisField, + const int thisIndex, + const XT_Function * f); + void unfix_field (const string nodesetName, + const FieldName thisField, + const int thisIndex); + /** un/set fluxes */ + void fix_flux (const string facesetName, + const FieldName thisField, + const int thisIndex, + const XT_Function * f); + void unfix_flux(const string facesetName, + const FieldName thisField, + const int thisIndex); + /** un/set sources */ + void fix_source(const string nodesetName, + const FieldName thisField, + const int thisIndex, + const XT_Function * f); + void unfix_source(const string nodesetName, + const FieldName thisField, + const int thisIndex); + /** get initial conditions */ + void set_initial_conditions(const double time, + FIELDS & fields, + FIELDS & dot_fields, + FIELDS & ddot_fields, + FIELDS & dddot_fields); + /** get "boundary" conditions on fields */ + void set_fixed_fields(const double time, + FIELDS & fields, + FIELDS & dot_fields, + FIELDS & ddot_fields, + FIELDS & dddot_fields); + /** get "boundary" conditions on a single field */ + void set_fixed_field(const double time, + const FieldName & fieldName, + DENS_MAT & fieldMatrix); + /** get "boundary" conditions on a single time derivative field */ + void set_fixed_dfield(const double time, + const FieldName & fieldName, + DENS_MAT & dfieldMatrix); + /** get "sources" (flux and sources: divided by leading coef of ODE) */ + void set_sources(const double time, + FIELDS & sources); + + /** debugging status output */ + void print(void); + + private: + /** number of nodes */ + int nNodes_; + + /** number of elements */ + int nElems_; + + /** names and sizes of fields */ + map fieldSizes_; + + /** access to all the FE computations */ + FE_Engine * feEngine_; + + // node numbering & dof numbering : contiguous + // fieldname & bc_type : types/enums + /** ics : XT_Function * f = ics_[field](inode,idof) */ + map < FieldName, Array2D < XT_Function * > > ics_; + + /** bcs: essential bcs XT_Function * f = bcs_[field][face](idof) */ + map < FieldName, Array2D < XT_Function * > > bcs_; + + /** sources : XT_Function * f = faceSources_[field][face](idof) */ + map < FieldName, map < pair , Array < XT_Function * > > > + faceSources_; + /** sources : XT_Function * f = elementSources_[field](ielem,idof) */ + map < FieldName, Array2D < XT_Function * > > elementSources_; + }; + +} + +#endif diff --git a/lib/atc/Quadrature.h b/lib/atc/Quadrature.h new file mode 100644 index 0000000000..482b62161f --- /dev/null +++ b/lib/atc/Quadrature.h @@ -0,0 +1,50 @@ +/** Quadrature : creat Gaussian quadrature lists */ +#ifndef QUADRATURE_H +#define QUADRATURE_H + +using namespace std; + +static const int line_ngauss = 10; +static double line_xg[line_ngauss], line_wg[line_ngauss]; +/** domain of integration is -1 to 1 */ +static void set_line_quadrature(int ng, double* xg, double* wg) +{ + /** integration schemes : 3, 4, 5, 10 point Gauss */ + + if (ng == 3) { + xg[0] = 0.0; wg[0] = 8.0/9.0; + xg[1] = -sqrt(3.0/5.0); wg[1] = 5.0/9.0; + xg[2] = sqrt(3.0/5.0); wg[2] = 5.0/9.0; + } + else if (ng == 4) { + xg[0] = -sqrt((3.0/7.0)-(sqrt(120.0)/35.0)); wg[0] = (1.0/2.0)+(5.0/(3.0*sqrt(120.0))); + xg[1] = sqrt((3.0/7.0)-(sqrt(120.0)/35.0)); wg[1] = (1.0/2.0)+(5.0/(3.0*sqrt(120.0))); + xg[2] = -sqrt((3.0/7.0)+(sqrt(120.0)/35.0)); wg[2] = (1.0/2.0)-(5.0/(3.0*sqrt(120.0))); + xg[3] = sqrt((3.0/7.0)+(sqrt(120.0)/35.0)); wg[3] = (1.0/2.0)-(5.0/(3.0*sqrt(120.0))); + } + else if (ng == 5) { + xg[0] = 0.0; wg[0] = 0.5688888888888889; + xg[1] = -sqrt((35.0-sqrt(280.0))/63.0); wg[1] = 0.4786286704993658; + xg[2] = sqrt((35.0-sqrt(280.0))/63.0); wg[2] = 0.4786286704993658; + xg[3] = -sqrt((35.0+sqrt(280.0))/63.0); wg[3] = 0.2369268850561891; + xg[4] = sqrt((35.0+sqrt(280.0))/63.0); wg[4] = 0.2369268850561891; + } + else if (ng == 10) { + xg[0] = -0.14887434; wg[0] = 0.29552422; + xg[1] = 0.14887434; wg[1] = 0.29552422; + xg[2] = -0.43339539; wg[2] = 0.26926672; + xg[3] = 0.43339539; wg[3] = 0.26926672; + xg[4] = -0.67940957; wg[4] = 0.21908636; + xg[5] = 0.67940957; wg[5] = 0.21908636; + xg[6] = -0.86506337; wg[6] = 0.14945135; + xg[7] = 0.86506337; wg[7] = 0.14945135; + xg[8] = -0.97390653; wg[8] = 0.06667134; + xg[9] = 0.97390653; wg[9] = 0.06667134; + } + else { cout << "Invalid choice of number of Gauss points for Quadrature" << endl; } + //else { throw ATC_Error(0,"Invalid choice of number of Gauss points for Quadrature"); } + + return; +}; + +#endif diff --git a/lib/atc/README b/lib/atc/README new file mode 100644 index 0000000000..1d59d99b3f --- /dev/null +++ b/lib/atc/README @@ -0,0 +1,25 @@ +ATC (Atom To Continuum methods) + +Reese Jones, Jeremy Templeton, Jonathan Zimmerman +September 2009 + +-------------- + +This is version 1.0 of the ATC library, which provides continuum field +estimation and molecular dynamics-finite element coupling methods. + +This library must be built with a C++ compiler, before LAMMPS is +built, so LAMMPS can link against it. This library is dependent on the +system libraries MPI, BLAS and LAPACK which will be linked when LAMMPS +is built. + +Build the library using one of the provided Makefiles or creating your +own, specific to your compiler and system. For example: + +make -f Makefile.g++ + +Note that the ATC library makes MPI calls, so you should +build it with the same MPI library that is used to build +LAMMPS, e.g. in the lammps/src/MAKE/Makefile.foo file. + +If the build is successful, you should end up with a libatc.a file. diff --git a/lib/atc/Solver.cpp b/lib/atc/Solver.cpp new file mode 100644 index 0000000000..db8a67497e --- /dev/null +++ b/lib/atc/Solver.cpp @@ -0,0 +1,147 @@ +#include "Solver.h" +#include +#include +#include + +namespace ATC { + // Utility functions used by solvers, but not globally accessible. + static const double PI_OVER_3 = acos(-1.0)*(1.0/3.0); + static bool is_zero(double x) + { + static double GT_ZERO = 1.0e2*std::numeric_limits::epsilon(); + static double LT_ZERO = -GT_ZERO; + return x>LT_ZERO && x0]; + } + + // Linear solver + int solve_linear(double c[2], double x0[1]) + { + if (c[1] == 0) return 0; // constant function + *x0 = -c[0] / c[1]; + return 1; + } + + // Quadratic solver + int solve_quadratic(double c[3], double x0[2]) + { + if (is_zero(c[2])) return solve_linear(c, x0); + const double ainv = 1.0/c[2]; // ax^2 + bx + c = 0 + const double p = 0.5 * c[1] * ainv; // -b/2a + const double q = c[0] * ainv; // c/a + double D = p*p-q; + + if (is_zero(D)) { // quadratic has one repeated root + x0[0] = -p; + return 1; + } + if (D > 0) { // quadratic has two real roots + D = sqrt(D); + x0[0] = D - p; + x0[1] = -D - p; + return 2; + } + return 0; // quadratic has no real roots + } + + // Cubic solver + int solve_cubic(double c[4], double x0[3]) + { + int num_roots; + if (is_zero(c[3])) return solve_quadratic(c, x0); + // normalize to x^3 + Ax^2 + Bx + C = 0 + const double c3inv = 1.0/c[3]; + const double A = c[2] * c3inv; + const double B = c[1] * c3inv; + const double C = c[0] * c3inv; + + // substitute x = t - A/3 so t^3 + pt + q = 0 + const double A2 = A*A; + const double p = (1.0/3.0)*((-1.0/3.0)*A2 + B); + const double q = 0.5*((2.0/27.0)*A*A2 - (1.0/3.0)*A*B + C); + + // Cardano's fomula + const double p3 = p*p*p; + const double D = q*q + p3; + if (is_zero(D)) { + if (is_zero(q)) { // one triple soln + x0[0] = 0.0; + num_roots = 1; + } + else { // one single and one double soln + const double u = pow(fabs(q), 1.0/3.0)*sign(q); + x0[0] = -2.0*u; + x0[1] = u; + num_roots = 2; + } + } + else { + if (D < 0.0) { // three real roots + const double phi = 1.0/3.0 * acos(-q/sqrt(-p3)); + const double t = 2.0 * sqrt(-p); + x0[0] = t * cos(phi); + x0[1] = -t * cos(phi + PI_OVER_3); + x0[2] = -t * cos(phi - PI_OVER_3); + num_roots = 3; + } + else { // one real root + const double sqrt_D = sqrt(D); + const double u = pow(sqrt_D + fabs(q), 1.0/3.0); + if (q > 0) x0[0] = -u + p / u; + else x0[0] = u - p / u; + num_roots = 1; + } + } + double sub = (1.0/3.0)*A; + for (int i=0; i1?"s":"") << " of: "; + switch (NC) { + case 4: std::cout << c[3] << "x^3+"; + case 3: std::cout << c[2] << "x^2+"; + case 2: std::cout << c[1] << "x+"; + case 1: std::cout << c[0]; + default: std::cout << " are "; + } + std::cout << "x = {"; + for (int i=0; i +TRI_COORD::TRI_COORD(unsigned row, unsigned col) : i(row), j(col) {} +template +TRI_COORD::TRI_COORD(unsigned row, unsigned col, T val, bool add_to) + : i(row), j(col), v(val), add(add_to) {} + +// General flat index by value operator (by nth nonzero) +template inline T SparseMatrix::operator[](INDEX i) const +{ + VICK(i); return _val[i]; +} + +// General flat index by reference operator (by nth nonzero) +template inline T& SparseMatrix::operator[](INDEX i) +{ + VICK(i); return _val[i]; +} + +template +T SparseMatrix::_zero = T(0); + +//----------------------------------------------------------------------------- +// triplet comparison operator returns true if x < y +//----------------------------------------------------------------------------- +template +bool triplet_comparision(const TRI_COORD &x, const TRI_COORD &y) +{ + const bool row_less = (x.i) < (y.i); + const bool row_equal = (x.i) == (y.i); + const bool col_less = (x.j) < (y.j); + return (row_less || (row_equal && col_less)); +} +//----------------------------------------------------------------------------- +// triplet comparison operator returns true if x == y +//----------------------------------------------------------------------------- +template +bool triplets_equal(const TRI_COORD &x, const TRI_COORD &y) +{ + return x.i==y.i && x.j==y.j; +} +//----------------------------------------------------------------------------- +// multiply sparse matrix by a vector +//----------------------------------------------------------------------------- +template +DenseVector operator*(const SparseMatrix &A, const Vector& x) +{ + SparseMatrix::compress(A); + GCK(A, x, A.nCols()!=x.size(), "SparseMatrix * Vector") + DenseVector y(A.nRows(), true); + INDEX i, j; + for (i=0; i +DenseVector operator*(const Vector& x, const SparseMatrix &A) +{ + return A.transMat(x); +} +//----------------------------------------------------------------------------- +// multiply sparse matrix by dense matrix +//----------------------------------------------------------------------------- +template +DenseMatrix operator*(const SparseMatrix &A, const Matrix& D) +{ + GCK(A, D, A.nCols()!=D.nRows(),"SparseMatrix * DenseMatrix") + DenseMatrix C(A.nRows(), D.nCols(), true); // initialized to zero + + const INDEX J = D.nCols(); + + INDEX i, ik, j, k; + for (i=0; i +SparseMatrix operator*(const SparseMatrix &A, const DiagonalMatrix& D) +{ + GCK(A, D, A.nCols()!=D.nRows(),"SparseMatrix * DiagonalMatrix") + SparseMatrix C(A); // C has same sparcity as A + + // C(i,j) = A(i,k) * D(k, j) * j==k + INDEX i, ij, j; + for (i=0; i +SparseMatrix operator*(const SparseMatrix &A, const SparseMatrix &B) +{ + SparseMatrix At(A.transpose()); + SparseMatrix::compress(B); + + GCK(A, B, A.nCols()!=B.nRows(), "SparseMatrix * SparseMatrix"); + + SparseMatrix C(A.nRows(), B.nCols()); + if (At.empty() || B.empty()) return C; + + INDEX k, ki, kj; + INDEX K = std::min(At._nRowsCRS, B._nRowsCRS); + for (k=0; k +SparseMatrix::SparseMatrix(INDEX rows, INDEX cols) + : _val(NULL), _ia(NULL), _ja(NULL), _size(0), _nRowsCRS(0), + _nRows(rows),_nCols(cols) {} +//----------------------------------------------------------------------------- +// copy constructor +//----------------------------------------------------------------------------- +template +SparseMatrix::SparseMatrix(const SparseMatrix& C) + : _val(NULL), _ia(NULL), _ja(NULL) +{ + _copy(C); +} +//----------------------------------------------------------------------------- +// copy constructor - converts from DenseMatrix +//----------------------------------------------------------------------------- +template +SparseMatrix::SparseMatrix(const DenseMatrix& C) + : _val(NULL), _ia(NULL), _ja(NULL) +{ + reset(C); +} +//----------------------------------------------------------------------------- +// destructor, cleans up internal storage +//----------------------------------------------------------------------------- +template +SparseMatrix::~SparseMatrix() +{ + _delete(); +} +//----------------------------------------------------------------------------- +// assigns internal storage for CRS +//----------------------------------------------------------------------------- +template +void SparseMatrix::_create(INDEX size, INDEX nrows) +{ + _size = size; + _nRowsCRS = nrows; + // assign memory to hold matrix + try + { + _val = (_size*nrows) ? new T [_size] : NULL; + _ia = (_size*nrows) ? new INDEX [_nRowsCRS+1] : NULL; + _ja = (_size*nrows) ? new INDEX [_size] : NULL; + } + catch (std::exception &e) + { + cout << "Could not allocate SparseMatrix of "<< _size << " nonzeros.\n"; + exit(EXIT_FAILURE); + } + if (!_ia) return; + // automatically handle the ends of rowpointer + *_ia = 0; // first non-zero is the zero index + _ia[_nRowsCRS] = _size; // last row pointer is the size +} +//----------------------------------------------------------------------------- +// cleans up internal storage, but retains nRows & nCols +//----------------------------------------------------------------------------- +template +void SparseMatrix::_delete() +{ + vector >().swap(_tri); // completely deletes _tri + delete [] _val; + delete [] _ia; + delete [] _ja; + _size = _nRowsCRS = 0; + _val = NULL; + _ia = _ja = NULL; +} +//----------------------------------------------------------------------------- +// full memory copy of C into this +//----------------------------------------------------------------------------- +template +void SparseMatrix::_copy(const SparseMatrix &C) +{ + compress(C); + _delete(); + _create(C.size(), C._nRowsCRS); + if (_size) { + std::copy(C._val, C._val+_size, _val); + std::copy(C._ja, C._ja+_size, _ja); + } + if (_nRowsCRS) { + std::copy(C._ia, C._ia+_nRowsCRS+1, _ia); + } + _nCols = C._nCols; + _nRows = C._nRows; +} +//---------------------------------------------------------------------------- +// general sparse matrix assignment +//---------------------------------------------------------------------------- +template +void SparseMatrix::_set_equal(const Matrix &r) +{ + this->resize(r.nRows(), r.nCols()); + const Matrix *ptr_r = &r; + + const SparseMatrix *s_ptr = dynamic_cast*>(ptr_r); + if (s_ptr) this->reset(*s_ptr); + else if (dynamic_cast*>(ptr_r)) + for (INDEX i=0; i*>(ptr_r)) this->reset(r); + else + { + cout <<"Error in general sparse matrix assignment\n"; + exit(1); + } +} +//----------------------------------------------------------------------------- +// returns the first row number with a nonzero entry or -1 if no rows +//----------------------------------------------------------------------------- +template +int SparseMatrix::_first_nonzero_row_crs() const +{ + if (!_nRowsCRS) return -1; + INDEX r; + for (r=0; r<_nRowsCRS; r++) + if (_ia[r+1]>0) return r; + return -1; +} +//----------------------------------------------------------------------------- +// converts T to CRS +//----------------------------------------------------------------------------- +template +void SparseMatrix::compress(const SparseMatrix &C) +{ + const_cast*>(&C)->compress(); +} +//----------------------------------------------------------------------------- +// merges all the _tri triples with CRS storage +//----------------------------------------------------------------------------- +template +void SparseMatrix::compress() +{ + if (_tri.empty()) return; + + // sort and find the number of unique triplets + const INDEX nUnique = CountUniqueTriplets(); + + // max number of rows in new CRS structure + const INDEX nRows = std::max((INDEX)_tri.back().i+1, _nRowsCRS); + + // make a new CRS structure + INDEX *ia = new INDEX [nRows+1]; + INDEX *ja = new INDEX [nUnique]; + T *val = new T [nUnique]; + + ia[0] = 0; + INDEX i; + for (i=1; i nextCRS, nextTRI(_tri[0]), next; + int first_row = _first_nonzero_row_crs(); + if (first_row != -1) nextCRS = TRI_COORD(first_row, _ja[0], _val[0]); + + // merge sorted triplets into a new CRS structure + crs_pt = crs_row = tri_ct = 0; // initialize counters + for (i=0; i=_size)) { + next = nextTRI; + // advance the triplet counter, and skip voided TRIPLET entries + do tri_ct++; + while ( tri_ct<_tri.size() && !~_tri[tri_ct].j ); + + // if not at the end of the vector, set the next triplet + if (tri_ct<_tri.size()) nextTRI = _tri[tri_ct]; + } + // is the next nonzero in the old CRS data + else if (crs_pt < _size) { + next = nextCRS; + // advance the CRS counter, skip if it was the last one + if (++crs_pt >= _size) continue; + crs_row += _ia[crs_row+1]==crs_pt; // did the row advance + nextCRS = TRI_COORD(crs_row, _ja[crs_pt], _val[crs_pt]); + } + else cout << "SparseMatrix - Error in compressing CRS\n"; + + // add next to the new CRS structure + // is this a new row (is j>0 and is ja[j] == 0)? + if (ia[next.i]==~0) ia[next.i] = i; + ja[i] = next.j; + val[i] = next.v; + } + // sweep backwards through row pointers and check for skipped rows + for (i=nRows-1; i>0; i--) ia[i] = (ia[i]==~0) ? ia[i+1] : ia[i]; + _delete(); + _val = val; + _ia = ia; + _ja = ja; + _size = nUnique; + _nRowsCRS = nRows; +} +//----------------------------------------------------------------------------- +// Sorts the triplets, condenses duplicates, and returns the # of unique values +//----------------------------------------------------------------------------- +template +INDEX SparseMatrix::CountUniqueTriplets() +{ + std::sort(_tri.begin(), _tri.end(), triplet_comparision); + INDEX i, nUnique=1 + _size; + // count unique entries + for (i=_tri.size()-1; i>0; i--) { // for all new triplets + if (triplets_equal(_tri[i-1], _tri[i])) { // previous has same index? + if (_tri[i].add) _tri[i-1].v += _tri[i].v; // add to previous + else _tri[i-1].v = _tri[i].v; // replace previous + _tri[i].j = ~0; // void this entry's col + } + else nUnique++; + } + return nUnique; +} +//----------------------------------------------------------------------------- +// Index by copy operator - return zero if not found +//----------------------------------------------------------------------------- +template +T SparseMatrix::operator()(INDEX i, INDEX j) const +{ + MICK(i,j); // Matrix Index ChecKing + compress(*this); + if (i>=_nRowsCRS || _ia[i+1]==_ia[i]) return 0.0; + unsigned f = std::lower_bound(_ja+_ia[i], _ja+_ia[i+1]-1, j) - _ja; + if (f>=_ia[i] && f<_ia[i+1] && _ja[f] == j) return _val[f]; + return 0.0; +} +//----------------------------------------------------------------------------- +// Index by reference operator - add to _tri if not found +//----------------------------------------------------------------------------- +template +T& SparseMatrix::operator()(INDEX i, INDEX j) +{ + compress(*this); + MICK(i,j); // Matrix Index ChecKing + if (i < _nRowsCRS && _ia[i+1]>_ia[i]) { + unsigned f = std::lower_bound(_ja+_ia[i], _ja+_ia[i+1]-1, j) - _ja; + if (f>=_ia[i] && f<_ia[i+1] && _ja[f] == j) return _val[f]; + } + // NEVER use index operator as LHS to modify values not already in the + // sparcity pattern - the crude check below will only catch this on the + // second infraction. + if (_zero != T(0)) cout << "Use add or set for SparseMatrix\n"; + return _zero; +} +//----------------------------------------------------------------------------- +// Sets (i,j) to value +//----------------------------------------------------------------------------- +template +void SparseMatrix::set(INDEX i, INDEX j, T v) +{ + MICK(i,j); // Matrix Index ChecKing + if (i < _nRowsCRS) + { + const int loc = Utility::SearchSorted(_ja, j, _ia[i], _ia[i+1]); + if (loc >=0 ) + { + _val[loc] = v; + return; + } + } + _tri.push_back(TRI_COORD(i,j,v,false)); +} +//----------------------------------------------------------------------------- +// Adds (i,j) to value +//----------------------------------------------------------------------------- +template +void SparseMatrix::add(INDEX i, INDEX j, T v) +{ + MICK(i,j); // Matrix Index ChecKing + if (i < _nRowsCRS) + { + const int loc = Utility::SearchSorted(_ja, j, _ia[i], _ia[i+1]); + if (loc >=0 ) + { + _val[loc] += v; + return; + } + } + _tri.push_back(TRI_COORD(i,j,v,true)); +} +//----------------------------------------------------------------------------- +// returns a triplet value of the ith nonzero +//----------------------------------------------------------------------------- +template +TRIPLET SparseMatrix::get_triplet(INDEX i) const +{ + compress(*this); + if (i >= _ia[_nRowsCRS]) { + gerror("ERROR: tried indexing triplet of sparse matrix beyond range"); + } + + INDEX row(std::lower_bound(_ia, _ia+_nRowsCRS, i)-_ia); + row -= _ia[row] != i; + return TRIPLET(row, _ja[i], _val[i]); +} +//----------------------------------------------------------------------------- +// full reset - completely wipes out all SparseMatrix data, zero is ignored +//----------------------------------------------------------------------------- +template +void SparseMatrix::reset(INDEX rows, INDEX cols, bool zero) +{ + _delete(); + _nRows = rows; + _nCols = cols; +} +//----------------------------------------------------------------------------- +// resize - changes the _nRows and _nCols without changing anything else +//----------------------------------------------------------------------------- +template +void SparseMatrix::resize(INDEX rows, INDEX cols, bool copy) +{ + GCHK(_nRowsCRS>rows, "SparseMatrix::resize CRS rows exceed specified rows"); + _nRows = rows; + _nCols = cols; // a check on this would be expensive +} +//----------------------------------------------------------------------------- +// get sparsity from DenseMatrix, if TOL < 0, then only zero values are added +//----------------------------------------------------------------------------- +template +void SparseMatrix::reset(const DenseMatrix& D, double TOL) +{ + _delete(); // clears all values + // if TOL is specified then TOL = TOL^2 * max(abs(D))^2 + if (TOL > 0.0) + { + TOL *= D.maxabs(); + TOL *= TOL; + } + _nRows = D.nRows(); + _nCols = D.nCols(); + for (INDEX i=0; i= TOL) // if TOL wasn't specified then TOL < 0 + set(i, j, D(i,j)); + + compress(); +} +//----------------------------------------------------------------------------- +// copy - dangerous: ignores rows & columns +//----------------------------------------------------------------------------- +template +void SparseMatrix::copy(const T * ptr, INDEX rows, INDEX cols) +{ + cout << "SparseMatrix::copy() has no effect.\n"; +} +//----------------------------------------------------------------------------- +// dense_copy - copy to dense matrix +//----------------------------------------------------------------------------- +template +void SparseMatrix::dense_copy(DenseMatrix & D ) const +{ + SparseMatrix::compress(*this); + D.reset(nRows(),nCols()); + for (INDEX i=0; i<_nRowsCRS; i++) + for (INDEX j=_ia[i]; j<_ia[i+1]; j++) + D(i, _ja[j]) = _val[j]; +} +template +DenseMatrix SparseMatrix::dense_copy(void) const +{ + DenseMatrix D; + dense_copy(D); + return D; +} +//----------------------------------------------------------------------------- +// returns true if the matrix has no non-zero elements +//----------------------------------------------------------------------------- +template +bool SparseMatrix::empty() const +{ + return _size==0 && _tri.empty(); +} +//----------------------------------------------------------------------------- +// returns the number of rows specified by the user +//----------------------------------------------------------------------------- +template +inline INDEX SparseMatrix::nRows() const +{ + return _nRows; +} +//----------------------------------------------------------------------------- +// returns the number of columns specified by the user +//----------------------------------------------------------------------------- +template +inline INDEX SparseMatrix::nCols() const +{ + return _nCols; +} +//----------------------------------------------------------------------------- +// returns the number of non-zeros in the matrix +//----------------------------------------------------------------------------- +template +INDEX SparseMatrix::size() const +{ + compress(*this); + return _size; +} +//----------------------------------------------------------------------------- +// returns the number of nonzero elements in a row +//----------------------------------------------------------------------------- +template +INDEX SparseMatrix::RowSize(INDEX r) const +{ + compress(*this); + GCHK(r>=_nRows, "Rowsize: invalid row"); + if (r >= _nRowsCRS) return 0; + return _ia[r+1]-_ia[r]; +} +//----------------------------------------------------------------------------- +// returns a pointer to the data, causes a compress +//----------------------------------------------------------------------------- +template +T* SparseMatrix::get_ptr() const +{ + compress(*this); + return _val; +} +//----------------------------------------------------------------------------- +// returns true if (i,j) falls in the user specified range +//----------------------------------------------------------------------------- +template +bool SparseMatrix::in_range(INDEX i, INDEX j) const +{ + return i < nRows() && j < nCols(); +} +//----------------------------------------------------------------------------- +// assigns this sparsematrix from another one - full memory copy +//----------------------------------------------------------------------------- +template +SparseMatrix& SparseMatrix::operator=(const SparseMatrix &C) +{ + _delete(); + _copy(C); + return *this; +} +//----------------------------------------------------------------------------- +// assigns this sparsematrix from another one - full memory copy +//----------------------------------------------------------------------------- +template +SparseMatrix& SparseMatrix::operator=(const T v) +{ + // set_all_elements only changes _data, so we need a compress + compress(*this); + return set_all_elements_to(v); +} +//----------------------------------------------------------------------------- +// scales this sparse matrix by a constant +//----------------------------------------------------------------------------- +template +SparseMatrix& SparseMatrix::operator*=(const T &a) +{ + compress(*this); + for (INDEX i=0; i +DenseVector SparseMatrix::transMat(const Vector &x) const +{ + DenseVector y(nCols(), true); + GCK(*this, x, nRows()!=x.size(),"operator *: Sparse matrix incompatible with Vector.") + + INDEX i, ij; + for(i=0; i<_nRowsCRS; i++) + for(ij=_ia[i]; ij<_ia[i+1]; ij++) + y(_ja[ij]) += _val[ij]*x(i); + return y; +} +//----------------------------------------------------------------------------- +// Return matrix transpose +//----------------------------------------------------------------------------- +template +SparseMatrix SparseMatrix::transpose() const +{ + compress(*this); + SparseMatrix At(nCols(), nRows()); + + for (INDEX i=0; i<_nRowsCRS; i++) + for (INDEX ij=_ia[i]; ij<_ia[i+1]; ij++) + At.set(_ja[ij], i, _val[ij]); + compress(At); + return At; +} +//----------------------------------------------------------------------------- +// Matrix Transpose/DenseMatrix multiply +//----------------------------------------------------------------------------- +template +DenseMatrix SparseMatrix::transMat(const DenseMatrix &D) const +{ + compress(*this); + GCK(*this, D, nRows()!=D.nRows(),"transMat: Sparse matrix incompatible with DenseMatrix.") + DenseMatrix C(nCols(), D.nCols(), true); // initialized to zero + INDEX j, k, ki; + + for (k=0; k<_nRowsCRS; k++) + for (ki=_ia[k]; ki<_ia[k+1]; ki++) + for (j=0; j +DenseMatrix SparseMatrix::transMat(const SparseMatrix &D) const +{ + compress(*this); + GCK(*this, D, nRows()!=D.nRows(),"transMat: Sparse matrix incompatible with DenseMatrix.") + DenseMatrix C(nCols(), D.nCols(), true); // initialized to zero + + INDEX k, ki, kj; + for (k=0; k<_nRowsCRS; k++) + for (kj=D._ia[k]; kj +SparseMatrix& SparseMatrix::row_scale(const Vector &v) +{ + compress(*this); + INDEX i,ij; + GCK(*this, v, v.size()!=nRows(), "Incompatible Vector length in row_scale."); + for(i=0; i<_nRowsCRS; i++) + for(ij=_ia[i]; ij<_ia[i+1]; ij++) _val[ij] *= v[i]; + return *this; +} +//----------------------------------------------------------------------------- +// multiples each column by the corresponding element in Vector scale +//----------------------------------------------------------------------------- +template +SparseMatrix& SparseMatrix::col_scale(const Vector &v) +{ + compress(*this); + INDEX i,ij; + GCK(*this, v, v.size()!=nCols(), "Incompatible Vector length in col_scale."); + for(i=0; i<_nRowsCRS; i++) + for(ij=_ia[i]; ij<_ia[i+1]; ij++) _val[ij] *= v[_ja[ij]]; + return *this; +} +//----------------------------------------------------------------------------- +// Returns a vector of the sums of each column +//----------------------------------------------------------------------------- +template +DenseVector SparseMatrix::col_sum() const +{ + compress(*this); + INDEX i,ij; + GCHK(!nRows(), "SparseMatrix::Matrix not initialized in col_sum.") + DenseVector csum(nCols()); + for(i=0; i<_nRowsCRS; i++) + for(ij=_ia[i]; ij<_ia[i+1]; ij++) csum(_ja[ij]) += _val[ij]; + return(csum); +} +//----------------------------------------------------------------------------- +// Returns a vector with the number of nonzeros in each column +//----------------------------------------------------------------------------- +template +DenseVector SparseMatrix::column_count() const +{ + compress(*this); + INDEX i,j; + Vector counts(nCols()); + + for (i=0; i<_nRowsCRS; i++) + for(j=_ia[i]; j<_ia[i+1]; j++) counts(_ja[j])++; + return(counts); +} +//----------------------------------------------------------------------------- +// Writes a the nonzeros of a row to a vector +//----------------------------------------------------------------------------- +template +void SparseMatrix::get_row(INDEX i, DenseVector& row, DenseVector& indx) const +{ + GCHK(i>=nRows(), "get_row() - invalid row number"); + row.resize(RowSize(i)); + indx.resize(row.size()); + INDEX idx=0, ij; + for(ij=_ia[i]; ij<_ia[i+1]; ij++) + { + row(idx) = _val[ij]; + indx(idx++) = _ja[ij]; + } +} +//----------------------------------------------------------------------------- +// Computes the product of N'DN +//----------------------------------------------------------------------------- +template +void SparseMatrix:: +WeightedLeastSquares(const SparseMatrix &N, const DiagonalMatrix &D) +{ + compress(N); + GCK(N,D,N.nRows()!=D.nRows(),"SparseMatrix::WeightedLeastSquares()"); + INDEX k, ki, kj; + + resize(N.nCols(), N.nCols()); // set size of this matrix + for (k=0; k<_size; k++) _val[k] = 0.0; + // compute R(i,j) = N(k,i) D(k,q) N(i,j) = N(k,i)*D(k,k)*N(k,j) (sum on k) + for (k=0; k +DiagonalMatrix SparseMatrix::get_diag() const +{ + compress(*this); + DiagonalMatrix D(nRows(), true); // initialized to zero + INDEX i, ij; + for (i=0; i<_nRowsCRS; i++) + { + for(ij=_ia[i]; ij<_ia[i+1]; ij++) + { + if (_ja[ij]>=i) // have we reached or passed the diagonal? + { + if (_ja[ij]==i) D[i]=_val[ij]; // this this the diagonal? + break; // D[i] is already zero if there is no diagonal + } + } + } + return D; +} +//----------------------------------------------------------------------------- +// output function - builds a string with each nonzero triplet value +//----------------------------------------------------------------------------- +template +string SparseMatrix::tostring() const +{ + compress(*this); + string out; + INDEX i, ij; + for(i=0; i<_nRowsCRS; i++) + { + for(ij=_ia[i]; ij<_ia[i+1]; ij++) + { + if (ij) out += "\n"; // append newline if not first nonzero + out += "(" + ATC_STRING::tostring(i) + ", "; // append "(i," + out += ATC_STRING::tostring(_ja[ij]) + ") = "; // append "j) = " + out += ATC_STRING::tostring(_val[ij]); // append "value" + } + } + return out; // return the completed string +} +//----------------------------------------------------------------------------- +// returns the maximum value in the row +//----------------------------------------------------------------------------- +template +T SparseMatrix::get_row_max(INDEX row) const +{ + compress(*this); + if (!RowSize(row)) return (T)0; // if there are no nonzeros in the row + INDEX ij; + T max = _val[_ia[row]]; + for(ij=_ia[row]+1; ij<_ia[row+1]; ij++) max = std::max(max,_val[ij]); + return max; +} +//----------------------------------------------------------------------------- +// returns the minimum value in the row +//----------------------------------------------------------------------------- +template +T SparseMatrix::get_row_min(INDEX row) const +{ + compress(*this); + if (!RowSize(row)) return (T)0; // if there are no nonzeros in the row + INDEX ij; + T min = _val[_ia[row]]; + for(ij=_ia[row]+1; ij<_ia[row+1]; ij++) min = std::min(min,_val[ij]); + return min; +} +//----------------------------------------------------------------------------- +// prints a histogram of the values of a row to the screen +//----------------------------------------------------------------------------- +template +void SparseMatrix::print_row_histogram(const string &name, INDEX nbins) const +{ + compress(*this); + cout << "Begin histogram " << name << "\n"; + cout << "# rows: " << _nRows << " columns: " << _nCols + << " size: " << _size << "\n"; + for(INDEX i=0; i<_nRows; i++) + { + print_row_histogram(i, nbins); + cout << "\n"; + } + cout << "End histogram " << name << "\n"; +} +//----------------------------------------------------------------------------- +// prints a histogram of the values of a row to the screen +//----------------------------------------------------------------------------- +template +void SparseMatrix::print_row_histogram(INDEX row, INDEX nbins) const +{ + compress(*this); + if (!nbins) nbins++; + vector counts(nbins, 0); + const T min = get_row_min(row); + const T max = get_row_max(row); + const T range = max-min; + const double bin_size = range/double(nbins); + if (range<=0.0) counts[nbins-1]=RowSize(row); + else + { + for(INDEX ij=_ia[row]; ij<_ia[row+1]; ij++) + { + INDEX bin = INDEX((_val[ij]-min)/bin_size); + counts[bin-(bin==nbins)]++; + } + } + cout< +void SparseMatrix::matlab(ostream &o, const string &s) const +{ + compress(*this); + INDEX i, ij; + o << s <<" = sparse(" << nRows() << "," << nCols() << ");\n"; + o << showbase << scientific; + for(i=0; i<_nRowsCRS; i++) + for(ij=_ia[i]; ij<_ia[i+1]; ij++) + o< +void SparseMatrix::binary_write(std::fstream& f) const +{ + compress(*this); + f.write((char*)&_size, sizeof(INDEX)); // writes number of nonzeros + f.write((char*)&_nRowsCRS, sizeof(INDEX)); // writes number of rows in crs + f.write((char*)&_nRows, sizeof(INDEX)); // write matrix rows + f.write((char*)&_nCols, sizeof(INDEX)); // write number of columns + if (!_size) return; + f.write((char*)_val, sizeof(T) *_size); + f.write((char*)_ja, sizeof(INDEX)*_size); + f.write((char*)_ia, sizeof(INDEX)*(_nRowsCRS+1)); +} +//----------------------------------------------------------------------------- +// Reads a SparseMatrix from a binary file. (wipes out any original data) +//----------------------------------------------------------------------------- +template +void SparseMatrix::binary_read(std::fstream& f) +{ + _delete(); + f.read((char*)&_size, sizeof(INDEX)); + f.read((char*)&_nRowsCRS, sizeof(INDEX)); + f.read((char*)&_nRows, sizeof(INDEX)); + f.read((char*)&_nCols, sizeof(INDEX)); + if (!_size) return; + _create(_size,_nRowsCRS); + f.read((char*)_val, sizeof(T)*_size); + f.read((char*)_ja, sizeof(INDEX)*_size); + f.read((char*)_ia, sizeof(INDEX)*(_nRowsCRS+1)); +} + +//----------------------------------------------------------------------------- +// Writes the sparse matrix to a file in a binary format +//----------------------------------------------------------------------------- +template +void SparseMatrix::write_restart(FILE *f) const +{ + compress(*this); + fwrite(&_size, sizeof(INDEX), 1 ,f); // write number of nonzeros + fwrite(&_nRowsCRS, sizeof(INDEX), 1 ,f); // write number of rows + fwrite(&_nRows, sizeof(INDEX), 1 ,f); // write number of columns + fwrite(&_nCols, sizeof(INDEX), 1 ,f); // write number of columns + if (!_size) return; + fwrite(_val, sizeof(T), _size ,f); + fwrite(_ja, sizeof(T), _size ,f); + fwrite(_ia, sizeof(INDEX), _nRowsCRS+1 ,f); +} +#endif + diff --git a/lib/atc/SparseMatrix.h b/lib/atc/SparseMatrix.h new file mode 100644 index 0000000000..e6ab913f1d --- /dev/null +++ b/lib/atc/SparseMatrix.h @@ -0,0 +1,180 @@ +#ifndef SPARSEMATRIX_H +#define SPARSEMATRIX_H +#include +#include "MatrixLibrary.h" +#include + +/** + * @struct TRI_COORD + * @brief Triplet SparseMatrix entry + */ +template +struct TRI_COORD +{ + TRI_COORD(unsigned row=0, unsigned col=0); + TRI_COORD(unsigned row, unsigned col, T val, bool add_to=0); + unsigned i, j; + T v; + bool add; +}; + +/** + * @class SparseMatrix + * @brief Stores data in triplet format or CRS format + */ +template +class SparseMatrix : public Matrix +{ + //* SparseMatrix-Vector multiplication (S * v) + friend DenseVector operator*(const SparseMatrix &A, const Vector& x); + //* SparseMatrix-DenseMatrix multiplication (S * F) + friend DenseMatrix operator*(const SparseMatrix &A, const Matrix& D); + //* SparseMatrix-DiagonalMatrix multiplication (S * D) + friend SparseMatrix operator*(const SparseMatrix &A, const DiagonalMatrix& D); + //* SparseMatrix-SparseMatrix multiplication (S * S) + friend SparseMatrix operator*(const SparseMatrix &A, const SparseMatrix &B); + //* computes the product of a SparseMatrix tranpose with a SparseVector (M'*v). + friend SparseVector operator*(const SparseMatrix &M, const SparseVector &v); + //* computes the product of a SparseMatrix tranpose with a SparseVector (M'*v). + friend SparseVector operator*(const SparseVector &v, const SparseMatrix &M); +public: + SparseMatrix(INDEX rows=0, INDEX cols=0); + SparseMatrix(const SparseMatrix& c); + SparseMatrix(const DenseMatrix& c); + ~SparseMatrix(); + + //* General index by value (requires a binary search on the row) + T operator()(INDEX i, INDEX j) const; + //* General index by reference (requires a binary search on the row) + T& operator()(INDEX i, INDEX j); + //* General flat index by value operator (by nth nonzero) + T operator[](INDEX i) const; + //* General flat index by reference operator (by nth nonzero) + T& operator[](INDEX i); + //* adds a value to index i,j + void set(INDEX i, INDEX j, T v); + //* sets a value to index i,j + void add(INDEX i, INDEX j, T v); + //* return a triplet value of the ith nonzero + TRIPLET get_triplet(INDEX i) const; + + //* full reset - completely wipes out all SparseMatrix data + void reset(INDEX rows=0, INDEX cols=0, bool zero=true); + //* only changes the bounds of the matrix, no deletion + void resize(INDEX rows=0, INDEX cols=0, bool zero=true); + //* reset - from DenseMatrix - this will be SLOW + void reset(const DenseMatrix& D, double TOL=-1.0); + //* copy data + void copy(const T * ptr, INDEX rows=0, INDEX cols=0); + + void dense_copy(DenseMatrix& D) const; + DenseMatrix dense_copy(void) const; + + //* returns true if the matrix has no nonzero elements + bool empty() const; + //* returns the user-specified number of rows + INDEX nRows() const; + //* returns the user-specified number of cols + INDEX nCols() const; + //* returns the number of non-zero elements + INDEX size() const; + //* returns the number of non-zeros in a row + INDEX RowSize(INDEX r) const; + //* returns a pointer to the nonzero data + inline T* get_ptr() const; + //* checks if the index i,j falls in the user-specified range + bool in_range(INDEX i, INDEX j) const; + +/* + * \section assignment operators + */ + //* copies SparseMatrix R to this + SparseMatrix& operator=(const SparseMatrix &R); + //* sets all nonzero values to a constant + SparseMatrix& operator=(const T v); + //* scales all nonzero values by a constant + SparseMatrix& operator*=(const T &a); + +/* + * \section Multiplication operations + */ + + //* S' * F + DenseMatrix transMat(const DenseMatrix &D) const; + //* S' * S + DenseMatrix transMat(const SparseMatrix &S) const; + //* S' * v + DenseVector transMat(const Vector &x) const; + + SparseMatrix transpose() const; + SparseMatrix& row_scale(const Vector &v); + SparseMatrix& col_scale(const Vector &v); + DenseVector col_sum() const; + DenseVector column_count() const; + DiagonalMatrix get_diag() const; + void get_row(INDEX i, DenseVector& row, DenseVector& indx) const; + void WeightedLeastSquares(const SparseMatrix &N, const DiagonalMatrix &D); + + T get_row_max(INDEX row) const; + T get_row_min(INDEX row) const; + +/* + * \section I/O functions + */ + //* outputs this SparseMatrix to a formatted string + string tostring() const; + using Matrix::matlab; + //* writes a command to recreate this matrix in matlab to a stream + void matlab(ostream &o, const string &name="S") const; + //* prints a row histogram for each row + void print_row_histogram(const string &name, INDEX nbins = 10) const; + //* prints a histogram of the values in a row + void print_row_histogram(INDEX row, INDEX nbins) const; + //! Writes the matrix to a binary file (after a compress). + void binary_write(std::fstream& f) const; + //! Reads a SparseMatrix from a binary file. (wipes out any original data) + void binary_read(std::fstream& f); + //* Dump templated type to disk; operation not safe for all types + void write_restart(FILE *f) const; + +/* + * \section Utility functions + */ + //* converts all triplets and merges with CRS + void compress(); + //* converts T to CRS + static void compress(const SparseMatrix &C); + //* sorts and returns the # of unique triplets + INDEX CountUniqueTriplets(); + +private: + //* creates a CRS structure + void _create(INDEX size, INDEX nrows); + //* clears all memory and nulls references + void _delete(); + //* copys all data from another SparseMatrix + void _copy(const SparseMatrix &C); + //* general sparse matrix assignment + void _set_equal(const Matrix &r); + //* returns the first row with a nonzero in it (from the CRS structure only) + int _first_nonzero_row_crs() const; + +/* + * \section CRS storage variables + */ + T * _val; // matrix non-zeros + INDEX *_ia, *_ja; // ptrs to rows, column indexes + INDEX _size, _nRowsCRS; // # of non-zeros, rows + + //* new (unsorted triplet values - won't intersect CRS values) + mutable vector > _tri; +/* + * \section User specified variables + */ + INDEX _nRows, _nCols; + static T _zero; +}; + +#include "SparseMatrix-inl.h" +#endif + diff --git a/lib/atc/SparseVector-inl.h b/lib/atc/SparseVector-inl.h new file mode 100644 index 0000000000..28bda9be47 --- /dev/null +++ b/lib/atc/SparseVector-inl.h @@ -0,0 +1,188 @@ +// SparseVector-inl.h: provides templated functions for SparseVector in +// separate header + +template +SparseVector sparse_rand(unsigned n, unsigned fill, int seed=1234) +{ + srand(seed); + const double rmax_inv = 1.0/double(RAND_MAX); + SparseVector r(n); + while (r.size() +DenseVector operator*(const Matrix &M, const SparseVector &v) +{ + DenseVector y(M.nRows()); + STORE::const_iterator it=v.data_.begin(); + for (; it!=v.data_.end(); it++) { + const unsigned j = it->first; + const T& vj = it->second; + for (unsigned i=0; i +DenseVector operator*(const SparseVector &v, const Matrix &M) +{ + DenseVector y(M.nCols()); + STORE::const_iterator it=v.data_.begin(); + for (; it!=v.data_.end(); it++) { + const unsigned i = it->first; + const T& vi = it->second; + for (unsigned j=0; j +T dot(const SparseVector &a, const SparseVector &b) +{ + double v = 0.0; + for (STORE::const_iterator ai=a.data_.begin(); ai!=a.data_.end(); ai++) { + STORE::const_iterator bi=b.data_.find(ai->first); + if (bi == b.data_.end()) continue; + v += ai->second * bi->second; + } + return v; +} +// Computes the product of a SparseMatrix tranpose with a SparseVector (M'*v). +template +SparseVector operator*(const SparseMatrix &M, const SparseVector &v) +{ + SparseVector y(M.nRows()); + for (unsigned i=0; isecond; + } + if (yi!=0.0) y(i)+=yi; + } + return y; +} + +// computes the product of a SparseMatrix tranpose with a SparseVector (M'*v). +template +SparseVector operator*(const SparseVector &v, const SparseMatrix &M) +{ + SparseVector y(M.nCols()); + for (unsigned i=0; isecond * M._v[ij]; + } + return y; +} + +// General constructor - sets the vector length. +template +SparseVector::SparseVector(unsigned n) : length_(n){} + +// Outputs the vector to string +template +std::string SparseVector::tostring() const +{ + if (size() > nRows()/2) return Vector::tostring(); + STORE::const_iterator it=data_.begin(); + std::string str; + using ATC_STRING::tostring; + for (; it!=data_.end(); it++) + str += tostring(it->first)+": "+tostring(it->second)+'\n'; + return str; +} + +// Indexes the ith component of the vector or returns zero if not found. +template +T SparseVector::operator()(unsigned i, unsigned j) const +{ + STORE::const_iterator it = data_.find(i); + if (it == data_.end()) return 0.0; + return it->second; +} + +// Indexes the ith component of the vector or returns zero if not found. +template +T& SparseVector::operator()(unsigned i, unsigned j) +{ + return data_[i]; +} + +// Indexes the ith component of the vector or returns zero if not found. +template T SparseVector::operator[](unsigned i) const +{ + return (*this)(i); +} + +// Indexes the ith component of the vector or returns zero if not found. +template T& SparseVector::operator[](unsigned i) +{ + return (*this)[i]; +} + +// Returns the number of nonzeros in the sparse vector. +template inline unsigned SparseVector::size() const +{ return data_.size(); } + +// Returns the number of nonzeros in the sparse vector. +template inline unsigned SparseVector::nRows() const +{ return length_; } + +// Changes the size of the SparseVector +template +void SparseVector::resize(unsigned nRows, unsigned nCols, bool copy) +{ + length_ = nRows; + STORE::iterator it; + for (it=data_.begin(); it!=data_.end(); it++) { + if (it->second >= length_) data_.erase(it); + else if (!copy) it->second=T(0); + } +} +// same as resize, but zero rather than copy +template +void SparseVector::reset(unsigned nRows, unsigned nCols, bool zero) +{ + resize(nRows, nCols, !zero); +} +// sets all elements to zero but preserves sparcity +template +void SparseVector::zero() +{ + STORE::iterator it; + for (it=data_.begin(); it!=data_.end(); it++) it->second=T(0); +} + +// TODO +template +void SparseVector::copy(const T* ptr, unsigned nRows, unsigned nCols) +{ + +} + +// TODO +template +void SparseVector::write_restart(FILE *F) const +{ + +} + +// writes a stream to a matlab script to recreate this variable +template +void SparseVector::matlab(ostream &o, const string &s) const +{ + o << s << "=sparse(" << nRows() << ",1);\n"; + o << showbase << scientific; + STORE::const_iterator it; + for (it=data_.begin(); it!=data_.end(); it++) + o << s << "(" << it->first+1 << ") = " << it->second << ";\n"; +} diff --git a/lib/atc/SparseVector.h b/lib/atc/SparseVector.h new file mode 100644 index 0000000000..779df6a977 --- /dev/null +++ b/lib/atc/SparseVector.h @@ -0,0 +1,85 @@ +#ifndef SPARSEVECTOR_H +#define SPARSEVECTOR_H +#include "MatrixLibrary.h" + +// No C++ templated typedefs, so use a define, gets cleaned up at end, +// so don't use outside of this class +#define STORE typename std::map + +/** SparseVector class - an implimentation of a vector that contains a + ** majority of zero element, and provides the relevant operations + ** specified by the base class Vector. + **/ +template +class SparseVector : public Vector { + //* Multiplies a Matrix by a SparseVector (M*v) and returns a DenseVector. + friend DenseVector operator*(const Matrix &M, const SparseVector &v); + //* Multiplies a SparseVector by a Matrix (M'*v) and returns a DenseVector. + friend DenseVector operator*(const SparseVector &v, const Matrix &M); + //* Computes the dot product between two SparseVectors of equal length. + friend T dot(const SparseVector &a, const SparseVector &b); + //* computes the product of a SparseMatrix tranpose with a SparseVector (M'*v). + friend SparseVector operator*(const SparseMatrix &M, const SparseVector &v); + //* computes the product of a SparseMatrix tranpose with a SparseVector (M'*v). + friend SparseVector operator*(const SparseVector &v, const SparseMatrix &M); +public: + //* Constructor - sets length of vector (NOT # of nonzeros). + SparseVector(unsigned length=0); + //* Copies another SparseVector + SparseVector(const SparseVector &c); + //* Copies a general Vector (avoid if possible, its going to be slow). + SparseVector(const Vector &c); + + //* Overrides output to string function to list only nonzeros and indices. + std::string tostring() const; + //* Indexing operators (w/ const overloading). + //@{ + T operator()(unsigned i, unsigned j=0) const; + // NOTE reading a non-const SparseVector will call this and add zero entries. + T& operator()(unsigned i, unsigned j=0); + T operator[](unsigned i) const; + T& operator[](unsigned i); + //@} + //* assignment operators + //@{ + SparseVector& operator=(SparseVector &c); + SparseVector& operator=(Vector &c); + //@} + //* Return the number of rows in the Vector. + unsigned nRows() const; + //* Returns the number of columns - always 1. + unsigned nCols() const { return 1; } + //* Change # of Vector rows Vector and optionally keeps nonzeros (ignores nCols). + void resize(unsigned nRows, unsigned nCols=1, bool copy=0); + //* Return the number of nonzeros in the Vector. + unsigned size() const; + //* Changes size of Vector rows and optionally removes nonzeros. + void reset (unsigned nRows, unsigned nCols=1, bool zero=0); + //* zeros out all elements while preserving sparcity pattern + void zero(); + //* TODO impliment copy (or maybe not necessary) + void copy(const T* ptr, unsigned nRows, unsigned nCols=1); + //* Writes a restart file (TODO impliment this if needed/wanted). + void write_restart(FILE *F) const; + + // output to matlab (is this needed) +// using Matrix::matlab; + //* Writes a matlab string to a stream that creates this object with a name. + void matlab(ostream &o, const string &s="v") const; + +protected: + //* Banned operators + //@{ + SparseVector(const Matrix &c); + SparseVector& operator=(Matrix &c); + T* get_ptr() const {return NULL; } + //@} + + STORE data_; //*> sparse data structure + unsigned length_; //*> number of rows +}; + +#include "SparseVector-inl.h" +#undef STORE +#endif + diff --git a/lib/atc/StringManip.h b/lib/atc/StringManip.h new file mode 100644 index 0000000000..3e4777de4e --- /dev/null +++ b/lib/atc/StringManip.h @@ -0,0 +1,122 @@ +#ifndef STRINGMANIP_H +#define STRINGMANIP_H + +#include +#include +#include +#include +#include +#include +#include + +using std::cout; +using std::transform; +using std::fstream; +using std::vector; +using std::string; + +//* A collection of string convesion/manipulation routines +namespace ATC_STRING +{ + //* converts anything that has iostream::<< defined to a string + template + inline string tostring(const T &v, int precision=0) + { + std::ostringstream out; + if (precision) out << std::setprecision(precision); + out << v; + return out.str(); + } + + //* convert a string to anything that has iostream::>> defined + template + inline T str2T(const string &s, T v) + { + std::istringstream in(s); + if (!(in >> v)) cout<<"Error: str2T invalid string conversion\n";\ + return v; + } + + //* convert a string to a double + inline double str2dbl(const string &s) { return str2T(s, double(0.0)); } + + //* convert a string to an int + inline int str2int(const string &s) { return str2T(s, int(0)); } + + //* replaces all characters in a set of characters with a character + //* @param str input string + //* @param *s pointer to array of characters that will be replaced + //* @param r character to replace characters in s[] with + static void replace_all_of(string &str, const char *s, const char r) + { + int found; + found = str.find_first_of(s); + while (found != string::npos) + { + str[found] = r; + found = str.find_first_of(s, found+1); + } + } + + //* converts the string to lowercase + inline string& to_lower(string &s) + { + transform(s.begin(),s.end(),s.begin(),static_cast(tolower)); + return s; + } + + //* converts the string to uppercase + inline string& to_upper(string &s) + { + transform(s.begin(),s.end(),s.begin(),static_cast(toupper)); + return s; + } + + //* removes any whitespace from the beginning or end of string + static string& trim(string &s) + { + if (s.empty()) return s; + size_t found = s.find_last_not_of(" \t\n"); + if (found < s.size()-1) s.erase(found+1); + found = s.find_first_not_of(" \t\n"); + if (found != string::npos) s = s.substr(found); + return s; + } + + //* splits delimited string into a vector of strings + static void split_up_string(const string &s, vector &ss, char del=' ') + { + ss.clear(); + size_t begin=0, end=0; + while (end != string::npos) + { + begin = s.find_first_not_of(del, end); // find beginning of fragment + end = s.find_first_of(del,begin); + if (begin != string::npos) // safe if end is npos-1 + ss.push_back(s.substr(begin,end-begin)); + } + } + + //* scans a string for a list of commands delimited by ', \t' with # comments + //* @param line The input line to be parsed + //* @cs A vector of strings parsed from the input line + static void get_command_strings(string line, vector &cs) + { + if (line.find('#') != string::npos) line.erase(line.find("#")); + replace_all_of(line, "\t,", ' '); + to_lower(trim(line)); + split_up_string(line, cs); + } + + //* reads a single line from a file and splits it into a vector of strings + //* returns the number of strings in the vector + inline int get_command_line(fstream &fid, vector &cs) + { + string line; + getline(fid, line); + get_command_strings(line, cs); + return cs.size(); + } +} + +#endif diff --git a/lib/atc/Thermostat.cpp b/lib/atc/Thermostat.cpp new file mode 100644 index 0000000000..eb24ebb1b8 --- /dev/null +++ b/lib/atc/Thermostat.cpp @@ -0,0 +1,1579 @@ +// ATC_Transfer Headers +#include "Thermostat.h" +#include "CG.h" +#include "ATC_Error.h" +#include "PrescribedDataManager.h" +#include "TimeIntegrator.h" + +namespace ATC { + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class Thermostat + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + Thermostat::Thermostat(ATC_Transfer * atcTransfer) : + AtomicRegulator(atcTransfer), + thermostatType_(NONE) + { + // nothing to do + } + + //-------------------------------------------------------- + // modify: + // parses and adjusts thermostat state based on + // user input, in the style of LAMMPS user input + //-------------------------------------------------------- + bool Thermostat::modify(int narg, char **arg) + { + bool foundMatch = false; + + // thermostat type + /*! \page man_thermal_control fix_modify AtC transfer thermal control + \section syntax + fix_modify AtC transfer thermal control \n + - control_type (string) = none | rescale | hoover | flux\n + + fix_modify AtC transfer thermal control rescale \n + - frequency (int) = time step frequency for applying velocity rescaling \n + + fix_modify AtC transfer thermal control hoover \n + + fix_modify AtC transfer thermal control flux \n + - boundary_integration_type (string) = faceset | interpolate\n + - face_set_id (string), optional = id of boundary face set, if not specified + (or not possible when the atomic domain does not line up with + mesh boundaries) defaults to an atomic-quadrature approximate + evaulation, does not work with interpolate\n + \section examples + fix_modify AtC transfer thermal control none \n + fix_modify AtC transfer thermal control rescale 10 \n + fix_modify AtC transfer thermal control hoover \n + fix_modify AtC transfer thermal control flux bndy_faces \n + \section description + Sets the energy exchange mechansim from the finite elements to the atoms, managed through a control algorithm. Rescale computes a scale factor for each atom to match the finite element temperature. Hoover is a Gaussian least-constraint isokinetic thermostat enforces that the nodal restricted atomic temperature matches the finite element temperature. Flux is a similar mode, but rather adds energy to the atoms based on conservation of energy. Hoover and flux allows the prescription of sources or fixed temperatures on the atoms. + \section restrictions + only for be used with specific transfers : + thermal (rescale, hoover, flux), two_temperature (flux) \n + rescale not valid with time filtering activated + \section related + \section default + none\n + rescale frequency is 1\n + flux boundary_integration_type is interpolate + */ + int argIndex = 0; + if (strcmp(arg[argIndex],"none")==0) { // restore defaults + thermostatType_ = NONE; + howOften_ = 1; + boundaryIntegrationType_ = ATC_Transfer::NO_QUADRATURE; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"rescale")==0) { + argIndex++; + howOften_ = atoi(arg[argIndex]); + if (howOften_ < 1) { + throw ATC_Error(0,"Bad rescaling thermostat frequency"); + } + else { + thermostatType_ = RESCALE; + boundaryIntegrationType_ = ATC_Transfer::NO_QUADRATURE; + foundMatch = true; + } + } + else if (strcmp(arg[argIndex],"hoover")==0) { + thermostatType_ = HOOVER; + howOften_ = 1; + boundaryIntegrationType_ = ATC_Transfer::NO_QUADRATURE; + foundMatch = true; + } + else if (strcmp(arg[argIndex],"flux")==0) { + thermostatType_ = FLUX; + howOften_ = 1; + argIndex++; + boundaryIntegrationType_ = atcTransfer_->parse_boundary_integration(narg-argIndex,&arg[argIndex],boundaryFaceSet_); + foundMatch = true; + } + + if (!foundMatch) + foundMatch = AtomicRegulator::modify(narg,arg); + if (foundMatch) + needReset_ = true; + return foundMatch; + } + + //-------------------------------------------------------- + // reset_nodal_lambda_power: + // resets the thermostat generated power to a + // prescribed value + //-------------------------------------------------------- + void Thermostat::reset_lambda_power(DENS_MAT & target) + { + for (int i = 0; i < nNodes_; ++i) + lambdaPowerFiltered_(i) = target(i,0); + } + + //-------------------------------------------------------- + // initialize: + // sets up methods before a run + // NOTE we currently only include time filter + // dependence, but in general there is also a + // time integrator dependence. In general the + // precedence order is: + // time filter -> time integrator -> kinetostat + // In the future this may need to be added if + // different types of time integrators can be + // specified. + //-------------------------------------------------------- + void Thermostat::initialize() + { + //TimeIntegrationType myIntegrationType = (atcTransfer_->get_time_integrator())->get_time_integration_type(); + // HACK until thermal time integrator is implemented + TimeIntegrator::TimeIntegrationType myIntegrationType = TimeIntegrator::VERLET; + TimeFilterManager * timeFilterManager = atcTransfer_->get_time_filter_manager(); + + // reset data if needed and perform any error/conflict checking + if (resetData_) { + AtomicRegulator::reset_data(); + + // set up storage + lambda_.reset(nNodes_,1); + nodalAtomicLambdaPower_.reset(nNodes_); + lambdaPowerFiltered_.reset(nNodes_); + } + + if (needReset_ || timeFilterManager->need_reset() || timeFilterManager->end_equilibrate()) { + // eliminate existing methods + destroy(); + if (timeFilterManager->need_reset()) { + if (timeFilter_) + delete timeFilter_; + if (myIntegrationType == TimeIntegrator::VERLET) + timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT); + else if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) + timeFilter_ = timeFilterManager->construct(TimeFilterManager::EXPLICIT_IMPLICIT); + } + + if (timeFilterManager->filter_dynamics()) { + switch (thermostatType_) { + case NONE: { + break; + } + case RESCALE: { // error check, rescale and filtering not supported together + throw ATC_Error(0,"Cannot use rescaling thermostat with time filtering"); + break; + } + case HOOVER: { + if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) + //regulatorMethod_ = new ThermostatHooverFiltered(this); + cout << "temporary line\n"; + else + regulatorMethod_ = new ThermostatHooverVerletFiltered(this); + break; + } + case FLUX: { + if (atcTransfer_->use_localized_lambda()) + throw ATC_Error(0,"Cannot use flux thermostating with localized lambda"); + if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) + regulatorMethod_ = new ThermostatPowerFiltered(this); + else + regulatorMethod_ = new ThermostatPowerVerletFiltered(this); + break; + } + default: + throw ATC_Error(0,"Unknown thermostat type in Thermostat::initialize"); + } + } + else { + switch (thermostatType_) { + case NONE: { + break; + } + case RESCALE: { + regulatorMethod_ = new ThermostatRescale(this); + break; + } + case HOOVER: { + if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) + regulatorMethod_ = new ThermostatHoover(this); + else + regulatorMethod_ = new ThermostatHooverVerlet(this); + break; + } + case FLUX: { + if (atcTransfer_->use_localized_lambda()) + throw ATC_Error(0,"Cannot use flux thermostating with localized lambda"); + if (myIntegrationType == TimeIntegrator::FRACTIONAL_STEP) + regulatorMethod_ = new ThermostatPower(this); + else + regulatorMethod_ = new ThermostatPowerVerlet(this); + break; + } + default: + throw ATC_Error(0,"Unknown thermostat type in Thermostat::initialize"); + } + } + + AtomicRegulator::reset_method(); + } + + AtomicRegulator::initialize(); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatShapeFunction + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ThermostatShapeFunction::ThermostatShapeFunction(Thermostat * thermostat) : + RegulatorShapeFunction(thermostat), + thermostat_(thermostat), + v_(atcTransfer_->get_v()), + mdMassMatrix_(atcTransfer_->get_mass_mat_md(TEMPERATURE)) + { + fieldMask_(TEMPERATURE,FLUX) = true; + } + + //--------------------------------------------------------- + // set_weights: + // set the diagonal weighting matrix to be the atomic + // temperatures + //--------------------------------------------------------- + void ThermostatShapeFunction::set_weights(DIAG_MAT & weights) + { + if (nLocalLambda_>0) { + DENS_VEC weightVector(nLocal_); + for (int i = 0; i < nLocal_; i++) + for (int j = 0; j < nsd_; j++) + weightVector(i) += v_[internalToAtom_(i)][j]*v_[internalToAtom_(i)][j]; + DENS_VEC maskedWeightVector(nLocalLambda_); + maskedWeightVector = internalToOverlapMap_*weightVector; + weights.reset(maskedWeightVector); + } + } + + //-------------------------------------------------------- + // reset_nlocal: + // resets data dependent on local atom count + //-------------------------------------------------------- + void ThermostatShapeFunction::reset_nlocal() + { + RegulatorShapeFunction::reset_nlocal(); + if (nLocal_ > 0) { + atcTransfer_->compute_atomic_mass(atomicMass_); + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatRescale + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ThermostatRescale::ThermostatRescale(Thermostat * thermostat) : + ThermostatShapeFunction(thermostat), + nodalTemperature_(atcTransfer_->get_field(TEMPERATURE)) + { + // do nothing + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the thermostat in the post corrector phase + //-------------------------------------------------------- + void ThermostatRescale::apply_post_corrector(double dt) + { + // compute right-hand side + DENS_MAT rhs(nNodes_,1); + rhs = mdMassMatrix_*nodalTemperature_; + + // solve equations + solve_for_lambda(rhs); + + // application of rescaling lambda due + apply_to_atoms(v_,dt); + } + + //-------------------------------------------------------- + // apply_lambda_to_atoms: + // applies the velocity rescale with an existing lambda + // note oldAtomicQuantity and dt are not used + //-------------------------------------------------------- + void ThermostatRescale::apply_to_atoms(double ** atomicQuantity, + const double dt) + { + if (nLocal_>0) { + DENS_MAT lambdaAtom(nLocal_,1); + atcTransfer_->prolong(lambda_,lambdaAtom); + + double xi; + for (int i = 0; i < nLocal_; i++) { + xi = sqrt(lambdaAtom(i,0)/atomicMass_(i)); + for (int j = 0; j < nsd_; j++) { + atomicQuantity[internalToAtom_(i)][j] *= xi; + } + } + } + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void ThermostatRescale::output(double dt, OUTPUT_LIST & outputData) + { + outputData["lambda"] = &(lambda_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatGlc + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + ThermostatGlc::ThermostatGlc(Thermostat * thermostat) : + ThermostatShapeFunction(thermostat), + timeFilter_(atomicRegulator_->get_time_filter()), + nodalAtomicLambdaPower_(thermostat_->get_nodal_atomic_lambda_power()), + lambdaPowerFiltered_(thermostat_->get_filtered_lambda_power()), + lambdaForce_(atomicRegulator_->get_lambda_force()), + isFixedNode_(atcTransfer_->get_fixed_node_flags(TEMPERATURE)) + { + // do nothing + } + + //-------------------------------------------------------- + // apply_to_atoms: + // determines what if any contributions to the + // atomic moition is needed for + // consistency with the thermostat + //-------------------------------------------------------- + void ThermostatGlc::apply_to_atoms(double ** atomicQuantity, + const DENS_MAT & lambdaForce, + double dt) + { + if (nLocal_>0) { + // explicit update + for (int i = 0; i < nLocal_; i++) { + for (int j = 0; j < nsd_; j++) { + atomicQuantity[internalToAtom_(i)][j] += dt*lambdaForce(i,j)/atomicMass_(i); + } + } + } + } + + //--------------------------------------------------------- + // compute_lambda_force: + // computes the atomic force applied by lambda + //--------------------------------------------------------- + void ThermostatGlc::compute_lambda_force(double * const * atomicQuantity, + DENS_MAT & lambdaForce) + { + if (nLocal_>0) { + // scaled prolongation to (unique) nodes + DENS_MAT lambdaAtom(nLocal_,1); + atcTransfer_->prolong(lambda_,lambdaAtom); + + // explicit update + for (int i = 0; i < nLocal_; i++) { + for (int j = 0; j < nsd_; j++) { + lambdaForce(i,j) = -lambdaAtom(i,0)*atomicQuantity[internalToAtom_(i)][j]; + } + } + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatPower + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and thermostat data + //-------------------------------------------------------- + ThermostatPower::ThermostatPower(Thermostat * thermostat) : + ThermostatGlc(thermostat), + nodalTemperature_(atcTransfer_->get_field(TEMPERATURE)), + nodalAtomicTemperature_(atcTransfer_->get_atomic_field(TEMPERATURE)), + heatSource_(atcTransfer_->get_atomic_source(TEMPERATURE)), + nodalWeights_(atcTransfer_->get_shape_function_weights()), + myAtomicVelocity_(NULL), + f_(atcTransfer_->get_f()), + nLocalTotal_(0), + lambdaMaxIterations_(3), + lambdaTolerance_(1.e-14), + filterCoefficient_(1.), + Nstar_(atcTransfer_->get_shape_function_ghost_overlap()), + ghostToAtom_(atcTransfer_->get_ghost_to_atom_map()) + { + deltaTemperature_.reset(nNodes_,1); + deltaNodalAtomicTemperature_.reset(nNodes_,1); + nodalTemperatureRoc_.reset(nNodes_,1); + + // sets up time filter for cases where variables temporally filtered + TimeFilterManager * timeFilterManager = atcTransfer_->get_time_filter_manager(); + if (!timeFilterManager->end_equilibrate()) { + // NOTE do we need a good initial guess for lambdaForce_? + nodalAtomicLambdaPower_ = 0.; + lambdaPowerFiltered_ = 0.; + timeFilter_->initialize(lambdaPowerFiltered_); + } + } + + //-------------------------------------------------------- + // Destructor + // delete thermostat data + //-------------------------------------------------------- + ThermostatPower::~ThermostatPower() + { + destroy(); + } + + //-------------------------------------------------------- + // destroy + // deletes allocated memory + //-------------------------------------------------------- + void ThermostatPower::destroy() + { + if (myAtomicVelocity_) { + for (int i = 0; i < nLocalTotal_; i++) + delete [] myAtomicVelocity_[i]; + delete [] myAtomicVelocity_; + } + } + + //-------------------------------------------------------- + // reset_nlocal: + // resizes myAtomicVelocity force if necessary + //-------------------------------------------------------- + void ThermostatPower::reset_nlocal() + { + destroy(); + ThermostatGlc::reset_nlocal(); + nLocalTotal_ = atcTransfer_->get_nlocal_total(); + if (nLocal_ > 0) { + myAtomicVelocity_ = new double*[nLocalTotal_]; + for (int i = 0; i < nLocalTotal_; i++) + myAtomicVelocity_[i] = new double[nsd_]; + } + } + + //-------------------------------------------------------- + // apply_predictor: + // apply the thermostat to the atoms in the first step + // of the Verlet algorithm + //-------------------------------------------------------- + void ThermostatPower::apply_pre_predictor(double dt) + { + // initialize delta temperatures + deltaTemperature_ = -1.*nodalTemperature_; + deltaNodalAtomicTemperature_ = -1.*nodalAtomicTemperature_; + + // apply lambda force to atoms and compute instantaneous lambda power + apply_to_atoms(v_,lambdaForce_,dt); + + // update nodal variables + update_nodal_quantities_predictor(dt); + } + + //-------------------------------------------------------- + // update_nodal_quantities_predictor: + // updates all needed nodal quantities after the + // predictor step + //-------------------------------------------------------- + void ThermostatPower::update_nodal_quantities_predictor(double dt) + { + atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_, + nodalTemperatureRoc_, + TEMPERATURE); + DENS_MAT boundaryFluxRoc(nNodes_,1); + atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE], + boundaryFluxRoc, + TEMPERATURE); + nodalTemperatureRoc_ += boundaryFluxRoc; + + // update FE temperature + nodalTemperature_ += dt*nodalTemperatureRoc_; + + // finalize filtered lambda power + timeFilter_->apply_pre_step1(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the thermostat to the atoms in the first part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void ThermostatPower::apply_pre_corrector(double dt) + { + // copy velocities over into temporary storage + if (nLocal_ > 0) { + for (int i = 0; i < nLocalTotal_; i++) + for (int j = 0; j < nsd_; j++) + myAtomicVelocity_[i][j] = v_[i][j]; + } + + // compute predicted changes in FE and nodal atomic temperatures + compute_delta_nodal_atomic_temperature(dt); + DENS_MAT myDeltaTemperature(deltaTemperature_); + deltaTemperature_ += nodalTemperature_; + + // set up rhs for lambda equation + DENS_MAT rhs(nNodes_,1); + set_thermostat_rhs(rhs,dt); + + // solve linear system for lambda guess + solve_for_lambda(rhs); + + // iterate to solution + iterate_lambda(rhs,v_,dt); + + // compute force applied by lambda + compute_lambda_force(v_,lambdaForce_); + + // apply lambda force to atoms and compute instantaneous lambda power + apply_to_atoms(myAtomicVelocity_,lambdaForce_,dt); + + // update nodal variables + update_nodal_quantities_pre_corrector(dt); + + // reset temporary variables + deltaTemperature_ = myDeltaTemperature; + } + + //-------------------------------------------------------- + // compute_delta_nodal_atomic_temperature: + // computes the change in the nodal atomic temperature + // that has occured over the past timestep + //-------------------------------------------------------- + void ThermostatPower::compute_delta_nodal_atomic_temperature(double dt) + { + // set delta temperatures based on predicted atomic velocities + DENS_MAT atomicTemperature(nLocal_,1); + atcTransfer_->compute_atomic_temperature(atomicTemperature, v_); + DENS_MAT nodalAtomicTemperature(nNodes_,1); + // NOTE change this when we use physical mass matrices + atcTransfer_->restrict(atomicTemperature,nodalAtomicTemperature); + deltaNodalAtomicTemperature_ += nodalAtomicTemperature; + } + + //-------------------------------------------------------- + // update_nodal_quantities_pre_corrector: + // updates all needed nodal quantities after the + // pre-corrector step + //-------------------------------------------------------- + void ThermostatPower::update_nodal_quantities_pre_corrector(double dt) + { + atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_, + nodalTemperatureRoc_, + TEMPERATURE); + DENS_MAT boundaryFluxRoc(nNodes_,1); + atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE], + boundaryFluxRoc, + TEMPERATURE); + nodalTemperatureRoc_ += boundaryFluxRoc; + + // update FE temperature + nodalTemperature_ += dt*nodalTemperatureRoc_; + } + + //-------------------------------------------------------- + // undo_update_nodal_quantities_pre_corrector: + // undoes the nodal quantities update from after the + // pre-corrector step + //-------------------------------------------------------- + void ThermostatPower::undo_update_nodal_quantities_pre_corrector(double dt) + { + // remove predicted power effects + nodalTemperature_ -= dt*nodalTemperatureRoc_; + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the thermostat to the atoms in the second part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void ThermostatPower::apply_post_corrector(double dt) + { + // remove predicted power effects + undo_update_nodal_quantities_pre_corrector(dt); + + // set delta FE temperature + deltaTemperature_ += nodalTemperature_; + + // set up rhs for lambda equation + DENS_MAT rhs(nNodes_,1); + set_thermostat_rhs(rhs,dt); + + // solve linear system for lambda guess + solve_for_lambda(rhs); + + // iterate to solution + iterate_lambda(rhs,myAtomicVelocity_,dt); + + // compute force applied by lambda + compute_lambda_force(myAtomicVelocity_,lambdaForce_); + + // apply lambda force to atoms and compute instantaneous lambda power + apply_to_atoms(v_,lambdaForce_,dt); + + // update nodal variables + update_nodal_quantities_post_corrector(dt); + } + + //-------------------------------------------------------- + // update_nodal_quantities_post_corrector: + // updates all needed nodal quantities after the + // post-corrector step + //-------------------------------------------------------- + void ThermostatPower::update_nodal_quantities_post_corrector(double dt) + { + // finalize filtered lambda power + timeFilter_->apply_post_step1(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt); + + atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_, + nodalTemperatureRoc_, + TEMPERATURE); + DENS_MAT boundaryFluxRoc(nNodes_,1); + atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE], + boundaryFluxRoc, + TEMPERATURE); + nodalTemperatureRoc_ += boundaryFluxRoc; + + // update FE temperature + nodalTemperature_ += dt*nodalTemperatureRoc_; + } + + //-------------------------------------------------------- + // apply_to_atoms: + // add the thermostat force to the atoms and compute + // the instantaneous induced power + //-------------------------------------------------------- + void ThermostatPower::apply_to_atoms(double ** atomicVelocity, + const DENS_MAT & lambdaForce, + double dt) + { + // compute initial contributions to lambda power + DENS_VEC atomicTemperature(nLocal_); + DENS_VEC nodalAtomicTemperature(nNodes_); + atcTransfer_->compute_atomic_temperature(atomicTemperature, atomicVelocity); + atcTransfer_->restrict_unscaled(atomicTemperature,nodalAtomicTemperature); + nodalAtomicLambdaPower_ = -1.*nodalAtomicTemperature; + + ThermostatGlc::apply_to_atoms(atomicVelocity,lambdaForce,dt); + + // finalize lambda power + atcTransfer_->compute_atomic_temperature(atomicTemperature, atomicVelocity); + atcTransfer_->restrict_unscaled(atomicTemperature,nodalAtomicTemperature); + nodalAtomicLambdaPower_ += nodalAtomicTemperature; + nodalAtomicLambdaPower_ = (1./dt)*nodalAtomicLambdaPower_; + } + + //--------------------------------------------------------- + // set_weights: + // set the diagonal weighting matrix to be the atomic + // temperatures + //--------------------------------------------------------- + void ThermostatPower::set_weights(DIAG_MAT & weights) + { + if (nLocalLambda_>0) { + DENS_VEC weightVector(nLocal_); + DENS_VEC maskedWeightVector(nLocalLambda_); + atcTransfer_->compute_atomic_temperature(weightVector,v_,myAtomicVelocity_); + maskedWeightVector = internalToOverlapMap_*weightVector; + weights.reset(maskedWeightVector); + } + } + + //-------------------------------------------------------- + // set_thermostat_rhs: + // sets up the right-hand side including boundary + // fluxes (coupling & prescribed), heat sources, and + // fixed (uncoupled) nodes + //-------------------------------------------------------- + void ThermostatPower::set_thermostat_rhs(DENS_MAT & rhs_nodes, + double dt) + { + // NOTE is the scaling right? (a) E/t/kB & (b) T/t/kB + // only tested with flux != 0 + ess bc = 0 + + // (a) for flux based : + // form rhs : 2/3kB * W_I^-1 * \int N_I r dV + // vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I + // fluxes are set in ATC transfer + // NOTE change coef and nodalWeights when physical mass matrices are used + // and remove rhoCp + double rhoCp = LammpsInterface::instance()->heat_capacity(); + double k_boltzmann = LammpsInterface::instance()->kBoltzmann(); + double coef = filterCoefficient_*rhoCp*2./(nsd_*k_boltzmann); + rhs_nodes = coef*heatSource_*nodalWeights_; + + // (b) for essential bcs (fixed nodes) : + // form rhs : (delThetaV - delTheta)/dt + DENS_MAT rhs_nodes_prescribed(nNodes_,1); + rhs_nodes_prescribed = (1./dt)*filterCoefficient_*(deltaNodalAtomicTemperature_-deltaTemperature_); + + // replace rhs for prescribed nodes + // NOTE can we make is_fixed member data? + for (int i = 0; i < nNodes_; i++) { + if (isFixedNode_(i,0)) { + rhs_nodes(i,0) = rhs_nodes_prescribed(i,0); + } + } + } + + //-------------------------------------------------------- + // iterate_lambda: + // iteratively solves the equations for lambda + // for the higher order dt corrections, assuming + // an initial guess for lambda + //-------------------------------------------------------- + void ThermostatPower::iterate_lambda(const MATRIX & rhs, + double * const * atomicVelocity, + double dt) + { + DENS_VEC lambdaOverlap(nNodeOverlap_); + atcTransfer_->map_unique_to_overlap(lambda_, lambdaOverlap); + + DENS_VEC atomicTemperature; + DENS_MAT lambdaAtom; + DENS_MAT lambdaSquaredT; + if (nLocalLambda_>0) { + lambdaAtom.reset(nLocal_,1); + lambdaSquaredT.reset(nLocal_,1); + atomicTemperature.reset(nLocal_); + atcTransfer_->compute_atomic_temperature(atomicTemperature, atomicVelocity); + } + + DENS_MAT lambdaOld(nNodes_,1); + DENS_MAT rhsOverlap(nNodeOverlap_,1); + DENS_MAT rhsTotalLocal(nNodes_,1); + DENS_MAT rhsTotal(nNodes_,1); + // solve assuming we get initial guess for lambda + for (int i = 0; i < lambdaMaxIterations_; ++i) { + lambdaOld = lambda_; + + // assemble quadratic term + if (nLocalLambda_ > 0) { + atcTransfer_->prolong_scaled(lambda_, lambdaAtom); + for (int i = 0; i < nLocal_; i++) { + lambdaSquaredT(i,0) = lambdaAtom(i,0)*lambdaAtom(i,0)*atomicTemperature(i); + } + rhsOverlap = (dt/4.)*shapeFunctionMatrix_.transMat(internalToOverlapMap_*lambdaSquaredT); + atcTransfer_->map_overlap_to_unique(rhsOverlap,rhsTotalLocal); + } + LammpsInterface::instance()->allsum(rhsTotalLocal.get_ptr(),rhsTotal.get_ptr(),rhsTotal.size()); + + // the system with the new rhs + rhsTotal += rhs; + solve_for_lambda(rhsTotal); + + // check convergence + DENS_MAT difference = lambda_-lambdaOld; + if (difference.col_norm()/lambdaOld.col_norm() < lambdaTolerance_) break; + + } + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatHoover + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and thermostat data + //-------------------------------------------------------- + ThermostatHoover::ThermostatHoover(Thermostat * thermostat) : + ThermostatPower(thermostat), + nodeToOverlapMap_(atcTransfer_->get_node_to_overlap_map()) + { + lambdaForceHoover_.reset(nLocal_,nsd_); + } + + //-------------------------------------------------------- + // reset_nlocal: + // resizes Hoover lambda force if necessary + //-------------------------------------------------------- + void ThermostatHoover::reset_nlocal() + { + ThermostatPower::reset_nlocal(); + if (nLocal_ > 0) + lambdaForceHoover_.reset(nLocal_,nsd_); + } + + //-------------------------------------------------------- + // apply_predictor: + // apply the thermostat to the atoms in the first step + // of the Verlet algorithm + //-------------------------------------------------------- + void ThermostatHoover::apply_pre_predictor(double dt) + { + ThermostatPower::apply_pre_predictor(dt); + + // apply lambda force to atoms and compute instantaneous lambda power + DENS_VEC myNodalAtomicLambdaPower(nodalAtomicLambdaPower_); + apply_to_atoms(v_,lambdaForceHoover_,dt); + + // compute nodal atomic power from Hoover coupling + // only add in contribution to uncoupled nodes + if (atcTransfer_->use_localized_lambda()) + add_to_lambda_power_predictor(dt); + else + nodalAtomicLambdaPower_ = 0.; + + // finish updating lambda power + nodalAtomicLambdaPower_ += myNodalAtomicLambdaPower; + } + + //-------------------------------------------------------- + // add_to_lambda_power_predictor: + // adds Hoover contributions to the lambda power term + //-------------------------------------------------------- + void ThermostatHoover::add_to_lambda_power_predictor(double dt) + { + atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_, + nodalTemperatureRoc_, + TEMPERATURE); + + // update FE temperature + for (int i = 0; i < nNodes_; ++i) { + if (nodeToOverlapMap_(i)==-1) + nodalTemperature_(i,0) += dt*nodalTemperatureRoc_(i,0); + else + nodalAtomicLambdaPower_(i) = 0.; + } + + // finalize filtered lambda power + timeFilter_->apply_pre_step2(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the thermostat to the atoms in the first part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void ThermostatHoover::apply_pre_corrector(double dt) + { + ThermostatPower::apply_pre_corrector(dt); + + // set up Hoover rhs + DENS_MAT myDeltaTemperature(deltaTemperature_); + deltaTemperature_ += nodalTemperature_; + DENS_MAT rhs(nNodes_,1); + set_hoover_rhs(rhs,dt); + + // solve linear system for lambda + solve_for_lambda(rhs); + + // iterate to solution + iterate_lambda(rhs,v_,dt); + + // compute force applied by lambda + compute_lambda_force(v_,lambdaForceHoover_); + + // apply the force to the atoms + DENS_VEC myNodalAtomicLambdaPower(nodalAtomicLambdaPower_); + apply_to_atoms(myAtomicVelocity_,lambdaForceHoover_,dt); + + // compute nodal atomic power from Hoover coupling + // only add in contribution to uncoupled nodes + if (atcTransfer_->use_localized_lambda()) + add_to_lambda_power_pre_corrector(dt); + else + nodalAtomicLambdaPower_ = 0.; + + // finish updating lambda power + nodalAtomicLambdaPower_ += myNodalAtomicLambdaPower; + + // reset temporary variables + deltaTemperature_ = myDeltaTemperature; + } + + //-------------------------------------------------------- + // add_to_lambda_power_pre_corrector: + // adds Hoover contributions to the lambda power term + //-------------------------------------------------------- + void ThermostatHoover::add_to_lambda_power_pre_corrector(double dt) + { + DENS_MAT myNodalTemperatureRoc(nodalTemperatureRoc_); + atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_, + nodalTemperatureRoc_, + TEMPERATURE); + + // update FE temperature + for (int i = 0; i < nNodes_; ++i) { + if (nodeToOverlapMap_(i)==-1) + nodalTemperature_(i,0) += dt*nodalTemperatureRoc_(i,0); + else { + nodalTemperatureRoc_(i,0) = 0.; + nodalAtomicLambdaPower_(i) = 0.; + } + } + + // correct nodal temperature rate of change + nodalTemperatureRoc_ += myNodalTemperatureRoc; + } + + //-------------------------------------------------------- + // apply_post_corrector: + // apply the thermostat to the atoms in the second part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void ThermostatHoover::apply_post_corrector(double dt) + { + ThermostatPower::apply_post_corrector(dt); + + // set up Hoover rhs + DENS_MAT rhs(nNodes_,1); + set_hoover_rhs(rhs,dt); + + // solve linear system for lambda + solve_for_lambda(rhs); + + // iterate to solution + iterate_lambda(rhs,myAtomicVelocity_,dt); + + // compute force applied by lambda + compute_lambda_force(myAtomicVelocity_,lambdaForceHoover_); + + // apply the force to the atoms + DENS_VEC myNodalAtomicLambdaPower(nodalAtomicLambdaPower_); + apply_to_atoms(v_,lambdaForceHoover_,dt); + + // compute nodal atomic power from Hoover coupling + // only add in contribution to uncoupled nodes + if (atcTransfer_->use_localized_lambda()) + add_to_lambda_power_post_corrector(dt); + else + nodalAtomicLambdaPower_ = 0.; + + // finish updating lambda power + nodalAtomicLambdaPower_ += myNodalAtomicLambdaPower; + } + + //-------------------------------------------------------- + // add_to_lambda_power_post_corrector: + // adds Hoover contributions to the lambda power term + //-------------------------------------------------------- + void ThermostatHoover::add_to_lambda_power_post_corrector(double dt) + { + // finalize filtered lambda power + timeFilter_->apply_post_step2(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt); + + atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_, + nodalTemperatureRoc_, + TEMPERATURE); + + // update FE temperature + for (int i = 0; i < nNodes_; ++i) { + if (nodeToOverlapMap_(i)==-1) + nodalTemperature_(i,0) += dt*nodalTemperatureRoc_(i,0); + else + nodalAtomicLambdaPower_(i) = 0.; + } + } + + //-------------------------------------------------------- + // set_hoover_rhs: + // sets up the right-hand side for Hoover coupling with + // boundary nodes + //-------------------------------------------------------- + void ThermostatHoover::set_hoover_rhs(DENS_MAT & rhs_nodes, + double dt) + { + // form rhs : (delThetaV - delTheta)/dt + rhs_nodes = (1./dt)*filterCoefficient_*(deltaNodalAtomicTemperature_-deltaTemperature_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatPowerFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and thermostat data + //-------------------------------------------------------- + ThermostatPowerFiltered::ThermostatPowerFiltered(Thermostat * thermostat) : + ThermostatPower(thermostat) + { + // do nothing + } + + //-------------------------------------------------------- + // update_nodal_quantities_predictor: + // updates all needed nodal quantities after the + // predictor step + //-------------------------------------------------------- + void ThermostatPowerFiltered::update_nodal_quantities_predictor(double dt) + { + // update nodal atomic temperature + //atcTransfer_->apply_inverse_mass_matrix(lambdaPowerFiltered_, + // nodalTemperatureRoc_, + // TEMPERATURE); + // NOTE temporary until new mass matrices are used + DIAG_MAT & atomicWeights(atcTransfer_->get_atomic_weights()); + nodalTemperatureRoc_ = (1./atomicWeights(0,0))*nodalWeights_*lambdaPowerFiltered_; + nodalAtomicTemperature_ += dt*nodalTemperatureRoc_; + + // update FE temperature + atcTransfer_->apply_inverse_mass_matrix(lambdaPowerFiltered_, + nodalTemperatureRoc_, + TEMPERATURE); + //DENS_MAT boundaryFluxRoc(nNodes_,1); + //atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE], + // boundaryFluxRoc, + // TEMPERATURE); + //nodalTemperatureRoc_ += boundaryFluxRoc; + nodalTemperature_ += 0.5*dt*nodalTemperatureRoc_; + + // finalize filtered lambda power + timeFilter_->apply_pre_step1(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt); + } + + //-------------------------------------------------------- + // compute_delta_nodal_atomic_temperature: + // computes the change in the nodal atomic temperature + // that has occured over the past timestep + //-------------------------------------------------------- + void ThermostatPowerFiltered::compute_delta_nodal_atomic_temperature(double dt) + { + // compute next lambda power assuming zero new force + //DENS_VEC myLambdaPowerFiltered(lambdaPowerFiltered_); + DENS_VEC zeroNodalLambdaPower(nNodes_); + timeFilter_->apply_post_step1(lambdaPowerFiltered_,zeroNodalLambdaPower,dt); + + // update nodal atomic temperature + //atcTransfer_->apply_inverse_mass_matrix(lambdaPowerFiltered_, + // nodalTemperatureRoc_, + // TEMPERATURE); + // NOTE temporary until new mass matrices are used + DIAG_MAT & atomicWeights(atcTransfer_->get_atomic_weights()); + nodalTemperatureRoc_ = (1./atomicWeights(0,0))*nodalWeights_*lambdaPowerFiltered_; + nodalAtomicTemperature_ += dt*nodalTemperatureRoc_; + + // update FE temperature + atcTransfer_->apply_inverse_mass_matrix(lambdaPowerFiltered_, + nodalTemperatureRoc_, + TEMPERATURE); + nodalTemperature_ += .5*dt*nodalTemperatureRoc_; + + deltaNodalAtomicTemperature_ += nodalAtomicTemperature_; + } + + //-------------------------------------------------------- + // update_nodal_quantities_pre_corrector: + // updates all needed nodal quantities after the + // pre-corrector step + //-------------------------------------------------------- + void ThermostatPowerFiltered::update_nodal_quantities_pre_corrector(double dt) + { + atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_, + nodalTemperatureRoc_, + TEMPERATURE); + + double filterCoefficient = timeFilter_->get_unfiltered_coefficient_post_s1(dt); + + //DENS_MAT boundaryFluxRoc(nNodes_,1); + //atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE], + // boundaryFluxRoc, + // TEMPERATURE); + //nodalTemperatureRoc_ = boundaryFluxRoc + filterCoefficient*nodalTemperatureRoc_; + nodalTemperatureRoc_ = 0.5*filterCoefficient*nodalTemperatureRoc_; + // update FE temperature + nodalTemperature_ += dt*nodalTemperatureRoc_; + } + +// //-------------------------------------------------------- +// // undo_update_nodal_quantities_pre_corrector: +// // undoes the update of all nodal quantities from after +// // the pre-corrector step +// //-------------------------------------------------------- +// void ThermostatPowerFiltered::undo_update_nodal_quantities_pre_corrector(double dt) +// { +// double filterCoefficient = timeFilter_->get_unfiltered_coefficient_pre_s2(dt); + +// // update FE temperature +// nodalTemperature_ -= dt*filterCoefficient*nodalTemperatureRoc_; +// } + + //-------------------------------------------------------- + // update_nodal_quantities_post_corrector: + // updates all needed nodal quantities after the + // post-corrector step + //-------------------------------------------------------- + void ThermostatPowerFiltered::update_nodal_quantities_post_corrector(double dt) + { + // finalize filtered lambda power + timeFilter_->apply_post_step2(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt); + double filterCoefficient = timeFilter_->get_unfiltered_coefficient_post_s1(dt); + + // update nodal atomic temperature + //atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_, + // nodalTemperatureRoc_, + // TEMPERATURE); + // NOTE temporary until new mass matrices are used + DIAG_MAT & atomicWeights(atcTransfer_->get_atomic_weights()); + nodalTemperatureRoc_ = (1./atomicWeights(0,0))*nodalWeights_*nodalAtomicLambdaPower_; + //nodalTemperatureRoc_.print("NTR"); + nodalAtomicTemperature_ += filterCoefficient*dt*nodalTemperatureRoc_; + + // update FE temperature + atcTransfer_->apply_inverse_mass_matrix(nodalAtomicLambdaPower_, + nodalTemperatureRoc_, + TEMPERATURE); + //DENS_MAT boundaryFluxRoc(nNodes_,1); + //atcTransfer_->apply_inverse_mass_matrix(boundaryFlux_[TEMPERATURE], + // boundaryFluxRoc, + // TEMPERATURE); + //nodalTemperatureRoc_ = boundaryFluxRoc + filterCoefficient*nodalTemperatureRoc_; + nodalTemperatureRoc_ = .5*filterCoefficient*nodalTemperatureRoc_; + nodalTemperature_ += dt*nodalTemperatureRoc_; + } + + //-------------------------------------------------------- + // set_thermostat_rhs: + // sets up the right-hand side including boundary + // fluxes (coupling & prescribed), heat sources, and + // fixed (uncoupled) nodes + //-------------------------------------------------------- + void ThermostatPowerFiltered::set_thermostat_rhs(DENS_MAT & rhs_nodes, + double dt) + { + filterCoefficient_ = 1./(timeFilter_->get_unfiltered_coefficient_post_s1(dt)); + // NOTE change the 0.5 when we change how the lambda power is accounted + // NOTE change rhoCp^-1 when physical mass matrices are used + double rhoCp = LammpsInterface::instance()->heat_capacity(); + heatSource_ += (0.5)*lambdaPowerFiltered_; + ThermostatPower::set_thermostat_rhs(rhs_nodes,dt); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatPowerVerlet + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and thermostat data + //-------------------------------------------------------- + ThermostatPowerVerlet::ThermostatPowerVerlet(Thermostat * thermostat) : + ThermostatGlc(thermostat), + nodalTemperatureRoc_(atcTransfer_->get_field_roc(TEMPERATURE)), + heatSource_(atcTransfer_->get_atomic_source(TEMPERATURE)), + f_(atcTransfer_->get_f()) + { + // sets up time filter for cases where variables temporally filtered + TimeFilterManager * timeFilterManager = atcTransfer_->get_time_filter_manager(); + if (!timeFilterManager->end_equilibrate()) { + nodalAtomicLambdaPower_ = 0.; + lambdaPowerFiltered_ = 0.; + timeFilter_->initialize(lambdaPowerFiltered_); + } + } + + //-------------------------------------------------------- + // apply_predictor: + // apply the thermostat to the atoms in the first step + // of the Verlet algorithm + //-------------------------------------------------------- + void ThermostatPowerVerlet::apply_pre_predictor(double dt) + { + compute_thermostat(dt); + + // apply lambda force to atoms + apply_to_atoms(v_,lambdaForce_,dt); + } + + //-------------------------------------------------------- + // apply_pre_corrector: + // apply the thermostat to the atoms in the first part + // of the corrector step of the Verlet algorithm + //-------------------------------------------------------- + void ThermostatPowerVerlet::apply_pre_corrector(double dt) + { + compute_thermostat(dt); + + // apply lambda force to atoms + apply_to_atoms(v_,lambdaForce_,dt); + } + + //-------------------------------------------------------- + // add_to_rhs: + // determines what if any contributions to the + // finite element equations are needed for + // consistency with the thermostat + //-------------------------------------------------------- + void ThermostatPowerVerlet::add_to_rhs(FIELDS & rhs) + { + rhs[TEMPERATURE] += nodalAtomicLambdaPower_ + boundaryFlux_[TEMPERATURE]; + } + + //-------------------------------------------------------- + // set_thermostat_rhs: + // sets up the right-hand side including boundary + // fluxes (coupling & prescribed), heat sources, and + // fixed (uncoupled) nodes + //-------------------------------------------------------- + void ThermostatPowerVerlet::set_thermostat_rhs(DENS_MAT & rhs_nodes) + { + // (a) for flux based : + // form rhs : \int N_I r dV + // vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I + // fluxes are set in ATC transfer + rhs_nodes = heatSource_; + + // (b) for ess. bcs + // form rhs : {sum_a (2 * N_Ia * v_ia * f_ia) - (dtheta/dt)_I} + DENS_MAT atomicPower; + atcTransfer_->compute_atomic_power(atomicPower, v_, f_); + DENS_MAT rhs_nodes_prescribed(nNodes_,1); + atcTransfer_->restrict_volumetric_quantity(atomicPower, rhs_nodes_prescribed); + rhs_nodes_prescribed -= 0.5*(mdMassMatrix_*nodalTemperatureRoc_); + + // replace rhs for prescribed nodes + for (int i = 0; i < nNodes_; i++) { + if (isFixedNode_(i,0)) { + rhs_nodes(i,0) = rhs_nodes_prescribed(i,0); + } + } + } + + //-------------------------------------------------------- + // compute_thermostat: + // sets up and solves the thermostat equations since + // they are the same at different parts of the time + // step + //-------------------------------------------------------- + void ThermostatPowerVerlet::compute_thermostat(double dt) + { + // set up rhs + DENS_MAT rhs(nNodes_,1); + set_thermostat_rhs(rhs); + + // solve linear system for lambda + solve_for_lambda(rhs); + + // compute force applied by lambda + compute_lambda_force(v_,lambdaForce_); + + // compute nodal atomic power + compute_nodal_lambda_power(dt); + } + + //-------------------------------------------------------- + // compute_nodal_lambda_power: + // determines the power exerted by the thermostat at + // each FE node + //-------------------------------------------------------- + void ThermostatPowerVerlet::compute_nodal_lambda_power(double dt) + { + DENS_VEC atomicLambdaPower; + atcTransfer_->compute_atomic_power(atomicLambdaPower,v_,lambdaForce_); + atcTransfer_->restrict_volumetric_quantity(atomicLambdaPower,nodalAtomicLambdaPower_); + nodalAtomicLambdaPower_ *= 2.; // accounts for kinetic definition of temperature + timeFilter_->apply_pre_step1(lambdaPowerFiltered_,nodalAtomicLambdaPower_,dt); + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void ThermostatPowerVerlet::output(double dt, OUTPUT_LIST & outputData) + { + outputData["lambda"] = &(lambda_); + outputData["nodalLambdaPower"] = &(nodalAtomicLambdaPower_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatHooverVerlet + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and thermostat data + //-------------------------------------------------------- + ThermostatHooverVerlet::ThermostatHooverVerlet(Thermostat * thermostat) : + ThermostatPowerVerlet(thermostat), + nodeToOverlapMap_(atcTransfer_->get_node_to_overlap_map()) + { + // do nothing + } + + //-------------------------------------------------------- + // compute_thermostat: + // sets up and solves the thermostat equations since + // they are the same at different parts of the time + // step + //-------------------------------------------------------- + void ThermostatHooverVerlet::compute_thermostat(double dt) + { + // apply prescribed/extrinsic sources and fixed nodes + ThermostatPowerVerlet::compute_thermostat(dt); + + // set up Hoover rhs + DENS_MAT rhs(nNodes_); + set_hoover_rhs(rhs); + + // solve linear system for lambda + solve_for_lambda(rhs); + + // compute force applied by lambda + DENS_MAT lambdaForceHoover(nLocal_,nsd_); + compute_lambda_force(v_,lambdaForceHoover); + lambdaForce_ += lambdaForceHoover; + + // compute nodal atomic power from Hoover coupling + // only add in contribution to uncoupled nodes + if (atcTransfer_->use_localized_lambda()) + add_to_lambda_power(lambdaForceHoover,dt); + } + + //-------------------------------------------------------- + // set_hoover_rhs: + // sets up the right-hand side for fixed value, + // i.e. Hoover coupling + //-------------------------------------------------------- + void ThermostatHooverVerlet::set_hoover_rhs(DENS_MAT & rhs_nodes) + { + // form rhs : sum_a ( N_Ia * v_ia * f_ia) - 0.5*M_MD*(dtheta/dt)_I + DENS_MAT atomicPower(atcTransfer_->get_nlocal(),1); + atcTransfer_->compute_atomic_power(atomicPower, v_, f_); + atcTransfer_->restrict_volumetric_quantity(atomicPower, rhs_nodes); + rhs_nodes -= 0.5*(mdMassMatrix_*nodalTemperatureRoc_); + } + + //-------------------------------------------------------- + // add_to_nodal_lambda_power: + // determines the power exerted by the Hoover + // thermostat at each FE node + //-------------------------------------------------------- + void ThermostatHooverVerlet::add_to_lambda_power(DENS_MAT & myLambdaForce, + double dt) + { + DENS_VEC atomicLambdaPower; + atcTransfer_->compute_atomic_power(atomicLambdaPower,v_,myLambdaForce); + DENS_VEC myNodalLambdaPower(nNodes_); + atcTransfer_->restrict_volumetric_quantity(atomicLambdaPower,myNodalLambdaPower); + myNodalLambdaPower *= 2.; + for (int i = 0; i < nNodes_; ++i) { + if (nodeToOverlapMap_(i)==-1) + nodalAtomicLambdaPower_(i) += myNodalLambdaPower(i); + else + myNodalLambdaPower(i) = 0.; + } + timeFilter_->apply_post_step1(lambdaPowerFiltered_,myNodalLambdaPower,dt); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatPowerVerletFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and thermostat data + //-------------------------------------------------------- + ThermostatPowerVerletFiltered::ThermostatPowerVerletFiltered(Thermostat * thermostat) : + ThermostatPowerVerlet(thermostat), + nodalTemperature2Roc_(atcTransfer_->get_field_2roc(TEMPERATURE)), + fieldsRoc_(atcTransfer_->get_fields_roc()), + filterScale_((atcTransfer_->get_time_filter_manager())->get_filter_scale()) + { + heatSourceRoc_.reset(nNodes_,1); + fluxRoc_[TEMPERATURE].reset(nNodes_,1); + } + + //-------------------------------------------------------- + // compute_boundary_flux + // also sets time derivatives of boundary flux and + // heat sources + //-------------------------------------------------------- + void ThermostatPowerVerletFiltered::compute_boundary_flux(FIELDS & fields) + { + ThermostatPowerVerlet::compute_boundary_flux(fields); + + // compute boundary flux rate of change + fluxRoc_[TEMPERATURE] = 0.; + atcTransfer_->compute_boundary_flux(fieldMask_, + fieldsRoc_, + fluxRoc_); + + // NOTE add time derivatives from sources, currently not implemented + + // compute extrinsic model rate of change + (atcTransfer_->get_extrinsic_model_manager()).set_sources(fieldsRoc_,fluxRoc_); + heatSourceRoc_ = fluxRoc_[TEMPERATURE]; + + + } + + //-------------------------------------------------------- + // add_to_rhs: + // determines what if any contributions to the + // finite element equations are needed for + // consistency with the thermostat + //-------------------------------------------------------- + void ThermostatPowerVerletFiltered::add_to_rhs(std::map > & rhs) + { + rhs[TEMPERATURE] += lambdaPowerFiltered_ + boundaryFlux_[TEMPERATURE]; + } + + //-------------------------------------------------------- + // set_thermostat_rhs: + // sets up the right-hand side including boundary + // fluxes (coupling & prescribed), heat sources, and + // fixed (uncoupled) nodes + //-------------------------------------------------------- + void ThermostatPowerVerletFiltered::set_thermostat_rhs(DENS_MAT & rhs_nodes) + { + // (a) for flux based : + // form rhs : \int N_I r dV + // vs Wagner, CMAME, 2008 eq(24) RHS_I = 2/(3kB) flux_I + // fluxes are set in ATC transfer +// for (int i = 0; i < nNodes_; i++) { +// rhs_nodes(i) = heatSource_(i,0) + filterScale_*heatSourceRoc_(i,0); +// } + rhs_nodes = heatSource_ + filterScale_*heatSourceRoc_; + + // (b) for ess. bcs + // form rhs : {sum_a (N_Ia * v_ia * f_ia) - 0.5*(dtheta/dt)_I} + DENS_MAT atomicPower; + atcTransfer_->compute_atomic_power(atomicPower, v_, f_); + DENS_MAT rhs_nodes_prescribed(nNodes_,1); + atcTransfer_->restrict_volumetric_quantity(atomicPower, rhs_nodes_prescribed); +// DENS_VEC myTemperatureRoc(nNodes_); +// for (int i = 0; i < nNodes_; i++) { +// myTemperatureRoc(i) = nodalTemperatureRoc_(i,0) + filterScale_*nodalTemperature2Roc_(i,0); +// } + rhs_nodes_prescribed -= 0.5*(mdMassMatrix_*(nodalTemperatureRoc_ + filterScale_*nodalTemperature2Roc_)); + + // replace rhs for prescribed nodes + for (int i = 0; i < nNodes_; i++) { + if (isFixedNode_(i,0)) { + rhs_nodes(i,0) = rhs_nodes_prescribed(i,0); + } + } + } + + //-------------------------------------------------------- + // output: + // adds all relevant output to outputData + //-------------------------------------------------------- + void ThermostatPowerVerletFiltered::output(double dt, OUTPUT_LIST & outputData) + { + outputData["lambda"] = &(lambda_); + outputData["nodalLambdaPower"] = &(lambdaPowerFiltered_); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class HooverVerletFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab references to ATC and thermostat data + //-------------------------------------------------------- + ThermostatHooverVerletFiltered::ThermostatHooverVerletFiltered(Thermostat * thermostat) : + ThermostatPowerVerletFiltered(thermostat), + nodeToOverlapMap_(atcTransfer_->get_node_to_overlap_map()) + { + // do nothing + } + + //-------------------------------------------------------- + // compute_thermostat: + // sets up and solves the thermostat equations since + // they are the same at different parts of the time + // step + //-------------------------------------------------------- + void ThermostatHooverVerletFiltered::compute_thermostat(double dt) + { + // apply prescribed/extrinsic sources and fixed nodes + ThermostatPowerVerletFiltered::compute_thermostat(dt); + + // set up Hoover rhs + DENS_MAT rhs(nNodes_,1); + set_hoover_rhs(rhs); + + // solve linear system for lambda + solve_for_lambda(rhs); + + // NOTE modify these sections to be for hoover + // compute force applied by lambda + DENS_MAT lambdaForceHoover(nLocal_,nsd_); + compute_lambda_force(v_,lambdaForceHoover); + lambdaForce_ += lambdaForceHoover; + + // compute nodal atomic power from Hoover coupling + // only add in contribution to uncoupled nodes + if (atcTransfer_->use_localized_lambda()) + add_to_lambda_power(lambdaForceHoover,dt); + } + + //-------------------------------------------------------- + // set_hoover_rhs: + // sets up the right-hand side for fixed value, + // i.e. Hoover coupling + //-------------------------------------------------------- + void ThermostatHooverVerletFiltered::set_hoover_rhs(DENS_MAT & rhs_nodes) + { + // form rhs : sum_a (N_Ia * v_ia * f_ia) - 0.5*M_MD*(dtheta/dt)_I + DENS_MAT atomicPower(atcTransfer_->get_nlocal(),1); + atcTransfer_->compute_atomic_power(atomicPower, v_, f_); + atcTransfer_->restrict_volumetric_quantity(atomicPower, rhs_nodes); +// DENS_VEC myTemperatureRoc(nNodes_); +// for (int i = 0; i < nNodes_; i++) { +// myTemperatureRoc(i) = nodalTemperatureRoc_(i,0) + filterScale_*nodalTemperature2Roc_(i,0); +// } + rhs_nodes -= 0.5*(mdMassMatrix_*(nodalTemperatureRoc_ + filterScale_*nodalTemperature2Roc_)); + } + + //-------------------------------------------------------- + // add_to_nodal_lambda_power: + // determines the power exerted by the Hoover + // thermostat at each FE node + //-------------------------------------------------------- + void ThermostatHooverVerletFiltered::add_to_lambda_power(DENS_MAT & myLambdaForce, + double dt) + { + DENS_VEC atomicLambdaPower; + atcTransfer_->compute_atomic_power(atomicLambdaPower,v_,myLambdaForce); + DENS_VEC myNodalLambdaPower(nNodes_); + atcTransfer_->restrict_volumetric_quantity(atomicLambdaPower,myNodalLambdaPower); + myNodalLambdaPower *= 2.; + for (int i = 0; i < nNodes_; ++i) { + if (nodeToOverlapMap_(i)==-1) + nodalAtomicLambdaPower_(i) += myNodalLambdaPower(i); + else + myNodalLambdaPower(i) = 0.; + } + timeFilter_->apply_post_step1(lambdaPowerFiltered_,myNodalLambdaPower,dt); + } + +}; diff --git a/lib/atc/Thermostat.h b/lib/atc/Thermostat.h new file mode 100644 index 0000000000..1073e08e1c --- /dev/null +++ b/lib/atc/Thermostat.h @@ -0,0 +1,629 @@ +/** Thermostat : a class for atom-continuum control of energy/temperature */ + +#ifndef THERMOSTAT_H +#define THERMOSTAT_H + +// ATC_Transfer headers +#include "AtomicRegulator.h" + +// other headers +#include +#include + +namespace ATC { + + /** + * @class Thermtostat + * @brief Manager class for atom-continuum control of thermal energy + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class Thermostat + //-------------------------------------------------------- + //-------------------------------------------------------- + + class Thermostat : public AtomicRegulator { + + public: + + /** thermostat types */ + enum ThermostatType { + NONE=0, + RESCALE, + HOOVER, + FLUX + }; + + // constructor + Thermostat(ATC_Transfer * atcTransfer); + + // destructor + ~Thermostat(){}; + + /** parser/modifier */ + virtual bool modify(int narg, char **arg); + + /** pre time integration */ + virtual void initialize(); + + // data access, intended for method objects + /** reset the nodal power to a prescribed value */ + void reset_lambda_power(DENS_MAT & target); + /** return the nodal power induced by lambda */ + DENS_VEC & get_nodal_atomic_lambda_power(){return nodalAtomicLambdaPower_;}; + /** return value filtered lambda */ + DENS_VEC & get_filtered_lambda_power(){return lambdaPowerFiltered_;}; + /** access to thermostat type */ + ThermostatType get_thermostat_type() const {return thermostatType_;}; + + protected: + + /** thermostat type flag */ + ThermostatType thermostatType_; + + // thermostat data + /** lambda power applied to atoms */ + DENS_VEC nodalAtomicLambdaPower_; + /** filtered lambda power */ + DENS_VEC lambdaPowerFiltered_; + + private: + + // DO NOT define this + Thermostat(); + + }; + + /** + * @class ThermostatShapeFunction + * @brief Base class for implementation of thermostat algorithms using the shape function matrices + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatShapeFunction + // base class for all thermostats of general form + // of N^T w N lambda = rhs + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ThermostatShapeFunction : public RegulatorShapeFunction { + + public: + + ThermostatShapeFunction(Thermostat * thermostat); + + ~ThermostatShapeFunction(){}; + + /** reset number of local atoms, as well as atomic data */ + virtual void reset_nlocal(); + + protected: + + // methods + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(DIAG_MAT & weights); + + // member data + /** pointer to thermostat object for data */ + Thermostat * thermostat_; + /** pointer to lammps atomic velocities */ + double ** v_; + /** MD mass matrix */ + MATRIX & mdMassMatrix_; + /** mass of ATC internal atoms on this processor */ + DENS_VEC atomicMass_; + + private: + + // DO NOT define this + ThermostatShapeFunction(); + + }; + + /** + * @class ThermostatRescale + * @brief Enforces constraint on atomic kinetic energy based on FE temperature + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatRescale + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ThermostatRescale : public ThermostatShapeFunction { + + public: + + ThermostatRescale(Thermostat * thermostat); + + ~ThermostatRescale(){}; + + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt); + + /** get data for output */ + virtual void output(double dt, OUTPUT_LIST & outputData); + + protected: + + /** apply solution to atomic quantities */ + void apply_to_atoms(double ** atomicVelocity, + double dt); + + /** FE temperature field */ + DENS_MAT & nodalTemperature_; + + private: + + // DO NOT define this + ThermostatRescale(); + + }; + + /** + * @class ThermostatGlc + * @brief Base class for implementation of thermostat algorithms based on Gaussian least constraints (GLC) + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatGlc + // base clas for all thermostats of general form of a + // Gaussian least constraint (GLC) + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ThermostatGlc : public ThermostatShapeFunction { + + public: + + ThermostatGlc(Thermostat * thermostat); + + ~ThermostatGlc(){}; + + protected: + + // methods + + /** compute force induced by lambda */ + virtual void compute_lambda_force(double * const * atomicQuantity, + DENS_MAT & lambdaForce); + + /** apply forces to atoms */ + virtual void apply_to_atoms(double ** atomicVelocity, + const DENS_MAT & lambdaForce, + double dt); + + // member data + /** pointer to a time filtering object */ + TimeFilter * timeFilter_; + + /** power induced by lambda */ + DENS_VEC & nodalAtomicLambdaPower_; + + /** filtered lambda power */ + DENS_VEC & lambdaPowerFiltered_; + + /** atomic force induced by lambda */ + DENS_MAT & lambdaForce_; + + /** bool to determine if node is fixed or not */ + Array2D & isFixedNode_; + + private: + + // DO NOT define this + ThermostatGlc(); + + }; + + /** + * @class ThermostatPower + * @brief Enforces GLC on atomic forces based on FE power + * when using fractional step time integration + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatPower + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ThermostatPower : public ThermostatGlc { + + public: + + ThermostatPower(Thermostat * thermostat); + + ~ThermostatPower(); + + /** reset number of local atoms, as well as atomic data */ + virtual void reset_nlocal(); + + /** applies thermostat to atoms in the predictor phase */ + virtual void apply_pre_predictor(double dt); + + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); + + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt); + + protected: + + // methods + /** destroys allocated memory */ + void destroy(); + + /** update the nodal quantities after predictor step */ + virtual void update_nodal_quantities_predictor(double dt); + + /** compute the change in nodal atomic temperature */ + virtual void compute_delta_nodal_atomic_temperature(double dt); + + /** update the nodal quantities after pre-corrector step */ + virtual void update_nodal_quantities_pre_corrector(double dt); + + /** undoes the update the nodal quantities after pre-corrector step */ + virtual void undo_update_nodal_quantities_pre_corrector(double dt); + + /** update the nodal quantities after post-corrector step */ + virtual void update_nodal_quantities_post_corrector(double dt); + + /** sets up appropriate rhs for thermostat equations */ + virtual void set_thermostat_rhs(DENS_MAT & rhs_nodes, + double dt); + + /** apply forces to atoms */ + virtual void apply_to_atoms(double ** atomicVelocity, + const DENS_MAT & lambdaForce, + double dt); + + /** solves the non-linear equation for lambda iteratively */ + void iterate_lambda(const MATRIX & rhs, + double * const * atomicVelocity, + double dt); + + /** set weighting factor for in matrix Nhat^T * weights * Nhat */ + virtual void set_weights(DIAG_MAT & weights); + + // data + /** reference to AtC FE temperature */ + DENS_MAT & nodalTemperature_; + + /** reference to AtC restricted atomic temperature */ + DENS_MAT & nodalAtomicTemperature_; + + /** reference to ATC sources coming from prescribed data, AtC coupling, and extrinsic coupling */ + DENS_MAT & heatSource_; + + /** reference to weights for Nhat */ + DIAG_MAT & nodalWeights_; + + /** change in FE temperature over a timestep */ + DENS_MAT deltaTemperature_; + + /** change in restricted atomic FE temperature over a timestep */ + DENS_MAT deltaNodalAtomicTemperature_; + + /** FE temperature rate of change */ + DENS_MAT nodalTemperatureRoc_; + + /** local version of velocity used as predicted final veloctiy */ + double ** myAtomicVelocity_; + + /** pointer to lammps atomic forces */ + double ** f_; + + /** number of total atoms on this processor */ + int nLocalTotal_; + + /** maximum number of iterations used in iterative solve for lambda */ + int lambdaMaxIterations_; + + /** tolerance used in iterative solve for lambda */ + double lambdaTolerance_; + + /** coefficient to account for effect of time filtering on rhs terms */ + double filterCoefficient_; + /** reference to ATC unity shape function on ghost atoms */ + SPAR_MAT & Nstar_; + + /** maps ghost atom and LAMMPS atom ids */ + Array & ghostToAtom_; + + private: + + // DO NOT define this + ThermostatPower(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatHoover + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ThermostatHoover : public ThermostatPower { + + public: + + ThermostatHoover(Thermostat * thermostat); + + ~ThermostatHoover(); + + /** reset number of local atoms, as well as atomic data */ + virtual void reset_nlocal(); + + /** applies thermostat to atoms in the predictor phase */ + virtual void apply_pre_predictor(double dt); + + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); + + /** applies thermostat to atoms in the post-corrector phase */ + virtual void apply_post_corrector(double dt); + + protected: + + // methods + /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields) {boundaryFlux_[TEMPERATURE] = 0.;}; + + /** adds Hoover power terms to nodal variables after the predictor phase */ + void add_to_lambda_power_predictor(double dt); + + /** adds Hoover power terms to nodal variables after the pre-corrector phase */ + void add_to_lambda_power_pre_corrector(double dt); + + /** adds Hoover power terms to nodal variables after the post-corrector phase */ + void add_to_lambda_power_post_corrector(double dt); + + /** sets up appropriate rhs for thermostat equations */ + virtual void set_hoover_rhs(DENS_MAT & rhs_nodes, + double dt); + + /** add Hoover contributions to lambda power */ + void add_to_lambda_power(DENS_MAT & myLambdaForce); + + // data + /** force coming from Hoover contribution */ + DENS_MAT lambdaForceHoover_; + + /** reference to ATC map from global nodes to overlap nodes */ + Array & nodeToOverlapMap_; + + private: + + // DO NOT define this + ThermostatHoover(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatPowerFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ThermostatPowerFiltered : public ThermostatPower { + + public: + + ThermostatPowerFiltered(Thermostat * thermostat); + + ~ThermostatPowerFiltered(){}; + + protected: + + /** update the nodal quantities after predictor step */ + virtual void update_nodal_quantities_predictor(double dt); + + /** compute the change in nodal atomic temperature */ + virtual void compute_delta_nodal_atomic_temperature(double dt); + + /** update the nodal quantities after pre-corrector step */ + virtual void update_nodal_quantities_pre_corrector(double dt); + + /** update the nodal quantities after post-corrector step */ + virtual void update_nodal_quantities_post_corrector(double dt); + + /** sets up appropriate rhs for thermostat equations */ + virtual void set_thermostat_rhs(DENS_MAT & rhs_nodes, + double dt); + + private: + + // DO NOT define this + ThermostatPowerFiltered(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatPowerVerlet + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ThermostatPowerVerlet : public ThermostatGlc { + + public: + + ThermostatPowerVerlet(Thermostat * thermostat); + + ~ThermostatPowerVerlet(){}; + + /** applies thermostat to atoms in the predictor phase */ + virtual void apply_pre_predictor(double dt); + + /** applies thermostat to atoms in the pre-corrector phase */ + virtual void apply_pre_corrector(double dt); + + /** get data for output */ + virtual void output(double dt, OUTPUT_LIST & outputData); + + protected: + + /** nodal temperature rate of change */ + DENS_MAT & nodalTemperatureRoc_; + + /** reference to ATC sources coming from prescribed data, AtC coupling, and extrinsic coupling */ + DENS_MAT & heatSource_; + + /** pointer to lammps atomic forces */ + double ** f_; + + /** sets up and solves thermostat equations */ + virtual void compute_thermostat(double dt); + + /** sets up appropriate rhs for thermostat equations */ + virtual void set_thermostat_rhs(DENS_MAT & rhs_nodes); + + /** computes the nodal FE power applied by the thermostat */ + virtual void compute_nodal_lambda_power(double dt); + + /** add contributions (if any) to the finite element right-hand side */ + virtual void add_to_rhs(FIELDS & rhs); + + private: + + // DO NOT define this + ThermostatPowerVerlet(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatHooverVerlet + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ThermostatHooverVerlet : public ThermostatPowerVerlet { + + public: + + ThermostatHooverVerlet(Thermostat * thermostat); + + ~ThermostatHooverVerlet(){}; + + protected: + + /** reference to ATC map from global nodes to overlap nodes */ + Array & nodeToOverlapMap_; + + /** sets up and solves thermostat equations */ + virtual void compute_thermostat(double dt); + + /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields) {boundaryFlux_[TEMPERATURE] = 0.;}; + + /** sets up Hoover component of the thermostat */ + void set_hoover_rhs(DENS_MAT & rhs_nodes); + + /** add Hoover contributions to lambda power */ + void add_to_lambda_power(DENS_MAT & myLambdaForce, + double dt); + + private: + + // DO NOT implement this + ThermostatHooverVerlet(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatPowerVerletFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ThermostatPowerVerletFiltered : public ThermostatPowerVerlet { + + public: + + ThermostatPowerVerletFiltered(Thermostat * thermostat); + + ~ThermostatPowerVerletFiltered(){}; + + /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields); + + /** get data for output */ + virtual void output(double dt, OUTPUT_LIST & outputData); + + protected: + + /** sets up appropriate rhs for thermostat equations */ + virtual void set_thermostat_rhs(DENS_MAT & rhs_nodes); + + /** add contributions (if any) to the finite element right-hand side */ + virtual void add_to_rhs(FIELDS & rhs); + + /** nodal temperature 2nd rate of change (i.e. second time derivative) */ + DENS_MAT & nodalTemperature2Roc_; + + /** reference to ATC rate of change sources coming from prescribed data, AtC coupling, and extrinsic coupling */ + DENS_MAT heatSourceRoc_; + + /** references to ATC field rates of changing for inverting the filtered heat sources */ + FIELDS & fieldsRoc_; + + /** flux rate of changes for inverting filtered fluxes */ + FIELDS fluxRoc_; + + /** time scale for the time filter */ + double filterScale_; + + private: + + // DO NOT define this + ThermostatPowerVerletFiltered(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class ThermostatHooverVerletFiltered + //-------------------------------------------------------- + //-------------------------------------------------------- + + class ThermostatHooverVerletFiltered : public ThermostatPowerVerletFiltered { + + public: + + ThermostatHooverVerletFiltered(Thermostat * thermostat); + + ~ThermostatHooverVerletFiltered(){}; + + protected: + + /** reference to ATC map from global nodes to overlap nodes */ + Array & nodeToOverlapMap_; + + /** sets up and solves thermostat equations */ + virtual void compute_thermostat(double dt); + + /** compute boundary flux, requires thermostat input since it is part of the coupling scheme */ + virtual void compute_boundary_flux(FIELDS & fields) {boundaryFlux_[TEMPERATURE] = 0.;}; + + /** sets up Hoover component of the thermostat */ + void set_hoover_rhs(DENS_MAT & rhs_nodes); + + /** add Hoover contributions to lambda power */ + void add_to_lambda_power(DENS_MAT & myLambdaForce, + double dt); + + private: + + // DO NOT implement this + ThermostatHooverVerletFiltered(); + + }; + +}; + +#endif diff --git a/lib/atc/TimeFilter.cpp b/lib/atc/TimeFilter.cpp new file mode 100644 index 0000000000..3b6b6811bf --- /dev/null +++ b/lib/atc/TimeFilter.cpp @@ -0,0 +1,345 @@ +// ATC_Transfer headers +#include "ATC_Transfer.h" +#include "TimeFilter.h" + +namespace ATC { + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterManager + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + TimeFilterManager::TimeFilterManager(ATC_Transfer * atcTransfer) : + atcTransfer_(atcTransfer), + filterType_(NO_FILTER), + filterScale_(0.), + useFilter_(false), + equilibrateFilter_(false), + endEquilibrate_(false), + needReset_(true) + { + // do nothing + } + + //-------------------------------------------------------- + // modify + // parses input commands + //-------------------------------------------------------- + bool TimeFilterManager::modify(int narg, char ** arg) + { + bool foundMatch = false; + + // filter scale size + /*! \page man_filter_scale fix_modify AtC transfer filter scale + \section syntax + fix_modify AtC transfer filter scale \n + - scale (real) = characteristic time scale of the filter \n + + \section examples + fix_modify AtC transfer filter scale 10.0 \n + + \section description + Filters the MD dynamics to construct a more appropriate continuous field. Equilibrating first filters the time derivatives without changing the dynamics to provide a better initial condition to the filtered dynamics + \section restrictions + only for be used with specific transfers: + thermal, two_temperature + + \section related + \ref man_time_filter + + \section default + 0. + */ + if (strcmp(arg[0],"scale")==0) { + filterScale_ = atof(arg[1]); + if (filterScale_<=0.) + throw ATC_Error(0,"Bad filtering time scale"); + foundMatch = true; + } + + // time filtering activation switch + /*! \page man_time_filter fix_modify AtC transfer filter + \section syntax + fix_modify AtC transfer filter \n + - on | off (keyword) = turns filter on or off\n + - equilibrate = runs dynamics without filtering but initializes filtered quantities + \section examples + fix_modify atc transfer filter on \n + + \section description + Filters the MD dynamics to construct a more appropriate continuous field. Equilibrating first filters the time derivatives without changing the dynamics to provide a better initial condition to the filtered dynamics + + \section restrictions + only for be used with specific transfers: + thermal, two_temperature + + \section related + \ref man_filter_scale \n + \ref man_equilibrium_start + + \section default + off + */ + else if (strcmp(arg[0],"on")==0) { + if (filterScale_<=0.) + throw ATC_Error(0,"Filtering time scale not initialized"); + useFilter_ = true; + if (!equilibrateFilter_) { + needReset_ = true; + endEquilibrate_ = false; + } + else { + endEquilibrate_ = true; + } + equilibrateFilter_ = false; + foundMatch = true; + } + else if (strcmp(arg[0],"off")==0) { + useFilter_ = false; + equilibrateFilter_ = false; + endEquilibrate_ = false; + needReset_ = true; + foundMatch = true; + } + else if (strcmp(arg[0],"equilibrate")==0) { + if (filterScale_<=0.) + throw ATC_Error(0,"Filtering time scale not initialized"); + equilibrateFilter_ = true; + endEquilibrate_ = false; + useFilter_ = false; + needReset_ = true; + foundMatch = true; + } + + /** Example command for to set time filter scale: + fix_modify atc transfer filter type step*/ + else if (strcmp(arg[0],"type")==0) { + if (strcmp(arg[1],"exponential")==0) { + filterType_ = EXPONENTIAL_FILTER; + } + else if (strcmp(arg[1],"no_filter")==0) { + filterType_ = NO_FILTER; + } + else throw ATC_Error(0,"Not a supported time filter type"); + foundMatch = true; + } + + + return foundMatch; + } + + //-------------------------------------------------------- + // initialize + // filter set up before a run + //-------------------------------------------------------- + void TimeFilterManager::initialize() + { + needReset_ = false; + endEquilibrate_ = false; + } + + //-------------------------------------------------------- + // construct + // instantiates the filter + //-------------------------------------------------------- + // NOTE currently the caller is responsible for deletion + TimeFilter * TimeFilterManager::construct(const FilterIntegrationType intType) + { + if (useFilter_ || equilibrateFilter_) { + if (filterType_ == EXPONENTIAL_FILTER) { + if (intType == IMPLICIT_EXPLICIT) { + return new TimeFilterImplicitExplicit(*this); + } + else if (intType == EXPLICIT_IMPLICIT) { + return new TimeFilterExplicitImplicit(*this); + } + else if (intType == EXPLICIT) { + return new TimeFilterExplicit(*this); + } + else if (intType == IMPLICIT) { + return new TimeFilterImplicit(*this); + } + else if (intType == IMPLICIT_UPDATE) { + return new TimeFilterImplicitUpdate(*this); + } + else { + return new TimeFilterCrankNicolson(*this); + } + } + } + + // default to return base class + return new TimeFilter(*this); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilter + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + TimeFilter::TimeFilter(TimeFilterManager & timeFilterManager) : + atcTransfer_(timeFilterManager.get_atc_transfer()), + filterScale_(timeFilterManager.get_filter_scale()) + { + // do nothing + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterExponential + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + TimeFilterExponential::TimeFilterExponential(TimeFilterManager & timeFilterManager) : + TimeFilter(timeFilterManager) + { + TimeFilter::filterType_ = TimeFilterManager::EXPONENTIAL_FILTER; + } + + //-------------------------------------------------------- + // initialize + // resets memory as needed + //-------------------------------------------------------- + void TimeFilterExponential::initialize(const MATRIX & target) + { + TimeFilter::initialize(target); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterCrankNicoslon + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + TimeFilterCrankNicolson::TimeFilterCrankNicolson(TimeFilterManager & timeFilterManager) : + TimeFilterExponential(timeFilterManager) + { + // do nothing + } + + //-------------------------------------------------------- + // initialize + // resets memory as needed + //-------------------------------------------------------- + void TimeFilterCrankNicolson::initialize(const MATRIX & target) + { + TimeFilterExponential::initialize(target); + unFilteredQuantityOld_.reset(target.nRows(),target.nCols()); + unFilteredQuantityOld_ = target; + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterExplicit + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + TimeFilterExplicit::TimeFilterExplicit(TimeFilterManager & timeFilterManager) : + TimeFilterExponential(timeFilterManager) + { + // do nothing + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterImplicit + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + TimeFilterImplicit::TimeFilterImplicit(TimeFilterManager & timeFilterManager) : + TimeFilterExponential(timeFilterManager) + { + // do nothing + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterImplicitExplicit + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + TimeFilterImplicitExplicit::TimeFilterImplicitExplicit(TimeFilterManager & timeFilterManager) : + TimeFilterExponential(timeFilterManager) + { + // do nothing + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterExplicitImplicit + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + TimeFilterExplicitImplicit::TimeFilterExplicitImplicit(TimeFilterManager & timeFilterManager) : + TimeFilterExponential(timeFilterManager) + { + // do nothing + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterImplicitUpdate + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + TimeFilterImplicitUpdate::TimeFilterImplicitUpdate(TimeFilterManager & timeFilterManager) : + TimeFilterExponential(timeFilterManager) + { + // do nothing + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterStep + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + TimeFilterStep::TimeFilterStep(TimeFilterManager & timeFilterManager) : + TimeFilter(timeFilterManager) + { + // do nothing + } + + //-------------------------------------------------------- + // initialize + // resets memory as needed + //-------------------------------------------------------- + void TimeFilterStep::initialize(const MATRIX & target) + { + TimeFilter::initialize(target); + } + + +}; diff --git a/lib/atc/TimeFilter.h b/lib/atc/TimeFilter.h new file mode 100644 index 0000000000..9b6756978a --- /dev/null +++ b/lib/atc/TimeFilter.h @@ -0,0 +1,696 @@ +// The type of filter used should be determined by the +// integrator since filtering much match the time integration scheme + +#ifndef TIME_FILTER_H +#define TIME_FILTER_H + +// ATC_Transfer headers +#include "MatrixLibrary.h" +#include "ATC_Error.h" + +using namespace std; +namespace ATC { + + // forward declarations + class ATC_Transfer; + class TimeFilter; + + /** + * @class TimeFilterManager + * @brief Handles parsing and parameter storage for time filters + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterManager + //-------------------------------------------------------- + //-------------------------------------------------------- + + class TimeFilterManager { + + public: + + /** enumeration for the functional form underlying the filter */ + enum TimeFilterType { + NO_FILTER=0, // default + EXPONENTIAL_FILTER, + STEP_FILTER + }; + + /** enumeration for the functional form underlying the filter */ + enum FilterIntegrationType { + CRANK_NICHOLSON=0, // default + IMPLICIT_EXPLICIT, + EXPLICIT_IMPLICIT, + EXPLICIT, + IMPLICIT, + IMPLICIT_UPDATE + }; + + // constructor + TimeFilterManager(ATC_Transfer * atcTransfer); + + // destructor + ~TimeFilterManager(){}; + + /** parser/modifier */ + bool modify(int narg, char **arg); + + /** pre time integration */ + void initialize(); + + /** get filter base function */ + TimeFilterType get_filter_type() const {return filterType_;}; + + /** return filtering time scale */ + double get_filter_scale() const {return filterScale_;}; + + /** check if dynamics should be filtering */ + bool filter_dynamics() const {return useFilter_;}; + + /** check if variables should be filtered */ + bool filter_variables() const {return (useFilter_ || equilibrateFilter_);}; + + /** flag for if reset is needed */ + bool need_reset() const {return needReset_;}; + + /** flag if ending equilibration */ + bool end_equilibrate() const {return endEquilibrate_;}; + + /** get pointer to ATC transfer methods */ + ATC_Transfer * get_atc_transfer() {return atcTransfer_;}; + + /** construct the appropriate time filter */ + TimeFilter * construct(const FilterIntegrationType type = CRANK_NICHOLSON); + + protected: + + TimeFilterManager(){}; + + /** pointer to access ATC methods */ + ATC_Transfer * atcTransfer_; + + /** description of underlying function form of filter */ + TimeFilterType filterType_; + + /** filtering time scale */ + double filterScale_; + + /** flag to see if filtering is active */ + bool useFilter_; + + /** flag to see if we are equilibrating the filtered variables */ + bool equilibrateFilter_; + + /** flag to reset data */ + bool needReset_; + + /** flag to denote switch from equilibration to integration */ + bool endEquilibrate_; + + }; + + /** + * @class TimeFilter + * @brief Base class for various temporal filters of atomistic quantities + * default behavior is no filter + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilter + //-------------------------------------------------------- + //-------------------------------------------------------- + + + class TimeFilter { + + public: + + // constructor + TimeFilter(TimeFilterManager & timeFilterManager); + + // destructor + virtual ~TimeFilter(){}; + + /** pre time integration */ + virtual void initialize(const MATRIX & target){}; + + /** Step 1: + apply first step in a time filter update in the pre integration phase */ + virtual void apply_pre_step1(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) + { TimeFilter::unFilteredQuantityOld_ = unFilteredQuantity;} + + /** Step 2: + apply second step in a time filter update in pre integration phase */ + virtual void apply_pre_step2(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) {}; + + /** Step 3: + apply first step in a time filter update in post integration phase */ + virtual void apply_post_step1(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) {}; + + /** Step 4: + apply second step in a time filter update in post integration phase */ + virtual void apply_post_step2(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) + { filteredQuantity = unFilteredQuantity;} + + /** coefficient multipling unfiltered terms in apply_pre_step1 method */ + virtual double get_unfiltered_coefficient_pre_s1(double dt){return 0.;}; + + /** coefficient multipling unfiltered terms in apply_post_step1 method */ + virtual double get_unfiltered_coefficient_post_s1(double dt){return 0.;}; + + /** coefficient multipling unfiltered terms in apply_pre_step2 method */ + virtual double get_unfiltered_coefficient_pre_s2(double dt){return 0.;}; + + /** coefficient multipling unfiltered terms in apply_post_step2 method */ + virtual double get_unfiltered_coefficient_post_s2(double dt){return 0.;}; + + /** rate of filtered quantity to be called in post integration phase */ + virtual void rate(MATRIX & rate, + const MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt = 0.0) + { rate = 1/dt*(unFilteredQuantity - TimeFilter::unFilteredQuantityOld_);}; + + protected: + + TimeFilter(){}; + + /** pointer to access ATC methods */ + ATC_Transfer * atcTransfer_; + + /** filtering time scale */ + double filterScale_; + + /** filter type */ + TimeFilterManager::TimeFilterType filterType_; + + /** member data to track old unfiltered values */ + DENS_MAT unFilteredQuantityOld_; + + }; + + /** + * @class TimeFilterExponential + * @brief Base class for filters using an exponential kernel, + * derived classes implement specific integration schemes + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterExponential + //-------------------------------------------------------- + //-------------------------------------------------------- + + class TimeFilterExponential : public TimeFilter { + + public: + + // constructor + TimeFilterExponential(TimeFilterManager & timeFilterManager); + + // destructor + virtual ~TimeFilterExponential(){}; + + /** pre time integration */ + virtual void initialize(const MATRIX & target); + + /** apply first step in a time filter update in the pre integration phase */ + virtual void apply_pre_step1(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) {}; + + /** apply second step in a time filter update in pre integration phase */ + virtual void apply_pre_step2(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) {}; + + /** apply first step in a time filter update in post integration phase */ + virtual void apply_post_step1(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) {}; + + /** apply second step in a time filter update in post integration phase */ + virtual void apply_post_step2(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) {}; + + /** time rate of filtered quantity */ + virtual void rate(MATRIX & rate, + const MATRIX & filteredQuantity, + const MATRIX & unfilteredQuantity, + double dt = 0) + { double tau = TimeFilter::filterScale_; + rate = 1/tau*(unfilteredQuantity - filteredQuantity); }; + + protected: + + TimeFilterExponential(){}; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // filter integration functions not associated + // with any particular class + //-------------------------------------------------------- + //-------------------------------------------------------- + + void update_filter(MATRIX & filteredQuantity, + const MATRIX & unfilteredQuantity, + MATRIX & unfilteredQuantityOld, + double tau, + double dt) + { + filteredQuantity = 1./(1./dt+1./(2*tau))*( 1./(2*tau)* + (unfilteredQuantity+unfilteredQuantityOld) + + (1./dt-1./(2*tau))*filteredQuantity); + unfilteredQuantityOld = unfilteredQuantity; + }; + + void add_to_filter(MATRIX & filteredQuantity, + const MATRIX & unfilteredQuantity, + MATRIX & unfilteredQuantityOld, + double tau, + double dt) + { + filteredQuantity += 1./(1./dt+1./(2.*tau))*( 1./(2.*tau))*unfilteredQuantity; + unfilteredQuantityOld += unfilteredQuantity; + }; + + double get_unfiltered_coef(double tau, double dt) + { return 1./(1./dt+1./(2.*tau))*( 1./(2.*tau)); }; + + void update_filter_implicit(MATRIX & filteredQuantity, + const MATRIX & unfilteredQuantity, + double tau, + double dt) + // TODO: replace the rest of these like below: + { filteredQuantity = (1./(1.+dt/tau))*((dt/tau)*unfilteredQuantity + filteredQuantity); }; + // { + // filteredQuantity /= 1.0 + dt/tau; + // filteredQuantity += (dt)/(tau+dt)*unfilteredQuantity; + // } + + void add_to_filter_implicit(MATRIX & filteredQuantity, + const MATRIX & unfilteredQuantity, + double tau, + double dt) + { filteredQuantity += (1./(1.+dt/tau))*(dt/tau)*unfilteredQuantity; }; + + double get_unfiltered_coef_implicit(double tau, double dt) + { return (1./(1.+dt/tau))*(dt/tau); }; + + void update_filter_explicit(MATRIX & filteredQuantity, + const MATRIX & unfilteredQuantity, + double tau, + double dt) + { filteredQuantity = (dt/tau)*unfilteredQuantity + (1.-dt/tau)*filteredQuantity; }; + + void add_to_filter_explicit(MATRIX & filteredQuantity, + const MATRIX & unfilteredQuantity, + double tau, + double dt) + { filteredQuantity += (dt/tau)*unfilteredQuantity; }; + + double get_unfiltered_coef_explicit(double dt, double tau) + { return (dt/tau); }; + + }; + + /** + * @class TimeFilterCrankNicolson + * @brief Time Filter using Crank-Nicolson advancement of filtered quantity ODE's + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterCrankNicolson + //-------------------------------------------------------- + //-------------------------------------------------------- + class TimeFilterCrankNicolson : public TimeFilterExponential { + + public: + + // constructor + TimeFilterCrankNicolson(TimeFilterManager & timeFilterManager); + + // destructor + virtual ~TimeFilterCrankNicolson(){}; + + /** pre time integration */ + virtual void initialize(const MATRIX & target); + + /** applies first step in a time filter update in the pre integration phase */ + virtual void apply_pre_step1(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { update_filter(filteredQuantity,unFilteredQuantity,unFilteredQuantityOld_,TimeFilter::filterScale_,dt); }; + + /** applies first step in a time filter update after the pre integration phase */ + virtual void apply_post_step1(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { add_to_filter(filteredQuantity,unFilteredQuantity,unFilteredQuantityOld_,TimeFilter::filterScale_,dt); }; + + /** applies second step in a time filter update in the post integration phase */ + virtual void apply_post_step2(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { update_filter(filteredQuantity,unFilteredQuantity,unFilteredQuantityOld_,TimeFilter::filterScale_,dt); }; + + /** return coefficient multipling unfiltered terms in the apply_pre_step1 method */ + virtual double get_unfiltered_coefficient_pre_s1(double dt){return get_unfiltered_coef(TimeFilter::filterScale_,dt);}; + + /** return coefficient multipling unfiltered terms in the apply_post_step2 method */ + virtual double get_unfiltered_coefficient_post_s2(double dt){return get_unfiltered_coef(TimeFilter::filterScale_,dt);}; + + protected: + + TimeFilterCrankNicolson(); + + }; + + /** + * @class TimeFilterExplicit + * @brief Time Filter using explicit advancement of filtered quantity ODE's + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterExplicit + //-------------------------------------------------------- + //-------------------------------------------------------- + class TimeFilterExplicit : public TimeFilterExponential { + + public: + + // constructor + TimeFilterExplicit(TimeFilterManager & timeFilterManager); + + // destructor + virtual ~TimeFilterExplicit(){}; + + /** applies first step in a time filter update in the pre integration phase */ + virtual void apply_pre_step1(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + {update_filter_explicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** applies first step in a time filter update after the pre integration phase */ + virtual void apply_post_step1(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { add_to_filter_explicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** applies second step in a time filter update in the post integration phase */ + virtual void apply_post_step2(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { update_filter_explicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** return coefficient multipling unfiltered terms in the apply_pre_step1 method */ + virtual double get_unfiltered_coefficient_pre_s1(double dt){return get_unfiltered_coef_explicit(TimeFilter::filterScale_,dt);}; + + /** return coefficient multipling unfiltered terms in the apply_post_step2 method */ + virtual double get_unfiltered_coefficient_post_s2(double dt){return get_unfiltered_coef_explicit(TimeFilter::filterScale_,dt);}; + + protected: + + TimeFilterExplicit(); + + }; + + /** + * @class TimeFilterImplicit + * @brief Time Filter using implicit advancement of filtered quantity ODE's + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterImplicit + //-------------------------------------------------------- + //-------------------------------------------------------- + class TimeFilterImplicit : public TimeFilterExponential { + + public: + + // constructor + TimeFilterImplicit(TimeFilterManager & timeFilterManager); + + // destructor + virtual ~TimeFilterImplicit(){}; + + /** applies first step in a time filter update in the pre integration phase */ + virtual void apply_pre_step1(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { update_filter_implicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** applies second step in a time filter update in the post integration phase */ + virtual void apply_post_step2(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { update_filter_implicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** return coefficient multipling unfiltered terms in the apply_pre_step1 method */ + virtual double get_unfiltered_coefficient_pre_s1(double dt){return get_unfiltered_coef_implicit(TimeFilter::filterScale_,dt);}; + + /** return coefficient multipling unfiltered terms in the apply_post_step2 method */ + virtual double get_unfiltered_coefficient_post_s2(double dt){return get_unfiltered_coef_implicit(TimeFilter::filterScale_,dt);}; + + protected: + + TimeFilterImplicit(); + + }; + + /** + * @class TimeFilterImplicitExplicit + * @brief Time Filter using two-step implicit/explicit advancement of filtered quantity ODE's + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterImplicitExplicit + //-------------------------------------------------------- + //-------------------------------------------------------- + class TimeFilterImplicitExplicit : public TimeFilterExponential { + + public: + + // constructor + TimeFilterImplicitExplicit(TimeFilterManager & timeFilterManager); + + // destructor + virtual ~TimeFilterImplicitExplicit(){}; + + /** applies first step in a time filter update in the pre integration phase */ + virtual void apply_pre_step1(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { update_filter_implicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** applies second step in a time filter update in the post integration phase */ + virtual void apply_post_step2(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { update_filter_explicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** return coefficient multipling unfiltered terms in the apply_pre_step1 method */ + virtual double get_unfiltered_coefficient_pre_s1(double dt){return get_unfiltered_coef_implicit(TimeFilter::filterScale_,dt);}; + + /** return coefficient multipling unfiltered terms in the apply_post_step2 method */ + virtual double get_unfiltered_coefficient_post_s2(double dt){return get_unfiltered_coef_explicit(TimeFilter::filterScale_,dt);}; + + protected: + + TimeFilterImplicitExplicit(); + + }; + + /** + * @class TimeFilterExplicitImplicit + * @brief Time Filter using two-step explicit/implicit advancement of filtered quantity ODE's + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterExplicitImplicit + //-------------------------------------------------------- + //-------------------------------------------------------- + class TimeFilterExplicitImplicit : public TimeFilterExponential { + + public: + + // constructor + TimeFilterExplicitImplicit(TimeFilterManager & timeFilterManager); + + // destructor + virtual ~TimeFilterExplicitImplicit(){}; + + /** applies first step in a time filter update in the pre integration phase */ + virtual void apply_pre_step1(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { update_filter_explicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** applies second step in a time filter update in the pre integration phase */ + virtual void apply_pre_step2(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { add_to_filter_explicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** applies second step in a time filter update in the post integration phase */ + virtual void apply_post_step1(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { update_filter_implicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** applies second step in a time filter update in the post integration phase */ + virtual void apply_post_step2(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { add_to_filter_implicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** return coefficient multipling unfiltered terms in the apply_pre_step1 method */ + virtual double get_unfiltered_coefficient_pre_s1(double dt){return get_unfiltered_coef_explicit(TimeFilter::filterScale_,dt);}; + + /** return coefficient multipling unfiltered terms in the apply_post_step2 method */ + virtual double get_unfiltered_coefficient_post_s1(double dt){return get_unfiltered_coef_implicit(TimeFilter::filterScale_,dt);}; + + protected: + + TimeFilterExplicitImplicit(); + + }; + + /** + * @class TimeFilterImplicitUpdate + * @brief Time Filter using implicit advancement of filtered quantity ODE's but adds on contribution at the end of the second step + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterImplicitUpdate + //-------------------------------------------------------- + //-------------------------------------------------------- + class TimeFilterImplicitUpdate : public TimeFilterExponential { + + public: + + // constructor + TimeFilterImplicitUpdate(TimeFilterManager & timeFilterManager); + + // destructor + virtual ~TimeFilterImplicitUpdate(){}; + + /** applies first step in a time filter update in the pre integration phase */ + virtual void apply_pre_step1(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { update_filter_implicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** applies second step in a time filter update in the post integration phase */ + virtual void apply_post_step1(MATRIX & filteredQuantity, + MATRIX const & unFilteredQuantity, + double dt) + { add_to_filter_implicit(filteredQuantity,unFilteredQuantity,TimeFilter::filterScale_,dt); }; + + /** return coefficient multipling unfiltered terms in the apply_pre_step1 method */ + virtual double get_unfiltered_coefficient_pre_s1(double dt){return get_unfiltered_coef_implicit(TimeFilter::filterScale_,dt);}; + + /** return coefficient multipling unfiltered terms in the apply_post_step2 method */ + virtual double get_unfiltered_coefficient_post_s1(double dt){return get_unfiltered_coef_implicit(TimeFilter::filterScale_,dt);}; + + protected: + + TimeFilterImplicitUpdate(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeFilterStep + //-------------------------------------------------------- + //-------------------------------------------------------- + + class TimeFilterStep : public TimeFilter { + + public: + + // constructor + TimeFilterStep(TimeFilterManager & timeFilterManager); + + // destructor + virtual ~TimeFilterStep(){}; + + /** pre time integration */ + virtual void initialize(const MATRIX & target); + + /** apply first step in a time filter update in the pre integration phase */ + virtual void apply_pre_step1(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) {}; + + /** apply second step in a time filter update in pre integration phase */ + virtual void apply_pre_step2(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) {}; + + /** apply first step in a time filter update in post integration phase */ + virtual void apply_post_step1(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) {}; + + /** apply second step in a time filter update in post integration phase */ + virtual void apply_post_step2(MATRIX & filteredQuantity, + const MATRIX & unFilteredQuantity, + double dt) + { update_filter(filteredQuantity, unFilteredQuantity, + TimeFilter::unFilteredQuantityOld_, TimeFilter::filterScale_, dt); + } + + /** time rate of filtered quantity */ + virtual void rate(MATRIX & rate, + const MATRIX & filteredQuantity, + const MATRIX & unfilteredQuantity, + double dt = 0) + { rate = 1/elapsedTime_*(unfilteredQuantity - filteredQuantity); } + + protected: + + TimeFilterStep(){}; + + double elapsedTime_; + + void update_filter(MATRIX & filteredQuantity, + const MATRIX & unfilteredQuantity, + MATRIX & unfilteredQuantitySum, + double tau, + double dt) + { + elapsedTime_ += dt; + if (elapsedTime_ > tau) { + elapsedTime_ = dt; + unfilteredQuantitySum = unfilteredQuantity*dt; + filteredQuantity = unfilteredQuantity; + } + else { + unfilteredQuantitySum += unfilteredQuantity*dt; + filteredQuantity = unfilteredQuantitySum; + filteredQuantity /= elapsedTime_; + } + }; + }; + +}; + +#endif diff --git a/lib/atc/TimeIntegrator.cpp b/lib/atc/TimeIntegrator.cpp new file mode 100644 index 0000000000..df689a5aee --- /dev/null +++ b/lib/atc/TimeIntegrator.cpp @@ -0,0 +1,176 @@ +// ATC transfer headers +#include "TimeIntegrator.h" +#include "ATC_Transfer.h" +#include "ATC_Error.h" + +namespace ATC { + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeIntegrator + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + //-------------------------------------------------------- + TimeIntegrator::TimeIntegrator(ATC_Transfer * atcTransfer, + TimeIntegrationType timeIntegrationType) : + atcTransfer_(atcTransfer), + timeIntegrationMethod_(NULL), + timeFilter_(NULL), + timeFilterManager_(atcTransfer_->get_time_filter_manager()), + timeIntegrationType_(timeIntegrationType), + needReset_(true) + { + // do nothing + } + + //-------------------------------------------------------- + // Destructor + //-------------------------------------------------------- + TimeIntegrator::~TimeIntegrator() + { + if (timeFilter_) + delete timeFilter_; + + if (timeIntegrationMethod_) + delete timeIntegrationMethod_; + } + + //-------------------------------------------------------- + // pre_initial_integrate1 + // first time integration computations + // before Verlet step 1 + //-------------------------------------------------------- + void TimeIntegrator::pre_initial_integrate1(double dt) + { + timeIntegrationMethod_->pre_initial_integrate1(dt); + } + + //-------------------------------------------------------- + // pre_initial_integrate2 + // second time integration computations + // before Verlet step 1 + //-------------------------------------------------------- + void TimeIntegrator::pre_initial_integrate2(double dt) + { + timeIntegrationMethod_->pre_initial_integrate2(dt); + } + + //-------------------------------------------------------- + // mid_initial_integrate1 + // first time integration computations + // at the mid-point of Verlet step 1 + //-------------------------------------------------------- + void TimeIntegrator::mid_initial_integrate1(double dt) + { + timeIntegrationMethod_->mid_initial_integrate1(dt); + } + + //-------------------------------------------------------- + // mid_initial_integrate2 + // second time integration computations + // at the mid-point of Verlet step 1 + //-------------------------------------------------------- + void TimeIntegrator::mid_initial_integrate2(double dt) + { + timeIntegrationMethod_->mid_initial_integrate2(dt); + } + + //-------------------------------------------------------- + // post_initial_integrate1 + // first time integration computations + // after Verlet step 1 + //-------------------------------------------------------- + void TimeIntegrator::post_initial_integrate1(double dt) + { + timeIntegrationMethod_->post_initial_integrate1(dt); + } + + //-------------------------------------------------------- + // post_initial_integrate2 + // second time integration computations + // after Verlet step 1 + //-------------------------------------------------------- + void TimeIntegrator::post_initial_integrate2(double dt) + { + timeIntegrationMethod_->post_initial_integrate2(dt); + } + + //-------------------------------------------------------- + // pre_final_integrate1 + // first time integration computations + // before Verlet step 2 + //-------------------------------------------------------- + void TimeIntegrator::pre_final_integrate1(double dt) + { + timeIntegrationMethod_->pre_final_integrate1(dt); + } + + //-------------------------------------------------------- + // pre_final_integrate2 + // second time integration computations + // before Verlet step 2 + //-------------------------------------------------------- + void TimeIntegrator::pre_final_integrate2(double dt) + { + timeIntegrationMethod_->pre_final_integrate2(dt); + } + + //-------------------------------------------------------- + // post_final_integrate1 + // first time integration computations + // after Verlet step 2 + //-------------------------------------------------------- + void TimeIntegrator::post_final_integrate1(double dt) + { + timeIntegrationMethod_->post_final_integrate1(dt); + } + + //-------------------------------------------------------- + // post_final_integrate2 + // second time integration computations + // after Verlet step 2 + //-------------------------------------------------------- + void TimeIntegrator::post_final_integrate2(double dt) + { + timeIntegrationMethod_->post_final_integrate2(dt); + } + + //-------------------------------------------------------- + // post_process + // perform any post processing calculations + //-------------------------------------------------------- + void TimeIntegrator::post_process(double dt) + { + timeIntegrationMethod_->post_process(dt); + } + + //-------------------------------------------------------- + // output + // add variables to output list + //-------------------------------------------------------- + void TimeIntegrator::output(OUTPUT_LIST & outputData) + { + timeIntegrationMethod_->output(outputData); + } + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeIntegrationMethod + //-------------------------------------------------------- + //-------------------------------------------------------- + + //-------------------------------------------------------- + // Constructor + // Grab data from ATC + //-------------------------------------------------------- + TimeIntegrationMethod::TimeIntegrationMethod(TimeIntegrator * timeIntegrator) : + timeIntegrator_(timeIntegrator), + atcTransfer_(timeIntegrator_->get_atc_transfer()) + { + // do nothing + } + +}; diff --git a/lib/atc/TimeIntegrator.h b/lib/atc/TimeIntegrator.h new file mode 100644 index 0000000000..603ade1ffd --- /dev/null +++ b/lib/atc/TimeIntegrator.h @@ -0,0 +1,280 @@ +#ifndef TIME_INTEGRATOR_H +#define TIME_INTEGRATOR_H + +// ATC_Transfer headers +#include "MatrixLibrary.h" +#include "TimeFilter.h" +#include "ATC_TypeDefs.h" + +using namespace std; +namespace ATC { + + // forward declarations + class ATC_Transfer; + class TimeIntegrationMethod; + + /** + * @class TimeIntegrator + * @brief Base class fo various time integrators for FE quantities + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeIntegrator + //-------------------------------------------------------- + //-------------------------------------------------------- + + class TimeIntegrator { + + public: + + /** types of time integration */ + enum TimeIntegrationType { + STEADY, + VERLET, + GEAR, + FRACTIONAL_STEP, + EXPLICIT, + IMPLICIT, + CRANK_NICOLSON + }; + + // constructor + TimeIntegrator(ATC_Transfer * atcTransfer, + TimeIntegrationType timeIntegrationType = STEADY); + + // destructor + virtual ~TimeIntegrator(); + + /** parser/modifier */ + virtual bool modify(int narg, char **arg){return false;}; + + /** pre time integration */ + virtual void initialize(){needReset_ = false;}; + + /** flag if reset is needed */ + bool need_reset() {return needReset_;}; + + // time step methods, corresponding to ATC_Transfer + /** first part of pre_initial_integrate */ + virtual void pre_initial_integrate1(double dt); + /** second part of pre_initial_integrate */ + virtual void pre_initial_integrate2(double dt); + + /** first part of mid_initial_integrate */ + virtual void mid_initial_integrate1(double dt); + /** second part of mid_initial_integrate */ + virtual void mid_initial_integrate2(double dt); + + /** first part of post_initial_integrate */ + virtual void post_initial_integrate1(double dt); + /** second part of post_initial_integrate */ + virtual void post_initial_integrate2(double dt); + + /** first part of pre_final_integrate */ + virtual void pre_final_integrate1(double dt); + /** second part of pre_final_integrate */ + virtual void pre_final_integrate2(double dt); + + /** first part of post_final_integrate */ + virtual void post_final_integrate1(double dt); + /** second part of post_final_integrate */ + virtual void post_final_integrate2(double dt); + + /** post processing step */ + virtual void post_process(double dt); + /** add output data */ + virtual void output(OUTPUT_LIST & outputData); + + // Member data access + /** access to time integration type */ + TimeIntegrationType get_time_integration_type() const + { return timeIntegrationType_; }; + + /** access to ATC Transfer object */ + ATC_Transfer * get_atc_transfer() {return atcTransfer_;}; + + /** access to time filter object */ + TimeFilter * get_time_filter() {return timeFilter_;}; + + /** access to time filter manager object */ + TimeFilterManager * get_time_filter_manager() {return timeFilterManager_;}; + + /** force the integrator to be reset */ + void force_reset() {needReset_ = true;}; + + /** force the integrator not to be reset */ + void force_no_reset() {needReset_ = false;}; + + protected: + + /** pointer to time integrator method */ + TimeIntegrationMethod * timeIntegrationMethod_; + + /** pointer to access ATC methods */ + ATC_Transfer * atcTransfer_; + + /** time filter for specific updates */ + TimeFilter * timeFilter_; + + /** time filter manager for getting time filtering info */ + TimeFilterManager * timeFilterManager_; + + /** type of integration scheme being used */ + TimeIntegrationType timeIntegrationType_; + + /** flat to reset data */ + bool needReset_; + + private: + + // DO NOT define this + TimeIntegrator(); + + }; + + /** + * @class TimeIntegrationMethod + * @brief Base class fo various time integration methods + */ + + //-------------------------------------------------------- + //-------------------------------------------------------- + // Class TimeIntegrationMethod + // Base class for time integration methods which + // update the FE quantities + //-------------------------------------------------------- + //-------------------------------------------------------- + + class TimeIntegrationMethod { + + public: + + // constructor + TimeIntegrationMethod(TimeIntegrator * timeIntegrator); + + // destructor + virtual ~TimeIntegrationMethod(){}; + + // time step methods, corresponding to ATC_Transfer and TimeIntegrator + /** first part of pre_initial_integrate */ + virtual void pre_initial_integrate1(double dt){}; + /** second part of pre_initial_integrate */ + virtual void pre_initial_integrate2(double dt){}; + + /** first part of mid_initial_integrate */ + virtual void mid_initial_integrate1(double dt){}; + /** second part of mid_initial_integrate */ + virtual void mid_initial_integrate2(double dt){}; + + /** first part of post_initial_integrate */ + virtual void post_initial_integrate1(double dt){}; + /** second part of post_initial_integrate */ + virtual void post_initial_integrate2(double dt){}; + + /** first part of pre_final_integrate */ + virtual void pre_final_integrate1(double dt){}; + /** second part of pre_final_integrate */ + virtual void pre_final_integrate2(double dt){}; + + /** first part of post_final_integrate */ + virtual void post_final_integrate1(double dt){}; + /** second part of post_final_integrate */ + virtual void post_final_integrate2(double dt){}; + + /** post processing step */ + virtual void post_process(double dt){}; + /** add output data */ + virtual void output(OUTPUT_LIST & outputData){}; + + protected: + + /** owning time integrator */ + TimeIntegrator * timeIntegrator_; + + /** associated ATC transfer object */ + ATC_Transfer * atcTransfer_; + + private: + + // DO NOT define this + TimeIntegrationMethod(); + + }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + // time integration functions not associated + // with any particular class + //-------------------------------------------------------- + //-------------------------------------------------------- + + static void gear1_4_predict(MATRIX & f, + MATRIX & dot_f, + MATRIX & ddot_f, + MATRIX & dddot_f, + double dt) + // 4th order Gear integrator for 1rst order ODE predictor step + { + f = f + dot_f*dt + ddot_f*(1./2.*dt*dt) + dddot_f*(1./6.*dt*dt*dt); + dot_f = dot_f + ddot_f*dt+dddot_f*(1./2.*dt*dt); + ddot_f = ddot_f + dddot_f*dt; + }; + + static void gear1_3_predict(MATRIX & f, + MATRIX & dot_f, + MATRIX & ddot_f, + double dt) + // 3rd order Gear integrator for 1rst order ODE predictor step + { + f = f + dot_f*dt + ddot_f*(1./2.*dt*dt); + dot_f = dot_f + ddot_f*dt; + }; + + static void gear1_4_correct(MATRIX & f, + MATRIX & dot_f, + MATRIX & ddot_f, + MATRIX & dddot_f, + const MATRIX & R_f, + double dt) + // 4th order Gear integrator for 1rst order ODE corrector step + { + f = f + (3./8.)*R_f; + dot_f = dot_f + (1./dt)*R_f; + ddot_f = ddot_f + (3./2./dt/dt)*R_f; + dddot_f = dddot_f + (1./dt/dt/dt)*R_f; + }; + + static void gear1_3_correct(MATRIX & f, + MATRIX & dot_f, + MATRIX & ddot_f, + const MATRIX & R_f, + double dt) + // 3rd order Gear integrator for 1rst order ODE corrector step + { + f = f + (5./12.)*R_f; + dot_f = dot_f + (1./dt)*R_f; + ddot_f = ddot_f + (1./dt/dt)*R_f; + }; + + static void explicit_1(MATRIX & f, + MATRIX & dot_f, + double dt) + // 1rst order explict ODE update + { + f = f + dt*dot_f; + }; + + static void explicit_2(MATRIX & f, + MATRIX & dot_f, + MATRIX & ddot_f, + double dt) + // 2nd order explict ODE update + { + f = f + dt*dot_f + .5*dt*dt*ddot_f; + }; + +}; + +#endif + diff --git a/lib/atc/Utility.h b/lib/atc/Utility.h new file mode 100644 index 0000000000..5cc8241720 --- /dev/null +++ b/lib/atc/Utility.h @@ -0,0 +1,109 @@ +#ifndef UTILITY_H +#define UTILITY_H + +#include +#include +#undef near + +using std::vector; + +namespace Utility +{ +/////////////////////////////////////////////////////////////////////////////////////////////// +// Returns true if the value v is between min and max +/////////////////////////////////////////////////////////////////////////////////////////////// +template +inline bool InRange(const T &v, const T &min, const T &max) +{ + return v >= min && v <= max; +} +/////////////////////////////////////////////////////////////////////////////////////////////// +// Returns true if the value v is between min and max within the tolerance TOL +/////////////////////////////////////////////////////////////////////////////////////////////// +template +inline bool InRange(const T &v, const T &min, const T &max, const T &TOL) +{ + return InRange(v, min-TOL, max+TOL); +} +/////////////////////////////////////////////////////////////////////////////////////////////// +// Returns the value with the larger absolute value +/////////////////////////////////////////////////////////////////////////////////////////////// +template +inline T MaxAbs(const T &a, const T &b) +{ + return (a*a > b*b) ? a : b; +} +/////////////////////////////////////////////////////////////////////////////////////////////// +// Returns the value with the smaller absolute value +/////////////////////////////////////////////////////////////////////////////////////////////// +template +inline T MinAbs(const T &a, const T &b) +{ + return (a*a < b*b) ? a : b; +} +/////////////////////////////////////////////////////////////////////////////////////////////// +// A simple Matlab like timing function +/////////////////////////////////////////////////////////////////////////////////////////////// +inline double tictoc() +{ + double t_new = clock() / (double) CLOCKS_PER_SEC; + static double t = t_new; + double dt = t_new - t; + t = t_new; + return dt; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Binary search between low and high for value (assumes array is sorted) +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +inline int SearchSorted(const T* A, T value, unsigned low, unsigned high) +{ + unsigned mid; + while ((low+1)<=(high+1)) + { + mid = (low + high) >> 1; // set mid to mean of high and low + if (A[mid] > value) high = mid-1; // mid was too high, reduce high + else if (A[mid] < value) low = mid+1; // mid was too low, increase low + else return mid; + } + return -1; // value not found in array, return -1 +} +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Flat search between low and high for value (assumes array is NOT sorted) +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +inline int SearchUnsorted(const T* A, T value, unsigned low, unsigned high) +{ + for (low; low<=high; low++) if (A[low] == value) return low; + return -1; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Regular swap +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +inline void Swap(T &a, T &b) +{ + T temp = a; + a = b; + b = temp; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Returns the sign of a double precision number +/////////////////////////////////////////////////////////////////////////////////////////////////// +inline double Sign(double x) { return (x >= 0.0) ? 1.0 : -1.0; } +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Returns the sign of a single precision number +/////////////////////////////////////////////////////////////////////////////////////////////////// +inline float Sign(float x) { return (x >= 0.0f) ? 1.0f : -1.0f; } +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Returns the pythagorean distance with the two lengths +/////////////////////////////////////////////////////////////////////////////////////////////////// +inline double Norm(double x, double y) { return sqrt(x*x+y*y); } +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Returns square of a value +/////////////////////////////////////////////////////////////////////////////////////////////////// +template +inline T Square(T x) { return x*x; } +} + +#endif diff --git a/lib/atc/Vector.cpp b/lib/atc/Vector.cpp new file mode 100644 index 0000000000..d59a522816 --- /dev/null +++ b/lib/atc/Vector.cpp @@ -0,0 +1,30 @@ +#include "DenseVector.h" +/////////////////////////////////////////////////////////////////////////////// +//* performs a matrix-vector multiply with optional transposes BLAS version +void MultMv(const Matrix &A, const Vector &v, DenseVector &c, + const bool At, double a, double b) +{ + static char t[2] = {'N','T'}; + char *ta=t+At; + int sA[2] = {A.nRows(), A.nCols()}; // sizes of A + int sV[2] = {v.size(), 1}; // sizes of v + + GCK(A, v, sA[!At]!=sV[0], "MultAB: matrix-vector multiply"); + if (c.size() != sA[At]) + { + c.resize(sA[At]); // set size of C to final size + c.zero(); + } + // get pointers to the matrix sizes needed by BLAS + int *M = sA+At; // # of rows in op[A] (op[A] = A' if At='T' else A) + int *N = sV+1; // # of cols in op[B] + int *K = sA+!At; // # of cols in op[A] or # of rows in op[B] + + double *pa=A.get_ptr(), *pv=v.get_ptr(), *pc=c.get_ptr(); + +#ifdef COL_STORAGE + dgemm_(ta, t, M, N, K, &a, pa, sA, pv, sV, &b, pc, M); +#else + dgemm_(t, ta, N, M, K, &a, pv, sV+1, pa, sA+1, &b, pc, N); +#endif +} diff --git a/lib/atc/Vector.h b/lib/atc/Vector.h new file mode 100644 index 0000000000..fa50e8ff7f --- /dev/null +++ b/lib/atc/Vector.h @@ -0,0 +1,195 @@ +#ifndef VECTOR_H +#define VECTOR_H + +#include "Matrix.h" + +/////////////////////////////////////////////////////////////////////////////// +// forward declarations /////////////////////////////////////////////////////// + +//* Matrix-vector product +//template +//void MultMv(const Matrix &A, const Vector &v, DenseVector &c, +// const bool At=0, T a=1, T b=0); + +/****************************************************************************** +* abstract class Vector +******************************************************************************/ +template +class Vector : public Matrix +{ +public: + Vector() {} + Vector(const Vector &c); // do not implement! + virtual ~Vector() {} + + string tostring() const; + + // pure virtual functions + virtual T operator()(INDEX i, INDEX j=0) const=0; + virtual T& operator()(INDEX i, INDEX j=0) =0; + virtual T operator[](INDEX i) const=0; + virtual T& operator[](INDEX i) =0; + virtual INDEX nRows() const=0; + virtual T* get_ptr() const=0; + virtual void resize(INDEX nRows, INDEX nCols=1, bool copy=0)=0; + virtual void reset(INDEX nRows, INDEX nCols=1, bool zero=0)=0; + virtual void copy(const T * ptr, INDEX nRows, INDEX nCols=1)=0; + void write_restart(FILE *f) const; // will be virtual + + + // output to matlab + using Matrix::matlab; + void matlab(ostream &o, const string &s="v") const; + + INDEX nCols() const; + bool in_range(INDEX i) const; + bool same_size(const Vector &m) const; + static bool same_size(const Vector &a, const Vector &b); + + protected: + //* don't allow this + Vector& operator=(const Vector &r); +}; + +/////////////////////////////////////////////////////////////////////////////// +//* performs a matrix-vector multiply with default naive implementation +template +void MultMv(const Matrix &A, const Vector &v, DenseVector &c, + const bool At, T a, T b) +{ + const INDEX sA[2] = {A.nRows(), A.nCols()}; // m is sA[At] k is sA[!At] + const INDEX M=sA[At], K=sA[!At]; + GCK(A, v, v.size()!=K, "MultAb: matrix-vector multiply"); + if (c.size() != M) + { + c.resize(M); // set size of C + c.zero(); // do not add result to C + } + else c *= b; + for (INDEX p=0; p +DenseVector operator*(const Matrix &A, const Vector &b) +{ + DenseVector c; + MultMv(A, b, c, 0, 1.0, 0.0); + return c; +} +/////////////////////////////////////////////////////////////////////////////// +//* Operator for Vector-matrix product +template +DenseVector operator*(const Vector &a, const Matrix &B) +{ + DenseVector c; + MultMv(B, a, c, 1, 1.0, 0.0); + return c; +} +/////////////////////////////////////////////////////////////////////////////// +//* Multiply a vector by a scalar +template +DenseVector operator*(const Vector &v, const T s) +{ + DenseVector r(v); + r*=s; + return r; +} +/////////////////////////////////////////////////////////////////////////////// +//* Multiply a vector by a scalar - communitive +template +DenseVector operator*(const T s, const Vector &v) +{ + DenseVector r(v); + r*=s; + return r; +} +/////////////////////////////////////////////////////////////////////////////// +//* inverse scaling operator - must always create memory +template +DenseMatrix operator/(const Vector &v, const T s) +{ + DenseVector r(v); + r*=(1.0/s); // for integer types this may be worthless + return ; +} +/////////////////////////////////////////////////////////////////////////////// +//* Operator for Vector-Vector sum +template +DenseVector operator+(const Vector &a, const Vector &b) +{ + DenseVector c(a); + c+=b; + return c; +} +/////////////////////////////////////////////////////////////////////////////// +//* Operator for Vector-Vector subtraction +template +DenseVector operator-(const Vector &a, const Vector &b) +{ + DenseVector c(a); + c-=b; + return c; +} + +/////////////////////////////////////////////////////////////////////////////// +// Template definitions /////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//* output operator +template +string Vector::tostring() const +{ + string s; + FORi s += string(i?"\t":"") + ATC_STRING::tostring(VIDX(i),5); + return s; +} +/////////////////////////////////////////////////////////////////////////////// +//* Writes a matlab script defining the vector to the stream +template +void Vector::matlab(ostream &o, const string &s) const +{ + o << s <<"=zeros(" << this->size() << ",1);\n"; + FORi o << s << "("< +void Vector::write_restart(FILE *f) const +{ + INDEX size = this->size(); + fwrite(&size, sizeof(INDEX),1,f); + if (size) fwrite(this->get_ptr(), sizeof(T), this->size(), f); +} +/////////////////////////////////////////////////////////////////////////////// +//* returns the number of columns; always 1 +template +inline INDEX Vector::nCols() const +{ + return 1; +} +/////////////////////////////////////////////////////////////////////////////// +//* returns true if INDEX i is within the range of the vector +template +bool Vector::in_range(INDEX i) const +{ + return isize(); +} +/////////////////////////////////////////////////////////////////////////////// +//* returns true if m has the same number of elements this vector +template +bool Vector::same_size(const Vector &m) const +{ + return this->size() == m.size(); +} +/////////////////////////////////////////////////////////////////////////////// +//* returns true if a and b have the same number of elements +template +inline bool Vector::same_size(const Vector &a, const Vector &b) +{ + return a.same_size(b); +} + + +#endif diff --git a/lib/atc/XT_Function.cpp b/lib/atc/XT_Function.cpp new file mode 100644 index 0000000000..03ff021aa4 --- /dev/null +++ b/lib/atc/XT_Function.cpp @@ -0,0 +1,257 @@ +#include "XT_Function.h" +#include "ATC_Error.h" + +namespace ATC { + + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + // XT_Function + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + XT_Function::XT_Function(int narg, double* args) + { +// NOTE need to adjust scaling to match input nodal coordinates + if (narg > 5 ) { // NOTE kludge for temporal only functions + x0[0] = args[0]; + x0[1] = args[1]; + x0[2] = args[2]; + mask[0] = args[3]; + mask[1] = args[4]; + mask[2] = args[5]; + } + else { + x0[0] = 0.0; + x0[1] = 0.0; + x0[2] = 0.0; + mask[0] = 0.0; + mask[1] = 0.0; + mask[2] = 0.0; + } + } + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + // XT_Function_Mgr + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- +XT_Function_Mgr * XT_Function_Mgr::myInstance_ = NULL; + +// ----------------------------------------------------------------- +// instance() +// ----------------------------------------------------------------- +XT_Function_Mgr * XT_Function_Mgr::instance() +{ + if (myInstance_ == NULL) { + myInstance_ = new XT_Function_Mgr(); + } + return myInstance_; +} + + + // Destructor + XT_Function_Mgr::~XT_Function_Mgr() + { + // Delete all functions created using "new" + for (int i = 0; i < xtFunctionVec_.size(); i++) { + delete xtFunctionVec_[i]; + } + } + + // add user function into the if statement and assign returnFunction to it + XT_Function* XT_Function_Mgr::get_function(string & type, int nargs, double * args) + { + XT_Function * returnFunction; + if (type=="constant") { + returnFunction = new ConstantFunction(nargs,args); + } + else if (type=="temporal_ramp") { + returnFunction = new TemporalRamp(nargs,args); + } + else if (type=="linear") + returnFunction = new LinearFunction(nargs,args); + else if (type=="quadratic") + returnFunction = new QuadraticFunction(nargs,args); + else if (type=="sine") + returnFunction = new SineFunction(nargs,args); + else if (type=="gaussian") + returnFunction = new GaussianFunction(nargs,args); + else if (type=="gaussian_temporal_ramp") + returnFunction = new GaussianTemporalRamp(nargs,args); + else + throw ATC_Error(0,"Bad user function name"); + + xtFunctionVec_.push_back(returnFunction); + + return returnFunction; + } + + // add user function into the if statement and assign returnFunction to it + XT_Function* XT_Function_Mgr::get_function(char ** args, int nargs) + { + string type = args[0]; + int narg = nargs -1; + double dargs[narg]; + for (int i = 0; i < narg; ++i) dargs[i] = atof(args[i+1]); + + return get_function(type, narg, dargs); + } + + // add constant function + XT_Function* XT_Function_Mgr::get_constant_function(double c) + { + double args[1] = {c}; // NOTE kludge + XT_Function * returnFunction = new ConstantFunction(1,args); + xtFunctionVec_.push_back(returnFunction); + return (returnFunction); + } + + XT_Function* XT_Function_Mgr::copy_XT_function(XT_Function* other) + { + string tag = other->get_tag(); + + XT_Function * returnFunction = NULL; + if (tag=="linear") { + LinearFunction * other_cast = (LinearFunction*) other; + returnFunction = new LinearFunction(*other_cast); + } + if (tag=="quadratic") { + QuadraticFunction * other_cast = (QuadraticFunction*) other; + returnFunction = new QuadraticFunction(*other_cast); + } + else if (tag=="sine") { + SineFunction * other_cast = (SineFunction*) other; + returnFunction = new SineFunction(*other_cast); + } + else if (tag=="gaussian") { + GaussianFunction * other_cast = (GaussianFunction*) other; + returnFunction = new GaussianFunction(*other_cast); + } + else if (tag=="gaussian_temporal_ramp") { + GaussianTemporalRamp * other_cast = (GaussianTemporalRamp*) other; + returnFunction = new GaussianTemporalRamp(*other_cast); + } + else if (tag=="temporal_ramp") { + TemporalRamp * other_cast = (TemporalRamp*) other; + returnFunction = new TemporalRamp(*other_cast); + } + return returnFunction; + } + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + // ConstantFunction + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + ConstantFunction::ConstantFunction(int narg, double* args) + : XT_Function(narg,args), + C0(args[0]) + { + tag = "constant"; + } + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + // LinearFunction + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + LinearFunction::LinearFunction(int narg, double* args) + : XT_Function(narg,args) + { + C0 = args[6]; + tag = "linear"; + } + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + // QuadraticFunction + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + QuadraticFunction::QuadraticFunction(int narg, double* args) + : XT_Function(narg,args) + { + C0 = args[6]; + C2[0] = args[7]; + C2[1] = args[8]; + C2[2] = args[9]; + C2[3] = args[10]; + C2[4] = args[11]; + C2[5] = args[12]; + tag = "quadratic"; + } + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + // SineFunction + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + SineFunction::SineFunction(int narg, double* args) + : XT_Function(narg,args) + { + C = args[6]; + w = args[7]; + tag = "sine"; + } + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + // GaussianFunction + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + GaussianFunction::GaussianFunction(int narg, double* args) + : XT_Function(narg,args) + { + tau = args[6]; + C = args[7]; + C0 = args[8]; + tag = "gaussian"; + } + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + // GaussianTemporalRamp + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + GaussianTemporalRamp::GaussianTemporalRamp(int narg, double* args) + : GaussianFunction(narg,args) + { + tau_initial = args[9]; + C_initial = args[10]; + C0_initial = args[11]; + double delta_t = args[12]; + + tau_slope = (tau - tau_initial)/delta_t; + C_slope = (C - C_initial)/delta_t; + C0_slope = (C0 - C0_initial)/delta_t; + + tag = "gaussian_temporal_ramp"; + } + double GaussianTemporalRamp::f(double* x, double t) { + tau = tau_initial + tau_slope*t; + C = C_initial + C_slope*t; + C0 = C0_initial + C0_slope*t; + return GaussianFunction::f(x,t); + } + double GaussianTemporalRamp::dfdt(double* x, double t) { + tau = tau_initial + tau_slope*t; + C = C_initial + C_slope*t; + C0 = C0_initial + C0_slope*t; + double dfdt = 0.; + dfdt += C_slope*exp(-(mask[0]*(x[0]-x0[0])*(x[0]-x0[0]) + +mask[1]*(x[1]-x0[1])*(x[1]-x0[1]) + +mask[2]*(x[2]-x0[2])*(x[2]-x0[2])) + /tau/tau); + dfdt += C*exp(2.*tau_slope*(mask[0]*(x[0]-x0[0])*(x[0]-x0[0]) + +mask[1]*(x[1]-x0[1])*(x[1]-x0[1]) + +mask[2]*(x[2]-x0[2])*(x[2]-x0[2])) + /tau/tau/tau); + dfdt += C0_slope; + return dfdt; + } + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + // TemporalRamp + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- + TemporalRamp::TemporalRamp(int narg, double* args) + : XT_Function(narg,args) + { + f_initial = args[0]; + double f_final = args[1]; + double delta_t = args[2]; + slope = (f_final - f_initial)/delta_t; + tag = "temporal_ramp"; + } +} diff --git a/lib/atc/XT_Function.h b/lib/atc/XT_Function.h new file mode 100644 index 0000000000..616014e6ee --- /dev/null +++ b/lib/atc/XT_Function.h @@ -0,0 +1,172 @@ +#ifndef XT_FUNCTION_H +#define XT_FUNCTION_H + +#include +#include +#include +#include + +using namespace std; + +namespace ATC { + //------------------------------------------------------------------------ + // base class + //------------------------------------------------------------------------ + class XT_Function { + public: + XT_Function(int nargs, double* args); + ~XT_Function(void) {}; + + const string & get_tag() { return tag;} + + /** function value */ + virtual inline double f(double* x, double t) {return 0.0;}; + /** time derivative of function */ + virtual inline double dfdt(double* x, double t) {return 0.0;}; + /** 2nd time derivative of funtion */ + virtual inline double ddfdt(double* x, double t) {return 0.0;}; + /** 3rd time derivative of funtion */ + virtual inline double dddfdt(double* x, double t) {return 0.0;}; + + protected: + /** mask : masks x,y,z dependence, x0 : origin */ + double mask[3], x0[3]; + /** tag : name of function */ + string tag; + }; + + //------------------------------------------------------------------------ + // manager + //------------------------------------------------------------------------ + class XT_Function_Mgr { + public: + /** Static instance of this class */ + static XT_Function_Mgr * instance(); + + XT_Function* get_function(string & type, int nargs, double * arg); + XT_Function* get_function(char ** arg, int nargs); + XT_Function* get_constant_function(double c); + XT_Function* copy_XT_function(XT_Function* other); + protected: + XT_Function_Mgr() {}; + ~XT_Function_Mgr(); + /** Vector of ptrs to functions created thus far */ + std::vector xtFunctionVec_; + private: + static XT_Function_Mgr * myInstance_; + }; + + //------------------------------------------------------------------------ + // derived classes + //------------------------------------------------------------------------ + + /** a constant */ + class ConstantFunction : public XT_Function { + public: + ConstantFunction(int nargs, double* args); + ~ConstantFunction(void) {}; + + inline double f(double* x, double t) + {return C0;}; + + private : + double C0; + }; + + /** a linear in x-y-z */ + class LinearFunction : public XT_Function { + public: + LinearFunction(int nargs, double* args); + ~LinearFunction(void) {}; + + inline double f(double* x, double t) + {return mask[0]*(x[0]-x0[0])+mask[1]*(x[1]-x0[1])+mask[2]*(x[2]-x0[2]) + C0;}; + + private : + double C0; + }; + + /** a quadratic in x-y-z */ + class QuadraticFunction : public XT_Function { + public: + QuadraticFunction(int nargs, double* args); + ~QuadraticFunction(void) {}; + + inline double f(double* x, double t) + {return + C2[0]*(x[0]-x0[0])*(x[0]-x0[0])+ + C2[1]*(x[1]-x0[1])*(x[1]-x0[1])+ + C2[2]*(x[2]-x0[2])*(x[2]-x0[2])+ + 2.0*C2[3]*(x[0]-x0[0])*(x[1]-x0[1]) + + 2.0*C2[4]*(x[0]-x0[0])*(x[2]-x0[2]) + + 2.0*C2[5]*(x[1]-x0[1])*(x[2]-x0[2]) + + mask[0]*(x[0]-x0[0])+mask[1]*(x[1]-x0[1])+mask[2]*(x[2]-x0[2]) + C0;}; + + private : + double C0, C2[6]; // C2 1:xx 2:yy 3:zz 4:xy|yx 5:xz|zx 6:yz|zy + }; + + class SineFunction : public XT_Function { + public: + SineFunction(int nargs, double* args); + ~SineFunction(void); + + inline double f(double* x, double t) + {return C*sin( mask[0]*(x[0]-x0[0]) + +mask[1]*(x[1]-x0[1]) + +mask[2]*(x[2]-x0[2]) - w*t);}; + + private : + double C, w; + }; + + /** a spatial gaussian function */ + class GaussianFunction : public XT_Function { + public: + GaussianFunction(int nargs, double* args); + ~GaussianFunction(void){}; + + // 1/(2 pi \sigma)^(n/2) exp(-1/2 x.x/\sigma^2 ) for n = dimension + inline double f(double* x, double t) + {return C*exp(-(mask[0]*(x[0]-x0[0])*(x[0]-x0[0]) + +mask[1]*(x[1]-x0[1])*(x[1]-x0[1]) + +mask[2]*(x[2]-x0[2])*(x[2]-x0[2]))/tau/tau) + C0;}; + + protected: + double tau, C, C0; + }; + + /** a spatial gaussian function that has variables ramp up in time */ + class GaussianTemporalRamp : public GaussianFunction { + public: + GaussianTemporalRamp(int nargs, double* args); + ~GaussianTemporalRamp(void){}; + + double f(double* x, double t); + double dfdt(double* x, double t); + + protected: + double tau_initial, tau_slope; + double C_initial, C_slope; + double C0_initial, C0_slope; + }; + + /** a ramp in time */ + class TemporalRamp : public XT_Function { + public: + TemporalRamp(int nargs, double* args); + ~TemporalRamp(void) {}; + + inline double f(double* x, double t) + {return f_initial + slope*t;}; + + inline double dfdt(double* x, double t) + {return slope;}; + + private : + double f_initial, slope; + }; + + +} +#endif