1 /* 2 This example implements the model described in 3 4 Rauenzahn, Mousseau, Knoll. "Temporal accuracy of the nonequilibrium radiation diffusion 5 equations employing a Saha ionization model" 2005. 6 7 The paper discusses three examples, the first two are nondimensional with a simple 8 ionization model. The third example is fully dimensional and uses the Saha ionization 9 model with realistic parameters. 10 */ 11 12 #include <petscts.h> 13 #include <petscdm.h> 14 #include <petscdmda.h> 15 16 typedef enum { 17 BC_DIRICHLET, 18 BC_NEUMANN, 19 BC_ROBIN 20 } BCType; 21 static const char *const BCTypes[] = {"DIRICHLET", "NEUMANN", "ROBIN", "BCType", "BC_", 0}; 22 typedef enum { 23 JACOBIAN_ANALYTIC, 24 JACOBIAN_MATRIXFREE, 25 JACOBIAN_FD_COLORING, 26 JACOBIAN_FD_FULL 27 } JacobianType; 28 static const char *const JacobianTypes[] = {"ANALYTIC", "MATRIXFREE", "FD_COLORING", "FD_FULL", "JacobianType", "FD_", 0}; 29 typedef enum { 30 DISCRETIZATION_FD, 31 DISCRETIZATION_FE 32 } DiscretizationType; 33 static const char *const DiscretizationTypes[] = {"FD", "FE", "DiscretizationType", "DISCRETIZATION_", 0}; 34 typedef enum { 35 QUADRATURE_GAUSS1, 36 QUADRATURE_GAUSS2, 37 QUADRATURE_GAUSS3, 38 QUADRATURE_GAUSS4, 39 QUADRATURE_LOBATTO2, 40 QUADRATURE_LOBATTO3 41 } QuadratureType; 42 static const char *const QuadratureTypes[] = {"GAUSS1", "GAUSS2", "GAUSS3", "GAUSS4", "LOBATTO2", "LOBATTO3", "QuadratureType", "QUADRATURE_", 0}; 43 44 typedef struct { 45 PetscScalar E; /* radiation energy */ 46 PetscScalar T; /* material temperature */ 47 } RDNode; 48 49 typedef struct { 50 PetscReal meter, kilogram, second, Kelvin; /* Fundamental units */ 51 PetscReal Joule, Watt; /* Derived units */ 52 } RDUnit; 53 54 typedef struct _n_RD *RD; 55 56 struct _n_RD { 57 void (*MaterialEnergy)(RD, const RDNode *, PetscScalar *, RDNode *); 58 DM da; 59 PetscBool monitor_residual; 60 DiscretizationType discretization; 61 QuadratureType quadrature; 62 JacobianType jacobian; 63 PetscInt initial; 64 BCType leftbc; 65 PetscBool view_draw; 66 char view_binary[PETSC_MAX_PATH_LEN]; 67 PetscBool test_diff; 68 PetscBool endpoint; 69 PetscBool bclimit; 70 PetscBool bcmidpoint; 71 RDUnit unit; 72 73 /* model constants, see Table 2 and RDCreate() */ 74 PetscReal rho, K_R, K_p, I_H, m_p, m_e, h, k, c, sigma_b, beta, gamma; 75 76 /* Domain and boundary conditions */ 77 PetscReal Eapplied; /* Radiation flux from the left */ 78 PetscReal L; /* Length of domain */ 79 PetscReal final_time; 80 }; 81 82 static PetscErrorCode RDDestroy(RD *rd) 83 { 84 PetscFunctionBeginUser; 85 PetscCall(DMDestroy(&(*rd)->da)); 86 PetscCall(PetscFree(*rd)); 87 PetscFunctionReturn(PETSC_SUCCESS); 88 } 89 90 /* The paper has a time derivative for material energy (Eq 2) which is a dependent variable (computable from temperature 91 * and density through an uninvertible relation). Computing this derivative is trivial for trapezoid rule (used in the 92 * paper), but does not generalize nicely to higher order integrators. Here we use the implicit form which provides 93 * time derivatives of the independent variables (radiation energy and temperature), so we must compute the time 94 * derivative of material energy ourselves (could be done using AD). 95 * 96 * There are multiple ionization models, this interface dispatches to the one currently in use. 97 */ 98 static void RDMaterialEnergy(RD rd, const RDNode *n, PetscScalar *Em, RDNode *dEm) 99 { 100 rd->MaterialEnergy(rd, n, Em, dEm); 101 } 102 103 /* Solves a quadratic equation while propagating tangents */ 104 static void QuadraticSolve(PetscScalar a, PetscScalar a_t, PetscScalar b, PetscScalar b_t, PetscScalar c, PetscScalar c_t, PetscScalar *x, PetscScalar *x_t) 105 { 106 PetscScalar disc = b * b - 4. * a * c, disc_t = 2. * b * b_t - 4. * a_t * c - 4. * a * c_t, num = -b + PetscSqrtScalar(disc), /* choose positive sign */ 107 num_t = -b_t + 0.5 / PetscSqrtScalar(disc) * disc_t, den = 2. * a, den_t = 2. * a_t; 108 *x = num / den; 109 *x_t = (num_t * den - num * den_t) / PetscSqr(den); 110 } 111 112 /* The primary model presented in the paper */ 113 static void RDMaterialEnergy_Saha(RD rd, const RDNode *n, PetscScalar *inEm, RDNode *dEm) 114 { 115 PetscScalar Em, alpha, alpha_t, T = n->T, T_t = 1., chi = rd->I_H / (rd->k * T), chi_t = -chi / T * T_t, a = 1., a_t = 0, b = 4. * rd->m_p / rd->rho * PetscPowScalarReal(2. * PETSC_PI * rd->m_e * rd->I_H / PetscSqr(rd->h), 1.5) * PetscExpScalar(-chi) * PetscPowScalarReal(chi, 1.5), /* Eq 7 */ 116 b_t = -b * chi_t + 1.5 * b / chi * chi_t, c = -b, c_t = -b_t; 117 QuadraticSolve(a, a_t, b, b_t, c, c_t, &alpha, &alpha_t); /* Solve Eq 7 for alpha */ 118 Em = rd->k * T / rd->m_p * (1.5 * (1. + alpha) + alpha * chi); /* Eq 6 */ 119 if (inEm) *inEm = Em; 120 if (dEm) { 121 dEm->E = 0; 122 dEm->T = Em / T * T_t + rd->k * T / rd->m_p * (1.5 * alpha_t + alpha_t * chi + alpha * chi_t); 123 } 124 } 125 /* Reduced ionization model, Eq 30 */ 126 static void RDMaterialEnergy_Reduced(RD rd, const RDNode *n, PetscScalar *Em, RDNode *dEm) 127 { 128 PetscScalar alpha, alpha_t, T = n->T, T_t = 1., chi = -0.3 / T, chi_t = -chi / T * T_t, a = 1., a_t = 0., b = PetscExpScalar(chi), b_t = b * chi_t, c = -b, c_t = -b_t; 129 QuadraticSolve(a, a_t, b, b_t, c, c_t, &alpha, &alpha_t); 130 if (Em) *Em = (1. + alpha) * T + 0.3 * alpha; 131 if (dEm) { 132 dEm->E = 0; 133 dEm->T = alpha_t * T + (1. + alpha) * T_t + 0.3 * alpha_t; 134 } 135 } 136 137 /* Eq 5 */ 138 static void RDSigma_R(RD rd, RDNode *n, PetscScalar *sigma_R, RDNode *dsigma_R) 139 { 140 *sigma_R = rd->K_R * rd->rho * PetscPowScalar(n->T, -rd->gamma); 141 dsigma_R->E = 0; 142 dsigma_R->T = -rd->gamma * (*sigma_R) / n->T; 143 } 144 145 /* Eq 4 */ 146 static void RDDiffusionCoefficient(RD rd, PetscBool limit, RDNode *n, RDNode *nx, PetscScalar *D_R, RDNode *dD_R, RDNode *dxD_R) 147 { 148 PetscScalar sigma_R, denom; 149 RDNode dsigma_R, ddenom, dxdenom; 150 151 RDSigma_R(rd, n, &sigma_R, &dsigma_R); 152 denom = 3. * rd->rho * sigma_R + (int)limit * PetscAbsScalar(nx->E) / n->E; 153 ddenom.E = -(int)limit * PetscAbsScalar(nx->E) / PetscSqr(n->E); 154 ddenom.T = 3. * rd->rho * dsigma_R.T; 155 dxdenom.E = (int)limit * (PetscRealPart(nx->E) < 0 ? -1. : 1.) / n->E; 156 dxdenom.T = 0; 157 *D_R = rd->c / denom; 158 if (dD_R) { 159 dD_R->E = -rd->c / PetscSqr(denom) * ddenom.E; 160 dD_R->T = -rd->c / PetscSqr(denom) * ddenom.T; 161 } 162 if (dxD_R) { 163 dxD_R->E = -rd->c / PetscSqr(denom) * dxdenom.E; 164 dxD_R->T = -rd->c / PetscSqr(denom) * dxdenom.T; 165 } 166 } 167 168 static PetscErrorCode RDStateView(RD rd, Vec X, Vec Xdot, Vec F) 169 { 170 DMDALocalInfo info; 171 PetscInt i; 172 const RDNode *x, *xdot, *f; 173 MPI_Comm comm; 174 175 PetscFunctionBeginUser; 176 PetscCall(PetscObjectGetComm((PetscObject)rd->da, &comm)); 177 PetscCall(DMDAGetLocalInfo(rd->da, &info)); 178 PetscCall(DMDAVecGetArrayRead(rd->da, X, (void *)&x)); 179 PetscCall(DMDAVecGetArrayRead(rd->da, Xdot, (void *)&xdot)); 180 PetscCall(DMDAVecGetArrayRead(rd->da, F, (void *)&f)); 181 for (i = info.xs; i < info.xs + info.xm; i++) { 182 PetscCall(PetscSynchronizedPrintf(comm, "x[%" PetscInt_FMT "] (%10.2G,%10.2G) (%10.2G,%10.2G) (%10.2G,%10.2G)\n", i, (double)PetscRealPart(x[i].E), (double)PetscRealPart(x[i].T), (double)PetscRealPart(xdot[i].E), (double)PetscRealPart(xdot[i].T), 183 (double)PetscRealPart(f[i].E), (double)PetscRealPart(f[i].T))); 184 } 185 PetscCall(DMDAVecRestoreArrayRead(rd->da, X, (void *)&x)); 186 PetscCall(DMDAVecRestoreArrayRead(rd->da, Xdot, (void *)&xdot)); 187 PetscCall(DMDAVecRestoreArrayRead(rd->da, F, (void *)&f)); 188 PetscCall(PetscSynchronizedFlush(comm, PETSC_STDOUT)); 189 PetscFunctionReturn(PETSC_SUCCESS); 190 } 191 192 static PetscScalar RDRadiation(RD rd, const RDNode *n, RDNode *dn) 193 { 194 PetscScalar sigma_p = rd->K_p * rd->rho * PetscPowScalar(n->T, -rd->beta), sigma_p_T = -rd->beta * sigma_p / n->T, tmp = 4. * rd->sigma_b * PetscSqr(PetscSqr(n->T)) / rd->c - n->E, tmp_E = -1., tmp_T = 4. * rd->sigma_b * 4 * n->T * (PetscSqr(n->T)) / rd->c, rad = sigma_p * rd->c * rd->rho * tmp, rad_E = sigma_p * rd->c * rd->rho * tmp_E, rad_T = rd->c * rd->rho * (sigma_p_T * tmp + sigma_p * tmp_T); 195 if (dn) { 196 dn->E = rad_E; 197 dn->T = rad_T; 198 } 199 return rad; 200 } 201 202 static PetscScalar RDDiffusion(RD rd, PetscReal hx, const RDNode x[], PetscInt i, RDNode d[]) 203 { 204 PetscReal ihx = 1. / hx; 205 RDNode n_L, nx_L, n_R, nx_R, dD_L, dxD_L, dD_R, dxD_R, dfluxL[2], dfluxR[2]; 206 PetscScalar D_L, D_R, fluxL, fluxR; 207 208 n_L.E = 0.5 * (x[i - 1].E + x[i].E); 209 n_L.T = 0.5 * (x[i - 1].T + x[i].T); 210 nx_L.E = (x[i].E - x[i - 1].E) / hx; 211 nx_L.T = (x[i].T - x[i - 1].T) / hx; 212 RDDiffusionCoefficient(rd, PETSC_TRUE, &n_L, &nx_L, &D_L, &dD_L, &dxD_L); 213 fluxL = D_L * nx_L.E; 214 dfluxL[0].E = -ihx * D_L + (0.5 * dD_L.E - ihx * dxD_L.E) * nx_L.E; 215 dfluxL[1].E = +ihx * D_L + (0.5 * dD_L.E + ihx * dxD_L.E) * nx_L.E; 216 dfluxL[0].T = (0.5 * dD_L.T - ihx * dxD_L.T) * nx_L.E; 217 dfluxL[1].T = (0.5 * dD_L.T + ihx * dxD_L.T) * nx_L.E; 218 219 n_R.E = 0.5 * (x[i].E + x[i + 1].E); 220 n_R.T = 0.5 * (x[i].T + x[i + 1].T); 221 nx_R.E = (x[i + 1].E - x[i].E) / hx; 222 nx_R.T = (x[i + 1].T - x[i].T) / hx; 223 RDDiffusionCoefficient(rd, PETSC_TRUE, &n_R, &nx_R, &D_R, &dD_R, &dxD_R); 224 fluxR = D_R * nx_R.E; 225 dfluxR[0].E = -ihx * D_R + (0.5 * dD_R.E - ihx * dxD_R.E) * nx_R.E; 226 dfluxR[1].E = +ihx * D_R + (0.5 * dD_R.E + ihx * dxD_R.E) * nx_R.E; 227 dfluxR[0].T = (0.5 * dD_R.T - ihx * dxD_R.T) * nx_R.E; 228 dfluxR[1].T = (0.5 * dD_R.T + ihx * dxD_R.T) * nx_R.E; 229 230 if (d) { 231 d[0].E = -ihx * dfluxL[0].E; 232 d[0].T = -ihx * dfluxL[0].T; 233 d[1].E = ihx * (dfluxR[0].E - dfluxL[1].E); 234 d[1].T = ihx * (dfluxR[0].T - dfluxL[1].T); 235 d[2].E = ihx * dfluxR[1].E; 236 d[2].T = ihx * dfluxR[1].T; 237 } 238 return ihx * (fluxR - fluxL); 239 } 240 241 static PetscErrorCode RDGetLocalArrays(RD rd, TS ts, Vec X, Vec Xdot, PetscReal *Theta, PetscReal *dt, Vec *X0loc, RDNode **x0, Vec *Xloc, RDNode **x, Vec *Xloc_t, RDNode **xdot) 242 { 243 PetscBool istheta; 244 245 PetscFunctionBeginUser; 246 PetscCall(DMGetLocalVector(rd->da, X0loc)); 247 PetscCall(DMGetLocalVector(rd->da, Xloc)); 248 PetscCall(DMGetLocalVector(rd->da, Xloc_t)); 249 250 PetscCall(DMGlobalToLocalBegin(rd->da, X, INSERT_VALUES, *Xloc)); 251 PetscCall(DMGlobalToLocalEnd(rd->da, X, INSERT_VALUES, *Xloc)); 252 PetscCall(DMGlobalToLocalBegin(rd->da, Xdot, INSERT_VALUES, *Xloc_t)); 253 PetscCall(DMGlobalToLocalEnd(rd->da, Xdot, INSERT_VALUES, *Xloc_t)); 254 255 /* 256 The following is a hack to subvert TSTHETA which is like an implicit midpoint method to behave more like a trapezoid 257 rule. These methods have equivalent linear stability, but the nonlinear stability is somewhat different. The 258 radiation system is inconvenient to write in explicit form because the ionization model is "on the left". 259 */ 260 PetscCall(PetscObjectTypeCompare((PetscObject)ts, TSTHETA, &istheta)); 261 if (istheta && rd->endpoint) { 262 PetscCall(TSThetaGetTheta(ts, Theta)); 263 } else *Theta = 1.; 264 265 PetscCall(TSGetTimeStep(ts, dt)); 266 PetscCall(VecWAXPY(*X0loc, -(*Theta) * (*dt), *Xloc_t, *Xloc)); /* back out the value at the start of this step */ 267 if (rd->endpoint) { PetscCall(VecWAXPY(*Xloc, *dt, *Xloc_t, *X0loc)); /* move the abscissa to the end of the step */ } 268 269 PetscCall(DMDAVecGetArray(rd->da, *X0loc, x0)); 270 PetscCall(DMDAVecGetArray(rd->da, *Xloc, x)); 271 PetscCall(DMDAVecGetArray(rd->da, *Xloc_t, xdot)); 272 PetscFunctionReturn(PETSC_SUCCESS); 273 } 274 275 static PetscErrorCode RDRestoreLocalArrays(RD rd, Vec *X0loc, RDNode **x0, Vec *Xloc, RDNode **x, Vec *Xloc_t, RDNode **xdot) 276 { 277 PetscFunctionBeginUser; 278 PetscCall(DMDAVecRestoreArray(rd->da, *X0loc, x0)); 279 PetscCall(DMDAVecRestoreArray(rd->da, *Xloc, x)); 280 PetscCall(DMDAVecRestoreArray(rd->da, *Xloc_t, xdot)); 281 PetscCall(DMRestoreLocalVector(rd->da, X0loc)); 282 PetscCall(DMRestoreLocalVector(rd->da, Xloc)); 283 PetscCall(DMRestoreLocalVector(rd->da, Xloc_t)); 284 PetscFunctionReturn(PETSC_SUCCESS); 285 } 286 287 static PetscErrorCode PETSC_UNUSED RDCheckDomain_Private(RD rd, TS ts, Vec X, PetscBool *in) 288 { 289 PetscInt minloc; 290 PetscReal min; 291 292 PetscFunctionBeginUser; 293 PetscCall(VecMin(X, &minloc, &min)); 294 if (min < 0) { 295 SNES snes; 296 *in = PETSC_FALSE; 297 PetscCall(TSGetSNES(ts, &snes)); 298 PetscCall(SNESSetFunctionDomainError(snes)); 299 PetscCall(PetscInfo(ts, "Domain violation at %" PetscInt_FMT " field %" PetscInt_FMT " value %g\n", minloc / 2, minloc % 2, (double)min)); 300 } else *in = PETSC_TRUE; 301 PetscFunctionReturn(PETSC_SUCCESS); 302 } 303 304 /* Energy and temperature must remain positive */ 305 #define RDCheckDomain(rd, ts, X) \ 306 do { \ 307 PetscBool _in; \ 308 PetscCall(RDCheckDomain_Private(rd, ts, X, &_in)); \ 309 if (!_in) PetscFunctionReturn(PETSC_SUCCESS); \ 310 } while (0) 311 312 static PetscErrorCode RDIFunction_FD(TS ts, PetscReal t, Vec X, Vec Xdot, Vec F, void *ctx) 313 { 314 RD rd = (RD)ctx; 315 RDNode *x, *x0, *xdot, *f; 316 Vec X0loc, Xloc, Xloc_t; 317 PetscReal hx, Theta, dt; 318 DMDALocalInfo info; 319 PetscInt i; 320 321 PetscFunctionBeginUser; 322 PetscCall(RDGetLocalArrays(rd, ts, X, Xdot, &Theta, &dt, &X0loc, &x0, &Xloc, &x, &Xloc_t, &xdot)); 323 PetscCall(DMDAVecGetArray(rd->da, F, &f)); 324 PetscCall(DMDAGetLocalInfo(rd->da, &info)); 325 PetscCall(VecZeroEntries(F)); 326 327 hx = rd->L / (info.mx - 1); 328 329 for (i = info.xs; i < info.xs + info.xm; i++) { 330 PetscReal rho = rd->rho; 331 PetscScalar Em_t, rad; 332 333 rad = (1. - Theta) * RDRadiation(rd, &x0[i], 0) + Theta * RDRadiation(rd, &x[i], 0); 334 if (rd->endpoint) { 335 PetscScalar Em0, Em1; 336 RDMaterialEnergy(rd, &x0[i], &Em0, NULL); 337 RDMaterialEnergy(rd, &x[i], &Em1, NULL); 338 Em_t = (Em1 - Em0) / dt; 339 } else { 340 RDNode dEm; 341 RDMaterialEnergy(rd, &x[i], NULL, &dEm); 342 Em_t = dEm.E * xdot[i].E + dEm.T * xdot[i].T; 343 } 344 /* Residuals are multiplied by the volume element (hx). */ 345 /* The temperature equation does not have boundary conditions */ 346 f[i].T = hx * (rho * Em_t + rad); 347 348 if (i == 0) { /* Left boundary condition */ 349 PetscScalar D_R, bcTheta = rd->bcmidpoint ? Theta : 1.; 350 RDNode n, nx; 351 352 n.E = (1. - bcTheta) * x0[0].E + bcTheta * x[0].E; 353 n.T = (1. - bcTheta) * x0[0].T + bcTheta * x[0].T; 354 nx.E = ((1. - bcTheta) * (x0[1].E - x0[0].E) + bcTheta * (x[1].E - x[0].E)) / hx; 355 nx.T = ((1. - bcTheta) * (x0[1].T - x0[0].T) + bcTheta * (x[1].T - x[0].T)) / hx; 356 switch (rd->leftbc) { 357 case BC_ROBIN: 358 RDDiffusionCoefficient(rd, rd->bclimit, &n, &nx, &D_R, 0, 0); 359 f[0].E = hx * (n.E - 2. * D_R * nx.E - rd->Eapplied); 360 break; 361 case BC_NEUMANN: 362 f[0].E = x[1].E - x[0].E; 363 break; 364 default: 365 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Case %" PetscInt_FMT, rd->initial); 366 } 367 } else if (i == info.mx - 1) { /* Right boundary */ 368 f[i].E = x[i].E - x[i - 1].E; /* Homogeneous Neumann */ 369 } else { 370 PetscScalar diff = (1. - Theta) * RDDiffusion(rd, hx, x0, i, 0) + Theta * RDDiffusion(rd, hx, x, i, 0); 371 f[i].E = hx * (xdot[i].E - diff - rad); 372 } 373 } 374 PetscCall(RDRestoreLocalArrays(rd, &X0loc, &x0, &Xloc, &x, &Xloc_t, &xdot)); 375 PetscCall(DMDAVecRestoreArray(rd->da, F, &f)); 376 if (rd->monitor_residual) PetscCall(RDStateView(rd, X, Xdot, F)); 377 PetscFunctionReturn(PETSC_SUCCESS); 378 } 379 380 static PetscErrorCode RDIJacobian_FD(TS ts, PetscReal t, Vec X, Vec Xdot, PetscReal a, Mat A, Mat B, void *ctx) 381 { 382 RD rd = (RD)ctx; 383 RDNode *x, *x0, *xdot; 384 Vec X0loc, Xloc, Xloc_t; 385 PetscReal hx, Theta, dt; 386 DMDALocalInfo info; 387 PetscInt i; 388 389 PetscFunctionBeginUser; 390 PetscCall(RDGetLocalArrays(rd, ts, X, Xdot, &Theta, &dt, &X0loc, &x0, &Xloc, &x, &Xloc_t, &xdot)); 391 PetscCall(DMDAGetLocalInfo(rd->da, &info)); 392 hx = rd->L / (info.mx - 1); 393 PetscCall(MatZeroEntries(B)); 394 395 for (i = info.xs; i < info.xs + info.xm; i++) { 396 PetscInt col[3]; 397 PetscReal rho = rd->rho; 398 PetscScalar /*Em_t,rad,*/ K[2][6]; 399 RDNode dEm_t, drad; 400 401 /*rad = (1.-Theta)* */ RDRadiation(rd, &x0[i], 0); /* + Theta* */ 402 RDRadiation(rd, &x[i], &drad); 403 404 if (rd->endpoint) { 405 PetscScalar Em0, Em1; 406 RDNode dEm1; 407 RDMaterialEnergy(rd, &x0[i], &Em0, NULL); 408 RDMaterialEnergy(rd, &x[i], &Em1, &dEm1); 409 /*Em_t = (Em1 - Em0) / (Theta*dt);*/ 410 dEm_t.E = dEm1.E / (Theta * dt); 411 dEm_t.T = dEm1.T / (Theta * dt); 412 } else { 413 const PetscScalar epsilon = x[i].T * PETSC_SQRT_MACHINE_EPSILON; 414 RDNode n1; 415 RDNode dEm, dEm1; 416 PetscScalar Em_TT; 417 418 n1.E = x[i].E; 419 n1.T = x[i].T + epsilon; 420 RDMaterialEnergy(rd, &x[i], NULL, &dEm); 421 RDMaterialEnergy(rd, &n1, NULL, &dEm1); 422 /* The Jacobian needs another derivative. We finite difference here instead of 423 * propagating second derivatives through the ionization model. */ 424 Em_TT = (dEm1.T - dEm.T) / epsilon; 425 /*Em_t = dEm.E * xdot[i].E + dEm.T * xdot[i].T;*/ 426 dEm_t.E = dEm.E * a; 427 dEm_t.T = dEm.T * a + Em_TT * xdot[i].T; 428 } 429 430 PetscCall(PetscMemzero(K, sizeof(K))); 431 /* Residuals are multiplied by the volume element (hx). */ 432 if (i == 0) { 433 PetscScalar D, bcTheta = rd->bcmidpoint ? Theta : 1.; 434 RDNode n, nx; 435 RDNode dD, dxD; 436 437 n.E = (1. - bcTheta) * x0[0].E + bcTheta * x[0].E; 438 n.T = (1. - bcTheta) * x0[0].T + bcTheta * x[0].T; 439 nx.E = ((1. - bcTheta) * (x0[1].E - x0[0].E) + bcTheta * (x[1].E - x[0].E)) / hx; 440 nx.T = ((1. - bcTheta) * (x0[1].T - x0[0].T) + bcTheta * (x[1].T - x[0].T)) / hx; 441 switch (rd->leftbc) { 442 case BC_ROBIN: 443 RDDiffusionCoefficient(rd, rd->bclimit, &n, &nx, &D, &dD, &dxD); 444 K[0][1 * 2 + 0] = (bcTheta / Theta) * hx * (1. - 2. * D * (-1. / hx) - 2. * nx.E * dD.E + 2. * nx.E * dxD.E / hx); 445 K[0][1 * 2 + 1] = (bcTheta / Theta) * hx * (-2. * nx.E * dD.T); 446 K[0][2 * 2 + 0] = (bcTheta / Theta) * hx * (-2. * D * (1. / hx) - 2. * nx.E * dD.E - 2. * nx.E * dxD.E / hx); 447 break; 448 case BC_NEUMANN: 449 K[0][1 * 2 + 0] = -1. / Theta; 450 K[0][2 * 2 + 0] = 1. / Theta; 451 break; 452 default: 453 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Case %" PetscInt_FMT, rd->initial); 454 } 455 } else if (i == info.mx - 1) { 456 K[0][0 * 2 + 0] = -1. / Theta; 457 K[0][1 * 2 + 0] = 1. / Theta; 458 } else { 459 /*PetscScalar diff;*/ 460 RDNode ddiff[3]; 461 /*diff = (1.-Theta)*RDDiffusion(rd,hx,x0,i,0) + Theta* */ RDDiffusion(rd, hx, x, i, ddiff); 462 K[0][0 * 2 + 0] = -hx * ddiff[0].E; 463 K[0][0 * 2 + 1] = -hx * ddiff[0].T; 464 K[0][1 * 2 + 0] = hx * (a - ddiff[1].E - drad.E); 465 K[0][1 * 2 + 1] = hx * (-ddiff[1].T - drad.T); 466 K[0][2 * 2 + 0] = -hx * ddiff[2].E; 467 K[0][2 * 2 + 1] = -hx * ddiff[2].T; 468 } 469 470 K[1][1 * 2 + 0] = hx * (rho * dEm_t.E + drad.E); 471 K[1][1 * 2 + 1] = hx * (rho * dEm_t.T + drad.T); 472 473 col[0] = i - 1; 474 col[1] = i; 475 col[2] = i + 1 < info.mx ? i + 1 : -1; 476 PetscCall(MatSetValuesBlocked(B, 1, &i, 3, col, &K[0][0], INSERT_VALUES)); 477 } 478 PetscCall(RDRestoreLocalArrays(rd, &X0loc, &x0, &Xloc, &x, &Xloc_t, &xdot)); 479 PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY)); 480 PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY)); 481 if (A != B) { 482 PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY)); 483 PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY)); 484 } 485 PetscFunctionReturn(PETSC_SUCCESS); 486 } 487 488 /* Evaluate interpolants and derivatives at a select quadrature point */ 489 static void RDEvaluate(PetscReal interp[][2], PetscReal deriv[][2], PetscInt q, const RDNode x[], PetscInt i, RDNode *n, RDNode *nx) 490 { 491 PetscInt j; 492 n->E = 0; 493 n->T = 0; 494 nx->E = 0; 495 nx->T = 0; 496 for (j = 0; j < 2; j++) { 497 n->E += interp[q][j] * x[i + j].E; 498 n->T += interp[q][j] * x[i + j].T; 499 nx->E += deriv[q][j] * x[i + j].E; 500 nx->T += deriv[q][j] * x[i + j].T; 501 } 502 } 503 504 /* 505 Various quadrature rules. The nonlinear terms are non-polynomial so no standard quadrature will be exact. 506 */ 507 static PetscErrorCode RDGetQuadrature(RD rd, PetscReal hx, PetscInt *nq, PetscReal weight[], PetscReal interp[][2], PetscReal deriv[][2]) 508 { 509 PetscInt q, j; 510 const PetscReal *refweight, (*refinterp)[2], (*refderiv)[2]; 511 512 PetscFunctionBeginUser; 513 switch (rd->quadrature) { 514 case QUADRATURE_GAUSS1: { 515 static const PetscReal ww[1] = {1.}; 516 static const PetscReal ii[1][2] = { 517 {0.5, 0.5} 518 }; 519 static const PetscReal dd[1][2] = { 520 {-1., 1.} 521 }; 522 *nq = 1; 523 refweight = ww; 524 refinterp = ii; 525 refderiv = dd; 526 } break; 527 case QUADRATURE_GAUSS2: { 528 static const PetscReal ii[2][2] = { 529 {0.78867513459481287, 0.21132486540518713}, 530 {0.21132486540518713, 0.78867513459481287} 531 }; 532 static const PetscReal dd[2][2] = { 533 {-1., 1.}, 534 {-1., 1.} 535 }; 536 static const PetscReal ww[2] = {0.5, 0.5}; 537 *nq = 2; 538 refweight = ww; 539 refinterp = ii; 540 refderiv = dd; 541 } break; 542 case QUADRATURE_GAUSS3: { 543 static const PetscReal ii[3][2] = { 544 {0.8872983346207417, 0.1127016653792583}, 545 {0.5, 0.5 }, 546 {0.1127016653792583, 0.8872983346207417} 547 }; 548 static const PetscReal dd[3][2] = { 549 {-1, 1}, 550 {-1, 1}, 551 {-1, 1} 552 }; 553 static const PetscReal ww[3] = {5. / 18, 8. / 18, 5. / 18}; 554 *nq = 3; 555 refweight = ww; 556 refinterp = ii; 557 refderiv = dd; 558 } break; 559 case QUADRATURE_GAUSS4: { 560 static const PetscReal ii[][2] = { 561 {0.93056815579702623, 0.069431844202973658}, 562 {0.66999052179242813, 0.33000947820757187 }, 563 {0.33000947820757187, 0.66999052179242813 }, 564 {0.069431844202973658, 0.93056815579702623 } 565 }; 566 static const PetscReal dd[][2] = { 567 {-1, 1}, 568 {-1, 1}, 569 {-1, 1}, 570 {-1, 1} 571 }; 572 static const PetscReal ww[] = {0.17392742256872692, 0.3260725774312731, 0.3260725774312731, 0.17392742256872692}; 573 574 *nq = 4; 575 refweight = ww; 576 refinterp = ii; 577 refderiv = dd; 578 } break; 579 case QUADRATURE_LOBATTO2: { 580 static const PetscReal ii[2][2] = { 581 {1., 0.}, 582 {0., 1.} 583 }; 584 static const PetscReal dd[2][2] = { 585 {-1., 1.}, 586 {-1., 1.} 587 }; 588 static const PetscReal ww[2] = {0.5, 0.5}; 589 *nq = 2; 590 refweight = ww; 591 refinterp = ii; 592 refderiv = dd; 593 } break; 594 case QUADRATURE_LOBATTO3: { 595 static const PetscReal ii[3][2] = { 596 {1, 0 }, 597 {0.5, 0.5}, 598 {0, 1 } 599 }; 600 static const PetscReal dd[3][2] = { 601 {-1, 1}, 602 {-1, 1}, 603 {-1, 1} 604 }; 605 static const PetscReal ww[3] = {1. / 6, 4. / 6, 1. / 6}; 606 *nq = 3; 607 refweight = ww; 608 refinterp = ii; 609 refderiv = dd; 610 } break; 611 default: 612 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unknown quadrature %d", (int)rd->quadrature); 613 } 614 615 for (q = 0; q < *nq; q++) { 616 weight[q] = refweight[q] * hx; 617 for (j = 0; j < 2; j++) { 618 interp[q][j] = refinterp[q][j]; 619 deriv[q][j] = refderiv[q][j] / hx; 620 } 621 } 622 PetscFunctionReturn(PETSC_SUCCESS); 623 } 624 625 /* 626 Finite element version 627 */ 628 static PetscErrorCode RDIFunction_FE(TS ts, PetscReal t, Vec X, Vec Xdot, Vec F, void *ctx) 629 { 630 RD rd = (RD)ctx; 631 RDNode *x, *x0, *xdot, *f; 632 Vec X0loc, Xloc, Xloc_t, Floc; 633 PetscReal hx, Theta, dt, weight[5], interp[5][2], deriv[5][2]; 634 DMDALocalInfo info; 635 PetscInt i, j, q, nq; 636 637 PetscFunctionBeginUser; 638 PetscCall(RDGetLocalArrays(rd, ts, X, Xdot, &Theta, &dt, &X0loc, &x0, &Xloc, &x, &Xloc_t, &xdot)); 639 640 PetscCall(DMGetLocalVector(rd->da, &Floc)); 641 PetscCall(VecZeroEntries(Floc)); 642 PetscCall(DMDAVecGetArray(rd->da, Floc, &f)); 643 PetscCall(DMDAGetLocalInfo(rd->da, &info)); 644 645 /* Set up shape functions and quadrature for elements (assumes a uniform grid) */ 646 hx = rd->L / (info.mx - 1); 647 PetscCall(RDGetQuadrature(rd, hx, &nq, weight, interp, deriv)); 648 649 for (i = info.xs; i < PetscMin(info.xs + info.xm, info.mx - 1); i++) { 650 for (q = 0; q < nq; q++) { 651 PetscReal rho = rd->rho; 652 PetscScalar Em_t, rad, D_R, D0_R; 653 RDNode n, n0, nx, n0x, nt, ntx; 654 RDEvaluate(interp, deriv, q, x, i, &n, &nx); 655 RDEvaluate(interp, deriv, q, x0, i, &n0, &n0x); 656 RDEvaluate(interp, deriv, q, xdot, i, &nt, &ntx); 657 658 rad = (1. - Theta) * RDRadiation(rd, &n0, 0) + Theta * RDRadiation(rd, &n, 0); 659 if (rd->endpoint) { 660 PetscScalar Em0, Em1; 661 RDMaterialEnergy(rd, &n0, &Em0, NULL); 662 RDMaterialEnergy(rd, &n, &Em1, NULL); 663 Em_t = (Em1 - Em0) / dt; 664 } else { 665 RDNode dEm; 666 RDMaterialEnergy(rd, &n, NULL, &dEm); 667 Em_t = dEm.E * nt.E + dEm.T * nt.T; 668 } 669 RDDiffusionCoefficient(rd, PETSC_TRUE, &n0, &n0x, &D0_R, 0, 0); 670 RDDiffusionCoefficient(rd, PETSC_TRUE, &n, &nx, &D_R, 0, 0); 671 for (j = 0; j < 2; j++) { 672 f[i + j].E += (deriv[q][j] * weight[q] * ((1. - Theta) * D0_R * n0x.E + Theta * D_R * nx.E) + interp[q][j] * weight[q] * (nt.E - rad)); 673 f[i + j].T += interp[q][j] * weight[q] * (rho * Em_t + rad); 674 } 675 } 676 } 677 if (info.xs == 0) { 678 switch (rd->leftbc) { 679 case BC_ROBIN: { 680 PetscScalar D_R, D_R_bc; 681 PetscReal ratio, bcTheta = rd->bcmidpoint ? Theta : 1.; 682 RDNode n, nx; 683 684 n.E = (1 - bcTheta) * x0[0].E + bcTheta * x[0].E; 685 n.T = (1 - bcTheta) * x0[0].T + bcTheta * x[0].T; 686 nx.E = (x[1].E - x[0].E) / hx; 687 nx.T = (x[1].T - x[0].T) / hx; 688 RDDiffusionCoefficient(rd, PETSC_TRUE, &n, &nx, &D_R, 0, 0); 689 RDDiffusionCoefficient(rd, rd->bclimit, &n, &nx, &D_R_bc, 0, 0); 690 ratio = PetscRealPart(D_R / D_R_bc); 691 PetscCheck(ratio <= 1., PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Limited diffusivity is greater than unlimited"); 692 PetscCheck(ratio >= 1e-3, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Heavily limited diffusivity"); 693 f[0].E += -ratio * 0.5 * (rd->Eapplied - n.E); 694 } break; 695 case BC_NEUMANN: 696 /* homogeneous Neumann is the natural condition */ 697 break; 698 default: 699 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Case %" PetscInt_FMT, rd->initial); 700 } 701 } 702 703 PetscCall(RDRestoreLocalArrays(rd, &X0loc, &x0, &Xloc, &x, &Xloc_t, &xdot)); 704 PetscCall(DMDAVecRestoreArray(rd->da, Floc, &f)); 705 PetscCall(VecZeroEntries(F)); 706 PetscCall(DMLocalToGlobalBegin(rd->da, Floc, ADD_VALUES, F)); 707 PetscCall(DMLocalToGlobalEnd(rd->da, Floc, ADD_VALUES, F)); 708 PetscCall(DMRestoreLocalVector(rd->da, &Floc)); 709 710 if (rd->monitor_residual) PetscCall(RDStateView(rd, X, Xdot, F)); 711 PetscFunctionReturn(PETSC_SUCCESS); 712 } 713 714 static PetscErrorCode RDIJacobian_FE(TS ts, PetscReal t, Vec X, Vec Xdot, PetscReal a, Mat A, Mat B, void *ctx) 715 { 716 RD rd = (RD)ctx; 717 RDNode *x, *x0, *xdot; 718 Vec X0loc, Xloc, Xloc_t; 719 PetscReal hx, Theta, dt, weight[5], interp[5][2], deriv[5][2]; 720 DMDALocalInfo info; 721 PetscInt i, j, k, q, nq; 722 PetscScalar K[4][4]; 723 724 PetscFunctionBeginUser; 725 PetscCall(RDGetLocalArrays(rd, ts, X, Xdot, &Theta, &dt, &X0loc, &x0, &Xloc, &x, &Xloc_t, &xdot)); 726 PetscCall(DMDAGetLocalInfo(rd->da, &info)); 727 hx = rd->L / (info.mx - 1); 728 PetscCall(RDGetQuadrature(rd, hx, &nq, weight, interp, deriv)); 729 PetscCall(MatZeroEntries(B)); 730 for (i = info.xs; i < PetscMin(info.xs + info.xm, info.mx - 1); i++) { 731 PetscInt rc[2]; 732 733 rc[0] = i; 734 rc[1] = i + 1; 735 PetscCall(PetscMemzero(K, sizeof(K))); 736 for (q = 0; q < nq; q++) { 737 PetscScalar D_R; 738 PETSC_UNUSED PetscScalar rad; 739 RDNode n, nx, nt, ntx, drad, dD_R, dxD_R, dEm; 740 RDEvaluate(interp, deriv, q, x, i, &n, &nx); 741 RDEvaluate(interp, deriv, q, xdot, i, &nt, &ntx); 742 rad = RDRadiation(rd, &n, &drad); 743 RDDiffusionCoefficient(rd, PETSC_TRUE, &n, &nx, &D_R, &dD_R, &dxD_R); 744 RDMaterialEnergy(rd, &n, NULL, &dEm); 745 for (j = 0; j < 2; j++) { 746 for (k = 0; k < 2; k++) { 747 K[j * 2 + 0][k * 2 + 0] += (+interp[q][j] * weight[q] * (a - drad.E) * interp[q][k] + deriv[q][j] * weight[q] * ((D_R + dxD_R.E * nx.E) * deriv[q][k] + dD_R.E * nx.E * interp[q][k])); 748 K[j * 2 + 0][k * 2 + 1] += (+interp[q][j] * weight[q] * (-drad.T * interp[q][k]) + deriv[q][j] * weight[q] * (dxD_R.T * deriv[q][k] + dD_R.T * interp[q][k]) * nx.E); 749 K[j * 2 + 1][k * 2 + 0] += interp[q][j] * weight[q] * drad.E * interp[q][k]; 750 K[j * 2 + 1][k * 2 + 1] += interp[q][j] * weight[q] * (a * rd->rho * dEm.T + drad.T) * interp[q][k]; 751 } 752 } 753 } 754 PetscCall(MatSetValuesBlocked(B, 2, rc, 2, rc, &K[0][0], ADD_VALUES)); 755 } 756 if (info.xs == 0) { 757 switch (rd->leftbc) { 758 case BC_ROBIN: { 759 PetscScalar D_R, D_R_bc; 760 PetscReal ratio; 761 RDNode n, nx; 762 763 n.E = (1 - Theta) * x0[0].E + Theta * x[0].E; 764 n.T = (1 - Theta) * x0[0].T + Theta * x[0].T; 765 nx.E = (x[1].E - x[0].E) / hx; 766 nx.T = (x[1].T - x[0].T) / hx; 767 RDDiffusionCoefficient(rd, PETSC_TRUE, &n, &nx, &D_R, 0, 0); 768 RDDiffusionCoefficient(rd, rd->bclimit, &n, &nx, &D_R_bc, 0, 0); 769 ratio = PetscRealPart(D_R / D_R_bc); 770 PetscCall(MatSetValue(B, 0, 0, ratio * 0.5, ADD_VALUES)); 771 } break; 772 case BC_NEUMANN: 773 /* homogeneous Neumann is the natural condition */ 774 break; 775 default: 776 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Case %" PetscInt_FMT, rd->initial); 777 } 778 } 779 780 PetscCall(RDRestoreLocalArrays(rd, &X0loc, &x0, &Xloc, &x, &Xloc_t, &xdot)); 781 PetscCall(MatAssemblyBegin(B, MAT_FINAL_ASSEMBLY)); 782 PetscCall(MatAssemblyEnd(B, MAT_FINAL_ASSEMBLY)); 783 if (A != B) { 784 PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY)); 785 PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY)); 786 } 787 PetscFunctionReturn(PETSC_SUCCESS); 788 } 789 790 /* Temperature that is in equilibrium with the radiation density */ 791 static PetscScalar RDRadiationTemperature(RD rd, PetscScalar E) 792 { 793 return PetscPowScalar(E * rd->c / (4. * rd->sigma_b), 0.25); 794 } 795 796 static PetscErrorCode RDInitialState(RD rd, Vec X) 797 { 798 DMDALocalInfo info; 799 PetscInt i; 800 RDNode *x; 801 802 PetscFunctionBeginUser; 803 PetscCall(DMDAGetLocalInfo(rd->da, &info)); 804 PetscCall(DMDAVecGetArray(rd->da, X, &x)); 805 for (i = info.xs; i < info.xs + info.xm; i++) { 806 PetscReal coord = i * rd->L / (info.mx - 1); 807 switch (rd->initial) { 808 case 1: 809 x[i].E = 0.001; 810 x[i].T = RDRadiationTemperature(rd, x[i].E); 811 break; 812 case 2: 813 x[i].E = 0.001 + 100. * PetscExpReal(-PetscSqr(coord / 0.1)); 814 x[i].T = RDRadiationTemperature(rd, x[i].E); 815 break; 816 case 3: 817 x[i].E = 7.56e-2 * rd->unit.Joule / PetscPowScalarInt(rd->unit.meter, 3); 818 x[i].T = RDRadiationTemperature(rd, x[i].E); 819 break; 820 default: 821 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "No initial state %" PetscInt_FMT, rd->initial); 822 } 823 } 824 PetscCall(DMDAVecRestoreArray(rd->da, X, &x)); 825 PetscFunctionReturn(PETSC_SUCCESS); 826 } 827 828 static PetscErrorCode RDView(RD rd, Vec X, PetscViewer viewer) 829 { 830 Vec Y; 831 const RDNode *x; 832 PetscScalar *y; 833 PetscInt i, m, M; 834 const PetscInt *lx; 835 DM da; 836 MPI_Comm comm; 837 838 PetscFunctionBeginUser; 839 /* 840 Create a DMDA (one dof per node, zero stencil width, same layout) to hold Trad 841 (radiation temperature). It is not necessary to create a DMDA for this, but this way 842 output and visualization will have meaningful variable names and correct scales. 843 */ 844 PetscCall(DMDAGetInfo(rd->da, 0, &M, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 845 PetscCall(DMDAGetOwnershipRanges(rd->da, &lx, 0, 0)); 846 PetscCall(PetscObjectGetComm((PetscObject)rd->da, &comm)); 847 PetscCall(DMDACreate1d(comm, DM_BOUNDARY_NONE, M, 1, 0, lx, &da)); 848 PetscCall(DMSetFromOptions(da)); 849 PetscCall(DMSetUp(da)); 850 PetscCall(DMDASetUniformCoordinates(da, 0., rd->L, 0., 0., 0., 0.)); 851 PetscCall(DMDASetFieldName(da, 0, "T_rad")); 852 PetscCall(DMCreateGlobalVector(da, &Y)); 853 854 /* Compute the radiation temperature from the solution at each node */ 855 PetscCall(VecGetLocalSize(Y, &m)); 856 PetscCall(VecGetArrayRead(X, (const PetscScalar **)&x)); 857 PetscCall(VecGetArray(Y, &y)); 858 for (i = 0; i < m; i++) y[i] = RDRadiationTemperature(rd, x[i].E); 859 PetscCall(VecRestoreArrayRead(X, (const PetscScalar **)&x)); 860 PetscCall(VecRestoreArray(Y, &y)); 861 862 PetscCall(VecView(Y, viewer)); 863 PetscCall(VecDestroy(&Y)); 864 PetscCall(DMDestroy(&da)); 865 PetscFunctionReturn(PETSC_SUCCESS); 866 } 867 868 static PetscErrorCode RDTestDifferentiation(RD rd) 869 { 870 MPI_Comm comm; 871 RDNode n, nx; 872 PetscScalar epsilon; 873 874 PetscFunctionBeginUser; 875 PetscCall(PetscObjectGetComm((PetscObject)rd->da, &comm)); 876 epsilon = 1e-8; 877 { 878 RDNode dEm, fdEm; 879 PetscScalar T0 = 1000., T1 = T0 * (1. + epsilon), Em0, Em1; 880 n.E = 1.; 881 n.T = T0; 882 rd->MaterialEnergy(rd, &n, &Em0, &dEm); 883 n.E = 1. + epsilon; 884 n.T = T0; 885 rd->MaterialEnergy(rd, &n, &Em1, 0); 886 fdEm.E = (Em1 - Em0) / epsilon; 887 n.E = 1.; 888 n.T = T1; 889 rd->MaterialEnergy(rd, &n, &Em1, 0); 890 fdEm.T = (Em1 - Em0) / (T0 * epsilon); 891 PetscCall(PetscPrintf(comm, "dEm {%g,%g}, fdEm {%g,%g}, diff {%g,%g}\n", (double)PetscRealPart(dEm.E), (double)PetscRealPart(dEm.T), (double)PetscRealPart(fdEm.E), (double)PetscRealPart(fdEm.T), (double)PetscRealPart(dEm.E - fdEm.E), 892 (double)PetscRealPart(dEm.T - fdEm.T))); 893 } 894 { 895 PetscScalar D0, D; 896 RDNode dD, dxD, fdD, fdxD; 897 n.E = 1.; 898 n.T = 1.; 899 nx.E = 1.; 900 n.T = 1.; 901 RDDiffusionCoefficient(rd, rd->bclimit, &n, &nx, &D0, &dD, &dxD); 902 n.E = 1. + epsilon; 903 n.T = 1.; 904 nx.E = 1.; 905 n.T = 1.; 906 RDDiffusionCoefficient(rd, rd->bclimit, &n, &nx, &D, 0, 0); 907 fdD.E = (D - D0) / epsilon; 908 n.E = 1; 909 n.T = 1. + epsilon; 910 nx.E = 1.; 911 n.T = 1.; 912 RDDiffusionCoefficient(rd, rd->bclimit, &n, &nx, &D, 0, 0); 913 fdD.T = (D - D0) / epsilon; 914 n.E = 1; 915 n.T = 1.; 916 nx.E = 1. + epsilon; 917 n.T = 1.; 918 RDDiffusionCoefficient(rd, rd->bclimit, &n, &nx, &D, 0, 0); 919 fdxD.E = (D - D0) / epsilon; 920 n.E = 1; 921 n.T = 1.; 922 nx.E = 1.; 923 n.T = 1. + epsilon; 924 RDDiffusionCoefficient(rd, rd->bclimit, &n, &nx, &D, 0, 0); 925 fdxD.T = (D - D0) / epsilon; 926 PetscCall(PetscPrintf(comm, "dD {%g,%g}, fdD {%g,%g}, diff {%g,%g}\n", (double)PetscRealPart(dD.E), (double)PetscRealPart(dD.T), (double)PetscRealPart(fdD.E), (double)PetscRealPart(fdD.T), (double)PetscRealPart(dD.E - fdD.E), 927 (double)PetscRealPart(dD.T - fdD.T))); 928 PetscCall(PetscPrintf(comm, "dxD {%g,%g}, fdxD {%g,%g}, diffx {%g,%g}\n", (double)PetscRealPart(dxD.E), (double)PetscRealPart(dxD.T), (double)PetscRealPart(fdxD.E), (double)PetscRealPart(fdxD.T), (double)PetscRealPart(dxD.E - fdxD.E), 929 (double)PetscRealPart(dxD.T - fdxD.T))); 930 } 931 { 932 PetscInt i; 933 PetscReal hx = 1.; 934 PetscScalar a0; 935 RDNode n0[3], n1[3], d[3], fd[3]; 936 937 n0[0].E = 1.; 938 n0[0].T = 1.; 939 n0[1].E = 5.; 940 n0[1].T = 3.; 941 n0[2].E = 4.; 942 n0[2].T = 2.; 943 a0 = RDDiffusion(rd, hx, n0, 1, d); 944 for (i = 0; i < 3; i++) { 945 PetscCall(PetscMemcpy(n1, n0, sizeof(n0))); 946 n1[i].E += epsilon; 947 fd[i].E = (RDDiffusion(rd, hx, n1, 1, 0) - a0) / epsilon; 948 PetscCall(PetscMemcpy(n1, n0, sizeof(n0))); 949 n1[i].T += epsilon; 950 fd[i].T = (RDDiffusion(rd, hx, n1, 1, 0) - a0) / epsilon; 951 PetscCall(PetscPrintf(comm, "ddiff[%" PetscInt_FMT "] {%g,%g}, fd {%g %g}, diff {%g,%g}\n", i, (double)PetscRealPart(d[i].E), (double)PetscRealPart(d[i].T), (double)PetscRealPart(fd[i].E), (double)PetscRealPart(fd[i].T), 952 (double)PetscRealPart(d[i].E - fd[i].E), (double)PetscRealPart(d[i].T - fd[i].T))); 953 } 954 } 955 { 956 PetscScalar rad0, rad; 957 RDNode drad, fdrad; 958 n.E = 1.; 959 n.T = 1.; 960 rad0 = RDRadiation(rd, &n, &drad); 961 n.E = 1. + epsilon; 962 n.T = 1.; 963 rad = RDRadiation(rd, &n, 0); 964 fdrad.E = (rad - rad0) / epsilon; 965 n.E = 1.; 966 n.T = 1. + epsilon; 967 rad = RDRadiation(rd, &n, 0); 968 fdrad.T = (rad - rad0) / epsilon; 969 PetscCall(PetscPrintf(comm, "drad {%g,%g}, fdrad {%g,%g}, diff {%g,%g}\n", (double)PetscRealPart(drad.E), (double)PetscRealPart(drad.T), (double)PetscRealPart(fdrad.E), (double)PetscRealPart(fdrad.T), (double)PetscRealPart(drad.E - drad.E), 970 (double)PetscRealPart(drad.T - fdrad.T))); 971 } 972 PetscFunctionReturn(PETSC_SUCCESS); 973 } 974 975 static PetscErrorCode RDCreate(MPI_Comm comm, RD *inrd) 976 { 977 RD rd; 978 PetscReal meter = 0, kilogram = 0, second = 0, Kelvin = 0, Joule = 0, Watt = 0; 979 980 PetscFunctionBeginUser; 981 *inrd = 0; 982 PetscCall(PetscNew(&rd)); 983 984 PetscOptionsBegin(comm, NULL, "Options for nonequilibrium radiation-diffusion with RD ionization", NULL); 985 { 986 rd->initial = 1; 987 PetscCall(PetscOptionsInt("-rd_initial", "Initial condition (1=Marshak, 2=Blast, 3=Marshak+)", "", rd->initial, &rd->initial, 0)); 988 switch (rd->initial) { 989 case 1: 990 case 2: 991 rd->unit.kilogram = 1.; 992 rd->unit.meter = 1.; 993 rd->unit.second = 1.; 994 rd->unit.Kelvin = 1.; 995 break; 996 case 3: 997 rd->unit.kilogram = 1.e12; 998 rd->unit.meter = 1.; 999 rd->unit.second = 1.e9; 1000 rd->unit.Kelvin = 1.; 1001 break; 1002 default: 1003 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Unknown initial condition %" PetscInt_FMT, rd->initial); 1004 } 1005 /* Fundamental units */ 1006 PetscCall(PetscOptionsReal("-rd_unit_meter", "Length of 1 meter in nondimensional units", "", rd->unit.meter, &rd->unit.meter, 0)); 1007 PetscCall(PetscOptionsReal("-rd_unit_kilogram", "Mass of 1 kilogram in nondimensional units", "", rd->unit.kilogram, &rd->unit.kilogram, 0)); 1008 PetscCall(PetscOptionsReal("-rd_unit_second", "Time of a second in nondimensional units", "", rd->unit.second, &rd->unit.second, 0)); 1009 PetscCall(PetscOptionsReal("-rd_unit_Kelvin", "Temperature of a Kelvin in nondimensional units", "", rd->unit.Kelvin, &rd->unit.Kelvin, 0)); 1010 /* Derived units */ 1011 rd->unit.Joule = rd->unit.kilogram * PetscSqr(rd->unit.meter / rd->unit.second); 1012 rd->unit.Watt = rd->unit.Joule / rd->unit.second; 1013 /* Local aliases */ 1014 meter = rd->unit.meter; 1015 kilogram = rd->unit.kilogram; 1016 second = rd->unit.second; 1017 Kelvin = rd->unit.Kelvin; 1018 Joule = rd->unit.Joule; 1019 Watt = rd->unit.Watt; 1020 1021 PetscCall(PetscOptionsBool("-rd_monitor_residual", "Display residuals every time they are evaluated", "", rd->monitor_residual, &rd->monitor_residual, NULL)); 1022 PetscCall(PetscOptionsEnum("-rd_discretization", "Discretization type", "", DiscretizationTypes, (PetscEnum)rd->discretization, (PetscEnum *)&rd->discretization, NULL)); 1023 if (rd->discretization == DISCRETIZATION_FE) { 1024 rd->quadrature = QUADRATURE_GAUSS2; 1025 PetscCall(PetscOptionsEnum("-rd_quadrature", "Finite element quadrature", "", QuadratureTypes, (PetscEnum)rd->quadrature, (PetscEnum *)&rd->quadrature, NULL)); 1026 } 1027 PetscCall(PetscOptionsEnum("-rd_jacobian", "Type of finite difference Jacobian", "", JacobianTypes, (PetscEnum)rd->jacobian, (PetscEnum *)&rd->jacobian, NULL)); 1028 switch (rd->initial) { 1029 case 1: 1030 rd->leftbc = BC_ROBIN; 1031 rd->Eapplied = 4 * rd->unit.Joule / PetscPowRealInt(rd->unit.meter, 3); 1032 rd->L = 1. * rd->unit.meter; 1033 rd->beta = 3.0; 1034 rd->gamma = 3.0; 1035 rd->final_time = 3 * second; 1036 break; 1037 case 2: 1038 rd->leftbc = BC_NEUMANN; 1039 rd->Eapplied = 0.; 1040 rd->L = 1. * rd->unit.meter; 1041 rd->beta = 3.0; 1042 rd->gamma = 3.0; 1043 rd->final_time = 1 * second; 1044 break; 1045 case 3: 1046 rd->leftbc = BC_ROBIN; 1047 rd->Eapplied = 7.503e6 * rd->unit.Joule / PetscPowRealInt(rd->unit.meter, 3); 1048 rd->L = 5. * rd->unit.meter; 1049 rd->beta = 3.5; 1050 rd->gamma = 3.5; 1051 rd->final_time = 20e-9 * second; 1052 break; 1053 default: 1054 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Initial %" PetscInt_FMT, rd->initial); 1055 } 1056 PetscCall(PetscOptionsEnum("-rd_leftbc", "Left boundary condition", "", BCTypes, (PetscEnum)rd->leftbc, (PetscEnum *)&rd->leftbc, NULL)); 1057 PetscCall(PetscOptionsReal("-rd_E_applied", "Radiation flux at left end of domain", "", rd->Eapplied, &rd->Eapplied, NULL)); 1058 PetscCall(PetscOptionsReal("-rd_beta", "Thermal exponent for photon absorption", "", rd->beta, &rd->beta, NULL)); 1059 PetscCall(PetscOptionsReal("-rd_gamma", "Thermal exponent for diffusion coefficient", "", rd->gamma, &rd->gamma, NULL)); 1060 PetscCall(PetscOptionsBool("-rd_view_draw", "Draw final solution", "", rd->view_draw, &rd->view_draw, NULL)); 1061 PetscCall(PetscOptionsBool("-rd_endpoint", "Discretize using endpoints (like trapezoid rule) instead of midpoint", "", rd->endpoint, &rd->endpoint, NULL)); 1062 PetscCall(PetscOptionsBool("-rd_bcmidpoint", "Impose the boundary condition at the midpoint (Theta) of the interval", "", rd->bcmidpoint, &rd->bcmidpoint, NULL)); 1063 PetscCall(PetscOptionsBool("-rd_bclimit", "Limit diffusion coefficient in definition of Robin boundary condition", "", rd->bclimit, &rd->bclimit, NULL)); 1064 PetscCall(PetscOptionsBool("-rd_test_diff", "Test differentiation in constitutive relations", "", rd->test_diff, &rd->test_diff, NULL)); 1065 PetscCall(PetscOptionsString("-rd_view_binary", "File name to hold final solution", "", rd->view_binary, rd->view_binary, sizeof(rd->view_binary), NULL)); 1066 } 1067 PetscOptionsEnd(); 1068 1069 switch (rd->initial) { 1070 case 1: 1071 case 2: 1072 rd->rho = 1.; 1073 rd->c = 1.; 1074 rd->K_R = 1.; 1075 rd->K_p = 1.; 1076 rd->sigma_b = 0.25; 1077 rd->MaterialEnergy = RDMaterialEnergy_Reduced; 1078 break; 1079 case 3: 1080 /* Table 2 */ 1081 rd->rho = 1.17e-3 * kilogram / (meter * meter * meter); /* density */ 1082 rd->K_R = 7.44e18 * PetscPowRealInt(meter, 5) * PetscPowReal(Kelvin, 3.5) * PetscPowRealInt(kilogram, -2); /* */ 1083 rd->K_p = 2.33e20 * PetscPowRealInt(meter, 5) * PetscPowReal(Kelvin, 3.5) * PetscPowRealInt(kilogram, -2); /* */ 1084 rd->I_H = 2.179e-18 * Joule; /* Hydrogen ionization potential */ 1085 rd->m_p = 1.673e-27 * kilogram; /* proton mass */ 1086 rd->m_e = 9.109e-31 * kilogram; /* electron mass */ 1087 rd->h = 6.626e-34 * Joule * second; /* Planck's constant */ 1088 rd->k = 1.381e-23 * Joule / Kelvin; /* Boltzman constant */ 1089 rd->c = 3.00e8 * meter / second; /* speed of light */ 1090 rd->sigma_b = 5.67e-8 * Watt * PetscPowRealInt(meter, -2) * PetscPowRealInt(Kelvin, -4); /* Stefan-Boltzman constant */ 1091 rd->MaterialEnergy = RDMaterialEnergy_Saha; 1092 break; 1093 } 1094 1095 PetscCall(DMDACreate1d(comm, DM_BOUNDARY_NONE, 20, sizeof(RDNode) / sizeof(PetscScalar), 1, NULL, &rd->da)); 1096 PetscCall(DMSetFromOptions(rd->da)); 1097 PetscCall(DMSetUp(rd->da)); 1098 PetscCall(DMDASetFieldName(rd->da, 0, "E")); 1099 PetscCall(DMDASetFieldName(rd->da, 1, "T")); 1100 PetscCall(DMDASetUniformCoordinates(rd->da, 0., 1., 0., 0., 0., 0.)); 1101 1102 *inrd = rd; 1103 PetscFunctionReturn(PETSC_SUCCESS); 1104 } 1105 1106 int main(int argc, char *argv[]) 1107 { 1108 RD rd; 1109 TS ts; 1110 SNES snes; 1111 Vec X; 1112 Mat A, B; 1113 PetscInt steps; 1114 PetscReal ftime; 1115 1116 PetscFunctionBeginUser; 1117 PetscCall(PetscInitialize(&argc, &argv, 0, NULL)); 1118 PetscCall(RDCreate(PETSC_COMM_WORLD, &rd)); 1119 PetscCall(DMCreateGlobalVector(rd->da, &X)); 1120 PetscCall(DMSetMatType(rd->da, MATAIJ)); 1121 PetscCall(DMCreateMatrix(rd->da, &B)); 1122 PetscCall(RDInitialState(rd, X)); 1123 1124 PetscCall(TSCreate(PETSC_COMM_WORLD, &ts)); 1125 PetscCall(TSSetProblemType(ts, TS_NONLINEAR)); 1126 PetscCall(TSSetType(ts, TSTHETA)); 1127 PetscCall(TSSetDM(ts, rd->da)); 1128 switch (rd->discretization) { 1129 case DISCRETIZATION_FD: 1130 PetscCall(TSSetIFunction(ts, NULL, RDIFunction_FD, rd)); 1131 if (rd->jacobian == JACOBIAN_ANALYTIC) PetscCall(TSSetIJacobian(ts, B, B, RDIJacobian_FD, rd)); 1132 break; 1133 case DISCRETIZATION_FE: 1134 PetscCall(TSSetIFunction(ts, NULL, RDIFunction_FE, rd)); 1135 if (rd->jacobian == JACOBIAN_ANALYTIC) PetscCall(TSSetIJacobian(ts, B, B, RDIJacobian_FE, rd)); 1136 break; 1137 } 1138 PetscCall(TSSetMaxTime(ts, rd->final_time)); 1139 PetscCall(TSSetTimeStep(ts, 1e-3)); 1140 PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER)); 1141 PetscCall(TSSetFromOptions(ts)); 1142 1143 A = B; 1144 PetscCall(TSGetSNES(ts, &snes)); 1145 switch (rd->jacobian) { 1146 case JACOBIAN_ANALYTIC: 1147 break; 1148 case JACOBIAN_MATRIXFREE: 1149 break; 1150 case JACOBIAN_FD_COLORING: { 1151 PetscCall(SNESSetJacobian(snes, A, B, SNESComputeJacobianDefaultColor, 0)); 1152 } break; 1153 case JACOBIAN_FD_FULL: 1154 PetscCall(SNESSetJacobian(snes, A, B, SNESComputeJacobianDefault, ts)); 1155 break; 1156 } 1157 1158 if (rd->test_diff) PetscCall(RDTestDifferentiation(rd)); 1159 PetscCall(TSSolve(ts, X)); 1160 PetscCall(TSGetSolveTime(ts, &ftime)); 1161 PetscCall(TSGetStepNumber(ts, &steps)); 1162 PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Steps %" PetscInt_FMT " final time %g\n", steps, (double)ftime)); 1163 if (rd->view_draw) PetscCall(RDView(rd, X, PETSC_VIEWER_DRAW_WORLD)); 1164 if (rd->view_binary[0]) { 1165 PetscViewer viewer; 1166 PetscCall(PetscViewerBinaryOpen(PETSC_COMM_WORLD, rd->view_binary, FILE_MODE_WRITE, &viewer)); 1167 PetscCall(RDView(rd, X, viewer)); 1168 PetscCall(PetscViewerDestroy(&viewer)); 1169 } 1170 PetscCall(VecDestroy(&X)); 1171 PetscCall(MatDestroy(&B)); 1172 PetscCall(RDDestroy(&rd)); 1173 PetscCall(TSDestroy(&ts)); 1174 PetscCall(PetscFinalize()); 1175 return 0; 1176 } 1177 /*TEST 1178 1179 test: 1180 args: -da_grid_x 20 -rd_initial 1 -rd_discretization fd -rd_jacobian fd_coloring -rd_endpoint -ts_max_time 1 -ts_dt 2e-1 -ts_theta_initial_guess_extrapolate 0 -ts_monitor -snes_monitor_short -ksp_monitor_short 1181 requires: !single 1182 1183 test: 1184 suffix: 2 1185 args: -da_grid_x 20 -rd_initial 1 -rd_discretization fe -rd_quadrature lobatto2 -rd_jacobian fd_coloring -rd_endpoint -ts_max_time 1 -ts_dt 2e-1 -ts_theta_initial_guess_extrapolate 0 -ts_monitor -snes_monitor_short -ksp_monitor_short 1186 requires: !single 1187 1188 test: 1189 suffix: 3 1190 nsize: 2 1191 args: -da_grid_x 20 -rd_initial 1 -rd_discretization fd -rd_jacobian analytic -rd_endpoint -ts_max_time 3 -ts_dt 1e-1 -ts_theta_initial_guess_extrapolate 0 -ts_monitor -snes_monitor_short -ksp_monitor_short 1192 requires: !single 1193 1194 TEST*/ 1195