blob: 23fab68c937d64fb7253ef87d40328183610cce6 [file] [log] [blame]
/*
* Hydro.cc
*
* Created on: Dec 22, 2011
* Author: cferenba
*
* Copyright (c) 2012, Los Alamos National Security, LLC.
* All rights reserved.
* Use of this source code is governed by a BSD-style open-source
* license; see top-level LICENSE file for full license text.
*/
#include "Hydro.hh"
#include <string>
#include <vector>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include "Parallel.hh"
#include "Memory.hh"
#include "InputFile.hh"
#include "Mesh.hh"
#include "PolyGas.hh"
#include "TTS.hh"
#include "QCS.hh"
#include "HydroBC.hh"
using namespace std;
Hydro::Hydro(const InputFile* inp, Mesh* m) : mesh(m) {
cfl = inp->getDouble("cfl", 0.6);
cflv = inp->getDouble("cflv", 0.1);
rinit = inp->getDouble("rinit", 1.);
einit = inp->getDouble("einit", 0.);
rinitsub = inp->getDouble("rinitsub", 1.);
einitsub = inp->getDouble("einitsub", 0.);
uinitradial = inp->getDouble("uinitradial", 0.);
bcx = inp->getDoubleList("bcx", vector<double>());
bcy = inp->getDoubleList("bcy", vector<double>());
pgas = new PolyGas(inp, this);
tts = new TTS(inp, this);
qcs = new QCS(inp, this);
const double2 vfixx = double2(1., 0.);
const double2 vfixy = double2(0., 1.);
for (int i = 0; i < bcx.size(); ++i)
bcs.push_back(new HydroBC(mesh, vfixx, mesh->getXPlane(bcx[i])));
for (int i = 0; i < bcy.size(); ++i)
bcs.push_back(new HydroBC(mesh, vfixy, mesh->getYPlane(bcy[i])));
init();
}
Hydro::~Hydro() {
delete tts;
delete qcs;
for (int i = 0; i < bcs.size(); ++i) {
delete bcs[i];
}
}
void Hydro::init() {
const int numpch = mesh->numpch;
const int numzch = mesh->numzch;
const int nump = mesh->nump;
const int numz = mesh->numz;
const int nums = mesh->nums;
const double2* zx = mesh->zx;
const double* zvol = mesh->zvol;
// allocate arrays
pu = Memory::alloc<double2>(nump);
pu0 = Memory::alloc<double2>(nump);
pap = Memory::alloc<double2>(nump);
pf = Memory::alloc<double2>(nump);
pmaswt = Memory::alloc<double>(nump);
cmaswt = Memory::alloc<double>(nums);
zm = Memory::alloc<double>(numz);
zr = Memory::alloc<double>(numz);
zrp = Memory::alloc<double>(numz);
ze = Memory::alloc<double>(numz);
zetot = Memory::alloc<double>(numz);
zw = Memory::alloc<double>(numz);
zwrate = Memory::alloc<double>(numz);
zp = Memory::alloc<double>(numz);
zss = Memory::alloc<double>(numz);
zdu = Memory::alloc<double>(numz);
sfp = Memory::alloc<double2>(nums);
sfq = Memory::alloc<double2>(nums);
sft = Memory::alloc<double2>(nums);
cftot = Memory::alloc<double2>(nums);
// initialize hydro vars
#pragma omp parallel for schedule(static)
for (int zch = 0; zch < numzch; ++zch) {
int zfirst = mesh->zchzfirst[zch];
int zlast = mesh->zchzlast[zch];
fill(&zr[zfirst], &zr[zlast], rinit);
fill(&ze[zfirst], &ze[zlast], einit);
fill(&zwrate[zfirst], &zwrate[zlast], 0.);
const vector<double>& subrgn = mesh->subregion;
if (!subrgn.empty()) {
const double eps = 1.e-12;
#pragma ivdep
for (int z = zfirst; z < zlast; ++z) {
if (zx[z].x > (subrgn[0] - eps) &&
zx[z].x < (subrgn[1] + eps) &&
zx[z].y > (subrgn[2] - eps) &&
zx[z].y < (subrgn[3] + eps)) {
zr[z] = rinitsub;
ze[z] = einitsub;
}
}
}
#pragma ivdep
for (int z = zfirst; z < zlast; ++z) {
zm[z] = zr[z] * zvol[z];
zetot[z] = ze[z] * zm[z];
}
} // for sch
#pragma omp parallel for schedule(static)
for (int pch = 0; pch < numpch; ++pch) {
int pfirst = mesh->pchpfirst[pch];
int plast = mesh->pchplast[pch];
if (uinitradial != 0.)
initRadialVel(uinitradial, pfirst, plast);
else
fill(&pu[pfirst], &pu[plast], double2(0., 0.));
} // for pch
resetDtHydro();
}
void Hydro::initRadialVel(
const double vel,
const int pfirst,
const int plast) {
const double2* px = mesh->px;
const double eps = 1.e-12;
#pragma ivdep
for (int p = pfirst; p < plast; ++p) {
double pmag = length(px[p]);
if (pmag > eps)
pu[p] = vel * px[p] / pmag;
else
pu[p] = double2(0., 0.);
}
}
void Hydro::doCycle(
const double dt) {
const int numpch = mesh->numpch;
const int numsch = mesh->numsch;
double2* px = mesh->px;
double2* ex = mesh->ex;
double2* zx = mesh->zx;
double* sarea = mesh->sarea;
double* svol = mesh->svol;
double* zarea = mesh->zarea;
double* zvol = mesh->zvol;
double* sareap = mesh->sareap;
double* svolp = mesh->svolp;
double* zareap = mesh->zareap;
double* zvolp = mesh->zvolp;
double* zvol0 = mesh->zvol0;
double2* ssurfp = mesh->ssurfp;
double* elen = mesh->elen;
double2* px0 = mesh->px0;
double2* pxp = mesh->pxp;
double2* exp = mesh->exp;
double2* zxp = mesh->zxp;
double* smf = mesh->smf;
double* zdl = mesh->zdl;
// Begin hydro cycle
#pragma omp parallel for schedule(static)
for (int pch = 0; pch < numpch; ++pch) {
int pfirst = mesh->pchpfirst[pch];
int plast = mesh->pchplast[pch];
// save off point variable values from previous cycle
copy(&px[pfirst], &px[plast], &px0[pfirst]);
copy(&pu[pfirst], &pu[plast], &pu0[pfirst]);
// ===== Predictor step =====
// 1. advance mesh to center of time step
advPosHalf(px0, pu0, dt, pxp, pfirst, plast);
} // for pch
#pragma omp parallel for schedule(static)
for (int sch = 0; sch < numsch; ++sch) {
int sfirst = mesh->schsfirst[sch];
int slast = mesh->schslast[sch];
int zfirst = mesh->schzfirst[sch];
int zlast = mesh->schzlast[sch];
// save off zone variable values from previous cycle
copy(&zvol[zfirst], &zvol[zlast], &zvol0[zfirst]);
// 1a. compute new mesh geometry
mesh->calcCtrs(pxp, exp, zxp, sfirst, slast);
mesh->calcVols(pxp, zxp, sareap, svolp, zareap, zvolp,
sfirst, slast);
mesh->calcSurfVecs(zxp, exp, ssurfp, sfirst, slast);
mesh->calcEdgeLen(pxp, elen, sfirst, slast);
mesh->calcCharLen(sareap, zdl, sfirst, slast);
// 2. compute point masses
calcRho(zm, zvolp, zrp, zfirst, zlast);
calcCrnrMass(zrp, zareap, smf, cmaswt, sfirst, slast);
// 3. compute material state (half-advanced)
pgas->calcStateAtHalf(zr, zvolp, zvol0, ze, zwrate, zm, dt,
zp, zss, zfirst, zlast);
// 4. compute forces
pgas->calcForce(zp, ssurfp, sfp, sfirst, slast);
tts->calcForce(zareap, zrp, zss, sareap, smf, ssurfp, sft,
sfirst, slast);
qcs->calcForce(sfq, sfirst, slast);
sumCrnrForce(sfp, sfq, sft, cftot, sfirst, slast);
} // for sch
mesh->checkBadSides();
// sum corner masses, forces to points
mesh->sumToPoints(cmaswt, pmaswt);
mesh->sumToPoints(cftot, pf);
#pragma omp parallel for schedule(static)
for (int pch = 0; pch < numpch; ++pch) {
int pfirst = mesh->pchpfirst[pch];
int plast = mesh->pchplast[pch];
// 4a. apply boundary conditions
for (int i = 0; i < bcs.size(); ++i) {
int bfirst = bcs[i]->pchbfirst[pch];
int blast = bcs[i]->pchblast[pch];
bcs[i]->applyFixedBC(pu0, pf, bfirst, blast);
}
// 5. compute accelerations
calcAccel(pf, pmaswt, pap, pfirst, plast);
// ===== Corrector step =====
// 6. advance mesh to end of time step
advPosFull(px0, pu0, pap, dt, px, pu, pfirst, plast);
} // for pch
resetDtHydro();
#pragma omp parallel for schedule(static)
for (int sch = 0; sch < numsch; ++sch) {
int sfirst = mesh->schsfirst[sch];
int slast = mesh->schslast[sch];
int zfirst = mesh->schzfirst[sch];
int zlast = mesh->schzlast[sch];
// 6a. compute new mesh geometry
mesh->calcCtrs(px, ex, zx, sfirst, slast);
mesh->calcVols(px, zx, sarea, svol, zarea, zvol,
sfirst, slast);
// 7. compute work
fill(&zw[zfirst], &zw[zlast], 0.);
calcWork(sfp, sfq, pu0, pu, pxp, dt, zw, zetot,
sfirst, slast);
} // for sch
mesh->checkBadSides();
#pragma omp parallel for schedule(static)
for (int zch = 0; zch < mesh->numzch; ++zch) {
int zfirst = mesh->zchzfirst[zch];
int zlast = mesh->zchzlast[zch];
// 7a. compute work rate
calcWorkRate(zvol0, zvol, zw, zp, dt, zwrate, zfirst, zlast);
// 8. update state variables
calcEnergy(zetot, zm, ze, zfirst, zlast);
calcRho(zm, zvol, zr, zfirst, zlast);
// 9. compute timestep for next cycle
calcDtHydro(zdl, zvol, zvol0, dt, zfirst, zlast);
} // for zch
}
void Hydro::advPosHalf(
const double2* px0,
const double2* pu0,
const double dt,
double2* pxp,
const int pfirst,
const int plast) {
double dth = 0.5 * dt;
#pragma ivdep
for (int p = pfirst; p < plast; ++p) {
pxp[p] = px0[p] + pu0[p] * dth;
}
}
void Hydro::advPosFull(
const double2* px0,
const double2* pu0,
const double2* pa,
const double dt,
double2* px,
double2* pu,
const int pfirst,
const int plast) {
#pragma ivdep
for (int p = pfirst; p < plast; ++p) {
pu[p] = pu0[p] + pa[p] * dt;
px[p] = px0[p] + 0.5 * (pu[p] + pu0[p]) * dt;
}
}
void Hydro::calcCrnrMass(
const double* zr,
const double* zarea,
const double* smf,
double* cmaswt,
const int sfirst,
const int slast) {
#pragma ivdep
for (int s = sfirst; s < slast; ++s) {
int s3 = mesh->mapss3[s];
int z = mesh->mapsz[s];
double m = zr[z] * zarea[z] * 0.5 * (smf[s] + smf[s3]);
cmaswt[s] = m;
}
}
void Hydro::sumCrnrForce(
const double2* sf,
const double2* sf2,
const double2* sf3,
double2* cftot,
const int sfirst,
const int slast) {
#pragma ivdep
for (int s = sfirst; s < slast; ++s) {
int s3 = mesh->mapss3[s];
double2 f = (sf[s] + sf2[s] + sf3[s]) -
(sf[s3] + sf2[s3] + sf3[s3]);
cftot[s] = f;
}
}
void Hydro::calcAccel(
const double2* pf,
const double* pmass,
double2* pa,
const int pfirst,
const int plast) {
const double fuzz = 1.e-99;
#pragma ivdep
for (int p = pfirst; p < plast; ++p) {
pa[p] = pf[p] / max(pmass[p], fuzz);
}
}
void Hydro::calcRho(
const double* zm,
const double* zvol,
double* zr,
const int zfirst,
const int zlast) {
#pragma ivdep
for (int z = zfirst; z < zlast; ++z) {
zr[z] = zm[z] / zvol[z];
}
}
void Hydro::calcWork(
const double2* sf,
const double2* sf2,
const double2* pu0,
const double2* pu,
const double2* px,
const double dt,
double* zw,
double* zetot,
const int sfirst,
const int slast) {
// Compute the work done by finding, for each element/node pair,
// dwork= force * vavg
// where force is the force of the element on the node
// and vavg is the average velocity of the node over the time period
const double dth = 0.5 * dt;
for (int s = sfirst; s < slast; ++s) {
int p1 = mesh->mapsp1[s];
int p2 = mesh->mapsp2[s];
int z = mesh->mapsz[s];
double2 sftot = sf[s] + sf2[s];
double sd1 = dot( sftot, (pu0[p1] + pu[p1]));
double sd2 = dot(-sftot, (pu0[p2] + pu[p2]));
double dwork = -dth * (sd1 * px[p1].x + sd2 * px[p2].x);
zetot[z] += dwork;
zw[z] += dwork;
}
}
void Hydro::calcWorkRate(
const double* zvol0,
const double* zvol,
const double* zw,
const double* zp,
const double dt,
double* zwrate,
const int zfirst,
const int zlast) {
double dtinv = 1. / dt;
#pragma ivdep
for (int z = zfirst; z < zlast; ++z) {
double dvol = zvol[z] - zvol0[z];
zwrate[z] = (zw[z] + zp[z] * dvol) * dtinv;
}
}
void Hydro::calcEnergy(
const double* zetot,
const double* zm,
double* ze,
const int zfirst,
const int zlast) {
const double fuzz = 1.e-99;
#pragma ivdep
for (int z = zfirst; z < zlast; ++z) {
ze[z] = zetot[z] / (zm[z] + fuzz);
}
}
void Hydro::sumEnergy(
const double* zetot,
const double* zarea,
const double* zvol,
const double* zm,
const double* smf,
const double2* px,
const double2* pu,
double& ei,
double& ek,
const int zfirst,
const int zlast,
const int sfirst,
const int slast) {
// compute internal energy
double sumi = 0.;
for (int z = zfirst; z < zlast; ++z) {
sumi += zetot[z];
}
// multiply by 2\pi for cylindrical geometry
ei += sumi * 2 * M_PI;
// compute kinetic energy
// in each individual zone:
// zone ke = zone mass * (volume-weighted average of .5 * u ^ 2)
// = zm sum(c in z) [cvol / zvol * .5 * u ^ 2]
// = sum(c in z) [zm * cvol / zvol * .5 * u ^ 2]
double sumk = 0.;
for (int s = sfirst; s < slast; ++s) {
int s3 = mesh->mapss3[s];
int p1 = mesh->mapsp1[s];
int z = mesh->mapsz[s];
double cvol = zarea[z] * px[p1].x * 0.5 * (smf[s] + smf[s3]);
double cke = zm[z] * cvol / zvol[z] * 0.5 * length2(pu[p1]);
sumk += cke;
}
// multiply by 2\pi for cylindrical geometry
ek += sumk * 2 * M_PI;
}
void Hydro::calcDtCourant(
const double* zdl,
double& dtrec,
char* msgdtrec,
const int zfirst,
const int zlast) {
const double fuzz = 1.e-99;
double dtnew = 1.e99;
int zmin = -1;
for (int z = zfirst; z < zlast; ++z) {
double cdu = max(zdu[z], max(zss[z], fuzz));
double zdthyd = zdl[z] * cfl / cdu;
zmin = (zdthyd < dtnew ? z : zmin);
dtnew = (zdthyd < dtnew ? zdthyd : dtnew);
}
if (dtnew < dtrec) {
dtrec = dtnew;
snprintf(msgdtrec, 80, "Hydro Courant limit for z = %d", zmin);
}
}
void Hydro::calcDtVolume(
const double* zvol,
const double* zvol0,
const double dtlast,
double& dtrec,
char* msgdtrec,
const int zfirst,
const int zlast) {
double dvovmax = 1.e-99;
int zmax = -1;
for (int z = zfirst; z < zlast; ++z) {
double zdvov = abs((zvol[z] - zvol0[z]) / zvol0[z]);
zmax = (zdvov > dvovmax ? z : zmax);
dvovmax = (zdvov > dvovmax ? zdvov : dvovmax);
}
double dtnew = dtlast * cflv / dvovmax;
if (dtnew < dtrec) {
dtrec = dtnew;
snprintf(msgdtrec, 80, "Hydro dV/V limit for z = %d", zmax);
}
}
void Hydro::calcDtHydro(
const double* zdl,
const double* zvol,
const double* zvol0,
const double dtlast,
const int zfirst,
const int zlast) {
double dtchunk = 1.e99;
char msgdtchunk[80];
calcDtCourant(zdl, dtchunk, msgdtchunk, zfirst, zlast);
calcDtVolume(zvol, zvol0, dtlast, dtchunk, msgdtchunk,
zfirst, zlast);
if (dtchunk < dtrec) {
#pragma omp critical
{
// redundant test needed to avoid race condition
if (dtchunk < dtrec) {
dtrec = dtchunk;
strncpy(msgdtrec, msgdtchunk, 80);
}
}
}
}
void Hydro::getDtHydro(
double& dtnew,
string& msgdtnew) {
if (dtrec < dtnew) {
dtnew = dtrec;
msgdtnew = string(msgdtrec);
}
}
void Hydro::resetDtHydro() {
dtrec = 1.e99;
strcpy(msgdtrec, "Hydro default");
}
void Hydro::writeEnergyCheck() {
using Parallel::mype;
double ei = 0.;
double ek = 0.;
#pragma omp parallel for schedule(static)
for (int sch = 0; sch < mesh->numsch; ++sch) {
int sfirst = mesh->schsfirst[sch];
int slast = mesh->schslast[sch];
int zfirst = mesh->schzfirst[sch];
int zlast = mesh->schzlast[sch];
double eichunk = 0.;
double ekchunk = 0.;
sumEnergy(zetot, mesh->zarea, mesh->zvol, zm, mesh->smf,
mesh->px, pu, eichunk, ekchunk,
zfirst, zlast, sfirst, slast);
#pragma omp critical
{
ei += eichunk;
ek += ekchunk;
}
}
Parallel::globalSum(ei);
Parallel::globalSum(ek);
if (mype == 0) {
cout << scientific << setprecision(6);
cout << "Energy check: "
<< "total energy = " << setw(14) << ei + ek << endl;
cout << "(internal = " << setw(14) << ei
<< ", kinetic = " << setw(14) << ek << ")" << endl;
}
}