PageRenderTime 1733ms CodeModel.GetById 34ms RepoModel.GetById 9ms app.codeStats 0ms

/examples/interchange-instability/2fluid.cxx

https://github.com/bendudson/BOUT
C++ | 470 lines | 275 code | 112 blank | 83 comment | 26 complexity | b4b812acf966604cb64caba05a2c8217 MD5 | raw file
Possible License(s): GPL-3.0
  1. /*******************************************************************************
  2. * 2-fluid equations
  3. * Same as Maxim's version of BOUT - simplified 2-fluid for benchmarking
  4. *******************************************************************************/
  5. #include <bout.hxx>
  6. #include <boutmain.hxx>
  7. #include <initialprofiles.hxx>
  8. #include <derivs.hxx>
  9. #include <math.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. // 2D initial profiles
  13. Field2D Ni0, Ti0, Te0, Vi0, phi0, Ve0, rho0, Ajpar0;
  14. Vector2D b0xcv; // for curvature terms
  15. // 3D evolving fields
  16. Field3D rho, Te, Ni, Ajpar, Vi, Ti;
  17. // Derived 3D variables
  18. Field3D phi, Apar, Ve, jpar;
  19. // Non-linear coefficients
  20. Field3D nu, mu_i, kapa_Te, kapa_Ti;
  21. // 3D total values
  22. Field3D Nit, Tit, Tet, Vit;
  23. // pressures
  24. Field3D pei, pe;
  25. Field2D pei0, pe0;
  26. // Metric coefficients
  27. Field2D Rxy, Bpxy, Btxy, hthe;
  28. // parameters
  29. BoutReal Te_x, Ti_x, Ni_x, Vi_x, bmag, rho_s, fmei, AA, ZZ;
  30. BoutReal lambda_ei, lambda_ii;
  31. BoutReal nu_hat, mui_hat, wci, nueix, nuiix;
  32. BoutReal beta_p;
  33. // settings
  34. bool estatic, ZeroElMass; // Switch for electrostatic operation (true = no Apar)
  35. BoutReal zeff, nu_perp;
  36. bool evolve_rho, evolve_te, evolve_ni, evolve_ajpar, evolve_vi, evolve_ti;
  37. BoutReal ShearFactor;
  38. int phi_flags, apar_flags; // Inversion flags
  39. // Group fields together for communication
  40. FieldGroup comms;
  41. // Field routines
  42. int solve_phi_tridag(Field3D &r, Field3D &p, int flags);
  43. int solve_apar_tridag(Field3D &aj, Field3D &ap, int flags);
  44. int physics_init(bool restarting) {
  45. Field2D I; // Shear factor
  46. output << "Solving 6-variable 2-fluid equations\n";
  47. /************* LOAD DATA FROM GRID FILE ****************/
  48. // Load 2D profiles (set to zero if not found)
  49. GRID_LOAD(Ni0);
  50. GRID_LOAD(Ti0);
  51. GRID_LOAD(Te0);
  52. GRID_LOAD(Vi0);
  53. GRID_LOAD(Ve0);
  54. GRID_LOAD(phi0);
  55. GRID_LOAD(rho0);
  56. GRID_LOAD(Ajpar0);
  57. // Load magnetic curvature term
  58. b0xcv.covariant = false; // Read contravariant components
  59. mesh->get(b0xcv, "bxcv"); // b0xkappa terms
  60. b0xcv *= -1.0; // NOTE: THIS IS FOR 'OLD' GRID FILES ONLY
  61. // Load metrics
  62. GRID_LOAD(Rxy);
  63. GRID_LOAD(Bpxy);
  64. GRID_LOAD(Btxy);
  65. GRID_LOAD(hthe);
  66. mesh->get(mesh->dx, "dpsi");
  67. mesh->get(I, "sinty");
  68. mesh->get(mesh->zShift, "qinty");
  69. // Load normalisation values
  70. GRID_LOAD(Te_x);
  71. GRID_LOAD(Ti_x);
  72. GRID_LOAD(Ni_x);
  73. GRID_LOAD(bmag);
  74. Ni_x *= 1.0e14;
  75. bmag *= 1.0e4;
  76. /*************** READ OPTIONS *************************/
  77. // Read some parameters
  78. Options *globalOptions = Options::getRoot();
  79. Options *options = globalOptions->getSection("2fluid");
  80. OPTION(options, AA, 2.0);
  81. OPTION(options, ZZ, 1.0);
  82. OPTION(options, estatic, false);
  83. OPTION(options, ZeroElMass, false);
  84. OPTION(options, zeff, 1.0);
  85. OPTION(options, nu_perp, 0.0);
  86. OPTION(options, ShearFactor, 1.0);
  87. OPTION(options, phi_flags, 0);
  88. OPTION(options, apar_flags, 0);
  89. (globalOptions->getSection("Ni"))->get("evolve", evolve_ni, true);
  90. (globalOptions->getSection("rho"))->get("evolve", evolve_rho, true);
  91. (globalOptions->getSection("vi"))->get("evolve", evolve_vi, true);
  92. (globalOptions->getSection("te"))->get("evolve", evolve_te, true);
  93. (globalOptions->getSection("ti"))->get("evolve", evolve_ti, true);
  94. (globalOptions->getSection("Ajpar"))->get("evolve", evolve_ajpar, true);
  95. if(ZeroElMass)
  96. evolve_ajpar = false; // Don't need ajpar - calculated from ohm's law
  97. /************* SHIFTED RADIAL COORDINATES ************/
  98. if(mesh->ShiftXderivs) {
  99. ShearFactor = 0.0; // I disappears from metric
  100. b0xcv.z += I*b0xcv.x;
  101. }
  102. /************** CALCULATE PARAMETERS *****************/
  103. rho_s = 1.02*sqrt(AA*Te_x)/ZZ/bmag;
  104. fmei = 1./1836.2/AA;
  105. lambda_ei = 24.-log(sqrt(Ni_x)/Te_x);
  106. lambda_ii = 23.-log(ZZ*ZZ*ZZ*sqrt(2.*Ni_x)/pow(Ti_x, 1.5));
  107. wci = 9.58e3*ZZ*bmag/AA;
  108. nueix = 2.91e-6*Ni_x*lambda_ei/pow(Te_x, 1.5);
  109. nuiix = 4.78e-8*pow(ZZ,4.)*Ni_x*lambda_ii/pow(Ti_x, 1.5)/sqrt(AA);
  110. nu_hat = zeff*nueix/wci;
  111. if(nu_perp < 1.e-10) {
  112. mui_hat = (3./10.)*nuiix/wci;
  113. } else
  114. mui_hat = nu_perp;
  115. if(estatic) {
  116. beta_p = 1.e-29;
  117. }else
  118. beta_p = 4.03e-11*Ni_x*Te_x/bmag/bmag;
  119. Vi_x = wci * rho_s;
  120. /************** PRINT Z INFORMATION ******************/
  121. BoutReal hthe0;
  122. if(mesh->get(hthe0, "hthe0") == 0) {
  123. output.write(" ****NOTE: input from BOUT, Z length needs to be divided by %e\n", hthe0/rho_s);
  124. }
  125. /************** SHIFTED GRIDS LOCATION ***************/
  126. // Velocities defined on cell boundaries
  127. Vi.setLocation(CELL_YLOW);
  128. Ajpar.setLocation(CELL_YLOW);
  129. // Apar and jpar too
  130. Apar.setLocation(CELL_YLOW);
  131. jpar.setLocation(CELL_YLOW);
  132. /************** NORMALISE QUANTITIES *****************/
  133. output.write("\tNormalising to rho_s = %e\n", rho_s);
  134. // Normalise profiles
  135. Ni0 /= Ni_x/1.0e14;
  136. Ti0 /= Te_x;
  137. Te0 /= Te_x;
  138. phi0 /= Te_x;
  139. Vi0 /= Vi_x;
  140. // Normalise curvature term
  141. b0xcv.x /= (bmag/1e4);
  142. b0xcv.y *= rho_s*rho_s;
  143. b0xcv.z *= rho_s*rho_s;
  144. // Normalise geometry
  145. Rxy /= rho_s;
  146. hthe /= rho_s;
  147. I *= rho_s*rho_s*(bmag/1e4)*ShearFactor;
  148. mesh->dx /= rho_s*rho_s*(bmag/1e4);
  149. // Normalise magnetic field
  150. Bpxy /= (bmag/1.e4);
  151. Btxy /= (bmag/1.e4);
  152. mesh->Bxy /= (bmag/1.e4);
  153. // calculate pressures
  154. pei0 = (Ti0 + Te0)*Ni0;
  155. pe0 = Te0*Ni0;
  156. /**************** CALCULATE METRICS ******************/
  157. mesh->g11 = (Rxy*Bpxy)^2;
  158. mesh->g22 = 1.0 / (hthe^2);
  159. mesh->g33 = (I^2)*mesh->g11 + (mesh->Bxy^2)/mesh->g11;
  160. mesh->g12 = 0.0;
  161. mesh->g13 = -I*mesh->g11;
  162. mesh->g23 = -Btxy/(hthe*Bpxy*Rxy);
  163. mesh->J = hthe / Bpxy;
  164. mesh->g_11 = 1.0/mesh->g11 + ((I*Rxy)^2);
  165. mesh->g_22 = (mesh->Bxy*hthe/Bpxy)^2;
  166. mesh->g_33 = Rxy*Rxy;
  167. mesh->g_12 = Btxy*hthe*I*Rxy/Bpxy;
  168. mesh->g_13 = I*Rxy*Rxy;
  169. mesh->g_23 = Btxy*hthe*Rxy/Bpxy;
  170. mesh->geometry();
  171. /**************** SET EVOLVING VARIABLES *************/
  172. // Tell BOUT++ which variables to evolve
  173. // add evolving variables to the communication object
  174. if(evolve_rho) {
  175. bout_solve(rho, "rho");
  176. comms.add(rho);
  177. output.write("rho\n");
  178. }else
  179. initial_profile("rho", rho);
  180. if(evolve_ni) {
  181. bout_solve(Ni, "Ni");
  182. comms.add(Ni);
  183. output.write("ni\n");
  184. }else
  185. initial_profile("Ni", Ni);
  186. if(evolve_te) {
  187. bout_solve(Te, "Te");
  188. comms.add(Te);
  189. output.write("te\n");
  190. }else
  191. initial_profile("Te", Te);
  192. if(evolve_ajpar) {
  193. bout_solve(Ajpar, "Ajpar");
  194. comms.add(Ajpar);
  195. output.write("ajpar\n");
  196. }else {
  197. initial_profile("Ajpar", Ajpar);
  198. if(ZeroElMass)
  199. dump.add(Ajpar, "Ajpar", 1); // output calculated Ajpar
  200. }
  201. if(evolve_vi) {
  202. bout_solve(Vi, "Vi");
  203. comms.add(Vi);
  204. output.write("vi\n");
  205. }else
  206. initial_profile("Vi", Vi);
  207. if(evolve_ti) {
  208. bout_solve(Ti, "Ti");
  209. comms.add(Ti);
  210. output.write("ti\n");
  211. }else
  212. initial_profile("Ti", Ti);
  213. // Set boundary conditions
  214. jpar.setBoundary("jpar");
  215. /************** SETUP COMMUNICATIONS **************/
  216. // add extra variables to communication
  217. comms.add(phi);
  218. comms.add(Apar);
  219. // Add any other variables to be dumped to file
  220. dump.add(phi, "phi", 1);
  221. dump.add(Apar, "Apar", 1);
  222. dump.add(jpar, "jpar", 1);
  223. dump.add(Ni0, "Ni0", 0);
  224. dump.add(Te0, "Te0", 0);
  225. dump.add(Ti0, "Ti0", 0);
  226. dump.add(Te_x, "Te_x", 0);
  227. dump.add(Ti_x, "Ti_x", 0);
  228. dump.add(Ni_x, "Ni_x", 0);
  229. dump.add(rho_s, "rho_s", 0);
  230. dump.add(wci, "wci", 0);
  231. return(0);
  232. }
  233. // just define a macro for V_E dot Grad
  234. #define vE_Grad(f, p) ( b0xGrad_dot_Grad(p, f) / mesh->Bxy )
  235. int physics_run(BoutReal t)
  236. {
  237. // Solve EM fields
  238. solve_phi_tridag(rho, phi, phi_flags);
  239. if(estatic || ZeroElMass) {
  240. // Electrostatic operation
  241. Apar = 0.0;
  242. }else {
  243. solve_apar_tridag(Ajpar, Apar, apar_flags); // Linear Apar solver
  244. }
  245. // Communicate variables
  246. mesh->communicate(comms);
  247. // Update profiles
  248. Nit = Ni0; //+ Ni.DC();
  249. Tit = Ti0; // + Ti.DC();
  250. Tet = Te0; // + Te.DC();
  251. Vit = Vi0; // + Vi;
  252. // Update non-linear coefficients on the mesh
  253. nu = nu_hat * Nit / (Tet^1.5);
  254. mu_i = mui_hat * Nit / (Tit^0.5);
  255. kapa_Te = 3.2*(1./fmei)*(wci/nueix)*(Tet^2.5);
  256. kapa_Ti = 3.9*(wci/nuiix)*(Tit^2.5);
  257. // note: nonlinear terms are not here
  258. pei = (Te0+Ti0)*Ni + (Te + Ti)*Ni0;
  259. pe = Te0*Ni + Te*Ni0;
  260. if(ZeroElMass) {
  261. // Set jpar,Ve,Ajpar neglecting the electron inertia term
  262. jpar = ((Te0*Grad_par(Ni, CELL_YLOW)) - (Ni0*Grad_par(phi, CELL_YLOW)))/(fmei*0.51*nu);
  263. // Set boundary condition on jpar
  264. jpar.applyBoundary();
  265. // Need to communicate jpar
  266. mesh->communicate(jpar);
  267. Ve = Vi - jpar/Ni0;
  268. Ajpar = Ve;
  269. }else {
  270. Ve = Ajpar + Apar;
  271. jpar = Ni0*(Vi - Ve);
  272. }
  273. // DENSITY EQUATION
  274. ddt(Ni) = 0.0;
  275. if(evolve_ni) {
  276. ddt(Ni) -= vE_Grad(Ni0, phi);
  277. /*
  278. ddt(Ni) -= vE_Grad(Ni, phi0) + vE_Grad(Ni0, phi) + vE_Grad(Ni, phi);
  279. ddt(Ni) -= Vpar_Grad_par(Vi, Ni0) + Vpar_Grad_par(Vi0, Ni) + Vpar_Grad_par(Vi, Ni);
  280. ddt(Ni) -= Ni0*Div_par(Vi) + Ni*Div_par(Vi0) + Ni*Div_par(Vi);
  281. ddt(Ni) += Div_par(jpar);
  282. ddt(Ni) += 2.0*V_dot_Grad(b0xcv, pe);
  283. ddt(Ni) -= 2.0*(Ni0*V_dot_Grad(b0xcv, phi) + Ni*V_dot_Grad(b0xcv, phi0) + Ni*V_dot_Grad(b0xcv, phi));
  284. */
  285. }
  286. // ION VELOCITY
  287. ddt(Vi) = 0.0;
  288. if(evolve_vi) {
  289. ddt(Vi) -= vE_Grad(Vi0, phi) + vE_Grad(Vi, phi0) + vE_Grad(Vi, phi);
  290. ddt(Vi) -= Vpar_Grad_par(Vi0, Vi) + Vpar_Grad_par(Vi, Vi0) + Vpar_Grad_par(Vi, Vi);
  291. ddt(Vi) -= Grad_par(pei)/Ni0;
  292. }
  293. // ELECTRON TEMPERATURE
  294. ddt(Te) = 0.0;
  295. if(evolve_te) {
  296. ddt(Te) -= vE_Grad(Te0, phi) + vE_Grad(Te, phi0) + vE_Grad(Te, phi);
  297. ddt(Te) -= Vpar_Grad_par(Ve, Te0) + Vpar_Grad_par(Ve0, Te) + Vpar_Grad_par(Ve, Te);
  298. ddt(Te) += 1.333*Te0*( V_dot_Grad(b0xcv, pe)/Ni0 - V_dot_Grad(b0xcv, phi) );
  299. ddt(Te) += 3.333*Te0*V_dot_Grad(b0xcv, Te);
  300. ddt(Te) += (0.6666667/Ni0)*Div_par_K_Grad_par(kapa_Te, Te);
  301. }
  302. // ION TEMPERATURE
  303. ddt(Ti) = 0.0;
  304. if(evolve_ti) {
  305. ddt(Ti) -= vE_Grad(Ti0, phi) + vE_Grad(Ti, phi0) + vE_Grad(Ti, phi);
  306. ddt(Ti) -= Vpar_Grad_par(Vi, Ti0) + Vpar_Grad_par(Vi0, Ti) + Vpar_Grad_par(Vi, Ti);
  307. ddt(Ti) += 1.333*( Ti0*V_dot_Grad(b0xcv, pe)/Ni0 - Ti*V_dot_Grad(b0xcv, phi) );
  308. ddt(Ti) -= 3.333*Ti0*V_dot_Grad(b0xcv, Ti);
  309. ddt(Ti) += (0.6666667/Ni0)*Div_par_K_Grad_par(kapa_Ti, Ti);
  310. }
  311. // VORTICITY
  312. ddt(rho) = 0.0;
  313. if(evolve_rho) {
  314. /*
  315. ddt(rho) -= vE_Grad(rho0, phi) + vE_Grad(rho, phi0) + vE_Grad(rho, phi);
  316. ddt(rho) -= Vpar_Grad_par(Vi, rho0) + Vpar_Grad_par(Vi0, rho) + Vpar_Grad_par(Vi, rho);
  317. */
  318. //ddt(rho) += 2.0*Bxy*V_dot_Grad(b0xcv, pei);
  319. ddt(rho) += 2.0*mesh->Bxy*b0xcv*Grad(pei);
  320. //ddt(rho) += Bxy*Bxy*Div_par(jpar, CELL_CENTRE);
  321. }
  322. // AJPAR
  323. ddt(Ajpar) = 0.0;
  324. if(evolve_ajpar) {
  325. //ddt(Ajpar) -= vE_Grad(Ajpar0, phi) + vE_Grad(Ajpar, phi0) + vE_Grad(Ajpar, phi);
  326. ddt(Ajpar) += (1./fmei)*Grad_par(phi, CELL_YLOW);
  327. //ddt(Ajpar) -= (1./fmei)*(Te0/Ni0)*Grad_par(Ni, CELL_YLOW);
  328. //ddt(Ajpar) -= (1./fmei)*1.71*Grad_par(Te);
  329. ddt(Ajpar) += 0.51*nu*jpar/Ni0;
  330. }
  331. return(0);
  332. }
  333. /*******************************************************************************
  334. * FAST LINEAR FIELD SOLVERS
  335. *******************************************************************************/
  336. #include <invert_laplace.hxx>
  337. // Performs inversion of rho (r) to get phi (p)
  338. int solve_phi_tridag(Field3D &r, Field3D &p, int flags) {
  339. //output.write("Solving phi: %e, %e -> %e\n", max(abs(r)), min(Ni0), max(abs(r/Ni0)));
  340. if(invert_laplace(r/Ni0, p, flags, NULL)) {
  341. return 1;
  342. }
  343. //Field3D pertPi = Ti*Ni0 + Ni*Ti0;
  344. //p -= pertPi/Ni0;
  345. return(0);
  346. }
  347. int solve_apar_tridag(Field3D &aj, Field3D &ap, int flags) {
  348. static Field2D a;
  349. static int set = 0;
  350. if(set == 0) {
  351. // calculate a
  352. a = (-0.5*beta_p/fmei)*Ni0;
  353. set = 1;
  354. }
  355. if(invert_laplace(a*(Vi - aj), ap, flags, &a))
  356. return 1;
  357. return(0);
  358. }