1 /* ----------------------------------------------------------------------
2 SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
3 http://sparta.sandia.gov
4 Steve Plimpton, sjplimp@sandia.gov, Michael Gallis, magalli@sandia.gov
5 Sandia National Laboratories
6
7 Copyright (2014) Sandia Corporation. Under the terms of Contract
8 DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
9 certain rights in this software. This software is distributed under
10 the GNU General Public License.
11
12 See the README file in the top-level SPARTA directory.
13 ------------------------------------------------------------------------- */
14
15 /* ----------------------------------------------------------------------
16 Contributing author: Krishnan Gopalan (NASA Ames)
17 ------------------------------------------------------------------------- */
18
19 #include "collide.h"
20 #include "mpi.h"
21 #include "stdio.h"
22 #include "sparta.h"
23 #include "math.h"
24 #include "stdlib.h"
25 #include "string.h"
26 #include "surf_collide_cll.h"
27 #include "surf.h"
28 #include "surf_react.h"
29 #include "input.h"
30 #include "variable.h"
31 #include "particle.h"
32 #include "collide.h"
33 #include "domain.h"
34 #include "update.h"
35 #include "modify.h"
36 #include "comm.h"
37 #include "random_mars.h"
38 #include "random_knuth.h"
39 #include "math_const.h"
40 #include "math_extra.h"
41 #include "error.h"
42
43 using namespace SPARTA_NS;
44 using namespace MathConst;
45
46 enum{NONE,DISCRETE,SMOOTH};
47
48 /* ---------------------------------------------------------------------- */
49
SurfCollideCLL(SPARTA * sparta,int narg,char ** arg)50 SurfCollideCLL::SurfCollideCLL(SPARTA *sparta, int narg, char **arg) :
51 SurfCollide(sparta, narg, arg)
52 {
53 if (narg < 7) error->all(FLERR,"Illegal surf_collide cll command");
54
55 tstr = NULL;
56
57 if (strstr(arg[2],"v_") == arg[2]) {
58 int n = strlen(&arg[2][2]) + 1;
59 tstr = new char[n];
60 strcpy(tstr,&arg[2][2]);
61 } else {
62 twall = atof(arg[2]);
63 if (twall < 0.0) error->all(FLERR,"Illegal surf_collide cll command");
64 }
65
66 acc_n = atof(arg[3]);
67 acc_t = atof(arg[4]);
68 acc_rot = atof(arg[5]);
69 acc_vib = atof(arg[6]);
70
71 if (acc_n < 0.0 || acc_n > 1.0 || acc_t < 0.0 || acc_t > 1.0 ||
72 acc_rot < 0.0 || acc_rot > 1.0 || acc_vib < 0.0 || acc_vib > 1.0)
73 error->all(FLERR,"Surf_collide cll accommodation coeffs "
74 "must be >= 0 and <= 1");
75
76 // optional args
77
78 eccen = 0.0;
79 pflag = 0;
80 tflag = rflag = 0;
81
82 int iarg = 7;
83 while (iarg < narg) {
84
85 if (strcmp(arg[iarg],"partial") == 0) {
86 if (iarg+2 > narg) error->all(FLERR,"Illegal surf_collide cll command");
87 if (acc_n != acc_t)
88 error->all(FLERR,"Surf_collide cll partial requires acc_n = acc_t");
89 pflag = 1;
90 eccen = atof(arg[iarg+1]);
91 if (eccen < 0.0 || eccen >= 1.0 )
92 error->all(FLERR,"Surf_collide cll eccentricity must be >= 0 and <= 1");
93 iarg += 2;
94 } else if (strcmp(arg[iarg],"translate") == 0) {
95 if (iarg+4 > narg) error->all(FLERR,"Illegal surf_collide cll command");
96 tflag = 1;
97 vx = atof(arg[iarg+1]);
98 vy = atof(arg[iarg+2]);
99 vz = atof(arg[iarg+3]);
100 iarg += 4;
101 } else if (strcmp(arg[iarg],"rotate") == 0) {
102 if (iarg+7 > narg) error->all(FLERR,"Illegal surf_collide cll command");
103 rflag = 1;
104 px = atof(arg[iarg+1]);
105 py = atof(arg[iarg+2]);
106 pz = atof(arg[iarg+3]);
107 wx = atof(arg[iarg+4]);
108 wy = atof(arg[iarg+5]);
109 wz = atof(arg[iarg+6]);
110 if (domain->dimension == 2 && pz != 0.0)
111 error->all(FLERR,"Surf_collide cll rotation invalid for 2d");
112 if (domain->dimension == 2 && (wx != 0.0 || wy != 0.0))
113 error->all(FLERR,"Surf_collide cll rotation invalid for 2d");
114 iarg += 7;
115 } else error->all(FLERR,"Illegal surf_collide cll command");
116 }
117
118 if (tflag && rflag) error->all(FLERR,"Illegal surf_collide cll command");
119 if (tflag || rflag) trflag = 1;
120 else trflag = 0;
121
122 vstream[0] = vstream[1] = vstream[2] = 0.0;
123
124 // initialize RNG
125
126 random = new RanKnuth(update->ranmaster->uniform());
127 double seed = update->ranmaster->uniform();
128 random->reset(seed,comm->me,100);
129 }
130
131 /* ---------------------------------------------------------------------- */
132
~SurfCollideCLL()133 SurfCollideCLL::~SurfCollideCLL()
134 {
135 delete [] tstr;
136 delete random;
137 }
138
139 /* ---------------------------------------------------------------------- */
140
init()141 void SurfCollideCLL::init()
142 {
143 SurfCollide::init();
144
145 // check variable
146
147 if (tstr) {
148 tvar = input->variable->find(tstr);
149 if (tvar < 0)
150 error->all(FLERR,"Surf_collide cll variable name does not exist");
151 if (!input->variable->equal_style(tvar))
152 error->all(FLERR,"Surf_collide cll variable is invalid style");
153 }
154 }
155
156 /* ----------------------------------------------------------------------
157 particle collision with surface with optional chemistry
158 ip = particle with current x = collision pt, current v = incident v
159 isurf = index of surface element
160 norm = surface normal unit vector
161 isr = index of reaction model if >= 0, -1 for no chemistry
162 ip = reset to NULL if destroyed by chemsitry
163 return jp = new particle if created by chemistry
164 return reaction = index of reaction (1 to N) that took place, 0 = no reaction
165 resets particle(s) to post-collision outward velocity
166 ------------------------------------------------------------------------- */
167
168 Particle::OnePart *SurfCollideCLL::
collide(Particle::OnePart * & ip,double &,int isurf,double * norm,int isr,int & reaction)169 collide(Particle::OnePart *&ip, double &,
170 int isurf, double *norm, int isr, int &reaction)
171 {
172 nsingle++;
173
174 // if surface chemistry defined, attempt reaction
175 // reaction = 1 to N for which reaction took place, 0 for none
176 // velreset = 1 if reaction reset post-collision velocity, else 0
177
178 Particle::OnePart iorig;
179 Particle::OnePart *jp = NULL;
180 reaction = 0;
181 int velreset = 0;
182
183 if (isr >= 0) {
184 if (modify->n_surf_react) memcpy(&iorig,ip,sizeof(Particle::OnePart));
185 reaction = surf->sr[isr]->react(ip,isurf,norm,jp,velreset);
186 if (reaction) surf->nreact_one++;
187 }
188
189 // CLL reflection for each particle
190 // only if SurfReact did not already reset velocities
191 // also both partiticles need to trigger any fixes
192 // to update per-particle properties which depend on
193 // temperature of the particle, e.g. fix vibmode and fix ambipolar
194
195 if (ip) {
196 if (!velreset) cll(ip,norm);
197 if (modify->n_update_custom) {
198 int i = ip - particle->particles;
199 modify->update_custom(i,twall,twall,twall,vstream);
200 }
201 }
202 if (jp) {
203 if (!velreset) cll(jp,norm);
204 if (modify->n_update_custom) {
205 int j = jp - particle->particles;
206 modify->update_custom(j,twall,twall,twall,vstream);
207 }
208 }
209
210 // call any fixes with a surf_react() method
211 // they may reset j to -1, e.g. fix ambipolar
212 // in which case newly created j is deleted
213
214 if (reaction && modify->n_surf_react) {
215 int i = -1;
216 if (ip) i = ip - particle->particles;
217 int j = -1;
218 if (jp) j = jp - particle->particles;
219 modify->surf_react(&iorig,i,j);
220 if (jp && j < 0) {
221 jp = NULL;
222 particle->nlocal--;
223 }
224 }
225
226 return jp;
227 }
228
229 /* ----------------------------------------------------------------------
230 cll reflection
231 vrm = most probable speed of species, eqns (4.1) and (4.7)
232 vperp = velocity component perpendicular to surface along norm, eqn (12.3)
233 vtan12 = 2 velocity components tangential to surface
234 tangent1 = component of particle v tangential to surface,
235 check if tangent1 = 0 (normal collision), set randomly
236 tangent2 = norm x tangent1 = orthogonal tangential direction
237 tangent12 are both unit vectors
238 ------------------------------------------------------------------------- */
239
cll(Particle::OnePart * p,double * norm)240 void SurfCollideCLL::cll(Particle::OnePart *p, double *norm)
241 {
242 double tangent1[3],tangent2[3];
243 Particle::Species *species = particle->species;
244 int ispecies = p->ispecies;
245 double beta_un,normalized_distbn_fn;
246
247 double *v = p->v;
248 double dot = MathExtra::dot3(v,norm);
249 double vrm, vperp, vtan1, vtan2;
250
251 tangent1[0] = v[0] - dot*norm[0];
252 tangent1[1] = v[1] - dot*norm[1];
253 tangent1[2] = v[2] - dot*norm[2];
254
255 if (MathExtra::lensq3(tangent1) == 0.0) {
256 tangent2[0] = random->uniform();
257 tangent2[1] = random->uniform();
258 tangent2[2] = random->uniform();
259 MathExtra::cross3(norm,tangent2,tangent1);
260 }
261
262 MathExtra::norm3(tangent1);
263 MathExtra::cross3(norm,tangent1,tangent2);
264
265 double tan1 = MathExtra::dot3(v,tangent1);
266
267 vrm = sqrt(2.0*update->boltz * twall / species[ispecies].mass);
268
269 // CLL model normal velocity
270
271 double r_1 = sqrt(-acc_n*log(random->uniform()));
272 double theta_1 = MY_2PI * random->uniform();
273 double dot_norm = dot/vrm * sqrt(1-acc_n);
274 vperp = vrm * sqrt(r_1*r_1 + dot_norm*dot_norm + 2*r_1*dot_norm*cos(theta_1));
275
276 // CLL model tangential velocities
277
278 double r_2 = sqrt(-acc_t*log(random->uniform()));
279 double theta_2 = MY_2PI * random->uniform();
280 double vtangent = tan1/vrm * sqrt(1-acc_t);
281 vtan1 = vrm * (vtangent + r_2*cos(theta_2));
282 vtan2 = vrm * r_2 * sin(theta_2);
283
284 // partial keyword
285 // incomplete energy accommodation with partial/fully diffuse scattering
286 // adjust the final angle of the particle while keeping
287 // the velocity magnitude or speed according to CLL scattering
288
289 if (pflag) {
290 double tan2 = MathExtra::dot3(v,tangent2);
291 double theta_i, phi_i, psi_i, theta_f, phi_f, psi_f, cos_beta;
292
293 theta_i = acos(dot/sqrt(MathExtra::lensq3(v)));
294 psi_i = acos(dot*dot/MathExtra::lensq3(v));
295 phi_i = atan2(tan2,tan1);
296
297 double v_mag = sqrt(vperp*vperp + vtan1*vtan1 + vtan2*vtan2);
298
299 double P = 0;
300 while (random->uniform() > P) {
301 phi_f = MY_2PI*random->uniform();
302 psi_f = acos(1-random->uniform());
303 cos_beta = cos(psi_i)*cos(psi_f) +
304 sin(psi_i)*sin(psi_f)*cos(phi_i - phi_f);
305 P = (1-eccen)/(1-eccen*cos_beta);
306 }
307
308 theta_f = acos(sqrt(cos(psi_f)));
309
310 vperp = v_mag * cos(theta_f);
311 vtan1 = v_mag * sin(theta_f) * cos(phi_f);
312 vtan2 = v_mag * sin(theta_f) * sin(phi_f);
313 }
314
315 // add in translation or rotation vector if specified
316 // only keep portion of vector tangential to surface element
317
318 if (trflag) {
319 double vxdelta,vydelta,vzdelta;
320 if (tflag) {
321 vxdelta = vx; vydelta = vy; vzdelta = vz;
322 double dot = vxdelta*norm[0] + vydelta*norm[1] + vzdelta*norm[2];
323
324 if (fabs(dot) > 0.001) {
325 dot /= vrm;
326 do {
327 do {
328 beta_un = (6.0*random->gaussian() - 3.0);
329 } while (beta_un + dot < 0.0);
330 normalized_distbn_fn = 2.0 * (beta_un + dot) /
331 (dot + sqrt(dot*dot + 2.0)) *
332 exp(0.5 + (0.5*dot)*(dot-sqrt(dot*dot + 2.0)) - beta_un*beta_un);
333 } while (normalized_distbn_fn < random->uniform());
334 vperp = beta_un*vrm;
335 }
336
337 } else {
338 double *x = p->x;
339 vxdelta = wy*(x[2]-pz) - wz*(x[1]-py);
340 vydelta = wz*(x[0]-px) - wx*(x[2]-pz);
341 vzdelta = wx*(x[1]-py) - wy*(x[0]-px);
342 double dot = vxdelta*norm[0] + vydelta*norm[1] + vzdelta*norm[2];
343 vxdelta -= dot*norm[0];
344 vydelta -= dot*norm[1];
345 vzdelta -= dot*norm[2];
346 }
347
348 v[0] = vperp*norm[0] + vtan1*tangent1[0] + vtan2*tangent2[0] + vxdelta;
349 v[1] = vperp*norm[1] + vtan1*tangent1[1] + vtan2*tangent2[1] + vydelta;
350 v[2] = vperp*norm[2] + vtan1*tangent1[2] + vtan2*tangent2[2] + vzdelta;
351
352 // no translation or rotation
353
354 } else {
355 v[0] = vperp*norm[0] + vtan1*tangent1[0] + vtan2*tangent2[0];
356 v[1] = vperp*norm[1] + vtan1*tangent1[1] + vtan2*tangent2[1];
357 v[2] = vperp*norm[2] + vtan1*tangent1[2] + vtan2*tangent2[2];
358 }
359
360 // rotational component
361
362 if (!sparta->collide || sparta->collide->rotstyle == NONE ||
363 species[ispecies].rotdof < 2) p->erot = 0.0;
364
365 else {
366 double erot_mag = sqrt(p->erot*(1-acc_rot)/(update->boltz*twall));
367
368 double r_rot,cos_theta_rot,A_rot,X_rot;
369 if (species[ispecies].rotdof == 2) {
370 r_rot = sqrt(-acc_rot*log(random->uniform()));
371 cos_theta_rot = cos(MY_2PI*random->uniform());
372 }
373 else if (species[ispecies].rotdof > 2) {
374 A_rot = 0;
375 while (A_rot < random->uniform()) {
376 X_rot = 4*random->uniform();
377 A_rot = 2.71828182845904523536028747*X_rot*X_rot*exp(-X_rot*X_rot);
378 }
379 r_rot = sqrt(acc_rot)*X_rot;
380 cos_theta_rot = 2*random->uniform() - 1;
381 }
382
383 p->erot = update->boltz * twall *
384 (r_rot*r_rot + erot_mag*erot_mag + 2*r_rot*erot_mag*cos_theta_rot);
385 }
386
387 // vibrational component
388 // NOTE: check all references to species[]->vibtmp
389
390 int vibdof = species[ispecies].vibdof;
391 double r_vib, cos_theta_vib, A_vib, X_vib, evib_mag, evib_val;
392
393 if (!sparta->collide || sparta->collide->vibstyle == NONE || vibdof < 2)
394 p->evib = 0.0;
395
396 else if (sparta->collide->vibstyle == DISCRETE && vibdof == 2) {
397 double evib_star =
398 -log(1 - random->uniform() *
399 (1 - exp(-update->boltz*species[ispecies].vibtemp[0])));
400 evib_val = p->evib + evib_star;
401 evib_mag = sqrt(evib_val*(1-acc_vib)/(update->boltz*twall));
402 r_vib = sqrt(-acc_vib*log(random->uniform()));
403 cos_theta_vib = cos(MY_2PI*random->uniform());
404 evib_val = update->boltz * twall *
405 (r_vib*r_vib + evib_mag*evib_mag + 2*r_vib*evib_mag*cos_theta_vib);
406 int ivib = evib_val / (update->boltz*species[ispecies].vibtemp[0]);
407 p->evib = ivib * update->boltz * species[ispecies].vibtemp[0];
408 }
409
410 else if (sparta->collide->vibstyle == SMOOTH || vibdof >= 2) {
411 evib_mag = sqrt(p->evib*(1-acc_vib)/(update->boltz*twall));
412 if (vibdof == 2) {
413 r_vib = sqrt(-acc_vib*log(random->uniform()));
414 cos_theta_vib = cos(MY_2PI*random->uniform());
415 } else if (vibdof > 2) {
416 A_vib = 0;
417 while (A_vib < random->uniform()) {
418 X_vib = 4*random->uniform();
419 A_vib = 2.71828182845904523536028747*X_vib*X_vib*exp(-X_vib*X_vib);
420 }
421 r_vib = sqrt(acc_vib)*X_vib;
422 cos_theta_vib = 2*random->uniform() - 1;
423 }
424
425 p->evib = update->boltz * twall *
426 (r_vib*r_vib + evib_mag*evib_mag + 2*r_vib*evib_mag*cos_theta_vib);
427 }
428 }
429
430 /* ----------------------------------------------------------------------
431 wrapper on cll() method to perform collision for a single particle
432 pass in flags/coefficients to match command-line args for style cll
433 flags, coeffs can be NULL
434 called by SurfReactAdsorb
435 ------------------------------------------------------------------------- */
436
wrapper(Particle::OnePart * p,double * norm,int * flags,double * coeffs)437 void SurfCollideCLL::wrapper(Particle::OnePart *p, double *norm,
438 int *flags, double *coeffs)
439 {
440 if (flags) {
441 twall = coeffs[0];
442 acc_n = coeffs[1];
443 acc_t = coeffs[2];
444 acc_rot = coeffs[3];
445 acc_vib = coeffs[4];
446
447 if (flags[0]) eccen = coeffs[5];
448 else eccen = 0.0;
449 }
450
451 cll(p,norm);
452 }
453
454 /* ----------------------------------------------------------------------
455 return flags and coeffs for this SurfCollide instance to caller
456 ------------------------------------------------------------------------- */
457
flags_and_coeffs(int * flags,double * coeffs)458 void SurfCollideCLL::flags_and_coeffs(int *flags, double *coeffs)
459 {
460 coeffs[0] = twall;
461 coeffs[1] = acc_n;
462 coeffs[2] = acc_t;
463 coeffs[3] = acc_rot;
464 coeffs[4] = acc_vib;
465
466 flags[0] = 0;
467 if (eccen != 0.0) {
468 flags[0] = 1;
469 coeffs[5] = eccen;
470 }
471 }
472
473 /* ----------------------------------------------------------------------
474 set current surface temperature
475 ------------------------------------------------------------------------- */
476
dynamic()477 void SurfCollideCLL::dynamic()
478 {
479 twall = input->variable->compute_equal(tvar);
480 }
481