/* ---------------------------------------------------------------------- LIGGGHTS - LAMMPS Improved for General Granular and Granular Heat Transfer Simulations LIGGGHTS is part of the CFDEMproject www.liggghts.com | www.cfdem.com Christoph Kloss, christoph.kloss@cfdem.com Copyright 2009-2012 JKU Linz Copyright 2012- DCS Computing GmbH, Linz LIGGGHTS is based on LAMMPS LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov This software is distributed under the GNU General Public License. See the README file in the top-level directory. ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- Contributing authors: Christoph Kloss (JKU Linz, DCS Computing GmbH, Linz) Philippe Seil (JKU Linz) Evan Smuts (U Cape Town, surface velocity rotation) ------------------------------------------------------------------------- */ #include "fix_mesh.h" #include #include #include "error.h" #include "force.h" #include "bounding_box.h" #include "input_mesh_tri.h" #include "fix_contact_history.h" #include "fix_neighlist_mesh.h" #include "multi_node_mesh.h" #include "modify.h" #include "comm.h" #include "math_extra.h" using namespace LAMMPS_NS; using namespace FixConst; #define EPSILON_V 0.00001 FixMesh::FixMesh(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg), mesh_(NULL), atom_type_mesh_(-1), setupFlag_(false), pOpFlag_(false), manipulated_(false) { if(narg < 5) error->fix_error(FLERR,this,"not enough arguments - at least keyword 'file' and a filename are required."); //NP indicate that restart data is stored restart_global = 1; //NP this fix can force reneighboring force_reneighbor = 1; next_reneighbor = -1; // parse args iarg_ = 3; char mesh_fname[256]; if(strcmp(arg[iarg_++],"file")) error->fix_error(FLERR,this,"expecting keyword 'file'"); strcpy(mesh_fname,arg[iarg_++]); // parse type if(strcmp(arg[iarg_],"type") == 0) { iarg_++; atom_type_mesh_ = force->inumeric(arg[iarg_++]); } // construct a mesh - can be surface or volume mesh //NP need to do this in post_create() so can call virtual function // just create object and return if reading data from restart file //NP do not parse further args or if(modify->have_restart_data(this)) create_mesh_restart(); else create_mesh(mesh_fname); /*NL*/ //if(comm->me == 0) fprintf(screen,"# elements before parallel %d\n",mesh_->size()); // parse further args bool hasargs = true; while(iarg_ < narg && hasargs) { hasargs = false; if(strcmp(arg[iarg_],"move") == 0) { if (narg < iarg_+4) error->fix_error(FLERR,this,"not enough arguments"); moveMesh(force->numeric(arg[iarg_+1]),force->numeric(arg[iarg_+2]),force->numeric(arg[iarg_+3])); manipulated_ = true; iarg_ += 4; hasargs = true; } else if(strcmp(arg[iarg_],"rotate") == 0) { if (narg < iarg_+7) error->fix_error(FLERR,this,"not enough arguments"); if(strcmp(arg[iarg_+1],"axis")) error->fix_error(FLERR,this,"expecting keyword 'axis' after keyword 'rotate'"); if(strcmp(arg[iarg_+5],"angle")) error->fix_error(FLERR,this,"expecting keyword 'angle' after axis definition"); rotateMesh(force->numeric(arg[iarg_+2]),force->numeric(arg[iarg_+3]),force->numeric(arg[iarg_+4]), force->numeric(arg[iarg_+6])); manipulated_ = true; iarg_ += 7; hasargs = true; } else if(strcmp(arg[iarg_],"scale") == 0) { if (narg < iarg_+2) error->fix_error(FLERR,this,"not enough arguments"); scaleMesh(force->numeric(arg[iarg_+1])); manipulated_ = true; iarg_ += 2; hasargs = true; } else if (strcmp(arg[iarg_],"temperature") == 0) { iarg_++; double Temp_mesh = atof(arg[iarg_++]); mesh_->prop().addGlobalProperty< ScalarContainer >("Temp","comm_none","frame_invariant","restart_yes"); mesh_->prop().setGlobalProperty< ScalarContainer >("Temp",Temp_mesh); mesh_->prop().addGlobalProperty< ScalarContainer >("heatFlux","comm_none","frame_invariant","restart_no"); mesh_->prop().setGlobalProperty< ScalarContainer >("heatFlux",0.); mesh_->prop().addGlobalProperty< ScalarContainer >("heatFluxTotal","comm_none","frame_invariant","restart_yes"); mesh_->prop().setGlobalProperty< ScalarContainer >("heatFluxTotal",0.); /*NL*///ScalarContainer &test = *mesh_->prop().getMeshProperty< ScalarContainer >("Temp"); /*NL*///fprintf(screen,"test %f\n",test(0)); /*NL*///error->all(FLERR,"end"); hasargs = true; } } } /* ---------------------------------------------------------------------- */ FixMesh::~FixMesh() { delete mesh_; } /* ---------------------------------------------------------------------- */ void FixMesh::post_create() { } /* ---------------------------------------------------------------------- */ void FixMesh::create_mesh(char *mesh_fname) { //NP cannot do this object-oriented since cannot call //NP virtual functions out of constructor if(strncmp(style,"mesh/surface",12) == 0) { mesh_ = new TriMesh(lmp); static_cast(mesh_)->setMeshID(id); // read file // can be from STL file or VTK file InputMeshTri *mesh_input = new InputMeshTri(lmp,0,NULL); /*NL*///fprintf(screen,"READING MESH DATA\n"); mesh_input->meshtrifile(mesh_fname,static_cast(mesh_)); delete mesh_input; } else error->one(FLERR,"Illegal implementation of create_mesh();"); } /* ---------------------------------------------------------------------- */ void FixMesh::create_mesh_restart() { //NP cannot do this object-oriented since cannot call virtual functions //NP out of constructor if(strncmp(style,"mesh/surface",12) == 0) { mesh_ = new TriMesh(lmp); static_cast(mesh_)->setMeshID(id); } else error->one(FLERR,"Illegal implementation of create_mesh();"); } /* ---------------------------------------------------------------------- */ void FixMesh::pre_delete(bool unfixflag) { // error if moving mesh is operating on a mesh to be deleted //NP may only throw error upon unfixing - otherwise error would produce segfault //NP because Lammps::destroy() has been called already if(unfixflag) { if(mesh_->isMoving()) error->fix_error(FLERR,this, "illegal unfix command, may not unfix a mesh while a fix move is applied." "Unfix the fix move/mesh first"); } } /* ---------------------------------------------------------------------- */ int FixMesh::setmask() { int mask = 0; mask |= PRE_EXCHANGE; mask |= PRE_FORCE; mask |= FINAL_INTEGRATE; return mask; } /* ---------------------------------------------------------------------- */ void FixMesh::setup_pre_force(int vflag) { // first-time set-up //NP mesh_->parallelize() does equivalent of exchange(), borders() if(!setupFlag_) { initialSetup(); setupFlag_ = true; } // if mesh already set-up and parallelized //NP as in Verlet::setup() //NP call with setup flag so know to copy node_orig_ if necessary //NP copy node_orig_ is necessary if a second fix move/mesh is added else { mesh_->pbcExchangeBorders(1); } // clear reverse comm properties mesh_->clearReverse(); pOpFlag_ = false; /*NL*/ //fprintf(screen,"setup fix %s end\n",id); } /* ---------------------------------------------------------------------- */ void FixMesh::initialSetup() { //NP distribute mesh across processors //NP deletes all nodes that are not in my subbox and executes borders() //NP note that scale, translate, rotate can change node location //NP cannot do this before since needs subbox bounds which are set //NP via Verlet::setup() which calls domain->reset_box() //NP also note that must do after parsing args since curvature influences //NP neighbor build which is performed here as well mesh_->initalSetup(); // warn if there are elements that extend outside box if(!mesh_->allNodesInsideSimulationBox()) error->warning(FLERR,"Not all nodes of fix mesh inside simulation box, " "elements will be deleted or wrapped around periodic boundary conditions"); if(comm->me == 0) fprintf(screen,"Import and parallelization of mesh %s containing %d triangle(s) successful\n", id,mesh_->sizeGlobal()); } /* ---------------------------------------------------------------------- invoke parallelism ------------------------------------------------------------------------- */ void FixMesh::pre_exchange() { // flag parallel operations on this step //NP pre_exchange() is called on re-neigh steps only pOpFlag_ = true; } /* ---------------------------------------------------------------------- forward comm for mesh ------------------------------------------------------------------------- */ void FixMesh::pre_force(int vflag) { //NP TODO invoke reverse comm later if needed //NP should do this in final_integrate since post_force calculates values // case re-neigh step if(pOpFlag_) { //NP invoke comm //NP also does equivalent of forward comm mesh_->pbcExchangeBorders(0); pOpFlag_ = false; } // case regular step else { mesh_->forwardComm(); if(mesh_->decideRebuild()) { /*NL*/ //fprintf(screen,"mesh triggered neigh build at step %d\n",update->ntimestep); next_reneighbor = update->ntimestep + 1; } } // clear reverse comm properties mesh_->clearReverse(); } /* ---------------------------------------------------------------------- reverse comm for mesh ------------------------------------------------------------------------- */ void FixMesh::final_integrate() { mesh_->reverseComm(); } /* ---------------------------------------------------------------------- */ int FixMesh::min_type() { if(atom_type_mesh_ == -1) return 1; return atom_type_mesh_; } /* ---------------------------------------------------------------------- */ int FixMesh::max_type() { if(atom_type_mesh_ == -1) return 1; return atom_type_mesh_; } /* ---------------------------------------------------------------------- moves the mesh by a vector - (dx dy dz) is the displacement vector ------------------------------------------------------------------------- */ void FixMesh::moveMesh(double const dx, double const dy, double const dz) { if (comm->me == 0) { //fprintf(screen,"moving mesh by "); //fprintf(screen,"%f %f %f\n", dx,dy,dz); } double arg[3] = {dx,dy,dz}; mesh_->move(arg); } /* ---------------------------------------------------------------------- rotates the mesh around an axis through the origin phi the rotation angle (axisX axisY axisZ) vector in direction of the axis ------------------------------------------------------------------------- */ void FixMesh::rotateMesh(double const axisX, double const axisY, double const axisZ, double const phi) { double axis[3] = {axisX,axisY,axisZ}, p[3] = {0.,0.,0.}; if (comm->me == 0) { //fprintf(screen,"rotate "); //fprintf(screen,"%f %f %f %f\n", phi, axisX, axisY, axisZ); } mesh_->rotate(phi*3.14159265/180.0,axis,p); } /* ---------------------------------------------------------------------- scales the mesh by a factor in x, y and z direction can also be used to distort a mesh (factorX factorY factorZ) vector contains the factors to scale the mesh in the xyz direction ------------------------------------------------------------------------- */ void FixMesh::scaleMesh(double const factor) { if (comm->me == 0){ //fprintf(screen,"scale "); //fprintf(screen,"%f \n", factor); } mesh_->scale(factor); } /* ---------------------------------------------------------------------- pack entire state of Fix into one write ------------------------------------------------------------------------- */ void FixMesh::write_restart(FILE *fp) { mesh_->writeRestart(fp); } /* ---------------------------------------------------------------------- use state info from restart file to restart the Fix ------------------------------------------------------------------------- */ void FixMesh::restart(char *buf) { double *list = (double *) buf; mesh_->restart(list); } /* ---------------------------------------------------------------------- change box extent due to mesh node position ------------------------------------------------------------------------- */ void FixMesh::box_extent(double &xlo,double &xhi,double &ylo,double &yhi,double &zlo,double &zhi) { double node[3]; int size = mesh()->size(); int numNodes = mesh()->numNodes(); for(int i = 0; i < size; i++) { /*NL*/ //fprintf(screen,"i %d size %d\n",i,size); for(int j = 0; j < numNodes; j++) { mesh()->node_slow(i,j,node); xlo = MathExtraLiggghts::min(xlo,node[0]); xhi = MathExtraLiggghts::max(xhi,node[0]); ylo = MathExtraLiggghts::min(ylo,node[1]); yhi = MathExtraLiggghts::max(yhi,node[1]); zlo = MathExtraLiggghts::min(zlo,node[2]); zhi = MathExtraLiggghts::max(zhi,node[2]); } } }