/* ---------------------------------------------------------------------- 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. ------------------------------------------------------------------------- */ #include "math.h" #include "stdlib.h" #include "string.h" #include "fix_heat_gran.h" #include "atom.h" #include "domain.h" #include "group.h" #include "force.h" #include "comm.h" #include "update.h" #include "error.h" #include "modify.h" #include "neighbor.h" #include "neigh_list.h" #include "neigh_request.h" #include "pair_gran.h" #include "math_extra.h" #include "fix_property_global.h" #include "fix_property_atom.h" #include "fix_scalar_transport_equation.h" #include "mech_param_gran.h" #include "respa.h" #include "compute_pair_gran_local.h" using namespace LAMMPS_NS; using namespace FixConst; #define SMALL 1e-8 /* ---------------------------------------------------------------------- */ FixHeatGran::FixHeatGran(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { if ((!atom->radius_flag)||(!atom->rmass_flag)) error->all(FLERR,"Fix heat/gran needs per particle radius and mass"); if (narg < 5) error->fix_error(FLERR,this,"not enough arguments"); int iarg = 3; if(strcmp(arg[iarg++],"initial_temperature")) error->fix_error(FLERR,this,"expecting keyword 'initial_temperature'"); T0 = atof(arg[iarg++]); area_correction_flag = 0; bool hasargs = true; while(iarg < narg && hasargs) { hasargs = false; if(strcmp(arg[iarg],"area_correction") == 0) { if (iarg+2 > narg) error->fix_error(FLERR,this,"not enough arguments for keyword 'area_correction'"); if(strcmp(arg[iarg+1],"yes") == 0) area_correction_flag = 1; else if(strcmp(arg[iarg+1],"no") == 0) area_correction_flag = 0; else error->fix_error(FLERR,this,""); iarg += 2; hasargs = true; } else if(strcmp(style,"heat/gran") == 0) error->fix_error(FLERR,this,"unknown keyword"); } fix_temp = fix_heatFlux = fix_heatSource = NULL; fix_conductivity = NULL; fix_ste = NULL; conductivity = NULL; peratom_flag = 1; //NP 0/1 if per-atom data is stored size_peratom_cols = 0; //NP 0 = scalar, N = columns in peratom array peratom_freq = 1; time_depend = 1; scalar_flag = 1; //NP total thermal energy computed global_freq = 1; //NP available always cpl = NULL; } /* ---------------------------------------------------------------------- */ FixHeatGran::~FixHeatGran() { //NP could delete fixes with no callbacks here since FixHeatGran has no callbacks if(conductivity) delete []conductivity; } /* ---------------------------------------------------------------------- */ void FixHeatGran::post_create() { fix_ste = modify->find_fix_scalar_transport_equation("heattransfer"); if(!fix_ste) { char **newarg = new char*[15]; newarg[0] = (char *) "ste_heattransfer"; newarg[1] = group->names[igroup]; newarg[2] = (char *) "transportequation/scalar"; newarg[3] = (char *) "equation_id"; newarg[4] = (char *) "heattransfer"; newarg[5] = (char *) "quantity"; newarg[6] = (char *) "Temp"; newarg[7] = (char *) "default_value"; newarg[8] = new char[30]; sprintf(newarg[8],"%f",T0); newarg[9] = (char *) "flux_quantity"; newarg[10] = (char *) "heatFlux"; newarg[11] = (char *) "source_quantity"; newarg[12] = (char *) "heatSource"; newarg[13] = (char *) "capacity_quantity"; newarg[14] = (char *) "thermalCapacity"; modify->add_fix(15,newarg); delete [] newarg[8]; delete [] newarg; } } /* ---------------------------------------------------------------------- */ void FixHeatGran::pre_delete(bool unfixflag) { // tell cpl that this fix is deleted if(cpl && unfixflag) cpl->reference_deleted(); } /* ---------------------------------------------------------------------- */ int FixHeatGran::setmask() { int mask = 0; mask |= POST_FORCE; return mask; } /* ---------------------------------------------------------------------- */ void FixHeatGran::updatePtrs() { Temp = fix_temp->vector_atom; vector_atom = Temp; //NP so that it is possible to access it from the input script heatFlux = fix_heatFlux->vector_atom; heatSource = fix_heatSource->vector_atom; } /* ---------------------------------------------------------------------- */ void FixHeatGran::init() { const double *Y,*nu,*Y_orig; double expo,Yeff_ij,Yeff_orig_ij,ratio; Fix* ymo_fix; if (!atom->radius_flag || !atom->rmass_flag) error->all(FLERR,"Please use a granular atom style for fix heat/gran"); // check if a fix of this style already exists if(modify->n_fixes_style(style) > 1) error->fix_error(FLERR,this,"cannot have more than one fix of this style"); if(!force->pair_match("gran", 0)) error->all(FLERR,"Please use a granular pair style for fix heat/gran"); pair_gran = static_cast(force->pair_match("gran", 0)); history_flag = pair_gran->is_history(); fix_ste = modify->find_fix_scalar_transport_equation("heattransfer"); if(!fix_ste) error->all(FLERR,"Fix heat/gran needs a fix transportequation/scalar to work with"); fix_temp = static_cast(modify->find_fix_property("Temp","property/atom","scalar",0,0,style)); fix_heatFlux = static_cast(modify->find_fix_property("heatFlux","property/atom","scalar",0,0,style)); fix_heatSource = static_cast(modify->find_fix_property("heatSource","property/atom","scalar",0,0,style)); int max_type = pair_gran->mpg->max_type(); if(conductivity) delete []conductivity; conductivity = new double[max_type]; fix_conductivity = static_cast(modify->find_fix_property("thermalConductivity","property/global","peratomtype",max_type,0,style)); // pre-calculate conductivity for possible contact material combinations //NP apply coarse graining for(int i=1;i< max_type+1; i++) for(int j=1;jcg()*force->cg()*fix_conductivity->compute_vector(i-1); if(conductivity[i-1] < 0.) error->all(FLERR,"Fix heat/gran: Thermal conductivity must not be < 0"); } // calculate heat transfer correction ymo_fix = NULL; if(area_correction_flag) { ymo_fix = modify->find_fix_property("youngsModulusOriginal","property/global","peratomtype",0,0,style); if(force->pair_match("gran/hooke",0)) expo = 1.; else if(force->pair_match("gran/hertz",0)) expo = 2./3.; else error->all(FLERR,"Fix heat/gran with area correction could not identify the granular pair style you are using, supported are hooke and hertz types"); Y = static_cast(modify->find_fix_property("youngsModulus","property/global","peratomtype",max_type,0,style))->get_values(); nu = static_cast(modify->find_fix_property("poissonsRatio","property/global","peratomtype",max_type,0,style))->get_values(); Y_orig = static_cast(modify->find_fix_property("youngsModulusOriginal","property/global","peratomtype",max_type,0,style))->get_values(); // allocate a new array within youngsModulusOriginal static_cast(modify->find_fix_property("youngsModulusOriginal","property/global","peratomtype",max_type,0,style))->new_array(max_type,max_type); // feed deltan_ratio into this array for(int i = 1; i < max_type+1; i++) { for(int j = 1; j < max_type+1; j++) { Yeff_ij = 1./((1.-pow(nu[i-1],2.))/Y[i-1] +(1.-pow(nu[j-1],2.))/Y[j-1]); Yeff_orig_ij = 1./((1.-pow(nu[i-1],2.))/Y_orig[i-1]+(1.-pow(nu[j-1],2.))/Y_orig[j-1]); ratio = pow(Yeff_ij/Yeff_orig_ij,expo); /*NL*/ //fprintf(screen,"ratio for type pair %d/%d is %f, Yeff_ij %f, Yeff_orig_ij %f, expo %f\n",i,j,ratio,Yeff_ij,Yeff_orig_ij,expo); static_cast(modify->find_fix_property("youngsModulusOriginal","property/global","peratomtype",max_type,0,style))->array_modify(i-1,j-1,ratio); } } // get reference to deltan_ratio deltan_ratio = static_cast(modify->find_fix_property("youngsModulusOriginal","property/global","peratomtype",max_type,0,style))->get_array_modified(); } //NP Get pointer to all the fixes (also those that have the material properties) updatePtrs(); } /* ---------------------------------------------------------------------- */ void FixHeatGran::post_force(int vflag) { //template function for using touchflag or not if(history_flag == 0) post_force_eval<0>(vflag,0); if(history_flag == 1) post_force_eval<1>(vflag,0); } /* ---------------------------------------------------------------------- */ void FixHeatGran::cpl_evaluate(ComputePairGranLocal *caller) { if(caller != cpl) error->all(FLERR,"Illegal situation in FixHeatGran::cpl_evaluate"); if(history_flag == 0) post_force_eval<0>(0,1); if(history_flag == 1) post_force_eval<1>(0,1); } /* ---------------------------------------------------------------------- */ template void FixHeatGran::post_force_eval(int vflag,int cpl_flag) { double hc,contactArea,delta_n,flux; int i,j,ii,jj,inum,jnum; double xtmp,ytmp,ztmp,delx,dely,delz; double radi,radj,radsum,rsq,r,rinv,rsqinv,tcoi,tcoj; int *ilist,*jlist,*numneigh,**firstneigh; int *touch,**firsttouch; int newton_pair = force->newton_pair; //NP see implementation of match in atom.cpp, this accounts for hybrid/overlay if (strcmp(force->pair_style,"hybrid")==0) error->warning(FLERR,"Fix heat/gran implementation may not be valid for pair style hybrid"); if (strcmp(force->pair_style,"hybrid/overlay")==0) error->warning(FLERR,"Fix heat/gran implementation may not be valid for pair style hybrid/overlay"); inum = pair_gran->list->inum; ilist = pair_gran->list->ilist; numneigh = pair_gran->list->numneigh; firstneigh = pair_gran->list->firstneigh; if(HISTFLAG) firsttouch = pair_gran->listgranhistory->firstneigh; double *radius = atom->radius; double *rmass = atom->rmass; double **x = atom->x; int *type = atom->type; int nlocal = atom->nlocal; int *mask = atom->mask; //NP update because re-allocation might have taken place updatePtrs(); // loop over neighbors of my atoms for (ii = 0; ii < inum; ii++) { i = ilist[ii]; xtmp = x[i][0]; ytmp = x[i][1]; ztmp = x[i][2]; radi = radius[i]; jlist = firstneigh[i]; jnum = numneigh[i]; if(HISTFLAG) touch = firsttouch[i]; for (jj = 0; jj < jnum; jj++) { j = jlist[jj]; j &= NEIGHMASK; if (!(mask[i] & groupbit) && !(mask[j] & groupbit)) continue; //NP no touch flag available - need to evaluate here if(!HISTFLAG) { delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; rsq = delx*delx + dely*dely + delz*delz; radj = radius[j]; radsum = radi + radj; } //NP touchflag if available or distance if no touchflag available if (HISTFLAG && touch[jj] || !HISTFLAG && (rsq < radsum*radsum)) { //contact //NP for history we need to if(HISTFLAG) { delx = xtmp - x[j][0]; dely = ytmp - x[j][1]; delz = ztmp - x[j][2]; rsq = delx*delx + dely*dely + delz*delz; radj = radius[j]; radsum = radi + radj; } r = sqrt(rsq); //NP adjust overlap that may be superficially large due to softening if(area_correction_flag) { delta_n = radsum - r; delta_n *= deltan_ratio[type[i]-1][type[j]-1]; r = radsum - delta_n; } contactArea = - M_PI/4 * ( (r-radi-radj)*(r+radi-radj)*(r-radi+radj)*(r+radi+radj) )/(r*r); //contact area of the two spheres tcoi = conductivity[type[i]-1]; tcoj = conductivity[type[j]-1]; if (tcoi < SMALL || tcoj < SMALL) hc = 0.; else hc = 4.*tcoi*tcoj/(tcoi+tcoj)*sqrt(contactArea); flux = (Temp[j]-Temp[i])*hc; if(!cpl_flag) { heatFlux[i] += flux; if (newton_pair || j < nlocal) heatFlux[j] -= flux; } if(cpl_flag && cpl) cpl->add_heat(i,j,flux); } } } //NP reverse comm to send heat fluxes back //NP only necessary in case of newton_pair=1, since pair stored once on all procs if(newton_pair) fix_heatFlux->do_reverse_comm(); } /* ---------------------------------------------------------------------- */ double FixHeatGran::compute_scalar() { return fix_ste->compute_scalar(); } /* ---------------------------------------------------------------------- register and unregister callback to compute ------------------------------------------------------------------------- */ void FixHeatGran::register_compute_pair_local(ComputePairGranLocal *ptr) { /*NL*///fprintf(screen,"FixHeatGran::register_compute_pair_local, ptr->id %s\n",ptr->id); if(cpl != NULL) error->all(FLERR,"Fix heat/gran allows only one compute of type pair/local"); cpl = ptr; } void FixHeatGran::unregister_compute_pair_local(ComputePairGranLocal *ptr) { /*NL*///fprintf(screen,"FixHeatGran::unregister_compute_pair_local\n"); if(cpl != ptr) error->all(FLERR,"Illegal situation in FixHeatGran::unregister_compute_pair_local"); cpl = NULL; }