From c9e7aef72dd9a34d902b05b3a6d7689a0d51993c Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Wed, 8 Jul 2020 13:07:35 -0400 Subject: [PATCH 01/28] first commit adding rans --- src/physics/fluidflow/rans.cpp | 131 ++++++++ src/physics/fluidflow/rans.hpp | 71 +++++ src/physics/fluidflow/rans_fluxes.hpp | 261 ++++++++++++++++ src/physics/fluidflow/rans_integ.hpp | 363 +++++++++++++++++++++++ src/physics/fluidflow/rans_integ_def.hpp | 196 ++++++++++++ 5 files changed, 1022 insertions(+) create mode 100644 src/physics/fluidflow/rans.cpp create mode 100644 src/physics/fluidflow/rans.hpp create mode 100644 src/physics/fluidflow/rans_fluxes.hpp create mode 100644 src/physics/fluidflow/rans_integ.hpp create mode 100644 src/physics/fluidflow/rans_integ_def.hpp diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp new file mode 100644 index 00000000..63e35e8f --- /dev/null +++ b/src/physics/fluidflow/rans.cpp @@ -0,0 +1,131 @@ +#include + +#include "rans.hpp" +#include "rans_integ.hpp" + +using namespace mfem; +using namespace std; + +namespace mach +{ + +template +RANavierStokesSolver::RANavierStokesSolver(const string &opt_file_name, + unique_ptr smesh) + : NavierStokesSolver(opt_file_name, move(smesh)) +{ + if (entvar) + { + throw MachException("Entropy variables not implemented for RANS!"); + } + + // define free-stream parameters; may or may not be used, depending on case + chi_fs = this->options["flow-param"]["chi"].template get(); + mu = this->options["flow-param"]["mu"].template get(); +} + +template +void RANavierStokesSolver::addResVolumeIntegrators(double alpha) +{ + // add Navier Stokes integrators + cout << "Inside RANS add volume integrators" << endl; + this->res->AddDomainIntegrator(new IsmailRoeIntegrator( //Inviscid term + this->diff_stack, alpha)); + this->res->AddDomainIntegrator(new SAViscousIntegrator( //SAViscousIntegrator + this->diff_stack, re_fs, pr_fs, mu, alpha)); + // now add RANS integrators + // combine these, so we compute vorticity once + this->res->AddDomainIntegrator(new SASourceIntegrator( + this->diff_stack, alpha)); + // add LPS stabilization + double lps_coeff = options["space-dis"]["lps-coeff"].template get(); + this->res->AddDomainIntegrator(new EntStableLPSIntegrator( + this->diff_stack, alpha, lps_coeff)); + +} + +template +void NavierStokesSolver::addResBoundaryIntegrators(double alpha) +{ + // add base class integrators + auto &bcs = this->options["bcs"]; + double mu = this->options["flow-param"]["mu"].template get(); + int idx = 0; + if (bcs.find("slip-wall") != bcs.end()) + { // slip-wall boundary condition + vector tmp = bcs["slip-wall"].template get>(); + this->bndry_marker[idx].SetSize(tmp.size(), 0); + this->bndry_marker[idx].Assign(tmp.data()); + this->res->AddBdrFaceIntegrator( + new SAViscousSlipWallBC(this->diff_stack, this->fec.get(), + re_fs, pr_fs, mu, alpha), + this->bndry_marker[idx]); + idx++; + } + if (bcs.find("no-slip-adiabatic") != bcs.end()) + { + vector tmp = bcs["no-slip-adiabatic"].template get>(); + this->bndry_marker[idx].SetSize(tmp.size(), 0); + this->bndry_marker[idx].Assign(tmp.data()); + // reference state needed by penalty flux + Vector q_ref(dim+3); + this->getFreeStreamState(q_ref); + // Add the adiabatic flux BC + this->res->AddBdrFaceIntegrator( + new SANoSlipAdiabaticWallBC( + this->diff_stack, this->fec.get(), re_fs, pr_fs, q_ref, mu, alpha), + this->bndry_marker[idx]); + idx++; + } + if (bcs.find("viscous-inflow") != bcs.end()) + { + throw MachException("Viscous inflow bc not implemented!"); + } + if (bcs.find("viscous-outflow") != bcs.end()) + { + throw MachException("Viscous outflow bc not implemented!"); + } + if (bcs.find("far-field") != bcs.end()) + { + // far-field boundary conditions + vector tmp = bcs["far-field"].template get>(); + mfem::Vector qfar(dim+3); + this->getFreeStreamState(qfar); + this->bndry_marker[idx].SetSize(tmp.size(), 0); + this->bndry_marker[idx].Assign(tmp.data()); + this->res->AddBdrFaceIntegrator( + new SAFarFieldBC(this->diff_stack, this->fec.get(), qfar, + alpha), + this->bndry_marker[idx]); + idx++; + } + if (bcs.find("viscous-shock") != bcs.end()) + { + throw MachException("Viscous shock bc not implemented!"); + } +} + +template +void RANavierStokesSolver::getFreeStreamState(mfem::Vector &q_ref) +{ + q_ref = 0.0; + q_ref(0) = 1.0; + if (dim == 1) + { + q_ref(1) = q_ref(0)*mach_fs; // ignore angle of attack + } + else + { + q_ref(iroll+1) = q_ref(0)*mach_fs*cos(aoa_fs); + q_ref(ipitch+1) = q_ref(0)*mach_fs*sin(aoa_fs); + } + q_ref(dim+1) = 1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; + q_ref(dim+2) = chi_fs*(mu/q_ref(0)); +} + +// explicit instantiation +template class RANavierStokesSolver<1>; +template class RANavierStokesSolver<2>; +template class RANavierStokesSolver<3>; + +}//namespace mach \ No newline at end of file diff --git a/src/physics/fluidflow/rans.hpp b/src/physics/fluidflow/rans.hpp new file mode 100644 index 00000000..6b44c8ea --- /dev/null +++ b/src/physics/fluidflow/rans.hpp @@ -0,0 +1,71 @@ +#ifndef MACH_RANS +#define MACH_RANS + +#include "mfem.hpp" + +#include "navier_stokes.hpp" + +namespace mach +{ + +/// Solver for Reynolds-Averaged Navier-Stokes flows +/// dim - number of spatial dimensions (1, 2, or 3) +/// entvar - if true, the entropy variables are used in the integrators +template + +class RANavierStokesSolver : public NavierStokesSolver +{ +public: + + /// Sets `q_ref` to the free-stream conservative variables + void getFreeStreamState(mfem::Vector &q_ref); + + /// convert conservative variables to entropy variables + /// \param[in/out] state - the conservative/entropy variables + virtual void convertToEntvar(mfem::Vector &state) override + { + throw MachException("Entropy variables not implemented!!!"); + } + +protected: + /// Class constructor. + /// \param[in] opt_file_name - file where options are stored + /// \param[in] smesh - if provided, defines the mesh for the problem + /// \param[in] dim - number of dimensions + RANavierStokesSolver(const std::string &opt_file_name, + std::unique_ptr smesh = nullptr); + + /// Add volume/domain integrators to `res` based on `options` + /// \param[in] alpha - scales the data; used to move terms to rhs or lhs + /// \note This function calls NavierStokes::addVolumeIntegrators() first + virtual void addResVolumeIntegrators(double alpha); + + /// Add boundary-face integrators to `res` based on `options` + /// \param[in] alpha - scales the data; used to move terms to rhs or lhs + /// \note This function calls EulerSolver::addBoundaryIntegrators() first + virtual void addResBoundaryIntegrators(double alpha); + + /// Add interior-face integrators to `res` based on `options` + /// \param[in] alpha - scales the data; used to move terms to rhs or lhs + /// \note This function calls NavierStokes::addInterfaceIntegrators() first + virtual void addResInterfaceIntegrators(double alpha) + { + throw MachException("dsbp not implemented!!!"); + } + + /// Return the number of state variables + virtual int getNumState() override {return dim+3; } + + friend SolverPtr createSolver>( + const std::string &opt_file_name, + std::unique_ptr smesh); + + /// free-stream SA viscosity ratio (nu_tilde/nu_material) + double chi_fs; + /// material dynamic viscosity + double mu; +}; + +} //namespace mach + +#endif \ No newline at end of file diff --git a/src/physics/fluidflow/rans_fluxes.hpp b/src/physics/fluidflow/rans_fluxes.hpp new file mode 100644 index 00000000..9b811476 --- /dev/null +++ b/src/physics/fluidflow/rans_fluxes.hpp @@ -0,0 +1,261 @@ +/// Functions related to RANS/SA equations +#ifndef MACH_RANS_FLUXES +#define MACH_RANS_FLUXES + +#include // std::max + +#include "utils.hpp" +#include "navier_stokes_fluxes.hpp" + +/// Reference +/// sacs[0] = cb1 +/// sacs[1] = cb2 +/// sacs[2] = sigma +/// sacs[3] = kappa +/// sacs[4] = cw2 +/// sacs[5] = cw3 +/// sacs[6] = cv1 + +/// Not including trip terms for now + +/// sacs[7] = ct3 +/// sacs[8] = ct4 +/// sacs[9] = rlim +/// sacs[10] = cn1 + +namespace mach +{ + +/// For constants related to the RANS/SA equations +namespace rans +{ + +} // namespace rans + +/// Returns the laminar suppression term in SA +/// \param[in] q - state used to define the laminar suppression +/// \param[in] mu - **nondimensionalized** viscosity +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns ft2 - laminar suppression term +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSALaminarSuppression(const xdouble *q, const xdouble mu, + const xdouble *sacs) +{ + xdouble ct3 = sacs[7]; + xdouble ct4 = sacs[8]; + xdouble nu_tilde = q[dim+3]; + xdouble nu_mat = mu/q[0]; + xdouble chi = nu_tilde/nu_mat; + xdouble ft2 = ct3*exp(-ct4*chi*chi); + return ft2; +} + + + +/// Returns the modified vorticity in SA +/// \param[in] q - state used to define the destruction +/// \param[in] mu - **nondimensionalized** viscosity +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns St - modified vorticity +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSAModifiedVorticity(const xdouble *q, const xdouble S, + const xdouble mu, const xdouble d, + const xdouble *sacs) +{ + xdouble nu_tilde = q[dim+3]; + xdouble kappa = sacs[3]; + xdouble fv2 = calcSAProductionCoefficient(q, mu, sacs); + xdouble St = S + nu_tilde*fv2/(kappa*kappa*d*d); + return St; +} + +/// Returns the turbulent viscosity coefficient in SA +/// \param[in] q - state used to define the destruction +/// \param[in] mu - **nondimensionalized** viscosity +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns fn - turbulent viscosity coefficient +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSACoefficient(const xdouble *q, const xdouble mu, + const xdouble *sacs) +{ + xdouble cv1 = sacs[6]; + xdouble nu_tilde = q[dim+3]; + xdouble nu_mat = mu/q[0]; + xdouble chi = nu_tilde/nu_mat; + xdouble fv1 = chi*chi*chi/(cv1*cv1*cv1 + chi*chi*chi); + return fv1; +} + +/// Returns the production coefficient in SA +/// \param[in] q - state used to define the destruction +/// \param[in] mu - **nondimensionalized** viscosity +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns fv2 - production coefficient +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSAProductionCoefficient(const xdouble *q, const xdouble mu, + const xdouble *sacs) +{ + xdouble nu_tilde = q[dim+3]; + xdouble nu_mat = mu/q[0]; + xdouble chi = nu_tilde/nu_mat; + xdouble fv1 = calcSACoefficient(q, mu, sacs); + xdouble fv2 = 1 - chi/(1 + chi*fv1); + return fv2; +} + +/// Returns the destruction coefficient in SA +/// \param[in] q - state used to define the destruction +/// \param[in] mu - **nondimensionalized** viscosity +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns fw - destruction coefficient +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSADestructionCoefficient(const xdouble *q, + const xdouble mu, const xdouble d, + const xdouble S, const xdouble *sacs) +{ + xdouble nu_tilde = q[dim+3]; + xdouble kappa = sacs[3]; + xdouble cw2 = sacs[4]; + xdouble cw3 = sacs[5]; + xdouble rlim = sacs[9]; + + xdouble St = calcSAModifiedVorticity(q, S, mu, d, sacs); + xdouble work = nu_tilde/(St*kappa*kappa*d*d); + xdouble r = std::min(work, rlim); + xdouble g = r + cw2*(std::pow(r, 6) - r); + xdouble work2 = (1 + std::pow(cw3, 6))/ + (std::pow(g, 6) + std::pow(cw3, 6)); + xdouble fw = g*std::pow(work2, (1/6)); + return fw; +} + +/// Returns the production term in SA +/// \param[in] q - state used to define the production +/// \param[in] S - vorticity magnitude (how to compute in h_grad space?) +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns P - production term +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSAProduction(const xdouble *q, + const xdouble mu, const xdouble d, + const xdouble S, const xdouble *sacs) +{ + xdouble cb1 = sacs[0]; + xdouble nu_tilde = q[dim+3]; + xdouble St = calcSAModifiedVorticity(q, S, mu, d, sacs); + xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); + xdouble P = cb1*(1-ft2)*St*nu_tilde; + return P; +} + +/// Returns the destruction term in SA +/// \param[in] q - state used to define the destruction +/// \param[in] d - wall distance (precomputed from gridfunction) +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns D - destruction term +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSADestruction(const xdouble *q, + const xdouble mu, const xdouble d, + const xdouble S, const xdouble *sacs) +{ + xdouble cb1 = sacs[0]; + xdouble cb2 = sacs[1]; + xdouble sigma = sacs[2]; + xdouble kappa = sacs[3]; + xdouble chi_d = q[dim+3]/d; + xdouble cw1 = cb1/(kappa*kappa) + (1+cb2)/sigma; + xdouble D = (cw1*fw - (cb1/(kappa*kappa))*ft2)*chi_d*chi_d; + return D; +} + +/// Returns the production term in negative SA +/// \param[in] q - state used to define the production +/// \param[in] S - vorticity magnitude (how to compute in h_grad space?) +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns Pn - production term +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSANegativeProduction(const xdouble *q, const xdouble S, + const xdouble *sacs) +{ + xdouble cb1 = sacs[0]; + xdouble ct3 = sacs[7]; + xdouble nu_tilde = q[dim+3]; + xdouble Pn = cb1*(1-ct3)*S*nu_tilde; + return Pn; +} + +/// Returns the destruction term in negative SA +/// \param[in] q - state used to define the destruction +/// \param[in] d - wall distance (precomputed from gridfunction) +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns Dn - destruction term +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSANegativeDestruction(const xdouble *q, const xdouble d, + const xdouble *sacs) +{ + xdouble cb1 = sacs[0]; + xdouble cb2 = sacs[1]; + xdouble sigma = sacs[2]; + xdouble kappa = sacs[3]; + xdouble chi_d = q[dim+3]/d; + xdouble cw1 = cb1/(kappa*kappa) + (1+cb2)/sigma; + xdouble Dn = -cw1*chi_d*chi_d; + return Dn; +} + +/// Returns the turbulent viscosity coefficient in negative SA +/// \param[in] q - state used to define the destruction +/// \param[in] mu - **nondimensionalized** viscosity +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns fn - negative turbulent viscosity coefficient +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSANegativeCoefficient(const xdouble *q, const xdouble mu, + const xdouble *sacs) +{ + xdouble cn1 = sacs[10]; + xdouble nu_tilde = q[dim+3]; + xdouble nu_mat = mu/q[0]; + xdouble chi = nu_tilde/nu_mat; + xdouble fn = (cn1 + chi*chi*chi)/(cn1 - chi*chi*chi); + return fn; +} + +/// Returns the "source" term in SA +/// \param[in] q - state used to define the destruction +/// \param[in] dir - turbulent viscosity gradient +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns Sr - "source" term +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSASource(const xdouble *q, const xdouble *dir, + const xdouble *sacs) +{ + xdouble cb2 = sacs[1]; + xdouble sigma = sacs[2]; + xdouble Sr = (cb2/sigma)*dot(dir, dir)); + return Sr; +} + +} // namespace mach + +#endif diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp new file mode 100644 index 00000000..5da2b7b8 --- /dev/null +++ b/src/physics/fluidflow/rans_integ.hpp @@ -0,0 +1,363 @@ +#include "adept.h" +#include "mfem.hpp" + +#include "navier_stokes_integ.hpp" +#include "euler_fluxes.hpp" +#include "navier_stokes_fluxes.hpp" +#include "rans_fluxes.hpp" + +using adept::adouble; +using namespace std; /// TODO: this is polluting other headers! + +namespace mach +{ + +/// Entropy-stable volume integrator for Navier-Stokes viscous terms +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \note This derived class uses the CRTP +template +class ESViscousSAIntegrator : public ESViscousIntegrator > +{ +public: + /// Construct an entropy-stable viscous integrator + /// \param[in] diff_stack - for algorithmic differentiation + /// \param[in] Re_num - Reynolds number + /// \param[in] Pr_num - Prandtl number + /// \param[in] vis - nondimensional dynamic viscosity (use Sutherland if neg) + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + ESViscousSAIntegrator(adept::Stack &diff_stack, double Re_num, double Pr_num, + double vis = -1.0, double a = 1.0) + : ESViscousIntegrator >( + diff_stack, Re_num, Pr_num, vis, a) {} + +private: + +}; + +/// Integrator for RANS SA Production term +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \note This derived class uses the CRTP +template +class SASourceIntegrator : public mfem::NonlinearFormIntegrator +{ +public: + /// Construct an integrator for RANS SA Production term + /// \param[in] diff_stack - for algorithmic differentiation + /// \param[in] + /// \param[in] vis - nondimensional dynamic viscosity (use Sutherland if neg) + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + SASourceIntegrator(adept::Stack &diff_stack, mfem::GridFunction dist, + mfem::Vector sacs, double vis = -1.0, double a = 1.0) + : alpha(a), mu(vis), stack(diff_stack) {} + + /// Construct the element local residual + /// \param[in] el - the finite element whose residual we want + /// \param[in] Trans - defines the reference to physical element mapping + /// \param[in] elfun - element local state function + /// \param[out] elvect - element local residual + virtual void AssembleElementVector(const mfem::FiniteElement &el, + mfem::ElementTransformation &Trans, + const mfem::Vector &elfun, + mfem::Vector &elvect); + + /// Construct the element local Jacobian + /// \param[in] el - the finite element whose Jacobian we want + /// \param[in] Trans - defines the reference to physical element mapping + /// \param[in] elfun - element local state function + /// \param[out] elmat - element local Jacobian + virtual void AssembleElementGrad(const mfem::FiniteElement &el, + mfem::ElementTransformation &Trans, + const mfem::Vector &elfun, + mfem::DenseMatrix &elmat); + +private: + /// nondimensional dynamic viscosity + double mu; +protected: + /// scales the terms; can be used to move to rhs/lhs + double alpha; + /// stack used for algorithmic differentiation + adept::Stack &stack; +}; + +#if 0 +/// Integrator for no-slip adiabatic-wall boundary condition +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \note This derived class uses the CRTP +template +class NoSlipAdiabaticWallBC : public ViscousBoundaryIntegrator> +{ +public: + /// Constructs an integrator for a no-slip, adiabatic boundary flux + /// \param[in] diff_stack - for algorithmic differentiation + /// \param[in] fe_coll - used to determine the face elements + /// \param[in] Re_num - Reynolds number + /// \param[in] Pr_num - Prandtl number + /// \param[in] q_ref - a reference state (needed by penalty) + /// \param[in] vis - viscosity (if negative use Sutherland's law) + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + NoSlipAdiabaticWallBC(adept::Stack &diff_stack, + const mfem::FiniteElementCollection *fe_coll, + double Re_num, double Pr_num, + const mfem::Vector &q_ref, double vis = -1.0, + double a = 1.0) + : ViscousBoundaryIntegrator>( + diff_stack, fe_coll, dim + 2, a), + Re(Re_num), Pr(Pr_num), + qfs(q_ref), mu(vis), work_vec(dim + 2) {} + + /// converts conservative variables to entropy variables + /// \param[in] q - conservative variables that are to be converted + /// \param[out] w - entropy variables corresponding to `q` + /// \note a wrapper for the relevant function in `euler_fluxes.hpp` + void convertVars(const mfem::Vector &q, mfem::Vector &w) + { + calcEntropyVars(q.GetData(), w.GetData()); + } + + /// Compute the Jacobian of the mapping `convert` w.r.t. `u` + /// \param[in] q - conservative variables that are to be converted + /// \param[out] dwdu - Jacobian of entropy variables w.r.t. `u` + void convertVarsJacState(const mfem::Vector &q, mfem::DenseMatrix &dwdu) + { + convertVarsJac(q, this->stack, dwdu); + } + + /// Compute entropy-stable, no-slip, adiabatic-wall boundary flux + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_vec - value of the flux + void calcFlux(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::Vector &flux_vec); + + /// Compute Jacobian of entropy-stable, no-slip, adiabatic-wall boundary flux + /// w.r.t states + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_jac - jacobian of the flux w.r.t. states + void calcFluxJacState(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::DenseMatrix &flux_jac); + + /// Compute Jacobian of entropy-stable, no-slip, adiabatic-wall boundary flux + /// w.r.t vector of entropy-variables' derivatives + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_jac - jacobian of the flux w.r.t. entropy-variables' derivatives + void calcFluxJacDw(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + std::vector &flux_jac); + +private: + /// Reynolds number + double Re; + /// Prandtl number + double Pr; + /// nondimensionalized dynamic viscosity + double mu; + /// Fixed state used to compute no-slip penalty matrix + mfem::Vector qfs; + /// work space for flux computations + mfem::Vector work_vec; +}; + +/// Integrator for viscous slip-wall boundary condition +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \note This derived class uses the CRTP +/// \note This is the same as the inviscid slip wall, but it provides the +/// necessary entropy-variable gradient flux. +template +class ViscousSlipWallBC : public ViscousBoundaryIntegrator> +{ +public: + /// Constructs an integrator for a viscous inflow boundary + /// \param[in] diff_stack - for algorithmic differentiation + /// \param[in] fe_coll - used to determine the face elements + /// \param[in] Re_num - Reynolds number + /// \param[in] Pr_num - Prandtl number + /// \param[in] vis - viscosity (if negative use Sutherland's law) + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + ViscousSlipWallBC(adept::Stack &diff_stack, + const mfem::FiniteElementCollection *fe_coll, + double Re_num, double Pr_num, double vis = -1.0, + double a = 1.0) + : ViscousBoundaryIntegrator>( + diff_stack, fe_coll, dim + 2, a), + Re(Re_num), Pr(Pr_num), mu(vis), work_vec(dim + 2) {} + + /// converts conservative variables to entropy variables + /// \param[in] q - conservative variables that are to be converted + /// \param[out] w - entropy variables corresponding to `q` + /// \note a wrapper for the relevant function in `euler_fluxes.hpp` + void convertVars(const mfem::Vector &q, mfem::Vector &w) + { + calcEntropyVars(q.GetData(), w.GetData()); + } + + /// Compute the Jacobian of the mapping `convert` w.r.t. `u` + /// \param[in] q - conservative variables that are to be converted + /// \param[out] dwdu - Jacobian of entropy variables w.r.t. `u` + void convertVarsJacState(const mfem::Vector &q, mfem::DenseMatrix &dwdu) + { + convertVarsJac(q, this->stack, dwdu); + } + /// Compute flux corresponding to a viscous inflow boundary + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_vec - value of the flux + void calcFlux(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::Vector &flux_vec); + + /// Compute jacobian of flux corresponding to a viscous inflow boundary + /// w.r.t `states` + /// \param[in] x - coordinate location at which flux is evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables (not used yet) + /// \param[out] flux_jac - jacobian of the flux + void calcFluxJacState(const mfem::Vector &x, const mfem::Vector &dir, + double jac, const mfem::Vector &q, + const mfem::DenseMatrix &Dw, + mfem::DenseMatrix &flux_jac); + + /// Compute jacobian of flux corresponding to a viscous inflow boundary + /// w.r.t `entrpy-variables' derivatives` + /// \param[in] x - coordinate location at which flux is evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_jac - jacobian of the flux + void calcFluxJacDw(const mfem::Vector &x, const mfem::Vector &dir, + double jac, const mfem::Vector &q, + const mfem::DenseMatrix &Dw, + std::vector &flux_jac); + +private: + /// Reynolds number + double Re; + /// Prandtl number + double Pr; + /// nondimensionalized dynamic viscosity + double mu; + /// work space for flux computations + mfem::Vector work_vec; +}; + +/// Integrator for viscous far-field boundary conditions +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \note This derived class uses the CRTP +template +class ViscousFarFieldBC : public ViscousBoundaryIntegrator> +{ +public: + /// Constructs an integrator for a viscous far-field boundary + /// \param[in] diff_stack - for algorithmic differentiation + /// \param[in] fe_coll - used to determine the face elements + /// \param[in] Re_num - Reynolds number + /// \param[in] Pr_num - Prandtl number + /// \param[in] q_far - state at the far-field + /// \param[in] vis - viscosity (if negative use Sutherland's law) + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + ViscousFarFieldBC(adept::Stack &diff_stack, + const mfem::FiniteElementCollection *fe_coll, + double Re_num, double Pr_num, + const mfem::Vector &q_far, double vis = -1.0, + double a = 1.0) + : ViscousBoundaryIntegrator>( + diff_stack, fe_coll, dim + 2, a), + Re(Re_num), Pr(Pr_num), + qfs(q_far), mu(vis), work_vec(dim + 2) {} + + /// converts conservative variables to entropy variables + /// \param[in] q - conservative variables that are to be converted + /// \param[out] w - entropy variables corresponding to `q` + /// \note a wrapper for the relevant function in `euler_fluxes.hpp` + void convertVars(const mfem::Vector &q, mfem::Vector &w) + { + calcEntropyVars(q.GetData(), w.GetData()); + } + + /// Compute the Jacobian of the mapping `convert` w.r.t. `u` + /// \param[in] q - conservative variables that are to be converted + /// \param[out] dwdu - Jacobian of entropy variables w.r.t. `u` + void convertVarsJacState(const mfem::Vector &q, mfem::DenseMatrix &dwdu) + { + convertVarsJac(q, this->stack, dwdu); + } + + /// Compute flux corresponding to a viscous inflow boundary + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_vec - value of the flux + void calcFlux(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::Vector &flux_vec) + { + calcBoundaryFlux(dir.GetData(), qfs.GetData(), q.GetData(), + work_vec.GetData(), flux_vec.GetData()); + } + + /// Compute jacobian of flux corresponding to a viscous far-field boundary + /// w.r.t `states` + /// \param[in] x - coordinate location at which flux is evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables (not used yet) + /// \param[out] flux_jac - jacobian of the flux + void calcFluxJacState(const mfem::Vector &x, const mfem::Vector &dir, + double jac, const mfem::Vector &q, + const mfem::DenseMatrix &Dw, + mfem::DenseMatrix &flux_jac); + + /// Compute jacobian of flux corresponding to a viscous far-field boundary + /// w.r.t `entrpy-variables' derivatives` + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_jac - jacobian of the flux + void calcFluxJacDw(const mfem::Vector &x, const mfem::Vector &dir, + double jac, const mfem::Vector &q, + const mfem::DenseMatrix &Dw, + std::vector &flux_jac); + +private: + /// Reynolds number + double Re; + /// Prandtl number + double Pr; + /// nondimensionalized dynamic viscosity + double mu; + /// far-field boundary state + mfem::Vector qfs; + /// work space for flux computations + mfem::Vector work_vec; +}; +#endif + +#include "rans_integ_def.hpp" + +} // namespace mach + +#endif \ No newline at end of file diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp new file mode 100644 index 00000000..9f6a9a39 --- /dev/null +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -0,0 +1,196 @@ +//============================================================================== +// ESViscousSAIntegrator methods + + +//============================================================================== +// SASourceIntegrator methods + +template +void SASourceIntegrator::AssembleElementVector( + const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, + const mfem::Vector &elfun, mfem::Vector &elvect) +{ + using namespace mfem; + const SBPFiniteElement &sbp = dynamic_cast(el); + int num_nodes = sbp.GetDof(); + int dim = sbp.GetDim(); +#ifdef MFEM_THREAD_SAFE + Vector ui, xi, wj, uj, CDwi; + DenseMatrix adjJ_i, adjJ_j, adjJ_k, Dwi; +#endif + elvect.SetSize(num_states * num_nodes); + ui.SetSize(num_states); + xi.SetSize(dim); + wj.SetSize(num_states); + uj.SetSize(num_states); + Dwi.SetSize(num_states,dim); + CDwi.SetSize(num_states); + adjJ_i.SetSize(dim); + adjJ_j.SetSize(dim); + adjJ_k.SetSize(dim); + DenseMatrix u(elfun.GetData(), num_nodes, num_states); + DenseMatrix PDS(num_nodes, num_states); + DenseMatrix res(elvect.GetData(), num_nodes, num_states); + elvect = 0.0; PDS = 0.0; + for (int i = 0; i < num_nodes; ++i) + { + // get the Jacobian (Trans.Weight) and cubature weight (node.weight) + const IntegrationPoint &node = el.GetNodes().IntPoint(i); + Trans.SetIntPoint(&node); + CalcAdjugate(Trans.Jacobian(), adjJ_i); + u.GetRow(i, ui); + Trans.Transform(node, xi); + + // compute vorticity at node and take magnitude + double S; + + // compute gradient of turbulent viscosity at node + Vector dir; + + // get distance function value at node + double d; + + // accumulate source terms + double src = calcSASource(ui.GetData(), dir.GetData(), sacs.GetData()); + // use negative model if turbulent viscosity is negative + if (ui(dim+2) < 0) + { + src += calcSANegativeProduction(ui.GetData(), S, sacs.GetData()); + src += calcSANegativeDestruction(ui.GetData(), d, sacs.GetData()); + } + else + { + src += calcSAProduction(ui.GetData(), mu, d, S, sacs.GetData()); + src += calcSADestruction(ui.GetData(), mu, d, S, sacs.GetData()); + } + + PDS(i, dim+2) += alpha * src; + } // loop over element nodes i + + // NOTE: how to multiply cubature and transformation weights? + + // multiply by projection + sbp.multProjOperator(PDS, res); +} + +template +void SAViscousIntegrator::AssembleElementGrad( + const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, + const mfem::Vector &elfun, mfem::DenseMatrix &elmat) +{ + using namespace mfem; + using namespace std; + const SBPFiniteElement &sbp = dynamic_cast(el); + int num_nodes = sbp.GetDof(); + int dim = sbp.GetDim(); +#ifdef MFEM_THREAD_SAFE + Vector ui, xi, wj, uj; + DenseMatrix adjJ_i, adjJ_j, adjJ_k, Dwi, jac_term1, jac_term2, dwduj; + vector CDw_jac(dim); +#endif + ui.SetSize(num_states); + xi.SetSize(dim); + wj.SetSize(num_states); + uj.SetSize(num_states); + Dwi.SetSize(num_states,dim); + adjJ_i.SetSize(dim); + adjJ_j.SetSize(dim); + adjJ_k.SetSize(dim); + jac_term1.SetSize(num_states); + jac_term2.SetSize(num_states); + dwduj.SetSize(num_states); + CDw_jac.resize(dim); + for (int d = 0; d < dim; ++d) + { + CDw_jac[d].SetSize(num_states); + } + DenseMatrix u(elfun.GetData(), num_nodes, num_states); + elmat.SetSize(num_states*num_nodes); + elmat = 0.0; + + for (int i = 0; i < num_nodes; ++i) + { + // get the Jacobian (Trans.Weight) and cubature weight (node.weight) + const IntegrationPoint &node = el.GetNodes().IntPoint(i); + Trans.SetIntPoint(&node); + double Hinv = 1.0 / (sbp.getDiagNormEntry(i) * Trans.Weight()); + CalcAdjugate(Trans.Jacobian(), adjJ_i); + u.GetRow(i, ui); + Trans.Transform(node, xi); + + // compute the (physcial space) derivatives at node i + Dwi = 0.0; + for (int j = 0; j < num_nodes; ++j) + { + // Get mapping Jacobian adjugate and transform state to entropy vars + Trans.SetIntPoint(&el.GetNodes().IntPoint(j)); + CalcAdjugate(Trans.Jacobian(), adjJ_j); + u.GetRow(j, uj); + convert(uj, wj); + for (int d = 0; d < dim; ++d) + { + double Qij = sbp.getQEntry(d, i, j, adjJ_i, adjJ_j); + for (int s = 0; s < num_states; ++s) + { + Dwi(s,d) += Qij * wj(s); + } + } // loop over space dimensions d + } // loop over element nodes j + Dwi *= Hinv; + + for (int d = 0; d < dim; ++d) { + // scale(d, xi, ui, Dwi, CDwi); + scaleJacState(d, xi, ui, Dwi, jac_term1); + scaleJacDw(d, xi, ui, Dwi, CDw_jac); + for (int k = 0; k < num_nodes; ++k) + { + // Node k mapping Jacobian adjugate and + Trans.SetIntPoint(&el.GetNodes().IntPoint(k)); + CalcAdjugate(Trans.Jacobian(), adjJ_k); + double Qik = sbp.getQEntry(d, i, k, adjJ_i, adjJ_k); + + // Contribution to Jacobian due to scaling operation + for (int sk = 0; sk < num_states; ++sk) + { + for (int si = 0; si < num_states; ++si) + { + // res(k, s) += alpha * Qik * CDwi(s); + elmat(sk*num_nodes+k, si*num_nodes+i) += Qik*jac_term1(sk,si); + } + } + + // Contribution to Jacobian assuming constant scaling operation + for (int j = 0; j < num_nodes; ++j) + { + // Node j mapping Jacobian adjugate and get Jacobian dw/du_j + Trans.SetIntPoint(&el.GetNodes().IntPoint(j)); + CalcAdjugate(Trans.Jacobian(), adjJ_j); + u.GetRow(j, uj); + convertJacState(uj, dwduj); + for (int d2 = 0; d2 < dim; ++d2) + { + // Following computes + // (Q_d1)_{ik}*H^{-1}_{ii}(C_{d,d2}(u_i)*(Q_d2)_{ij}*dw/du_j + // where C_{d,d2}(u_i) is a (state x state) matrix + // (Q_d2)_ij, (Q_d1)_ij, and H^{-1} are scalars, and + // (dw/du_j) is a (state x state) matrix + double Qij = sbp.getQEntry(d2, i, j, adjJ_i, adjJ_j); + Mult(CDw_jac[d2], dwduj, jac_term2); + jac_term2 *= (Qik*Hinv*Qij); + // Add to the Jacobian + for (int sk = 0; sk < num_states; ++sk) + { + for (int sj = 0; sj < num_states; ++sj) + { + elmat(sk * num_nodes + k, sj * num_nodes + j) += + jac_term2(sk, sj); + } + } + + } // loop over space dimension d2 + } // loop over the element nodes j + } // loop over element nodes k + } // loop over space dimensions d + } // loop over element nodes i + elmat *= alpha; +} \ No newline at end of file From 85154b3a110f3bd8585a550f11f2a4a2e0cea00c Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Tue, 14 Jul 2020 17:33:54 -0400 Subject: [PATCH 02/28] wrote vorticity and gradient subfunctions, added euler term --- src/physics/fluidflow/CMakeLists.txt | 5 + src/physics/fluidflow/rans.cpp | 6 +- src/physics/fluidflow/rans_fluxes.hpp | 4 +- src/physics/fluidflow/rans_integ.hpp | 73 ++++++- src/physics/fluidflow/rans_integ_def.hpp | 237 +++++++++++++++-------- test/unit/CMakeLists.txt | 1 + test/unit/test_rans_fluxes.cpp | 0 test/unit/test_rans_integ.cpp | 44 +++++ 8 files changed, 288 insertions(+), 82 deletions(-) create mode 100644 test/unit/test_rans_fluxes.cpp create mode 100644 test/unit/test_rans_integ.cpp diff --git a/src/physics/fluidflow/CMakeLists.txt b/src/physics/fluidflow/CMakeLists.txt index 83e4d356..c0103758 100644 --- a/src/physics/fluidflow/CMakeLists.txt +++ b/src/physics/fluidflow/CMakeLists.txt @@ -21,6 +21,11 @@ target_sources(mach navier_stokes.hpp viscous_integ_def.hpp viscous_integ.hpp + rans_fluxes.hpp + rans_integ.hpp + rans_integ_def.hpp + rans.cpp + rans.hpp ) target_include_directories(mach diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index 63e35e8f..a350b082 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -5,7 +5,7 @@ using namespace mfem; using namespace std; - +#if 0 namespace mach { @@ -128,4 +128,6 @@ template class RANavierStokesSolver<1>; template class RANavierStokesSolver<2>; template class RANavierStokesSolver<3>; -}//namespace mach \ No newline at end of file +}//namespace mach + +#endif \ No newline at end of file diff --git a/src/physics/fluidflow/rans_fluxes.hpp b/src/physics/fluidflow/rans_fluxes.hpp index 9b811476..f34ea556 100644 --- a/src/physics/fluidflow/rans_fluxes.hpp +++ b/src/physics/fluidflow/rans_fluxes.hpp @@ -177,6 +177,8 @@ xdouble calcSADestruction(const xdouble *q, xdouble kappa = sacs[3]; xdouble chi_d = q[dim+3]/d; xdouble cw1 = cb1/(kappa*kappa) + (1+cb2)/sigma; + xdouble fw = calcSADestructionCoefficient(q, mu, d, S, sacs); + xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); xdouble D = (cw1*fw - (cb1/(kappa*kappa))*ft2)*chi_d*chi_d; return D; } @@ -252,7 +254,7 @@ xdouble calcSASource(const xdouble *q, const xdouble *dir, { xdouble cb2 = sacs[1]; xdouble sigma = sacs[2]; - xdouble Sr = (cb2/sigma)*dot(dir, dir)); + xdouble Sr = (cb2/sigma)*dot(dir, dir); return Sr; } diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp index 5da2b7b8..d4c4196a 100644 --- a/src/physics/fluidflow/rans_integ.hpp +++ b/src/physics/fluidflow/rans_integ.hpp @@ -1,3 +1,6 @@ +#ifndef MACH_RANS_INTEG +#define MACH_RANS_INTEG + #include "adept.h" #include "mfem.hpp" @@ -16,7 +19,7 @@ namespace mach /// \tparam dim - number of spatial dimensions (1, 2, or 3) /// \note This derived class uses the CRTP template -class ESViscousSAIntegrator : public ESViscousIntegrator > +class ESViscousSAIntegrator : public ESViscousIntegrator { public: /// Construct an entropy-stable viscous integrator @@ -27,13 +30,50 @@ class ESViscousSAIntegrator : public ESViscousIntegrator >( + : ESViscousIntegrator ( diff_stack, Re_num, Pr_num, vis, a) {} private: }; +/// Volume integrator for inviscid terms, including SA variable +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \note This derived class uses the CRTP +template +class SAInviscidIntegrator : public IsmailRoeIntegrator +{ +public: + /// Construct an inviscid integrator with SA terms + /// \param[in] diff_stack - for algorithmic differentiation + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + SAInviscidIntegrator(adept::Stack &diff_stack, double a = 1.0) + : IsmailRoeIntegrator (diff_stack, a) {} + + + /// Ismail-Roe two-point (dyadic) flux function with additional variable + /// \param[in] di - physical coordinate direction in which flux is wanted + /// \param[in] qL - state variables at "left" state + /// \param[in] qR - state variables at "right" state + /// \param[out] flux - fluxes in the direction `di` + /// \note This is simply a wrapper for the function in `euler_fluxes.hpp` + void calcFlux(int di, const mfem::Vector &qL, + const mfem::Vector &qR, mfem::Vector &flux); + + /// Compute the Jacobians of `flux` with respect to `u_left` and `u_right` + /// \param[in] di - desired coordinate direction for flux + /// \param[in] qL - the "left" state + /// \param[in] qR - the "right" state + /// \param[out] jacL - Jacobian of `flux` w.r.t. `qL` + /// \param[out] jacR - Jacobian of `flux` w.r.t. `qR` + void calcFluxJacStates(int di, const mfem::Vector &qL, + const mfem::Vector &qR, + mfem::DenseMatrix &jacL, + mfem::DenseMatrix &jacR); +private: + +}; + /// Integrator for RANS SA Production term /// \tparam dim - number of spatial dimensions (1, 2, or 3) /// \note This derived class uses the CRTP @@ -70,9 +110,38 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator const mfem::Vector &elfun, mfem::DenseMatrix &elmat); + // Compute vorticity on an SBP element, needed for SA model terms + /// \param[in] q - the state over the element + /// \param[in] sbp - the sbp element whose shape functions we want + /// \param[in] Trans - defines the reference to physical element mapping + /// \param[out] curl - the curl of the velocity field at each node/int point + void calcVorticitySBP(const mfem::DenseMatrix &q, + const SBPFiniteElement &sbp, + const mfem::ElementTransformation &Trans, + mfem::DenseMatrix curl); + + // Compute gradient for the turbulence variable on an SBP element, + // needed for SA model terms + /// \param[in] q - the state over the element + /// \param[in] sbp - the sbp element whose shape functions we want + /// \param[in] Trans - defines the reference to physical element mapping + /// \param[out] grad - the gradient of the turbulence variable at each node + void calcGradSBP(const mfem::DenseMatrix &q, + const SBPFiniteElement &sbp, + const mfem::ElementTransformation &Trans, + mfem::DenseMatrix grad); + private: /// nondimensional dynamic viscosity double mu; + /// nu gradient on element + mfem::DenseMatrix grad; + /// nu gradient on node + mfem::Vector grad_i; + /// velocity curl on element + mfem::DenseMatrix curl; + /// velocity curl on node + mfem::Vector curl_i; protected: /// scales the terms; can be used to move to rhs/lhs double alpha; diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index 9f6a9a39..e32f8e9a 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -4,7 +4,7 @@ //============================================================================== // SASourceIntegrator methods - +#if 0 template void SASourceIntegrator::AssembleElementVector( const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, @@ -15,43 +15,42 @@ void SASourceIntegrator::AssembleElementVector( int num_nodes = sbp.GetDof(); int dim = sbp.GetDim(); #ifdef MFEM_THREAD_SAFE - Vector ui, xi, wj, uj, CDwi; - DenseMatrix adjJ_i, adjJ_j, adjJ_k, Dwi; + Vector ui, xi, uj, grad_i, curl_i; + DenseMatrix grad, curl; #endif elvect.SetSize(num_states * num_nodes); ui.SetSize(num_states); xi.SetSize(dim); - wj.SetSize(num_states); - uj.SetSize(num_states); - Dwi.SetSize(num_states,dim); - CDwi.SetSize(num_states); - adjJ_i.SetSize(dim); - adjJ_j.SetSize(dim); - adjJ_k.SetSize(dim); - DenseMatrix u(elfun.GetData(), num_nodes, num_states); - DenseMatrix PDS(num_nodes, num_states); + grad.SetSize(num_nodes, dim); + curl.SetSize(num_nodes, 3); + grad_i.SetSize(dim); + curl_i.SetSize(3); + DenseMatrix u(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well DenseMatrix res(elvect.GetData(), num_nodes, num_states); + // precompute certain values + calcVorticity(u, sbp, Trans, curl); + calcGrad(u, sbp, Trans, grad); elvect = 0.0; PDS = 0.0; for (int i = 0; i < num_nodes; ++i) { // get the Jacobian (Trans.Weight) and cubature weight (node.weight) const IntegrationPoint &node = el.GetNodes().IntPoint(i); Trans.SetIntPoint(&node); - CalcAdjugate(Trans.Jacobian(), adjJ_i); u.GetRow(i, ui); Trans.Transform(node, xi); // compute vorticity at node and take magnitude - double S; + curl.GetRow(i, curl_i); + double S = curl_i.Norml2(); // compute gradient of turbulent viscosity at node - Vector dir; + grad.GetRow(i, grad_i); // get distance function value at node double d; // accumulate source terms - double src = calcSASource(ui.GetData(), dir.GetData(), sacs.GetData()); + double src = calcSASource(ui.GetData(), grad_i.GetData(), sacs.GetData()); // use negative model if turbulent viscosity is negative if (ui(dim+2) < 0) { @@ -64,17 +63,12 @@ void SASourceIntegrator::AssembleElementVector( src += calcSADestruction(ui.GetData(), mu, d, S, sacs.GetData()); } - PDS(i, dim+2) += alpha * src; + res(i, dim+2) += alpha * Trans.Weight() * node.weight * src; } // loop over element nodes i - - // NOTE: how to multiply cubature and transformation weights? - - // multiply by projection - sbp.multProjOperator(PDS, res); } template -void SAViscousIntegrator::AssembleElementGrad( +void SASourceIntegrator::AssembleElementGrad( const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::DenseMatrix &elmat) { @@ -138,59 +132,148 @@ void SAViscousIntegrator::AssembleElementGrad( } // loop over element nodes j Dwi *= Hinv; - for (int d = 0; d < dim; ++d) { - // scale(d, xi, ui, Dwi, CDwi); - scaleJacState(d, xi, ui, Dwi, jac_term1); - scaleJacDw(d, xi, ui, Dwi, CDw_jac); - for (int k = 0; k < num_nodes; ++k) - { - // Node k mapping Jacobian adjugate and - Trans.SetIntPoint(&el.GetNodes().IntPoint(k)); - CalcAdjugate(Trans.Jacobian(), adjJ_k); - double Qik = sbp.getQEntry(d, i, k, adjJ_i, adjJ_k); + for (int d = 0; d < dim; ++d) + { + + } + elmat *= alpha; +} - // Contribution to Jacobian due to scaling operation - for (int sk = 0; sk < num_states; ++sk) - { - for (int si = 0; si < num_states; ++si) - { - // res(k, s) += alpha * Qik * CDwi(s); - elmat(sk*num_nodes+k, si*num_nodes+i) += Qik*jac_term1(sk,si); - } - } +template +void calcVorticitySBP(const DenseMatrix &q, const SBPFiniteElement sbp, + const ElementTransformation &Trans, DenseMatrix curl) +{ + DenseMatrix dq(q.Height(), q.Width()); //contains state derivatives in reference space + DenseMatrix dxi(dim*q.Height(), dim); //contains velocity derivatives in reference space + dq = 0.0 + for(int di = 0; di < dim; di++) + { + sbp.multWeakOperator(di, q, dq); - // Contribution to Jacobian assuming constant scaling operation - for (int j = 0; j < num_nodes; ++j) - { - // Node j mapping Jacobian adjugate and get Jacobian dw/du_j - Trans.SetIntPoint(&el.GetNodes().IntPoint(j)); - CalcAdjugate(Trans.Jacobian(), adjJ_j); - u.GetRow(j, uj); - convertJacState(uj, dwduj); - for (int d2 = 0; d2 < dim; ++d2) - { - // Following computes - // (Q_d1)_{ik}*H^{-1}_{ii}(C_{d,d2}(u_i)*(Q_d2)_{ij}*dw/du_j - // where C_{d,d2}(u_i) is a (state x state) matrix - // (Q_d2)_ij, (Q_d1)_ij, and H^{-1} are scalars, and - // (dw/du_j) is a (state x state) matrix - double Qij = sbp.getQEntry(d2, i, j, adjJ_i, adjJ_j); - Mult(CDw_jac[d2], dwduj, jac_term2); - jac_term2 *= (Qik*Hinv*Qij); - // Add to the Jacobian - for (int sk = 0; sk < num_states; ++sk) - { - for (int sj = 0; sj < num_states; ++sj) - { - elmat(sk * num_nodes + k, sj * num_nodes + j) += - jac_term2(sk, sj); - } - } - - } // loop over space dimension d2 - } // loop over the element nodes j - } // loop over element nodes k - } // loop over space dimensions d - } // loop over element nodes i - elmat *= alpha; -} \ No newline at end of file + dxi.CopyMN(dq, q.Height(), dim, 1, dim+1, di*q.Height, 0); + } + + DenseMatrix dx(dim, dim); //contains velocity derivatives in absolute space + DenseMatrix dxin(dim, dim); //contains velocity derivatives in reference space for a node + Vector dxrow(dim); Vector curln(dim); + for (int i = 0; i < num_nodes; ++i) + { + // get the Jacobian + const IntegrationPoint &node = el.GetNodes().IntPoint(i); + Trans.SetIntPoint(&node); + + // store nodal derivatives in a single matrix + for(int di = 0; di < dim; di++) + { + dxi.GetRow(di*q.Height() + i, dxrow); + dxin.SetRow(di, dxrow); + } + + // compute absolute derivatives + MultAtB(Trans.InverseJacobian(), dxin, dx); + + // compute curl at node and append + if(dim == 2) + { + curl(i, 0) = 0; + curl(i, 1) = 0; + curl(i, 2) = dx(1,0) - dx(0,1); + } + if(dim == 3) + { + curl(i, 0) = dx(2,1) - dx(1,2); + curl(i, 1) = dx(0,2) - dx(2,0); + curl(i, 2) = dx(1,0) - dx(0,1); + } + } +} + +template +void calcGradSBP(const DenseMatrix &q, const SBPFiniteElement sbp, + const ElementTransformation &Trans, DenseMatrix grad) +{ + DenseMatrix dq(q.Height(), q.Width()); //contains state derivatives in reference space + DenseMatrix dnu(q.Height(), dim); //contains turb variable derivatives in reference space + dq = 0.0 + for(int di = 0; di < dim; di++) + { + sbp.multWeakOperator(int di, const DenseMatrix &u, DenseMatrix &dq); + + dnu.SetCol(di, dq.GetColumn(dim+2)); + } + + Vector dnurow(dim); Vector gradn(dim); + for (int i = 0; i < num_nodes; ++i) + { + // get the Jacobian + const IntegrationPoint &node = el.GetNodes().IntPoint(i); + Trans.SetIntPoint(&node); + + // store nodal grad in a vector + dnu.GetRow(i, dnurow); + + // compute absolute derivatives + Trans.InverseJacobian().MultTranspose(dnurow, gradn); + + // append result + grad.SetRow(i, gradn); + } +} + +//==================================================================================== +//SA Inviscid Integrator Methods + +template +void SAInviscidIntegrator::calcFlux(int di, const mfem::Vector &qL, + const mfem::Vector &qR, + mfem::Vector &flux) +{ + //call base class, operate on original state variables + IsmailRoeIntegrator::calcFlux(di, qL, qR, flux); + //add flux term for SA + flux(dim+2) = 0.5*(qL(di+1)/qL(0) + qR(di+1)/qR(0))*0.5*(qL(dim+2) + qR(dim+2)); +} + +template +void SAInviscidIntegrator::calcFluxJacStates( + int di, const mfem::Vector &qL, const mfem::Vector &qR, + mfem::DenseMatrix &jacL, mfem::DenseMatrix &jacR) +{ + // store the full jacobian in jac + mfem::DenseMatrix jac(dim + 2, 2 * (dim + 2)); + // vector of active input variables + std::vector qL_a(qL.Size()); + std::vector qR_a(qR.Size()); + // initialize adouble inputs + adept::set_values(qL_a.data(), qL.Size(), qL.GetData()); + adept::set_values(qR_a.data(), qR.Size(), qR.GetData()); + // start recording + this->stack.new_recording(); + // create vector of active output variables + std::vector flux_a(qL.Size()); + // run algorithm + if (entvar) + { + mach::calcIsmailRoeFluxUsingEntVars(di, qL_a.data(), + qR_a.data(), + flux_a.data()); + } + else + { + mach::calcIsmailRoeFlux(di, qL_a.data(), + qR_a.data(), flux_a.data()); + } + // identify independent and dependent variables + this->stack.independent(qL_a.data(), qL.Size()); + this->stack.independent(qR_a.data(), qR.Size()); + this->stack.dependent(flux_a.data(), qL.Size()); + // compute and store jacobian in jac + this->stack.jacobian_reverse(jac.GetData()); + // retrieve the jacobian w.r.t left state + jacL.CopyCols(jac, 0, dim + 1); + // retrieve the jacobian w.r.t right state + jacR.CopyCols(jac, dim + 2, 2 * (dim + 2) - 1); +} + + +#endif \ No newline at end of file diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 1c37d974..31578a3c 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -27,6 +27,7 @@ set(FLUID_TEST_SRCS test_utils test_thermal_integ test_res_integ + test_rans_integ ) # group EM tests diff --git a/test/unit/test_rans_fluxes.cpp b/test/unit/test_rans_fluxes.cpp new file mode 100644 index 00000000..e69de29b diff --git a/test/unit/test_rans_integ.cpp b/test/unit/test_rans_integ.cpp new file mode 100644 index 00000000..37805467 --- /dev/null +++ b/test/unit/test_rans_integ.cpp @@ -0,0 +1,44 @@ +#include + +#include "catch.hpp" +#include "mfem.hpp" +#include "euler_integ.hpp" +#include "rans_fluxes.hpp" +#include "rans_integ.hpp" +#include "euler_test_data.hpp" + +TEMPLATE_TEST_CASE_SIG("SA Inviscid Test", "[sa_inviscid_test]", + ((int dim), dim), 1, 2, 3) +{ + using namespace euler_data; + mfem::Vector qL(dim + 3); + mfem::Vector qR(dim + 3); + mfem::Vector flux1(dim + 2); + mfem::Vector flux2(dim + 3); + mfem::Vector nrm(dim); + qL(0) = rho; qR(0) = rho*1.1; + qL(dim + 1) = rhoe; qR(dim + 1) = rhoe*1.1; + for (int di = 0; di < dim; ++di) + { + qL(di + 1) = rhou[di]; + qR(di + 1) = rhou[di]*1.1; + nrm(di) = dir[di]; + } + qL(dim + 2) = 5.0; qR(dim + 2) = 5.5; + adept::Stack stack; + mach::IsmailRoeIntegrator irinteg(stack); + mach::SAInviscidIntegrator sainteg(stack); + + SECTION("Check if SA integrator matches the output for the conservative variables") + { + // calculate the flux + irinteg.calcFlux(nrm, qL, qR, flux); + sainteg.calcFlux(nrm, qL, qR, flux); + + // check if euler variables are the same as before + for(int n = 0; n < dim+2; n++) + { + REQUIRE(flux1(n) - flux2(n) == Approx(0.0)); + } + } +} \ No newline at end of file From 5f03aea429e73e1b477657b3b9b300aed7263622 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Thu, 16 Jul 2020 17:29:02 -0400 Subject: [PATCH 03/28] source integrator functions compile, and euler integrator flux function confirmed not to break with the extra variable --- src/physics/fluidflow/rans_integ.hpp | 63 +++++--- src/physics/fluidflow/rans_integ_def.hpp | 180 +++++++---------------- test/unit/test_rans_integ.cpp | 6 +- 3 files changed, 101 insertions(+), 148 deletions(-) diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp index d4c4196a..1afda88d 100644 --- a/src/physics/fluidflow/rans_integ.hpp +++ b/src/physics/fluidflow/rans_integ.hpp @@ -41,14 +41,16 @@ class ESViscousSAIntegrator : public ESViscousIntegrator /// \tparam dim - number of spatial dimensions (1, 2, or 3) /// \note This derived class uses the CRTP template -class SAInviscidIntegrator : public IsmailRoeIntegrator +class SAInviscidIntegrator : public DyadicFluxIntegrator< + SAInviscidIntegrator> { public: /// Construct an inviscid integrator with SA terms /// \param[in] diff_stack - for algorithmic differentiation /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) SAInviscidIntegrator(adept::Stack &diff_stack, double a = 1.0) - : IsmailRoeIntegrator (diff_stack, a) {} + : DyadicFluxIntegrator>( + diff_stack, dim+3, a) {} /// Ismail-Roe two-point (dyadic) flux function with additional variable @@ -87,25 +89,25 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator /// \param[in] vis - nondimensional dynamic viscosity (use Sutherland if neg) /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) SASourceIntegrator(adept::Stack &diff_stack, mfem::GridFunction dist, - mfem::Vector sacs, double vis = -1.0, double a = 1.0) - : alpha(a), mu(vis), stack(diff_stack) {} + mfem::Vector sa_params, double vis = -1.0, double a = 1.0) + : alpha(a), mu(vis), stack(diff_stack), num_states(dim+3), sacs(sa_params) {} /// Construct the element local residual - /// \param[in] el - the finite element whose residual we want + /// \param[in] fe - the finite element whose residual we want /// \param[in] Trans - defines the reference to physical element mapping /// \param[in] elfun - element local state function /// \param[out] elvect - element local residual - virtual void AssembleElementVector(const mfem::FiniteElement &el, + virtual void AssembleElementVector(const mfem::FiniteElement &fe, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::Vector &elvect); /// Construct the element local Jacobian - /// \param[in] el - the finite element whose Jacobian we want + /// \param[in] fe - the finite element whose Jacobian we want /// \param[in] Trans - defines the reference to physical element mapping /// \param[in] elfun - element local state function /// \param[out] elmat - element local Jacobian - virtual void AssembleElementGrad(const mfem::FiniteElement &el, + virtual void AssembleElementGrad(const mfem::FiniteElement &fe, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::DenseMatrix &elmat); @@ -116,8 +118,8 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator /// \param[in] Trans - defines the reference to physical element mapping /// \param[out] curl - the curl of the velocity field at each node/int point void calcVorticitySBP(const mfem::DenseMatrix &q, - const SBPFiniteElement &sbp, - const mfem::ElementTransformation &Trans, + const mfem::FiniteElement &fe, + mfem::ElementTransformation &Trans, mfem::DenseMatrix curl); // Compute gradient for the turbulence variable on an SBP element, @@ -127,26 +129,45 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator /// \param[in] Trans - defines the reference to physical element mapping /// \param[out] grad - the gradient of the turbulence variable at each node void calcGradSBP(const mfem::DenseMatrix &q, - const SBPFiniteElement &sbp, - const mfem::ElementTransformation &Trans, + const mfem::FiniteElement &fe, + mfem::ElementTransformation &Trans, mfem::DenseMatrix grad); private: + +protected: /// nondimensional dynamic viscosity double mu; - /// nu gradient on element - mfem::DenseMatrix grad; - /// nu gradient on node - mfem::Vector grad_i; - /// velocity curl on element - mfem::DenseMatrix curl; - /// velocity curl on node - mfem::Vector curl_i; -protected: + /// number of states + int num_states; /// scales the terms; can be used to move to rhs/lhs double alpha; + /// vector of SA model parameters + mfem::Vector sacs; /// stack used for algorithmic differentiation adept::Stack &stack; +#ifndef MFEM_THREAD_SAFE + /// the coordinates of node i + mfem::Vector xi; + /// used to reference the states at node i + mfem::Vector ui; + /// used to reference the gradient at node i + mfem::Vector grad_i; + /// used to reference the curl at node i + mfem::Vector curl_i; + /// stores the result of calling the flux function + mfem::Vector fluxi; + /// used to store the gradient on the element + mfem::DenseMatrix grad; + /// used to store the gradient on the element + mfem::DenseMatrix curl; + /// used to store the flux Jacobian at node i + mfem::DenseMatrix flux_jaci; + /// used to store the flux at each node + mfem::DenseMatrix elflux; + /// used to store the residual in (num_states, Dof) format + mfem::DenseMatrix elres; +#endif }; #if 0 diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index e32f8e9a..00362202 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -4,16 +4,14 @@ //============================================================================== // SASourceIntegrator methods -#if 0 -template -void SASourceIntegrator::AssembleElementVector( - const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, +template +void SASourceIntegrator::AssembleElementVector( + const mfem::FiniteElement &fe, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::Vector &elvect) { using namespace mfem; - const SBPFiniteElement &sbp = dynamic_cast(el); + const SBPFiniteElement &sbp = dynamic_cast(fe); int num_nodes = sbp.GetDof(); - int dim = sbp.GetDim(); #ifdef MFEM_THREAD_SAFE Vector ui, xi, uj, grad_i, curl_i; DenseMatrix grad, curl; @@ -28,13 +26,13 @@ void SASourceIntegrator::AssembleElementVector( DenseMatrix u(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well DenseMatrix res(elvect.GetData(), num_nodes, num_states); // precompute certain values - calcVorticity(u, sbp, Trans, curl); - calcGrad(u, sbp, Trans, grad); - elvect = 0.0; PDS = 0.0; + calcVorticitySBP(u, sbp, Trans, curl); + calcGradSBP(u, sbp, Trans, grad); + elvect = 0.0; for (int i = 0; i < num_nodes; ++i) { // get the Jacobian (Trans.Weight) and cubature weight (node.weight) - const IntegrationPoint &node = el.GetNodes().IntPoint(i); + IntegrationPoint &node = fe.GetNodes().IntPoint(i); Trans.SetIntPoint(&node); u.GetRow(i, ui); Trans.Transform(node, xi); @@ -50,107 +48,60 @@ void SASourceIntegrator::AssembleElementVector( double d; // accumulate source terms - double src = calcSASource(ui.GetData(), grad_i.GetData(), sacs.GetData()); + double src = calcSASource( + ui.GetData(), grad_i.GetData(), sacs.GetData()); // use negative model if turbulent viscosity is negative if (ui(dim+2) < 0) { - src += calcSANegativeProduction(ui.GetData(), S, sacs.GetData()); - src += calcSANegativeDestruction(ui.GetData(), d, sacs.GetData()); + src += calcSANegativeProduction( + ui.GetData(), S, sacs.GetData()); + src += calcSANegativeDestruction( + ui.GetData(), d, sacs.GetData()); } else { - src += calcSAProduction(ui.GetData(), mu, d, S, sacs.GetData()); - src += calcSADestruction(ui.GetData(), mu, d, S, sacs.GetData()); + src += calcSAProduction( + ui.GetData(), mu, d, S, sacs.GetData()); + src += calcSADestruction( + ui.GetData(), mu, d, S, sacs.GetData()); } res(i, dim+2) += alpha * Trans.Weight() * node.weight * src; } // loop over element nodes i } -template -void SASourceIntegrator::AssembleElementGrad( - const mfem::FiniteElement &el, mfem::ElementTransformation &Trans, +template +void SASourceIntegrator::AssembleElementGrad( + const mfem::FiniteElement &fe, mfem::ElementTransformation &Trans, const mfem::Vector &elfun, mfem::DenseMatrix &elmat) { using namespace mfem; using namespace std; - const SBPFiniteElement &sbp = dynamic_cast(el); + const SBPFiniteElement &sbp = dynamic_cast(fe); int num_nodes = sbp.GetDof(); - int dim = sbp.GetDim(); #ifdef MFEM_THREAD_SAFE Vector ui, xi, wj, uj; DenseMatrix adjJ_i, adjJ_j, adjJ_k, Dwi, jac_term1, jac_term2, dwduj; vector CDw_jac(dim); #endif - ui.SetSize(num_states); - xi.SetSize(dim); - wj.SetSize(num_states); - uj.SetSize(num_states); - Dwi.SetSize(num_states,dim); - adjJ_i.SetSize(dim); - adjJ_j.SetSize(dim); - adjJ_k.SetSize(dim); - jac_term1.SetSize(num_states); - jac_term2.SetSize(num_states); - dwduj.SetSize(num_states); - CDw_jac.resize(dim); - for (int d = 0; d < dim; ++d) - { - CDw_jac[d].SetSize(num_states); - } - DenseMatrix u(elfun.GetData(), num_nodes, num_states); - elmat.SetSize(num_states*num_nodes); - elmat = 0.0; - - for (int i = 0; i < num_nodes; ++i) - { - // get the Jacobian (Trans.Weight) and cubature weight (node.weight) - const IntegrationPoint &node = el.GetNodes().IntPoint(i); - Trans.SetIntPoint(&node); - double Hinv = 1.0 / (sbp.getDiagNormEntry(i) * Trans.Weight()); - CalcAdjugate(Trans.Jacobian(), adjJ_i); - u.GetRow(i, ui); - Trans.Transform(node, xi); - // compute the (physcial space) derivatives at node i - Dwi = 0.0; - for (int j = 0; j < num_nodes; ++j) - { - // Get mapping Jacobian adjugate and transform state to entropy vars - Trans.SetIntPoint(&el.GetNodes().IntPoint(j)); - CalcAdjugate(Trans.Jacobian(), adjJ_j); - u.GetRow(j, uj); - convert(uj, wj); - for (int d = 0; d < dim; ++d) - { - double Qij = sbp.getQEntry(d, i, j, adjJ_i, adjJ_j); - for (int s = 0; s < num_states; ++s) - { - Dwi(s,d) += Qij * wj(s); - } - } // loop over space dimensions d - } // loop over element nodes j - Dwi *= Hinv; - - for (int d = 0; d < dim; ++d) - { - - } - elmat *= alpha; } -template -void calcVorticitySBP(const DenseMatrix &q, const SBPFiniteElement sbp, - const ElementTransformation &Trans, DenseMatrix curl) +template +void SASourceIntegrator::calcVorticitySBP(const mfem::DenseMatrix &q, const mfem::FiniteElement &fe, + mfem::ElementTransformation &Trans, mfem::DenseMatrix curl) { + using namespace mfem; + const SBPFiniteElement &sbp = dynamic_cast(fe); + int num_nodes = sbp.GetDof(); DenseMatrix dq(q.Height(), q.Width()); //contains state derivatives in reference space DenseMatrix dxi(dim*q.Height(), dim); //contains velocity derivatives in reference space - dq = 0.0 + dq = 0.0; for(int di = 0; di < dim; di++) { sbp.multWeakOperator(di, q, dq); - - dxi.CopyMN(dq, q.Height(), dim, 1, dim+1, di*q.Height, 0); + //need to scale with 1/H, probably when looping over nodes + dxi.CopyMN(dq, q.Height(), dim, 1, dim+1, di*q.Height(), 0); } DenseMatrix dx(dim, dim); //contains velocity derivatives in absolute space @@ -159,7 +110,7 @@ void calcVorticitySBP(const DenseMatrix &q, const SBPFiniteElement sbp, for (int i = 0; i < num_nodes; ++i) { // get the Jacobian - const IntegrationPoint &node = el.GetNodes().IntPoint(i); + IntegrationPoint &node = sbp.GetNodes().IntPoint(i); Trans.SetIntPoint(&node); // store nodal derivatives in a single matrix @@ -188,17 +139,20 @@ void calcVorticitySBP(const DenseMatrix &q, const SBPFiniteElement sbp, } } -template -void calcGradSBP(const DenseMatrix &q, const SBPFiniteElement sbp, - const ElementTransformation &Trans, DenseMatrix grad) +template +void SASourceIntegrator::calcGradSBP(const mfem::DenseMatrix &q, const mfem::FiniteElement &fe, + mfem::ElementTransformation &Trans, mfem::DenseMatrix grad) { + using namespace mfem; + const SBPFiniteElement &sbp = dynamic_cast(fe); + int num_nodes = sbp.GetDof(); DenseMatrix dq(q.Height(), q.Width()); //contains state derivatives in reference space DenseMatrix dnu(q.Height(), dim); //contains turb variable derivatives in reference space - dq = 0.0 + dq = 0.0; for(int di = 0; di < dim; di++) { - sbp.multWeakOperator(int di, const DenseMatrix &u, DenseMatrix &dq); - + sbp.multWeakOperator(di, q, dq); + //need to scale with 1/H dnu.SetCol(di, dq.GetColumn(dim+2)); } @@ -206,7 +160,7 @@ void calcGradSBP(const DenseMatrix &q, const SBPFiniteElement sbp, for (int i = 0; i < num_nodes; ++i) { // get the Jacobian - const IntegrationPoint &node = el.GetNodes().IntPoint(i); + IntegrationPoint &node = sbp.GetNodes().IntPoint(i); Trans.SetIntPoint(&node); // store nodal grad in a vector @@ -228,8 +182,18 @@ void SAInviscidIntegrator::calcFlux(int di, const mfem::Vector &qL, const mfem::Vector &qR, mfem::Vector &flux) { - //call base class, operate on original state variables - IsmailRoeIntegrator::calcFlux(di, qL, qR, flux); + ///NOTE: Differentiate this function with adept directly, check euler/navier stokes integrators + //operate on original state variables + if (entvar) + { + calcIsmailRoeFluxUsingEntVars(di, qL.GetData(), qR.GetData(), + flux.GetData()); + } + else + { + calcIsmailRoeFlux(di, qL.GetData(), qR.GetData(), + flux.GetData()); + } //add flux term for SA flux(dim+2) = 0.5*(qL(di+1)/qL(0) + qR(di+1)/qR(0))*0.5*(qL(dim+2) + qR(dim+2)); } @@ -239,41 +203,7 @@ void SAInviscidIntegrator::calcFluxJacStates( int di, const mfem::Vector &qL, const mfem::Vector &qR, mfem::DenseMatrix &jacL, mfem::DenseMatrix &jacR) { - // store the full jacobian in jac - mfem::DenseMatrix jac(dim + 2, 2 * (dim + 2)); - // vector of active input variables - std::vector qL_a(qL.Size()); - std::vector qR_a(qR.Size()); - // initialize adouble inputs - adept::set_values(qL_a.data(), qL.Size(), qL.GetData()); - adept::set_values(qR_a.data(), qR.Size(), qR.GetData()); - // start recording - this->stack.new_recording(); - // create vector of active output variables - std::vector flux_a(qL.Size()); - // run algorithm - if (entvar) - { - mach::calcIsmailRoeFluxUsingEntVars(di, qL_a.data(), - qR_a.data(), - flux_a.data()); - } - else - { - mach::calcIsmailRoeFlux(di, qL_a.data(), - qR_a.data(), flux_a.data()); - } - // identify independent and dependent variables - this->stack.independent(qL_a.data(), qL.Size()); - this->stack.independent(qR_a.data(), qR.Size()); - this->stack.dependent(flux_a.data(), qL.Size()); - // compute and store jacobian in jac - this->stack.jacobian_reverse(jac.GetData()); - // retrieve the jacobian w.r.t left state - jacL.CopyCols(jac, 0, dim + 1); - // retrieve the jacobian w.r.t right state - jacR.CopyCols(jac, dim + 2, 2 * (dim + 2) - 1); + } -#endif \ No newline at end of file diff --git a/test/unit/test_rans_integ.cpp b/test/unit/test_rans_integ.cpp index 37805467..51b70062 100644 --- a/test/unit/test_rans_integ.cpp +++ b/test/unit/test_rans_integ.cpp @@ -32,12 +32,14 @@ TEMPLATE_TEST_CASE_SIG("SA Inviscid Test", "[sa_inviscid_test]", SECTION("Check if SA integrator matches the output for the conservative variables") { // calculate the flux - irinteg.calcFlux(nrm, qL, qR, flux); - sainteg.calcFlux(nrm, qL, qR, flux); + irinteg.calcFlux(0, qL, qR, flux1); + sainteg.calcFlux(0, qL, qR, flux2); // check if euler variables are the same as before for(int n = 0; n < dim+2; n++) { + std::cout << "Ismail-Roe " << n << " Flux: " << flux1(n) << std::endl; + std::cout << "Spalart-Allmaras " << n << " Flux: " << flux2(n) << std::endl; REQUIRE(flux1(n) - flux2(n) == Approx(0.0)); } } From 94c8e9533422edc033853529ac9f825cd73a075c Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Fri, 24 Jul 2020 16:21:00 -0400 Subject: [PATCH 04/28] inviscid freestream rans test works, need to check more results --- sandbox/rans_freestream.cpp | 153 ++++++++++++++++ sandbox/rans_freestream_options.json | 50 ++++++ src/physics/fluidflow/rans.cpp | 107 ++++++++--- src/physics/fluidflow/rans.hpp | 17 +- src/physics/fluidflow/rans_integ.hpp | 139 +++++++------- src/physics/fluidflow/rans_integ_def.hpp | 167 ++++++++++++++++- test/unit/test_rans_integ.cpp | 220 ++++++++++++++++++++--- 7 files changed, 728 insertions(+), 125 deletions(-) create mode 100644 sandbox/rans_freestream.cpp create mode 100644 sandbox/rans_freestream_options.json diff --git a/sandbox/rans_freestream.cpp b/sandbox/rans_freestream.cpp new file mode 100644 index 00000000..3181597d --- /dev/null +++ b/sandbox/rans_freestream.cpp @@ -0,0 +1,153 @@ +// set this const expression to true in order to use entropy variables for state (doesn't work for rans) +constexpr bool entvar = false; + +#include +#include "adept.h" + +#include "mfem.hpp" +#include "rans.hpp" +#include +#include + +using namespace std; +using namespace mfem; +using namespace mach; + +std::default_random_engine gen(std::random_device{}()); +std::uniform_real_distribution normal_rand(-1.0,1.0); + +static double mu; +static double mach_fs; +static double aoa_fs; +static int iroll; +static int ipitch; +static double chi_fs; + +/// \brief Defines the random function for the jacobian check +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void pert(const Vector &x, Vector& p); + +/// \brief Defines the exact solution for the rans freestream problem +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void uexact(const Vector &x, Vector& u); + +int main(int argc, char *argv[]) +{ + const char *options_file = "rans_freestream_options.json"; + + // Initialize MPI + int num_procs, rank; + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + ostream *out = getOutStream(rank); + + // Parse command-line options + OptionsParser args(argc, argv); + int degree = 2.0; + int nx = 1; + int ny = 1; + args.AddOption(&nx, "-nx", "--numx", + "Number of elements in x direction"); + args.AddOption(&ny, "-ny", "--numy", + "Number of elements in y direction"); + args.Parse(); + if (!args.Good()) + { + args.PrintUsage(*out); + return 1; + } + + try + { + // construct the mesh + string opt_file_name(options_file); + nlohmann::json file_options; + std::ifstream opts(opt_file_name); + opts >> file_options; + mu = file_options["flow-param"]["mu"].template get(); + mach_fs = file_options["flow-param"]["mach"].template get(); + aoa_fs = file_options["flow-param"]["aoa"].template get()*M_PI/180; + iroll = file_options["flow-param"]["roll-axis"].template get(); + ipitch = file_options["flow-param"]["pitch-axis"].template get(); + chi_fs = file_options["flow-param"]["chi"].template get(); + // generate a simple tri mesh + std::unique_ptr smesh(new Mesh(nx, ny, + Element::TRIANGLE, true /* gen. edges */, 1.0, + 1.0, true)); + *out << "Number of elements " << smesh->GetNE() <<'\n'; + + // construct the solver and set initial conditions + auto solver = createSolver>(opt_file_name, + move(smesh)); + solver->setInitialCondition(uexact); + solver->printSolution("rans_init", 0); + + // get the initial density error + //double l2_error = (static_cast&>(*solver) + // .calcConservativeVarsL2Error(uexact, 0)); + double res_error = solver->calcResidualNorm(); + //*out << "\n|| rho_h - rho ||_{L^2} = " << l2_error; + *out << "\ninitial residual norm = " << res_error << endl; + solver->checkJacobian(pert); + solver->solveForState(); + solver->printSolution("rans_final",0); + // get the final density error + //l2_error = (static_cast&>(*solver) + // .calcConservativeVarsL2Error(uexact, 0)); + res_error = solver->calcResidualNorm(); + + *out << "\nfinal residual norm = " << res_error; + //*out << "\n|| rho_h - rho ||_{L^2} = " << l2_error << endl; + } + catch (MachException &exception) + { + exception.print_message(); + } + catch (std::exception &exception) + { + cerr << exception.what() << endl; + } + +#ifdef MFEM_USE_PETSC + MFEMFinalizePetsc(); +#endif + + MPI_Finalize(); +} + +// perturbation function used to check the jacobian in each iteration +void pert(const Vector &x, Vector& p) +{ + p.SetSize(5); + for (int i = 0; i < 5; i++) + { + p(i) = normal_rand(gen); + } +} + +// Exact solution; same as freestream bc +void uexact(const Vector &x, Vector& q) +{ + q.SetSize(5); + Vector u(5); + + u = 0.0; + u(0) = 1.0; + u(1) = u(0)*mach_fs*cos(aoa_fs); + u(2) = u(0)*mach_fs*sin(aoa_fs); + u(3) = 1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; + u(4) = chi_fs*(mu/u(0)); + + if (entvar == false) + { + q = u; + } + else + { + throw MachException("No entvar for this"); + } +} + diff --git a/sandbox/rans_freestream_options.json b/sandbox/rans_freestream_options.json new file mode 100644 index 00000000..158c15de --- /dev/null +++ b/sandbox/rans_freestream_options.json @@ -0,0 +1,50 @@ +{ + "flow-param": { + "mach": 2.5, + "aoa": 0.0, + "roll-axis": 0, + "pitch-axis": 1, + "chi": 3, + "Re": 10.0, + "Pr": 0.75, + "mu": 1.0, + "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16] + }, + "space-dis": { + "degree": 1, + "lps-coeff": 1.0, + "basis-type": "csbp" + }, + "time-dis": { + "steady": true, + "steady-abstol": 1e-12, + "steady-restol": 1e-10, + "const-cfl": true, + "ode-solver": "PTC", + "t-final": 100, + "dt": 0.1, + "cfl": 1.0 + }, + "newtonsolver":{ + "printlevel": 1, + "maxiter": 50, + "reltol": 1e-1, + "abstol": 1e-12 + }, + "petscsolver":{ + "ksptype": "gmres", + "pctype": "lu", + "abstol": 1e-10, + "reltol": 1e-10, + "maxiter": 100, + "printlevel": 2 + }, + "hypresolver":{ + "tol": 1e-10, + "maxiter": 100, + "printlevel": 0 + }, + "bcs": { + "far-field": [1, 1, 1, 1] + } +} diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index a350b082..33f9437b 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -5,7 +5,7 @@ using namespace mfem; using namespace std; -#if 0 + namespace mach { @@ -22,6 +22,8 @@ RANavierStokesSolver::RANavierStokesSolver(const string &opt_file_n // define free-stream parameters; may or may not be used, depending on case chi_fs = this->options["flow-param"]["chi"].template get(); mu = this->options["flow-param"]["mu"].template get(); + vector sa = this->options["flow-param"]["sa-consts"].template get>(); + sacs.SetData(sa.data()); } template @@ -29,28 +31,29 @@ void RANavierStokesSolver::addResVolumeIntegrators(double alpha) { // add Navier Stokes integrators cout << "Inside RANS add volume integrators" << endl; - this->res->AddDomainIntegrator(new IsmailRoeIntegrator( //Inviscid term + this->res->AddDomainIntegrator(new SAInviscidIntegrator( //Inviscid term this->diff_stack, alpha)); - this->res->AddDomainIntegrator(new SAViscousIntegrator( //SAViscousIntegrator - this->diff_stack, re_fs, pr_fs, mu, alpha)); + // this->res->AddDomainIntegrator(new SAViscousIntegrator( //SAViscousIntegrator + // this->diff_stack, re_fs, pr_fs, mu, alpha)); // now add RANS integrators - // combine these, so we compute vorticity once - this->res->AddDomainIntegrator(new SASourceIntegrator( - this->diff_stack, alpha)); + // this->res->AddDomainIntegrator(new SASourceIntegrator( + // this->diff_stack, sacs, mu, alpha)); // add LPS stabilization - double lps_coeff = options["space-dis"]["lps-coeff"].template get(); - this->res->AddDomainIntegrator(new EntStableLPSIntegrator( - this->diff_stack, alpha, lps_coeff)); + // double lps_coeff = this->options["space-dis"]["lps-coeff"].template get(); + // this->res->AddDomainIntegrator(new EntStableLPSIntegrator( + // this->diff_stack, alpha, lps_coeff)); } template -void NavierStokesSolver::addResBoundaryIntegrators(double alpha) +void RANavierStokesSolver::addResBoundaryIntegrators(double alpha) { // add base class integrators auto &bcs = this->options["bcs"]; double mu = this->options["flow-param"]["mu"].template get(); + int idx = 0; +#if 0 if (bcs.find("slip-wall") != bcs.end()) { // slip-wall boundary condition vector tmp = bcs["slip-wall"].template get>(); @@ -77,14 +80,38 @@ void NavierStokesSolver::addResBoundaryIntegrators(double alpha) this->bndry_marker[idx]); idx++; } - if (bcs.find("viscous-inflow") != bcs.end()) + + if (bcs.find("rans-inflow") != bcs.end()) { - throw MachException("Viscous inflow bc not implemented!"); + vector tmp = bcs["rans-inflow"].template get>(); + this->bndry_marker[idx].SetSize(tmp.size(), 0); + this->bndry_marker[idx].Assign(tmp.data()); + // get the in-flow state needed by the integrator + Vector q_in(dim+2); + getViscousInflowState(q_in); + // Add the rans-inflow integrator + this->res->AddBdrFaceIntegrator( + new RANSInflowBC(this->diff_stack, this->fec.get(), + re_fs, pr_fs, q_in, mu, alpha), + this->bndry_marker[idx]); + idx++; } - if (bcs.find("viscous-outflow") != bcs.end()) + if (bcs.find("rans-outflow") != bcs.end()) { - throw MachException("Viscous outflow bc not implemented!"); + vector tmp = bcs["rans-outflow"].template get>(); + this->bndry_marker[idx].SetSize(tmp.size(), 0); + this->bndry_marker[idx].Assign(tmp.data()); + // get the out-flow state needed by the integrator + Vector q_out(dim+2); + getRANSOutflowState(q_out); + // Add the rans-inflow integrator + this->res->AddBdrFaceIntegrator( + new RANSOutflowBC(this->diff_stack, this->fec.get(), + re_fs, pr_fs, q_out, mu, alpha), + this->bndry_marker[idx]); + idx++; } +#endif if (bcs.find("far-field") != bcs.end()) { // far-field boundary conditions @@ -94,15 +121,52 @@ void NavierStokesSolver::addResBoundaryIntegrators(double alpha) this->bndry_marker[idx].SetSize(tmp.size(), 0); this->bndry_marker[idx].Assign(tmp.data()); this->res->AddBdrFaceIntegrator( - new SAFarFieldBC(this->diff_stack, this->fec.get(), qfar, + new SAFarFieldBC(this->diff_stack, this->fec.get(), qfar, alpha), this->bndry_marker[idx]); idx++; } +#if 0 if (bcs.find("viscous-shock") != bcs.end()) { throw MachException("Viscous shock bc not implemented!"); } +#endif + +} + +template +void RANavierStokesSolver::getRANSInflowState(Vector &q_in) +{ + vector tmp = this->options["flow-param"] + ["inflow-state"] + .template get>(); + if (tmp.size() != dim+3) + { + throw MachException("inflow-state option has wrong number of entries" + " for problem dimension!"); + } + for (int i = 0; i < dim+3; ++i) + { + q_in(i) = tmp[i]; + } +} + +template +void RANavierStokesSolver::getRANSOutflowState(Vector &q_out) +{ + vector tmp = this->options["flow-param"] + ["outflow-state"] + .template get>(); + if (tmp.size() != dim+3) + { + throw MachException("outflow-state option has wrong number of entries" + " for problem dimension!"); + } + for (int i = 0; i < dim+3; ++i) + { + q_out(i) = tmp[i]; + } } template @@ -112,15 +176,15 @@ void RANavierStokesSolver::getFreeStreamState(mfem::Vector &q_ref) q_ref(0) = 1.0; if (dim == 1) { - q_ref(1) = q_ref(0)*mach_fs; // ignore angle of attack + q_ref(1) = q_ref(0)*this->mach_fs; // ignore angle of attack } else { - q_ref(iroll+1) = q_ref(0)*mach_fs*cos(aoa_fs); - q_ref(ipitch+1) = q_ref(0)*mach_fs*sin(aoa_fs); + q_ref(this->iroll+1) = q_ref(0)*this->mach_fs*cos(this->aoa_fs); + q_ref(this->ipitch+1) = q_ref(0)*this->mach_fs*sin(this->aoa_fs); } - q_ref(dim+1) = 1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; - q_ref(dim+2) = chi_fs*(mu/q_ref(0)); + q_ref(dim+1) = 1/(euler::gamma*euler::gami) + 0.5*this->mach_fs*this->mach_fs; + q_ref(dim+2) = this->chi_fs*(mu/q_ref(0)); } // explicit instantiation @@ -130,4 +194,3 @@ template class RANavierStokesSolver<3>; }//namespace mach -#endif \ No newline at end of file diff --git a/src/physics/fluidflow/rans.hpp b/src/physics/fluidflow/rans.hpp index 6b44c8ea..86b1fdc3 100644 --- a/src/physics/fluidflow/rans.hpp +++ b/src/physics/fluidflow/rans.hpp @@ -16,8 +16,13 @@ template class RANavierStokesSolver : public NavierStokesSolver { public: + /// Sets `q_in` to the inflow conservative + SA variables + void getRANSInflowState(mfem::Vector &q_in); - /// Sets `q_ref` to the free-stream conservative variables + /// Sets `q_out` to the outflow conservative + SA variables + void getRANSOutflowState(mfem::Vector &q_out); + + /// Sets `q_ref` to the free-stream conservative + SA variables void getFreeStreamState(mfem::Vector &q_ref); /// convert conservative variables to entropy variables @@ -45,14 +50,6 @@ class RANavierStokesSolver : public NavierStokesSolver /// \note This function calls EulerSolver::addBoundaryIntegrators() first virtual void addResBoundaryIntegrators(double alpha); - /// Add interior-face integrators to `res` based on `options` - /// \param[in] alpha - scales the data; used to move terms to rhs or lhs - /// \note This function calls NavierStokes::addInterfaceIntegrators() first - virtual void addResInterfaceIntegrators(double alpha) - { - throw MachException("dsbp not implemented!!!"); - } - /// Return the number of state variables virtual int getNumState() override {return dim+3; } @@ -60,6 +57,8 @@ class RANavierStokesSolver : public NavierStokesSolver const std::string &opt_file_name, std::unique_ptr smesh); + /// SA constants vector + mfem::Vector sacs; /// free-stream SA viscosity ratio (nu_tilde/nu_material) double chi_fs; /// material dynamic viscosity diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp index 1afda88d..b3966f06 100644 --- a/src/physics/fluidflow/rans_integ.hpp +++ b/src/physics/fluidflow/rans_integ.hpp @@ -170,31 +170,29 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator #endif }; -#if 0 -/// Integrator for no-slip adiabatic-wall boundary condition + +/// Integrator for SA far-field boundary conditions /// \tparam dim - number of spatial dimensions (1, 2, or 3) /// \note This derived class uses the CRTP template -class NoSlipAdiabaticWallBC : public ViscousBoundaryIntegrator> +class SAFarFieldBC : public ViscousBoundaryIntegrator> { public: - /// Constructs an integrator for a no-slip, adiabatic boundary flux + /// Constructs an integrator for a SA far-field boundary /// \param[in] diff_stack - for algorithmic differentiation /// \param[in] fe_coll - used to determine the face elements /// \param[in] Re_num - Reynolds number /// \param[in] Pr_num - Prandtl number - /// \param[in] q_ref - a reference state (needed by penalty) + /// \param[in] q_far - state at the far-field /// \param[in] vis - viscosity (if negative use Sutherland's law) /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) - NoSlipAdiabaticWallBC(adept::Stack &diff_stack, - const mfem::FiniteElementCollection *fe_coll, - double Re_num, double Pr_num, - const mfem::Vector &q_ref, double vis = -1.0, - double a = 1.0) - : ViscousBoundaryIntegrator>( - diff_stack, fe_coll, dim + 2, a), - Re(Re_num), Pr(Pr_num), - qfs(q_ref), mu(vis), work_vec(dim + 2) {} + SAFarFieldBC(adept::Stack &diff_stack, + const mfem::FiniteElementCollection *fe_coll, + const mfem::Vector &q_far, double vis = -1.0, + double a = 1.0) + : ViscousBoundaryIntegrator>( + diff_stack, fe_coll, dim + 3, a), + qfs(q_far), mu(vis), work_vec(dim + 2) {} /// converts conservative variables to entropy variables /// \param[in] q - conservative variables that are to be converted @@ -203,6 +201,7 @@ class NoSlipAdiabaticWallBC : public ViscousBoundaryIntegrator(q.GetData(), w.GetData()); + w(dim+2) = q(dim+2); } /// Compute the Jacobian of the mapping `convert` w.r.t. `u` @@ -210,10 +209,12 @@ class NoSlipAdiabaticWallBC : public ViscousBoundaryIntegrator(q, this->stack, dwdu); + dwdu(dim+2,dim+2) = 1.0; } - /// Compute entropy-stable, no-slip, adiabatic-wall boundary flux + /// Compute flux corresponding to a SA inflow boundary /// \param[in] x - coordinate location at which flux is evaluated (not used) /// \param[in] dir - vector normal to the boundary at `x` /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) @@ -224,28 +225,31 @@ class NoSlipAdiabaticWallBC : public ViscousBoundaryIntegrator &flux_jac); private: @@ -255,35 +259,37 @@ class NoSlipAdiabaticWallBC : public ViscousBoundaryIntegrator -class ViscousSlipWallBC : public ViscousBoundaryIntegrator> +class SAInflowBC : public ViscousBoundaryIntegrator> { public: - /// Constructs an integrator for a viscous inflow boundary + /// Constructs an integrator for a SA inflow boundary /// \param[in] diff_stack - for algorithmic differentiation /// \param[in] fe_coll - used to determine the face elements /// \param[in] Re_num - Reynolds number /// \param[in] Pr_num - Prandtl number + /// \param[in] q_inflow - state at the inflow /// \param[in] vis - viscosity (if negative use Sutherland's law) /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) - ViscousSlipWallBC(adept::Stack &diff_stack, - const mfem::FiniteElementCollection *fe_coll, - double Re_num, double Pr_num, double vis = -1.0, - double a = 1.0) - : ViscousBoundaryIntegrator>( + SAInflowBC(adept::Stack &diff_stack, + const mfem::FiniteElementCollection *fe_coll, + double Re_num, double Pr_num, + const mfem::Vector &q_inflow, double vis = -1.0, + double a = 1.0) + : ViscousBoundaryIntegrator>( diff_stack, fe_coll, dim + 2, a), - Re(Re_num), Pr(Pr_num), mu(vis), work_vec(dim + 2) {} + Re(Re_num), Pr(Pr_num), + q_in(q_inflow), mu(vis), work_vec(dim + 2) {} /// converts conservative variables to entropy variables /// \param[in] q - conservative variables that are to be converted @@ -301,7 +307,8 @@ class ViscousSlipWallBC : public ViscousBoundaryIntegrator(q, this->stack, dwdu); } - /// Compute flux corresponding to a viscous inflow boundary + + /// Compute flux corresponding to a SA inflow boundary /// \param[in] x - coordinate location at which flux is evaluated (not used) /// \param[in] dir - vector normal to the boundary at `x` /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) @@ -312,7 +319,7 @@ class ViscousSlipWallBC : public ViscousBoundaryIntegrator &flux_jac); + vector &flux_jac); private: /// Reynolds number @@ -345,34 +352,36 @@ class ViscousSlipWallBC : public ViscousBoundaryIntegrator -class ViscousFarFieldBC : public ViscousBoundaryIntegrator> +class SAOutflowBC : public ViscousBoundaryIntegrator> { public: - /// Constructs an integrator for a viscous far-field boundary + /// Constructs an integrator for a SA outflow boundary /// \param[in] diff_stack - for algorithmic differentiation /// \param[in] fe_coll - used to determine the face elements /// \param[in] Re_num - Reynolds number /// \param[in] Pr_num - Prandtl number - /// \param[in] q_far - state at the far-field + /// \param[in] q_outflow - state at the outflow /// \param[in] vis - viscosity (if negative use Sutherland's law) /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) - ViscousFarFieldBC(adept::Stack &diff_stack, - const mfem::FiniteElementCollection *fe_coll, - double Re_num, double Pr_num, - const mfem::Vector &q_far, double vis = -1.0, - double a = 1.0) - : ViscousBoundaryIntegrator>( + SAOutflowBC(adept::Stack &diff_stack, + const mfem::FiniteElementCollection *fe_coll, + double Re_num, double Pr_num, + const mfem::Vector &q_outflow, double vis = -1.0, + double a = 1.0) + : ViscousBoundaryIntegrator>( diff_stack, fe_coll, dim + 2, a), Re(Re_num), Pr(Pr_num), - qfs(q_far), mu(vis), work_vec(dim + 2) {} + q_out(q_outflow), mu(vis), work_vec(dim + 2) {} /// converts conservative variables to entropy variables /// \param[in] q - conservative variables that are to be converted @@ -391,7 +400,7 @@ class ViscousFarFieldBC : public ViscousBoundaryIntegrator(q, this->stack, dwdu); } - /// Compute flux corresponding to a viscous inflow boundary + /// Compute flux corresponding to a SA inflow boundary /// \param[in] x - coordinate location at which flux is evaluated (not used) /// \param[in] dir - vector normal to the boundary at `x` /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) @@ -400,13 +409,9 @@ class ViscousFarFieldBC : public ViscousBoundaryIntegrator(dir.GetData(), qfs.GetData(), q.GetData(), - work_vec.GetData(), flux_vec.GetData()); - } + mfem::Vector &flux_vec); - /// Compute jacobian of flux corresponding to a viscous far-field boundary + /// Compute jacobian of flux corresponding to a SA inflow boundary /// w.r.t `states` /// \param[in] x - coordinate location at which flux is evaluated /// \param[in] dir - vector normal to the boundary at `x` @@ -419,11 +424,10 @@ class ViscousFarFieldBC : public ViscousBoundaryIntegrator::AssembleElementVector( grad.GetRow(i, grad_i); // get distance function value at node - double d; + double d = 1.0; // accumulate source terms double src = calcSASource( @@ -80,11 +80,64 @@ void SASourceIntegrator::AssembleElementGrad( const SBPFiniteElement &sbp = dynamic_cast(fe); int num_nodes = sbp.GetDof(); #ifdef MFEM_THREAD_SAFE - Vector ui, xi, wj, uj; - DenseMatrix adjJ_i, adjJ_j, adjJ_k, Dwi, jac_term1, jac_term2, dwduj; - vector CDw_jac(dim); + Vector ui, xi, uj, grad_i, curl_i; + DenseMatrix grad, curl; #endif + ui.SetSize(num_states); + xi.SetSize(dim); + grad.SetSize(num_nodes, dim); + curl.SetSize(num_nodes, 3); + grad_i.SetSize(dim); + curl_i.SetSize(3); + DenseMatrix u(elfun.GetData(), num_nodes, num_states); + + //precompute vorticity and derivatives + calcVorticitySBP(u, sbp, Trans, curl); + + //precompute gradient and derivatives + calcGradSBP(u, sbp, Trans, grad); + + elmat = 0.0; + for (int i = 0; i < num_nodes; ++i) + { + // get the Jacobian (Trans.Weight) and cubature weight (node.weight) + IntegrationPoint &node = fe.GetNodes().IntPoint(i); + Trans.SetIntPoint(&node); + u.GetRow(i, ui); + Trans.Transform(node, xi); + + // compute vorticity at node and take magnitude + curl.GetRow(i, curl_i); + double S = curl_i.Norml2(); + + // compute gradient of turbulent viscosity at node + grad.GetRow(i, grad_i); + + // get distance function value at node + double d = 1.0; + + // accumulate source terms + double src = calcSASource( + ui.GetData(), grad_i.GetData(), sacs.GetData()); + double P; double D; + // use negative model if turbulent viscosity is negative + if (ui(dim+2) < 0) + { + P = calcSANegativeProduction( + ui.GetData(), S, sacs.GetData()); + D = calcSANegativeDestruction( + ui.GetData(), d, sacs.GetData()); + } + else + { + P = calcSAProduction( + ui.GetData(), mu, d, S, sacs.GetData()); + D = calcSADestruction( + ui.GetData(), mu, d, S, sacs.GetData()); + } + //res(i, dim+2) += alpha * Trans.Weight() * node.weight * src; + } // loop over element nodes i } template @@ -203,7 +256,113 @@ void SAInviscidIntegrator::calcFluxJacStates( int di, const mfem::Vector &qL, const mfem::Vector &qR, mfem::DenseMatrix &jacL, mfem::DenseMatrix &jacR) { + // store the full jacobian in jac + mfem::DenseMatrix jac(dim + 3, 2 * (dim + 3)); + // vector of active input variables + std::vector qL_a(qL.Size()); + std::vector qR_a(qR.Size()); + // initialize adouble inputs + adept::set_values(qL_a.data(), qL.Size(), qL.GetData()); + adept::set_values(qR_a.data(), qR.Size(), qR.GetData()); + // start recording + this->stack.new_recording(); + // create vector of active output variables + std::vector flux_a(qL.Size()); + // run algorithm + if (entvar) + { + mach::calcIsmailRoeFluxUsingEntVars(di, qL_a.data(), + qR_a.data(), + flux_a.data()); + } + else + { + mach::calcIsmailRoeFlux(di, qL_a.data(), + qR_a.data(), flux_a.data()); + } + // identify independent and dependent variables + this->stack.independent(qL_a.data(), qL.Size()); + this->stack.independent(qR_a.data(), qR.Size()); + this->stack.dependent(flux_a.data(), qL.Size()); + // compute and store jacobian in jac + this->stack.jacobian_reverse(jac.GetData()); + // retrieve the jacobian w.r.t left state + jacL.CopyCols(jac, 0, dim + 2); + // retrieve the jacobian w.r.t right state + jacR.CopyCols(jac, dim + 3, 2 * (dim + 3) - 1); + // add SA variable contributions + jacL(dim+2,0) = -0.25*(qL(di+1)/(qL(0)*qL(0)))*(qL(dim+2) + qR(dim+2)); + jacR(dim+2,0) = -0.25*(qR(di+1)/(qR(0)*qR(0)))*(qL(dim+2) + qR(dim+2)); + jacL(dim+2,di+1) = 0.25*(1.0/qL(0))*(qL(dim+2) + qR(dim+2)); + jacR(dim+2,di+1) = 0.25*(1.0/qR(0))*(qL(dim+2) + qR(dim+2)); + jacL(dim+2,dim+2) = 0.25*(qL(di+1)/qL(0) + qR(di+1)/qR(0)); + jacR(dim+2,dim+2) = 0.25*(qL(di+1)/qL(0) + qR(di+1)/qR(0)); } +//==================================================================================== +//SA Boundary Integrator Methods + +//==================================================================================== +//SA Far Field Integrator Methods +template +void SAFarFieldBC::calcFlux(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::Vector &flux_vec) +{ + calcBoundaryFlux(dir.GetData(), qfs.GetData(), q.GetData(), + work_vec.GetData(), flux_vec.GetData()); + //flux_vec(dim+2) = 0; + // handle SA variable + double Unrm = dot(dir.GetData(), q.GetData()+1); + if (Unrm > 0.0) + { + flux_vec(dim+2) = Unrm*q(dim+2)/q(0); + } + else + { + flux_vec(dim+2) = Unrm*qfs(dim+2)/qfs(0); + } +} + +template +void SAFarFieldBC::calcFluxJacState( + const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::DenseMatrix &flux_jac) +{ + flux_jac = 0.0; + mach::calcFluxJacState(x, dir, jac, q, Dw, qfs, work_vec, this->stack, flux_jac); + // handle SA variable + double Unrm = dot(dir.GetData(), q.GetData()+1); + if (Unrm > 0.0) + { + flux_jac(dim+2, 0) = -Unrm*q(dim+2)/(q(0)*q(0)); + for(int di = 0; di < dim; di++) + { + flux_jac(dim+2, di+1) = dir(di)*q(dim+2)/q(0); + } + flux_jac(dim+2, dim+2) = Unrm/q(0); + } + else + { + for(int di = 0; di < dim; di++) + { + flux_jac(dim+2, di+1) = dir(di)*qfs(dim+2)/qfs(0); + } + } +} + +template +void SAFarFieldBC::calcFluxJacDw( + const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + vector &flux_jac) +{ + // Presently, this BC has no dependence on the derivative + for (int i = 0; i < dim; ++i) + { + flux_jac[i] = 0.0; + } +} \ No newline at end of file diff --git a/test/unit/test_rans_integ.cpp b/test/unit/test_rans_integ.cpp index 51b70062..f39488eb 100644 --- a/test/unit/test_rans_integ.cpp +++ b/test/unit/test_rans_integ.cpp @@ -7,29 +7,204 @@ #include "rans_integ.hpp" #include "euler_test_data.hpp" -TEMPLATE_TEST_CASE_SIG("SA Inviscid Test", "[sa_inviscid_test]", +TEMPLATE_TEST_CASE_SIG("SA Inviscid Flux Test", "[sa_inviscid_flux_test]", ((int dim), dim), 1, 2, 3) { - using namespace euler_data; - mfem::Vector qL(dim + 3); - mfem::Vector qR(dim + 3); - mfem::Vector flux1(dim + 2); - mfem::Vector flux2(dim + 3); - mfem::Vector nrm(dim); - qL(0) = rho; qR(0) = rho*1.1; - qL(dim + 1) = rhoe; qR(dim + 1) = rhoe*1.1; - for (int di = 0; di < dim; ++di) - { - qL(di + 1) = rhou[di]; - qR(di + 1) = rhou[di]*1.1; - nrm(di) = dir[di]; - } - qL(dim + 2) = 5.0; qR(dim + 2) = 5.5; - adept::Stack stack; - mach::IsmailRoeIntegrator irinteg(stack); - mach::SAInviscidIntegrator sainteg(stack); - - SECTION("Check if SA integrator matches the output for the conservative variables") + using namespace euler_data; + mfem::Vector qL(dim + 3); + mfem::Vector qR(dim + 3); + mfem::Vector flux1(dim + 2); + mfem::Vector flux2(dim + 3); + mfem::Vector nrm(dim); + qL(0) = rho; qR(0) = rho*1.1; + qL(dim + 1) = rhoe; qR(dim + 1) = rhoe*1.1; + for (int di = 0; di < dim; ++di) + { + qL(di + 1) = rhou[di]; + qR(di + 1) = rhou[di]*1.1; + nrm(di) = dir[di]; + } + qL(dim + 2) = 5.0; qR(dim + 2) = 5.5; + adept::Stack stack; + mach::IsmailRoeIntegrator irinteg(stack); + mach::SAInviscidIntegrator sainteg(stack); + + SECTION("Check if SA integrator flux function matches the output for the conservative variables") + { + // calculate the flux + irinteg.calcFlux(0, qL, qR, flux1); + sainteg.calcFlux(0, qL, qR, flux2); + + // check if euler variables are the same as before + for(int n = 0; n < dim+2; n++) + { + std::cout << "Ismail-Roe " << n << " Flux: " << flux1(n) << std::endl; + std::cout << "Spalart-Allmaras " << n << " Flux: " << flux2(n) << std::endl; + REQUIRE(flux1(n) - flux2(n) == Approx(0.0)); + } + } +} + +TEMPLATE_TEST_CASE_SIG("SAInviscid Jacobian", "[SAInviscid]", + ((int dim), dim), 1, 2, 3) +{ + using namespace euler_data; + // copy the data into mfem vectors for convenience + mfem::Vector qL(dim + 3); + mfem::Vector qR(dim + 3); + mfem::Vector flux(dim + 3); + mfem::Vector flux_plus(dim + 3); + mfem::Vector flux_minus(dim + 3); + mfem::Vector v(dim + 3); + mfem::Vector jac_v(dim + 3); + mfem::DenseMatrix jacL(dim + 3, 2 * (dim + 3)); + mfem::DenseMatrix jacR(dim + 3, 2 * (dim + 3)); + double delta = 1e-5; + qL(0) = rho; + qL(dim + 1) = rhoe; + qL(dim + 2) = 4; + qR(0) = rho2; + qR(dim + 1) = rhoe2; + qR(dim + 2) = 4.5; + for (int di = 0; di < dim; ++di) + { + qL(di + 1) = rhou[di]; + qR(di + 1) = rhou2[di]; + } + // create perturbation vector + for (int di = 0; di < dim + 3; ++di) + { + v(di) = vec_pert[di]; + } + // perturbed vectors + mfem::Vector qL_plus(qL), qL_minus(qL); + mfem::Vector qR_plus(qR), qR_minus(qR); + adept::Stack diff_stack; + mach::SAInviscidIntegrator sainvinteg(diff_stack); + // +ve perturbation + qL_plus.Add(delta, v); + qR_plus.Add(delta, v); + // -ve perturbation + qL_minus.Add(-delta, v); + qR_minus.Add(-delta, v); + for (int di = 0; di < dim; ++di) + { + DYNAMIC_SECTION("Ismail-Roe flux jacsainvintegian is correct w.r.t left state ") + { + // get perturbed states flux vector + sainvinteg.calcFlux(di, qL_plus, qR, flux_plus); + sainvinteg.calcFlux(di, qL_minus, qR, flux_minus); + // compute the jacobian + sainvinteg.calcFluxJacStates(di, qL, qR, jacL, jacR); + jacL.Mult(v, jac_v); + // finite difference jacobian + mfem::Vector jac_v_fd(flux_plus); + jac_v_fd -= flux_minus; + jac_v_fd /= 2.0 * delta; + // compare each component of the matrix-vector products + for (int i = 0; i < dim + 2; ++i) + { + REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); + } + } + } +} + +TEMPLATE_TEST_CASE_SIG("SAFarFieldBC Jacobian", "[SAFarFieldBC]", + ((int dim), dim), 1, 2, 3) +{ + using namespace euler_data; + // copy the data into mfem vectors for convenience + mfem::Vector nrm(dim); mfem::Vector x(dim); + mfem::Vector q(dim + 3); + mfem::Vector qfar(dim + 3); + mfem::Vector flux(dim + 3); + mfem::Vector v(dim + 3); + mfem::Vector jac_v(dim + 3); + mfem::DenseMatrix Dw; + mfem::DenseMatrix jac(dim + 3, dim + 3); + double delta = 1e-5; + + x = 0.0; + q(0) = rho; + q(dim + 1) = rhoe; + q(dim + 2) = 4; + for (int di = 0; di < dim; ++di) + { + q(di + 1) = rhou[di]; + } + qfar.Set(1.1, q); + // create direction vector + for (int di = 0; di < dim; ++di) + { + nrm(di) = dir[di]; + } + // create perturbation vector + for (int di = 0; di < dim + 3; ++di) + { + v(di) = vec_pert[di]; + } + // perturbed vectors + mfem::Vector q_plus(q), q_minus(q); + mfem::Vector flux_plus(q), flux_minus(q); + adept::Stack diff_stack; + mfem::H1_FECollection fe_coll(1); //dummy + mach::SAFarFieldBC safarfieldinteg(diff_stack, &fe_coll, qfar, 1.0); + // +ve perturbation + q_plus.Add(delta, v); + // -ve perturbation + q_minus.Add(-delta, v); + for (int di = 0; di < 2; di++) //reverse direction to check both inflow and outflow + { + DYNAMIC_SECTION("SA Far-Field BC safarfieldinteg jacobian is correct w.r.t state ") + { + nrm *= -1.0; + // get perturbed states flux vector + safarfieldinteg.calcFlux(x, nrm, 1.0, q_plus, Dw, flux_plus); + safarfieldinteg.calcFlux(x, nrm, 1.0, q_minus, Dw, flux_minus); + // compute the jacobian + safarfieldinteg.calcFluxJacState(x, nrm, 1.0, q, Dw, jac); + jac.Mult(v, jac_v); + // finite difference jacobian + mfem::Vector jac_v_fd(flux_plus); + jac_v_fd -= flux_minus; + jac_v_fd /= 2.0 * delta; + // compare each component of the matrix-vector products + for (int i = 0; i < dim + 3; ++i) + { + std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; + REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); + } + } + } +} + + +#if 0 +TEMPLATE_TEST_CASE_SIG("SA Inviscid Integrator Test", "[sa_inviscid_integ_test]", + ((int dim), dim), 1, 2, 3) +{ + using namespace euler_data; + mfem::Vector qL(dim + 3); + mfem::Vector qR(dim + 3); + mfem::Vector flux1(dim + 2); + mfem::Vector flux2(dim + 3); + mfem::Vector nrm(dim); + qL(0) = rho; qR(0) = rho*1.1; + qL(dim + 1) = rhoe; qR(dim + 1) = rhoe*1.1; + for (int di = 0; di < dim; ++di) + { + qL(di + 1) = rhou[di]; + qR(di + 1) = rhou[di]*1.1; + nrm(di) = dir[di]; + } + qL(dim + 2) = 5.0; qR(dim + 2) = 5.5; + adept::Stack stack; + mach::IsmailRoeIntegrator irinteg(stack); + mach::SAInviscidIntegrator sainteg(stack); + + SECTION("Check if SA integrator flux function matches the output for the conservative variables") { // calculate the flux irinteg.calcFlux(0, qL, qR, flux1); @@ -43,4 +218,5 @@ TEMPLATE_TEST_CASE_SIG("SA Inviscid Test", "[sa_inviscid_test]", REQUIRE(flux1(n) - flux2(n) == Approx(0.0)); } } -} \ No newline at end of file +} +#endif \ No newline at end of file From cf5461282cfd62d3f29595d145102d01db073777 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Fri, 24 Jul 2020 18:29:10 -0400 Subject: [PATCH 05/28] jacobian seems fine, but PTC results in nan residual --- sandbox/rans_freestream.cpp | 34 ++++++++++++++++++++++------ sandbox/rans_freestream_options.json | 10 +++++++- src/physics/fluidflow/euler.cpp | 2 +- src/physics/fluidflow/rans.cpp | 1 + 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/sandbox/rans_freestream.cpp b/sandbox/rans_freestream.cpp index 3181597d..68f18908 100644 --- a/sandbox/rans_freestream.cpp +++ b/sandbox/rans_freestream.cpp @@ -33,6 +33,11 @@ void pert(const Vector &x, Vector& p); /// \param[out] u - conservative + SA variables stored as a 5-vector void uexact(const Vector &x, Vector& u); +/// \brief Defines a perturbed solution for the rans freestream problem +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void uinit_pert(const Vector &x, Vector& u); + int main(int argc, char *argv[]) { const char *options_file = "rans_freestream_options.json"; @@ -82,25 +87,25 @@ int main(int argc, char *argv[]) // construct the solver and set initial conditions auto solver = createSolver>(opt_file_name, move(smesh)); - solver->setInitialCondition(uexact); + solver->setInitialCondition(uinit_pert); solver->printSolution("rans_init", 0); // get the initial density error - //double l2_error = (static_cast&>(*solver) - // .calcConservativeVarsL2Error(uexact, 0)); + double l2_error_init = (static_cast&>(*solver) + .calcConservativeVarsL2Error(uexact, 0)); double res_error = solver->calcResidualNorm(); - //*out << "\n|| rho_h - rho ||_{L^2} = " << l2_error; *out << "\ninitial residual norm = " << res_error << endl; solver->checkJacobian(pert); solver->solveForState(); solver->printSolution("rans_final",0); // get the final density error - //l2_error = (static_cast&>(*solver) - // .calcConservativeVarsL2Error(uexact, 0)); + double l2_error_final = (static_cast&>(*solver) + .calcConservativeVarsL2Error(uexact, 0)); res_error = solver->calcResidualNorm(); *out << "\nfinal residual norm = " << res_error; - //*out << "\n|| rho_h - rho ||_{L^2} = " << l2_error << endl; + *out << "\n|| rho_h - rho ||_{L^2} init = " << l2_error_init << endl; + *out << "\n|| rho_h - rho ||_{L^2} final = " << l2_error_final << endl; } catch (MachException &exception) { @@ -151,3 +156,18 @@ void uexact(const Vector &x, Vector& q) } } +// initial guess perturbed from exact +void uinit_pert(const Vector &x, Vector& q) +{ + q.SetSize(5); + Vector u(5); + + u = 0.0; + u(0) = 1.1*1.0; + u(1) = 1.1*u(0)*mach_fs*cos(aoa_fs); + u(2) = 1.1*u(0)*mach_fs*sin(aoa_fs); + u(3) = 1.1*1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; + u(4) = 1.1*chi_fs*(mu/u(0)); + + q = u; +} diff --git a/sandbox/rans_freestream_options.json b/sandbox/rans_freestream_options.json index 158c15de..bdcb3251 100644 --- a/sandbox/rans_freestream_options.json +++ b/sandbox/rans_freestream_options.json @@ -26,11 +26,19 @@ "cfl": 1.0 }, "newtonsolver":{ - "printlevel": 1, + "printlevel": 0, "maxiter": 50, "reltol": 1e-1, "abstol": 1e-12 }, + "lin-solver": { + "type": "hypregmres", + "pctype": "hypreboomeramg", + "reltol": 1e-10, + "abstol": 0.0, + "printlevel": 0, + "maxiter": 50 + }, "petscsolver":{ "ksptype": "gmres", "pctype": "lu", diff --git a/src/physics/fluidflow/euler.cpp b/src/physics/fluidflow/euler.cpp index 4aa2ada2..52974388 100644 --- a/src/physics/fluidflow/euler.cpp +++ b/src/physics/fluidflow/euler.cpp @@ -383,7 +383,7 @@ double EulerSolver::calcConservativeVarsL2Error( double err = 0.0; if (entry < 0) { - for (int i = 0; i < dim+2; ++i) + for (int i = 0; i < num_state; ++i) { double dq = qdiscrete(i) - qexact(i); err += dq*dq; diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index 33f9437b..c1d012f9 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -24,6 +24,7 @@ RANavierStokesSolver::RANavierStokesSolver(const string &opt_file_n mu = this->options["flow-param"]["mu"].template get(); vector sa = this->options["flow-param"]["sa-consts"].template get>(); sacs.SetData(sa.data()); + } template From c42e57761b90783fbcde4b876ed994197ffe6f55 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Tue, 28 Jul 2020 20:14:42 -0400 Subject: [PATCH 06/28] Inviscid, LPS, and freestream BC integrators verified, but freestream problem doesn't converge even with just the EulerSolver --- sandbox/rans_freestream.cpp | 49 +++-- sandbox/rans_freestream_options.json | 9 +- src/physics/fluidflow/rans.cpp | 35 +++- src/physics/fluidflow/rans.hpp | 2 + src/physics/fluidflow/rans_integ.hpp | 67 +++++- src/physics/fluidflow/rans_integ_def.hpp | 243 +++++++++++++++++++++- test/unit/test_rans_integ.cpp | 250 ++++++++++++++++++++--- 7 files changed, 585 insertions(+), 70 deletions(-) diff --git a/sandbox/rans_freestream.cpp b/sandbox/rans_freestream.cpp index 68f18908..c0b40e59 100644 --- a/sandbox/rans_freestream.cpp +++ b/sandbox/rans_freestream.cpp @@ -16,6 +16,7 @@ using namespace mach; std::default_random_engine gen(std::random_device{}()); std::uniform_real_distribution normal_rand(-1.0,1.0); +static double pert_fs; static double mu; static double mach_fs; static double aoa_fs; @@ -52,8 +53,8 @@ int main(int argc, char *argv[]) // Parse command-line options OptionsParser args(argc, argv); int degree = 2.0; - int nx = 1; - int ny = 1; + int nx = 4; + int ny = 4; args.AddOption(&nx, "-nx", "--numx", "Number of elements in x direction"); args.AddOption(&ny, "-ny", "--numy", @@ -72,6 +73,7 @@ int main(int argc, char *argv[]) nlohmann::json file_options; std::ifstream opts(opt_file_name); opts >> file_options; + pert_fs = 1.0 + file_options["init-pert"].template get(); mu = file_options["flow-param"]["mu"].template get(); mach_fs = file_options["flow-param"]["mach"].template get(); aoa_fs = file_options["flow-param"]["aoa"].template get()*M_PI/180; @@ -85,26 +87,27 @@ int main(int argc, char *argv[]) *out << "Number of elements " << smesh->GetNE() <<'\n'; // construct the solver and set initial conditions - auto solver = createSolver>(opt_file_name, + auto solver = createSolver>(opt_file_name, move(smesh)); solver->setInitialCondition(uinit_pert); solver->printSolution("rans_init", 0); // get the initial density error - double l2_error_init = (static_cast&>(*solver) + double l2_error_init = (static_cast&>(*solver) .calcConservativeVarsL2Error(uexact, 0)); + *out << "\n|| rho_h - rho ||_{L^2} init = " << l2_error_init << endl; + double res_error = solver->calcResidualNorm(); *out << "\ninitial residual norm = " << res_error << endl; solver->checkJacobian(pert); solver->solveForState(); solver->printSolution("rans_final",0); // get the final density error - double l2_error_final = (static_cast&>(*solver) + double l2_error_final = (static_cast&>(*solver) .calcConservativeVarsL2Error(uexact, 0)); res_error = solver->calcResidualNorm(); *out << "\nfinal residual norm = " << res_error; - *out << "\n|| rho_h - rho ||_{L^2} init = " << l2_error_init << endl; *out << "\n|| rho_h - rho ||_{L^2} final = " << l2_error_final << endl; } catch (MachException &exception) @@ -126,8 +129,14 @@ int main(int argc, char *argv[]) // perturbation function used to check the jacobian in each iteration void pert(const Vector &x, Vector& p) { - p.SetSize(5); - for (int i = 0; i < 5; i++) + // p.SetSize(5); + // for (int i = 0; i < 5; i++) + // { + // p(i) = normal_rand(gen); + // } + + p.SetSize(4); + for (int i = 0; i < 4; i++) { p(i) = normal_rand(gen); } @@ -136,15 +145,17 @@ void pert(const Vector &x, Vector& p) // Exact solution; same as freestream bc void uexact(const Vector &x, Vector& q) { - q.SetSize(5); - Vector u(5); + q.SetSize(4); + Vector u(4); + // q.SetSize(5); + // Vector u(5); u = 0.0; u(0) = 1.0; u(1) = u(0)*mach_fs*cos(aoa_fs); u(2) = u(0)*mach_fs*sin(aoa_fs); u(3) = 1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; - u(4) = chi_fs*(mu/u(0)); + //u(4) = chi_fs*mu/u(0); if (entvar == false) { @@ -159,15 +170,17 @@ void uexact(const Vector &x, Vector& q) // initial guess perturbed from exact void uinit_pert(const Vector &x, Vector& q) { - q.SetSize(5); - Vector u(5); + q.SetSize(4); + Vector u(4); + // q.SetSize(5); + // Vector u(5); u = 0.0; - u(0) = 1.1*1.0; - u(1) = 1.1*u(0)*mach_fs*cos(aoa_fs); - u(2) = 1.1*u(0)*mach_fs*sin(aoa_fs); - u(3) = 1.1*1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; - u(4) = 1.1*chi_fs*(mu/u(0)); + u(0) = pert_fs*1.0; + u(1) = u(0)*mach_fs*cos(aoa_fs); + u(2) = u(0)*mach_fs*sin(aoa_fs); + u(3) = pert_fs*1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; +// u(4) = pert_fs*pert_fs*chi_fs*mu/u(0); q = u; } diff --git a/sandbox/rans_freestream_options.json b/sandbox/rans_freestream_options.json index bdcb3251..cd0410d6 100644 --- a/sandbox/rans_freestream_options.json +++ b/sandbox/rans_freestream_options.json @@ -1,7 +1,7 @@ { "flow-param": { - "mach": 2.5, - "aoa": 0.0, + "mach": 1.0, + "aoa": 0.3, "roll-axis": 0, "pitch-axis": 1, "chi": 3, @@ -25,7 +25,7 @@ "dt": 0.1, "cfl": 1.0 }, - "newtonsolver":{ + "newton":{ "printlevel": 0, "maxiter": 50, "reltol": 1e-1, @@ -54,5 +54,6 @@ }, "bcs": { "far-field": [1, 1, 1, 1] - } + }, + "init-pert": 1e-6 } diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index c1d012f9..8d241703 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -1,4 +1,5 @@ #include +#include #include "rans.hpp" #include "rans_integ.hpp" @@ -40,9 +41,9 @@ void RANavierStokesSolver::addResVolumeIntegrators(double alpha) // this->res->AddDomainIntegrator(new SASourceIntegrator( // this->diff_stack, sacs, mu, alpha)); // add LPS stabilization - // double lps_coeff = this->options["space-dis"]["lps-coeff"].template get(); - // this->res->AddDomainIntegrator(new EntStableLPSIntegrator( - // this->diff_stack, alpha, lps_coeff)); + double lps_coeff = this->options["space-dis"]["lps-coeff"].template get(); + this->res->AddDomainIntegrator(new SALPSIntegrator( + this->diff_stack, alpha, lps_coeff)); } @@ -122,8 +123,8 @@ void RANavierStokesSolver::addResBoundaryIntegrators(double alpha) this->bndry_marker[idx].SetSize(tmp.size(), 0); this->bndry_marker[idx].Assign(tmp.data()); this->res->AddBdrFaceIntegrator( - new SAFarFieldBC(this->diff_stack, this->fec.get(), qfar, - alpha), + new SAFarFieldBC(this->diff_stack, this->fec.get(), + this->re_fs, this->pr_fs, qfar, alpha), this->bndry_marker[idx]); idx++; } @@ -185,7 +186,29 @@ void RANavierStokesSolver::getFreeStreamState(mfem::Vector &q_ref) q_ref(this->ipitch+1) = q_ref(0)*this->mach_fs*sin(this->aoa_fs); } q_ref(dim+1) = 1/(euler::gamma*euler::gami) + 0.5*this->mach_fs*this->mach_fs; - q_ref(dim+2) = this->chi_fs*(mu/q_ref(0)); + q_ref(dim+2) = this->chi_fs*mu/q_ref(0); +} + +static void pert(const Vector &x, Vector& p); + +template +void RANavierStokesSolver::iterationHook(int iter, + double t, double dt) +{ + this->checkJacobian(pert); +} + +std::default_random_engine gen(std::random_device{}()); +std::uniform_real_distribution normal_rand(-1.0,1.0); + +// perturbation function used to check the jacobian in each iteration +void pert(const Vector &x, Vector& p) +{ + p.SetSize(5); + for (int i = 0; i < 5; i++) + { + p(i) = normal_rand(gen); + } } // explicit instantiation diff --git a/src/physics/fluidflow/rans.hpp b/src/physics/fluidflow/rans.hpp index 86b1fdc3..077f021e 100644 --- a/src/physics/fluidflow/rans.hpp +++ b/src/physics/fluidflow/rans.hpp @@ -53,6 +53,8 @@ class RANavierStokesSolver : public NavierStokesSolver /// Return the number of state variables virtual int getNumState() override {return dim+3; } + virtual void iterationHook(int iter, double t, double dt) override; + friend SolverPtr createSolver>( const std::string &opt_file_name, std::unique_ptr smesh); diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp index b3966f06..56c63f0e 100644 --- a/src/physics/fluidflow/rans_integ.hpp +++ b/src/physics/fluidflow/rans_integ.hpp @@ -188,11 +188,12 @@ class SAFarFieldBC : public ViscousBoundaryIntegrator> /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) SAFarFieldBC(adept::Stack &diff_stack, const mfem::FiniteElementCollection *fe_coll, + double Re_num, double Pr_num, const mfem::Vector &q_far, double vis = -1.0, double a = 1.0) : ViscousBoundaryIntegrator>( diff_stack, fe_coll, dim + 3, a), - qfs(q_far), mu(vis), work_vec(dim + 2) {} + Re(Re_num), Pr(Pr_num), qfs(q_far), mu(vis), work_vec(dim + 2) {} /// converts conservative variables to entropy variables /// \param[in] q - conservative variables that are to be converted @@ -449,6 +450,70 @@ class SAOutflowBC : public ViscousBoundaryIntegrator> mfem::Vector work_vec; }; +/// Integrator for local-projection stabilization with SA variable +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \tparam entvar - if true, the state variables are the entropy variables +/// \note This derived class uses the CRTP +template +class SALPSIntegrator : public LPSIntegrator< + SALPSIntegrator> +{ +public: + /// Construct an LPS integrator with the SA variable + /// \param[in] diff_stack - for algorithmic differentiation + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + /// \param[in] coeff - the LPS coefficient + SALPSIntegrator(adept::Stack &diff_stack, double a = 1.0, + double coeff = 1.0) + : LPSIntegrator>( + diff_stack, dim + 3, a, coeff) {} + + /// converts state variables to entropy variables, if necessary + /// \param[in] q - state variables that are to be converted + /// \param[out] w - entropy variables corresponding to `q` + /// \note a wrapper for the relevant function in `euler_fluxes.hpp` + void convertVars(const mfem::Vector &q, mfem::Vector &w); + + /// Compute the Jacobian of the mapping `convert` w.r.t. `u` + /// \param[in] q - conservative variables that are to be converted + /// \param[out] dwdu - Jacobian of entropy variables w.r.t. `u` + void convertVarsJacState(const mfem::Vector &q, mfem::DenseMatrix &dwdu); + + /// Applies the matrix `dQ/dW` to `vec`, and scales by the avg. spectral radius + /// \param[in] adjJ - the adjugate of the mapping Jacobian + /// \param[in] q - the state at which `dQ/dW` and radius are to be evaluated + /// \param[in] vec - the vector being multiplied + /// \param[out] mat_vec - the result of the operation + /// \warning adjJ must be supplied transposed from its `mfem` storage format, + /// so we can use pointer arithmetic to access its rows. + /// \note a wrapper for the relevant function in `euler_fluxes.hpp` + void applyScaling(const mfem::DenseMatrix &adjJ, const mfem::Vector &q, + const mfem::Vector &vec, mfem::Vector &mat_vec); + + /// Computes the Jacobian of the product `A(adjJ,q)*v` w.r.t. `q` + /// \param[in] adjJ - adjugate of the mapping Jacobian + /// \param[in] q - state at which `dQ/dW` and radius are evaluated + /// \param[in] vec - vector that is being multiplied + /// \param[out] mat_vec_jac - Jacobian of product w.r.t. `q` + /// \warning adjJ must be supplied transposed from its `mfem` storage format, + /// so we can use pointer arithmetic to access its rows. + void applyScalingJacState(const mfem::DenseMatrix &adjJ, + const mfem::Vector &q, + const mfem::Vector &vec, + mfem::DenseMatrix &mat_vec_jac); + + /// Computes the Jacobian of the product `A(adjJ,u)*v` w.r.t. `vec` + /// \param[in] adjJ - adjugate of the mapping Jacobian + /// \param[in] q - state at which the symmetric matrix `A` is evaluated + /// \param[out] mat_vec_jac - Jacobian of product w.r.t. `vec` + /// \note `mat_vec_jac` stores derivatives treating `adjJ` is a 1d array. + /// \note The size of `mat_vec_jac` must be set before calling this function + void applyScalingJacV(const mfem::DenseMatrix &adjJ, + const mfem::Vector &q, + mfem::DenseMatrix &mat_vec_jac); + +}; + #include "rans_integ_def.hpp" } // namespace mach diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index decc2177..2d1180e4 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -312,9 +312,29 @@ void SAFarFieldBC::calcFlux(const mfem::Vector &x, const mfem::Vector &dir, { calcBoundaryFlux(dir.GetData(), qfs.GetData(), q.GetData(), work_vec.GetData(), flux_vec.GetData()); + +#if 0 + // Part 2: evaluate the adiabatic flux + double mu_Re = mu; + if (mu < 0.0) + { + mu_Re = calcSutherlandViscosity(q.GetData()); + } + mu_Re /= Re; + + for (int d = 0; d < dim; ++d) + { + work_vec = 0.0; + applyViscousScaling(d, mu_Re, Pr, q.GetData(), Dw.GetData(), + work_vec.GetData()); + work_vec *= dir[d]; + flux_vec -= work_vec; + } +#endif + //flux_vec(dim+2) = 0; // handle SA variable - double Unrm = dot(dir.GetData(), q.GetData()+1); + double Unrm = dot(dir.GetData(), qfs.GetData()+1); if (Unrm > 0.0) { flux_vec(dim+2) = Unrm*q(dim+2)/q(0); @@ -333,24 +353,73 @@ void SAFarFieldBC::calcFluxJacState( mfem::DenseMatrix &flux_jac) { flux_jac = 0.0; - mach::calcFluxJacState(x, dir, jac, q, Dw, qfs, work_vec, this->stack, flux_jac); +// mach::calcFluxJacState(x, dir, jac, q, Dw, qfs, work_vec, this->stack, flux_jac); + + int Dw_size = Dw.Height() * Dw.Width(); + // create containers for active double objects for each input + std::vector q_a(q.Size()); + std::vector dir_a(dir.Size()); + std::vector x_a(x.Size()); + std::vector Dw_a(Dw_size); + std::vector qfs_a(q.Size()); + std::vector work_vec_a(work_vec.Size()); + // initialize active double containers with data from inputs + adept::set_values(q_a.data(), q.Size(), q.GetData()); + adept::set_values(dir_a.data(), dir.Size(), dir.GetData()); + adept::set_values(x_a.data(), x.Size(), x.GetData()); + adept::set_values(Dw_a.data(), Dw_size, Dw.GetData()); + adept::set_values(qfs_a.data(), qfs.Size(), qfs.GetData()); + // start new stack recording + this->stack.new_recording(); + // create container for active double flux output + std::vector flux_a(q.Size()); + // Part 1: apply the inviscid inflow boundary condition + mach::calcBoundaryFlux(dir_a.data(), qfs_a.data(), q_a.data(), + work_vec_a.data(), flux_a.data()); +#if 0 + adouble mu_Re = mu; + if (mu < 0.0) + { + mu_Re = mach::calcSutherlandViscosity(q_a.data()); + } + mu_Re /= Re; + for (int d = 0; d < dim; ++d) + { + for (int i = 0; i < work_vec_a.size(); ++i) + { + work_vec_a[i] = 0.0; + } + applyViscousScaling(d, mu_Re, Pr, q_a.data(), Dw_a.data(), + work_vec_a.data()); + for (int i = 0; i < flux_a.size(); ++i) + { + work_vec_a[i] *= dir_a[d]; + flux_a[i] -= work_vec_a[i]; // note the minus sign!!! + } + } +#endif + + this->stack.independent(q_a.data(), q.Size()); + this->stack.dependent(flux_a.data(), q.Size()); + this->stack.jacobian(flux_jac.GetData()); + // handle SA variable - double Unrm = dot(dir.GetData(), q.GetData()+1); + double Unrm = dot(dir.GetData(), qfs.GetData()+1); if (Unrm > 0.0) { flux_jac(dim+2, 0) = -Unrm*q(dim+2)/(q(0)*q(0)); - for(int di = 0; di < dim; di++) - { - flux_jac(dim+2, di+1) = dir(di)*q(dim+2)/q(0); - } + // for(int di = 0; di < dim; di++) + // { + // flux_jac(dim+2, di+1) = dir(di)*q(dim+2)/q(0); + // } flux_jac(dim+2, dim+2) = Unrm/q(0); } else { - for(int di = 0; di < dim; di++) - { - flux_jac(dim+2, di+1) = dir(di)*qfs(dim+2)/qfs(0); - } + // for(int di = 0; di < dim; di++) + // { + // flux_jac(dim+2, di+1) = dir(di)*qfs(dim+2)/qfs(0); + // } } } @@ -365,4 +434,156 @@ void SAFarFieldBC::calcFluxJacDw( { flux_jac[i] = 0.0; } +} + +//==================================================================================== +//SA LPS Integrator Methods + +template +void SALPSIntegrator::convertVars( + const mfem::Vector &q, mfem::Vector &w) +{ + // This conditional should have no overhead, if the compiler is good + if (entvar) + { + w = q; + } + else + { + calcEntropyVars(q.GetData(), w.GetData()); + // SA variable copy + w(dim+2) = q(dim+2); + } +} + +template +void SALPSIntegrator::convertVarsJacState( + const mfem::Vector &q, mfem::DenseMatrix &dwdu) +{ + if (entvar) + { + dwdu = 0.0; + for (int i = 0; i < dim+3; ++i) + { + dwdu(i,i) = 1.0; + } + } + else + { + dwdu = 0.0; + // vector of active input variables + std::vector q_a(q.Size()); + // initialize adouble inputs + adept::set_values(q_a.data(), q.Size(), q.GetData()); + // start recording + this->stack.new_recording(); + // create vector of active output variables + std::vector w_a(q.Size()); + // run algorithm + calcEntropyVars(q_a.data(), w_a.data()); + // identify independent and dependent variables + this->stack.independent(q_a.data(), q.Size()); + this->stack.dependent(w_a.data(), q.Size()); + // compute and store jacobian in dwdu + this->stack.jacobian(dwdu.GetData()); + // SA Variable Derivative + dwdu(dim+2, dim+2) = 1.0; + } +} + +template +void SALPSIntegrator::applyScaling( + const mfem::DenseMatrix &adjJ, const mfem::Vector &q, + const mfem::Vector &vec, mfem::Vector &mat_vec) +{ + if (entvar) + { + applyLPSScalingUsingEntVars(adjJ.GetData(), q.GetData(), + vec.GetData(), mat_vec.GetData()); + throw MachException("Entropy variables not yet supported"); + } + else + { + applyLPSScaling(adjJ.GetData(), q.GetData(), vec.GetData(), + mat_vec.GetData()); + // SA Variable + double U = sqrt(dot(q.GetData()+1, q.GetData()+1))/q(0); + mat_vec(dim+2) = U*vec(dim+2); + } +} + +template +void SALPSIntegrator::applyScalingJacState( + const mfem::DenseMatrix &adjJ, const mfem::Vector &q, + const mfem::Vector &vec, mfem::DenseMatrix &mat_vec_jac) +{ + // declare vectors of active input variables + int adjJ_a_size = adjJ.Height() * adjJ.Width(); + std::vector adjJ_a(adjJ_a_size); + std::vector q_a(q.Size()); + std::vector vec_a(vec.Size()); + // copy data from mfem::Vector + adept::set_values(adjJ_a.data(), adjJ_a_size, adjJ.GetData()); + adept::set_values(q_a.data(), q.Size(), q.GetData()); + adept::set_values(vec_a.data(), vec.Size(), vec.GetData()); + // start recording + this->stack.new_recording(); + // the dependent variable must be declared after the recording + std::vector mat_vec_a(q.Size()); + if (entvar) + { + applyLPSScalingUsingEntVars(adjJ_a.data(), q_a.data(), + vec_a.data(), mat_vec_a.data()); + } + else + { + applyLPSScaling(adjJ_a.data(), q_a.data(), + vec_a.data(), mat_vec_a.data()); + adouble U = sqrt(dot(q_a.data()+1, q_a.data()+1))/q_a[0]; + mat_vec_a[dim+2] = U*vec_a[dim+2]; + } + // set the independent and dependent variable + this->stack.independent(q_a.data(), q.Size()); + this->stack.dependent(mat_vec_a.data(), q.Size()); + // Calculate the jabobian + this->stack.jacobian(mat_vec_jac.GetData()); +} + +template +void SALPSIntegrator::applyScalingJacV( + const mfem::DenseMatrix &adjJ, const mfem::Vector &q, + mfem::DenseMatrix &mat_vec_jac) +{ + // declare vectors of active input variables + int adjJ_a_size = adjJ.Height() * adjJ.Width(); + std::vector adjJ_a(adjJ_a_size); + std::vector q_a(q.Size()); + std::vector vec_a(q.Size()); + // copy data from mfem::Vector + adept::set_values(adjJ_a.data(), adjJ_a_size, adjJ.GetData()); + adept::set_values(q_a.data(), q.Size(), q.GetData()); + // dependence on vec is linear, so any value is ok; use q + adept::set_values(vec_a.data(), q.Size(), q.GetData()); + // start recording + this->stack.new_recording(); + // the dependent variable must be declared after the recording + std::vector mat_vec_a(q.Size()); + if (entvar) + { + // applyLPSScalingUsingEntVars(adjJ_a.data(), q_a.data(), + // vec_a.data(), mat_vec_a.data()); + throw MachException("Entropy variables not yet supported"); + } + else + { + applyLPSScaling(adjJ_a.data(), q_a.data(), + vec_a.data(), mat_vec_a.data()); + adouble U = sqrt(dot(q_a.data()+1, q_a.data()+1))/q_a[0]; + mat_vec_a[dim+2] = U*vec_a[dim+2]; + } + // set the independent and dependent variable + this->stack.independent(vec_a.data(), q.Size()); + this->stack.dependent(mat_vec_a.data(), q.Size()); + // Calculate the jabobian + this->stack.jacobian(mat_vec_jac.GetData()); } \ No newline at end of file diff --git a/test/unit/test_rans_integ.cpp b/test/unit/test_rans_integ.cpp index f39488eb..8626e2c4 100644 --- a/test/unit/test_rans_integ.cpp +++ b/test/unit/test_rans_integ.cpp @@ -149,16 +149,20 @@ TEMPLATE_TEST_CASE_SIG("SAFarFieldBC Jacobian", "[SAFarFieldBC]", mfem::Vector flux_plus(q), flux_minus(q); adept::Stack diff_stack; mfem::H1_FECollection fe_coll(1); //dummy - mach::SAFarFieldBC safarfieldinteg(diff_stack, &fe_coll, qfar, 1.0); + double Re = 1.0; double Pr = 1.0; + mach::SAFarFieldBC safarfieldinteg(diff_stack, &fe_coll, Re, Pr, qfar, 1.0); // +ve perturbation q_plus.Add(delta, v); // -ve perturbation q_minus.Add(-delta, v); for (int di = 0; di < 2; di++) //reverse direction to check both inflow and outflow { - DYNAMIC_SECTION("SA Far-Field BC safarfieldinteg jacobian is correct w.r.t state ") - { + if(di == 1) nrm *= -1.0; + + DYNAMIC_SECTION("SA Far-Field BC safarfieldinteg jacobian is correct w.r.t state when di = "< irinteg(stack); - mach::SAInviscidIntegrator sainteg(stack); - - SECTION("Check if SA integrator flux function matches the output for the conservative variables") + // create perturbation vector + for (int di = 0; di < dim + 3; ++di) { - // calculate the flux - irinteg.calcFlux(0, qL, qR, flux1); - sainteg.calcFlux(0, qL, qR, flux2); + v(di) = vec_pert[di]; + } + // perturbed vectors + mfem::Vector q_plus(q), q_minus(q); + mfem::Vector vec_plus(vec), vec_minus(vec); + adept::Stack diff_stack; + mach::SALPSIntegrator salpsinteg(diff_stack); + // +ve perturbation + q_plus.Add(delta, v); + vec_plus.Add(delta, v); + // -ve perturbation + q_minus.Add(-delta, v); + vec_minus.Add(-delta, v); + DYNAMIC_SECTION("LPS salpsinteg convertVars jacobian is correct w.r.t state ") + { + // get perturbed states conv vector + salpsinteg.convertVars(q_plus, conv_plus); + salpsinteg.convertVars(q_minus, conv_minus); + salpsinteg.applyScaling(adjJ, q_plus, vec, scale_plus); + salpsinteg.applyScaling(adjJ, q_minus, vec, scale_minus); + salpsinteg.applyScaling(adjJ, q, vec_plus, scale_plus_2); + salpsinteg.applyScaling(adjJ, q, vec_minus, scale_minus_2); + // compute the jacobian + salpsinteg.convertVarsJacState(q, jac_conv); + salpsinteg.applyScalingJacState(adjJ, q, vec, jac_scale); + salpsinteg.applyScalingJacV(adjJ, q, jac_scale_2); + jac_conv.Mult(v, jac_v); + // finite difference jacobian + mfem::Vector jac_v_fd(conv_plus); + jac_v_fd -= conv_minus; + jac_v_fd /= 2.0 * delta; + // compare each component of the matrix-vector products + std::cout << "convertVars jac:" << std::endl; + for (int i = 0; i < dim + 3; ++i) + { + std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; + REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); + } + + jac_scale.Mult(v, jac_v); + // finite difference jacobian + jac_v_fd = scale_plus; + jac_v_fd -= scale_minus; + jac_v_fd /= 2.0 * delta; + std::cout << "applyScaling jac:" << std::endl; + for (int i = 0; i < dim + 3; ++i) + { + std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; + REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); + } - // check if euler variables are the same as before - for(int n = 0; n < dim+2; n++) + jac_scale_2.Mult(v, jac_v); + // finite difference jacobian + jac_v_fd = scale_plus_2; + jac_v_fd -= scale_minus_2; + jac_v_fd /= 2.0 * delta; + std::cout << "applyScaling jac vec:" << std::endl; + for (int i = 0; i < dim + 3; ++i) { - std::cout << "Ismail-Roe " << n << " Flux: " << flux1(n) << std::endl; - std::cout << "Spalart-Allmaras " << n << " Flux: " << flux2(n) << std::endl; - REQUIRE(flux1(n) - flux2(n) == Approx(0.0)); + std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; + REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); } } } + +TEST_CASE("SALPSIntegrator::AssembleElementGrad", "[SALPSIntegrator]") +{ + using namespace mfem; + using namespace euler_data; + + const int dim = 2; // templating is hard here because mesh constructors + int num_state = dim + 3; + adept::Stack diff_stack; + double delta = 1e-5; + + // generate a 2 element mesh + int num_edge = 2; + std::unique_ptr mesh(new Mesh(num_edge, num_edge, Element::TRIANGLE, + true /* gen. edges */, 1.0, 1.0, true)); + for (int p = 1; p <= 4; ++p) + { + DYNAMIC_SECTION("...for degree p = " << p) + { + std::unique_ptr fec( + new SBPCollection(p, dim)); + std::unique_ptr fes(new FiniteElementSpace( + mesh.get(), fec.get(), num_state, Ordering::byVDIM)); + + NonlinearForm res(fes.get()); + res.AddDomainIntegrator(new mach::SALPSIntegrator(diff_stack)); + + // initialize state; here we randomly perturb a constant state + GridFunction q(fes.get()); + VectorFunctionCoefficient pert(num_state, randBaselinePert<2>); + q.ProjectCoefficient(pert); + + // initialize the vector that the Jacobian multiplies + GridFunction v(fes.get()); + VectorFunctionCoefficient v_rand(num_state, randState); + v.ProjectCoefficient(v_rand); + + // evaluate the Jacobian and compute its product with v + Operator &Jac = res.GetGradient(q); + GridFunction jac_v(fes.get()); + Jac.Mult(v, jac_v); + + // now compute the finite-difference approximation... + GridFunction q_pert(q), r(fes.get()), jac_v_fd(fes.get()); + q_pert.Add(-delta, v); + res.Mult(q_pert, r); + q_pert.Add(2 * delta, v); + res.Mult(q_pert, jac_v_fd); + jac_v_fd -= r; + jac_v_fd /= (2 * delta); + + for (int i = 0; i < jac_v.Size(); ++i) + { + REQUIRE(jac_v(i) == Approx(jac_v_fd(i)).margin(1e-10)); + } + } + } +} + + +#if 0 +TEMPLATE_TEST_CASE_SIG("SAInviscid Gradient", + "[SAInviscidGrad]", + ((bool entvar), entvar), false) +{ + using namespace mfem; + using namespace euler_data; + + const int dim = 2; // templating is hard here because mesh constructors + double delta = 1e-5; + + // generate a 8 element mesh + int num_edge = 2; + std::unique_ptr mesh(new Mesh(num_edge, num_edge, Element::TRIANGLE, + true /* gen. edges */, 1.0, 1.0, true)); + mesh->EnsureNodes(); + for (int p = 1; p <= 4; ++p) + { + DYNAMIC_SECTION("...for degree p = " << p) + { + // get the finite-element space for the state and adjoint + std::unique_ptr fec( + new SBP_FECollection(p, dim)); + std::unique_ptr fes(new FiniteElementSpace( + mesh.get(), fec.get())); + + // we use res for finite-difference approximation + NonLinearForm res(fes.get()); + res.AddDomainIntegrator( + new SAInviscidIntegrator( //Inviscid term + this->diff_stack, alpha)); + + // initialize state ; here we randomly perturb a constant state + GridFunction state(fes.get()) + VectorFunctionCoefficient v_rand(dim+3, randState); + state.ProjectCoefficient(pert); + + // initialize the vector that we use to perturb the mesh nodes + GridFunction v(fes.get()); + VectorFunctionCoefficient v_rand(dim, randState); + v.ProjectCoefficient(v_rand); + + // evaluate df/dx and contract with v + GridFunction dfdx(*x_nodes); + dfdx_form.Mult(*x_nodes, dfdx); + double dfdx_v = dfdx * v; + + // now compute the finite-difference approximation... + GridFunction x_pert(*x_nodes); + GridFunction r(fes.get()); + x_pert.Add(delta, v); + mesh->SetNodes(x_pert); + res.Assemble(); + double dfdx_v_fd = adjoint * res; + x_pert.Add(-2 * delta, v); + mesh->SetNodes(x_pert); + res.Assemble(); + dfdx_v_fd -= adjoint * res; + dfdx_v_fd /= (2 * delta); + mesh->SetNodes(*x_nodes); // remember to reset the mesh nodes + + REQUIRE(dfdx_v == Approx(dfdx_v_fd).margin(1e-10)); + } + } +} #endif \ No newline at end of file From 500ece2a4c2e91e809a9cc2e5c76abafa9eb254e Mon Sep 17 00:00:00 2001 From: Jason Hicken Date: Tue, 28 Jul 2020 21:05:17 -0400 Subject: [PATCH 07/28] changed some options for the rans_freestream test --- sandbox/CMakeLists.txt | 1 + sandbox/rans_freestream_options.json | 25 +++++-------------------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/sandbox/CMakeLists.txt b/sandbox/CMakeLists.txt index 944ac5f9..75d149c2 100644 --- a/sandbox/CMakeLists.txt +++ b/sandbox/CMakeLists.txt @@ -25,6 +25,7 @@ set(SANDBOX_SRCS magnetostatic_wire thermal_square joule_wire + rans_freestream ) create_sandbox("${SANDBOX_SRCS}") diff --git a/sandbox/rans_freestream_options.json b/sandbox/rans_freestream_options.json index cd0410d6..8182f2c1 100644 --- a/sandbox/rans_freestream_options.json +++ b/sandbox/rans_freestream_options.json @@ -22,35 +22,20 @@ "const-cfl": true, "ode-solver": "PTC", "t-final": 100, - "dt": 0.1, + "dt": 1e6, "cfl": 1.0 }, "newton":{ - "printlevel": 0, + "printlevel": 1, "maxiter": 50, "reltol": 1e-1, "abstol": 1e-12 }, "lin-solver": { - "type": "hypregmres", - "pctype": "hypreboomeramg", - "reltol": 1e-10, - "abstol": 0.0, + "reltol": 1e-2, + "abstol": 1e-12, "printlevel": 0, - "maxiter": 50 - }, - "petscsolver":{ - "ksptype": "gmres", - "pctype": "lu", - "abstol": 1e-10, - "reltol": 1e-10, - "maxiter": 100, - "printlevel": 2 - }, - "hypresolver":{ - "tol": 1e-10, - "maxiter": 100, - "printlevel": 0 + "maxiter": 100 }, "bcs": { "far-field": [1, 1, 1, 1] From 4a2431854a435ece9709244fe6313525092c5e18 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Tue, 28 Jul 2020 21:19:00 -0400 Subject: [PATCH 08/28] added the CMakeList changes --- sandbox/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/sandbox/CMakeLists.txt b/sandbox/CMakeLists.txt index 944ac5f9..75d149c2 100644 --- a/sandbox/CMakeLists.txt +++ b/sandbox/CMakeLists.txt @@ -25,6 +25,7 @@ set(SANDBOX_SRCS magnetostatic_wire thermal_square joule_wire + rans_freestream ) create_sandbox("${SANDBOX_SRCS}") From 61e2a3d4638f4b00974397ba29367b18b74ddfcc Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Thu, 13 Aug 2020 02:12:19 -0400 Subject: [PATCH 09/28] source term grad remains --- sandbox/rans_freestream.cpp | 42 +- .../fluidflow/navier_stokes_fluxes.hpp | 2 +- src/physics/fluidflow/rans.cpp | 16 +- src/physics/fluidflow/rans_fluxes.hpp | 271 +++++++- src/physics/fluidflow/rans_integ.hpp | 336 +++++++-- src/physics/fluidflow/rans_integ_def.hpp | 656 ++++++++++++++---- test/unit/CMakeLists.txt | 1 + test/unit/euler_test_data.hpp | 3 + test/unit/test_rans_fluxes.cpp | 142 ++++ test/unit/test_rans_integ.cpp | 533 +++++++++++++- 10 files changed, 1789 insertions(+), 213 deletions(-) diff --git a/sandbox/rans_freestream.cpp b/sandbox/rans_freestream.cpp index c0b40e59..67044d4e 100644 --- a/sandbox/rans_freestream.cpp +++ b/sandbox/rans_freestream.cpp @@ -87,13 +87,13 @@ int main(int argc, char *argv[]) *out << "Number of elements " << smesh->GetNE() <<'\n'; // construct the solver and set initial conditions - auto solver = createSolver>(opt_file_name, + auto solver = createSolver>(opt_file_name, move(smesh)); solver->setInitialCondition(uinit_pert); solver->printSolution("rans_init", 0); // get the initial density error - double l2_error_init = (static_cast&>(*solver) + double l2_error_init = (static_cast&>(*solver) .calcConservativeVarsL2Error(uexact, 0)); *out << "\n|| rho_h - rho ||_{L^2} init = " << l2_error_init << endl; @@ -103,7 +103,7 @@ int main(int argc, char *argv[]) solver->solveForState(); solver->printSolution("rans_final",0); // get the final density error - double l2_error_final = (static_cast&>(*solver) + double l2_error_final = (static_cast&>(*solver) .calcConservativeVarsL2Error(uexact, 0)); res_error = solver->calcResidualNorm(); @@ -129,33 +129,33 @@ int main(int argc, char *argv[]) // perturbation function used to check the jacobian in each iteration void pert(const Vector &x, Vector& p) { - // p.SetSize(5); - // for (int i = 0; i < 5; i++) - // { - // p(i) = normal_rand(gen); - // } - - p.SetSize(4); - for (int i = 0; i < 4; i++) + p.SetSize(5); + for (int i = 0; i < 5; i++) { p(i) = normal_rand(gen); } + + // p.SetSize(4); + // for (int i = 0; i < 4; i++) + // { + // p(i) = normal_rand(gen); + // } } // Exact solution; same as freestream bc void uexact(const Vector &x, Vector& q) { - q.SetSize(4); - Vector u(4); - // q.SetSize(5); - // Vector u(5); + // q.SetSize(4); + // Vector u(4); + q.SetSize(5); + Vector u(5); u = 0.0; u(0) = 1.0; u(1) = u(0)*mach_fs*cos(aoa_fs); u(2) = u(0)*mach_fs*sin(aoa_fs); u(3) = 1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; - //u(4) = chi_fs*mu/u(0); + u(4) = chi_fs*mu/u(0); if (entvar == false) { @@ -170,17 +170,17 @@ void uexact(const Vector &x, Vector& q) // initial guess perturbed from exact void uinit_pert(const Vector &x, Vector& q) { - q.SetSize(4); - Vector u(4); - // q.SetSize(5); - // Vector u(5); + // q.SetSize(4); + // Vector u(4); + q.SetSize(5); + Vector u(5); u = 0.0; u(0) = pert_fs*1.0; u(1) = u(0)*mach_fs*cos(aoa_fs); u(2) = u(0)*mach_fs*sin(aoa_fs); u(3) = pert_fs*1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; -// u(4) = pert_fs*pert_fs*chi_fs*mu/u(0); + u(4) = pert_fs*pert_fs*chi_fs*mu/u(0); q = u; } diff --git a/src/physics/fluidflow/navier_stokes_fluxes.hpp b/src/physics/fluidflow/navier_stokes_fluxes.hpp index aafd1f7e..465c9c86 100644 --- a/src/physics/fluidflow/navier_stokes_fluxes.hpp +++ b/src/physics/fluidflow/navier_stokes_fluxes.hpp @@ -203,7 +203,7 @@ void calcNoSlipPenaltyFlux(const xdouble *dir, const xdouble Jac, } for (int d = 0; d < dim; ++d) { - applyCijMatrix(d, d, mu*dir[d], Pr, qfs, dw, flux); + applyCijMatrix(d, d, mu, Pr, qfs, dw, flux); } // scale the penalty xdouble fac = sqrt(dot(dir, dir))/Jac; diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index 8d241703..10947bea 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -35,8 +35,8 @@ void RANavierStokesSolver::addResVolumeIntegrators(double alpha) cout << "Inside RANS add volume integrators" << endl; this->res->AddDomainIntegrator(new SAInviscidIntegrator( //Inviscid term this->diff_stack, alpha)); - // this->res->AddDomainIntegrator(new SAViscousIntegrator( //SAViscousIntegrator - // this->diff_stack, re_fs, pr_fs, mu, alpha)); + this->res->AddDomainIntegrator(new SAViscousIntegrator( //SAViscousIntegrator + this->diff_stack, this->re_fs, this->pr_fs, sacs, mu, alpha)); // now add RANS integrators // this->res->AddDomainIntegrator(new SASourceIntegrator( // this->diff_stack, sacs, mu, alpha)); @@ -55,7 +55,7 @@ void RANavierStokesSolver::addResBoundaryIntegrators(double alpha) double mu = this->options["flow-param"]["mu"].template get(); int idx = 0; -#if 0 + if (bcs.find("slip-wall") != bcs.end()) { // slip-wall boundary condition vector tmp = bcs["slip-wall"].template get>(); @@ -63,10 +63,11 @@ void RANavierStokesSolver::addResBoundaryIntegrators(double alpha) this->bndry_marker[idx].Assign(tmp.data()); this->res->AddBdrFaceIntegrator( new SAViscousSlipWallBC(this->diff_stack, this->fec.get(), - re_fs, pr_fs, mu, alpha), + this->re_fs, this->pr_fs, sacs, mu, alpha), this->bndry_marker[idx]); idx++; } + if (bcs.find("no-slip-adiabatic") != bcs.end()) { vector tmp = bcs["no-slip-adiabatic"].template get>(); @@ -78,11 +79,12 @@ void RANavierStokesSolver::addResBoundaryIntegrators(double alpha) // Add the adiabatic flux BC this->res->AddBdrFaceIntegrator( new SANoSlipAdiabaticWallBC( - this->diff_stack, this->fec.get(), re_fs, pr_fs, q_ref, mu, alpha), - this->bndry_marker[idx]); + this->diff_stack, this->fec.get(), this->re_fs, + this->pr_fs, sacs, q_ref, mu, alpha), + this->bndry_marker[idx]); idx++; } - +#if 0 if (bcs.find("rans-inflow") != bcs.end()) { vector tmp = bcs["rans-inflow"].template get>(); diff --git a/src/physics/fluidflow/rans_fluxes.hpp b/src/physics/fluidflow/rans_fluxes.hpp index f34ea556..82702840 100644 --- a/src/physics/fluidflow/rans_fluxes.hpp +++ b/src/physics/fluidflow/rans_fluxes.hpp @@ -45,7 +45,7 @@ xdouble calcSALaminarSuppression(const xdouble *q, const xdouble mu, { xdouble ct3 = sacs[7]; xdouble ct4 = sacs[8]; - xdouble nu_tilde = q[dim+3]; + xdouble nu_tilde = q[dim+2]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble ft2 = ct3*exp(-ct4*chi*chi); @@ -66,7 +66,7 @@ xdouble calcSAModifiedVorticity(const xdouble *q, const xdouble S, const xdouble mu, const xdouble d, const xdouble *sacs) { - xdouble nu_tilde = q[dim+3]; + xdouble nu_tilde = q[dim+2]; xdouble kappa = sacs[3]; xdouble fv2 = calcSAProductionCoefficient(q, mu, sacs); xdouble St = S + nu_tilde*fv2/(kappa*kappa*d*d); @@ -85,7 +85,7 @@ xdouble calcSACoefficient(const xdouble *q, const xdouble mu, const xdouble *sacs) { xdouble cv1 = sacs[6]; - xdouble nu_tilde = q[dim+3]; + xdouble nu_tilde = q[dim+2]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble fv1 = chi*chi*chi/(cv1*cv1*cv1 + chi*chi*chi); @@ -103,7 +103,7 @@ template xdouble calcSAProductionCoefficient(const xdouble *q, const xdouble mu, const xdouble *sacs) { - xdouble nu_tilde = q[dim+3]; + xdouble nu_tilde = q[dim+2]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble fv1 = calcSACoefficient(q, mu, sacs); @@ -123,7 +123,7 @@ xdouble calcSADestructionCoefficient(const xdouble *q, const xdouble mu, const xdouble d, const xdouble S, const xdouble *sacs) { - xdouble nu_tilde = q[dim+3]; + xdouble nu_tilde = q[dim+2]; xdouble kappa = sacs[3]; xdouble cw2 = sacs[4]; xdouble cw3 = sacs[5]; @@ -152,7 +152,7 @@ xdouble calcSAProduction(const xdouble *q, const xdouble S, const xdouble *sacs) { xdouble cb1 = sacs[0]; - xdouble nu_tilde = q[dim+3]; + xdouble nu_tilde = q[dim+2]; xdouble St = calcSAModifiedVorticity(q, S, mu, d, sacs); xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); xdouble P = cb1*(1-ft2)*St*nu_tilde; @@ -175,7 +175,7 @@ xdouble calcSADestruction(const xdouble *q, xdouble cb2 = sacs[1]; xdouble sigma = sacs[2]; xdouble kappa = sacs[3]; - xdouble chi_d = q[dim+3]/d; + xdouble chi_d = q[dim+2]/d; xdouble cw1 = cb1/(kappa*kappa) + (1+cb2)/sigma; xdouble fw = calcSADestructionCoefficient(q, mu, d, S, sacs); xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); @@ -196,7 +196,7 @@ xdouble calcSANegativeProduction(const xdouble *q, const xdouble S, { xdouble cb1 = sacs[0]; xdouble ct3 = sacs[7]; - xdouble nu_tilde = q[dim+3]; + xdouble nu_tilde = q[dim+2]; xdouble Pn = cb1*(1-ct3)*S*nu_tilde; return Pn; } @@ -216,7 +216,7 @@ xdouble calcSANegativeDestruction(const xdouble *q, const xdouble d, xdouble cb2 = sacs[1]; xdouble sigma = sacs[2]; xdouble kappa = sacs[3]; - xdouble chi_d = q[dim+3]/d; + xdouble chi_d = q[dim+2]/d; xdouble cw1 = cb1/(kappa*kappa) + (1+cb2)/sigma; xdouble Dn = -cw1*chi_d*chi_d; return Dn; @@ -234,7 +234,7 @@ xdouble calcSANegativeCoefficient(const xdouble *q, const xdouble mu, const xdouble *sacs) { xdouble cn1 = sacs[10]; - xdouble nu_tilde = q[dim+3]; + xdouble nu_tilde = q[dim+2]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble fn = (cn1 + chi*chi*chi)/(cn1 - chi*chi*chi); @@ -258,6 +258,257 @@ xdouble calcSASource(const xdouble *q, const xdouble *dir, return Sr; } +#if 0 +// Compute vorticity on an SBP element, needed for SA model terms +/// \param[in] q - the state over the element +/// \param[in] sbp - the sbp element whose shape functions we want +/// \param[in] Trans - defines the reference to physical element mapping +/// \param[out] curl - the curl of the velocity field at each node/int point +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +void calcVorticitySBP(const mfem::DenseMatrix &q, const mfem::FiniteElement &fe, + mfem::ElementTransformation &Trans, mfem::DenseMatrix curl) +{ + using namespace mfem; + const SBPFiniteElement &sbp = dynamic_cast(fe); + int num_nodes = sbp.GetDof(); + DenseMatrix dq(q.Height(), q.Width()); //contains state derivatives in reference space + DenseMatrix dxi(dim*q.Height(), dim); //contains velocity derivatives in reference space + dq = 0.0; + for(int di = 0; di < dim; di++) + { + sbp.multWeakOperator(di, q, dq); + //need to scale with 1/H, probably when looping over nodes + dxi.CopyMN(dq, q.Height(), dim, 1, dim+1, di*q.Height(), 0); + } + + DenseMatrix dx(dim, dim); //contains velocity derivatives in absolute space + DenseMatrix dxin(dim, dim); //contains velocity derivatives in reference space for a node + Vector dxrow(dim); Vector curln(dim); + for (int i = 0; i < num_nodes; ++i) + { + // get the Jacobian + IntegrationPoint &node = sbp.GetNodes().IntPoint(i); + Trans.SetIntPoint(&node); + + // store nodal derivatives in a single matrix + for(int di = 0; di < dim; di++) + { + dxi.GetRow(di*q.Height() + i, dxrow); + dxin.SetRow(di, dxrow); + } + + // compute absolute derivatives + MultAtB(Trans.InverseJacobian(), dxin, dx); + + // compute curl at node and append + if(dim == 2) + { + curl(i, 0) = 0; + curl(i, 1) = 0; + curl(i, 2) = dx(1,0) - dx(0,1); + } + if(dim == 3) + { + curl(i, 0) = dx(2,1) - dx(1,2); + curl(i, 1) = dx(0,2) - dx(2,0); + curl(i, 2) = dx(1,0) - dx(0,1); + } + } +} + +// Compute gradient for the turbulence variable on an SBP element, +// needed for SA model terms +/// \param[in] q - the state over the element +/// \param[in] sbp - the sbp element whose shape functions we want +/// \param[in] Trans - defines the reference to physical element mapping +/// \param[out] grad - the gradient of the turbulence variable at each node +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +void calcGradSBP(const mfem::DenseMatrix &q, const mfem::FiniteElement &fe, + mfem::ElementTransformation &Trans, mfem::DenseMatrix grad) +{ + using namespace mfem; + const SBPFiniteElement &sbp = dynamic_cast(fe); + int num_nodes = sbp.GetDof(); + DenseMatrix dq(q.Height(), q.Width()); //contains state derivatives in reference space + DenseMatrix dnu(q.Height(), dim); //contains turb variable derivatives in reference space + dq = 0.0; + for(int di = 0; di < dim; di++) + { + sbp.multWeakOperator(di, q, dq); + //need to scale with 1/H + dnu.SetCol(di, dq.GetColumn(dim+2)); + } + + Vector dnurow(dim); Vector gradn(dim); + for (int i = 0; i < num_nodes; ++i) + { + // get the Jacobian + IntegrationPoint &node = sbp.GetNodes().IntPoint(i); + Trans.SetIntPoint(&node); + + // store nodal grad in a vector + dnu.GetRow(i, dnurow); + + // compute absolute derivatives + Trans.InverseJacobian().MultTranspose(dnurow, gradn); + + // append result + grad.SetRow(i, gradn); + } +} +#endif + +// Compute vorticity at a point on an element, needed for SA model terms +/// \param[in] Dq - the state gradient Dq +/// \param[in] jac_inv - inverse jacobian at node +/// \param[out] curl - the curl of the velocity field at the node +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// NOTE: Need inverse jacobian! Check EvalJacobian +template +void calcVorticity(const xdouble *Dq, const xdouble *jac_inv, + xdouble *curl) +{ + xdouble DqJ[dim*(dim+3)]; + + // compute absolute derivatives + for(int di = 0; di < dim+3; di++) + { + for(int d = 0; d < dim; d++) + { + xdouble work = 0; + for(int k = 0; k < dim; k++) + { + work += Dq[di+k*(dim+3)]*jac_inv[k+d*dim]; + } + DqJ[di+d*(dim+3)] = work; + } + } + + // compute curl at node and append + if(dim == 2) + { + curl[0] = 0; + curl[1] = 0; + curl[2] = DqJ[2] - DqJ[1 + dim+3]; + } + if(dim == 3) + { + curl[0] = DqJ[3 + dim+3] - DqJ[2 + 2*(dim+3)]; + curl[1] = DqJ[1 + 2*(dim+3)] - DqJ[3]; + curl[2] = DqJ[2] - DqJ[1 + dim+3]; + } +} + +// Compute vorticity jacobian w.r.t. state on an SBP element, needed for SA model terms +/// \param[in] stack - adept stack +/// \param[in] q - the state over the element +/// \param[in] sbp - the sbp element whose shape functions we want +/// \param[in] Trans - defines the reference to physical element mapping +/// \param[out] curl - the curl of the velocity field at each node/int point +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +void calcVorticityJacDw(adept::Stack &stack, const double *Dq, const double *jac_inv, + std::vector &jac_curl) +{ + // vector of active input variables + std::vector Dq_a(dim*(dim+3)); + std::vector jac_a(dim*(dim+3)); + // initialize adouble inputs + adept::set_values(Dq_a.data(), dim*(dim+3), Dq); + adept::set_values(jac_a.data(), dim*dim, jac_inv); + // start recording + stack.new_recording(); + // create vector of active output variables + std::vector curl_a(3); + // run algorithm + calcVorticity(Dq_a.data(), jac_a.data(), curl_a.data()); + // identify independent and dependent variables + stack.independent(Dq_a.data(), Dq_a.size()); + stack.dependent(curl_a.data(), curl_a.size()); + // compute and store jacobian in jac_inv + mfem::Vector work(dim*(dim+3)*3); + stack.jacobian_reverse(work.GetData()); + for (int i = 0; i < dim; ++i) + { + jac_curl[i] = (work.GetData() + i*(dim+3)*3); + } +} + +// Compute gradient for the turbulence variable on an element node, +// needed for SA model terms +/// \param[in] q - the derivative of the state at the node +/// \param[in] jac_inv - transformation jacobian at node +/// \param[out] grad - the gradient of the turbulence variable at each node +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +void calcGrad(const xdouble *Dq, const xdouble *jac_inv, + xdouble *grad) +{ + xdouble DqJ[dim*(dim+3)]; + + // compute absolute derivatives + for(int di = 0; di < dim+3; di++) + { + for(int d = 0; d < dim; d++) + { + xdouble work = 0; + for(int k = 0; k < dim; k++) + { + work += Dq[di+k*(dim+3)]*jac_inv[k+d*dim]; + } + DqJ[di+d*(dim+3)] = work; + } + } + + for (int i = 0; i < dim; ++i) + { + grad[i] = DqJ[dim+2 + i*(dim+3)]; + } +} + +// Compute grad jacobian w.r.t. state on an SBP element, needed for SA model terms +/// \param[in] stack - adept stack +/// \param[in] q - the state over the element +/// \param[in] sbp - the sbp element whose shape functions we want +/// \param[in] Trans - defines the reference to physical element mapping +/// \param[out] curl - the curl of the velocity field at each node/int point +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +void calcGradJacDw(adept::Stack &stack, const double *Dq, const double *jac_inv, + std::vector &jac_grad) +{ + // vector of active input variables + std::vector Dq_a(dim*(dim+3)); + std::vector jac_a(dim*(dim+3)); + // initialize adouble inputs + adept::set_values(Dq_a.data(), dim*(dim+3), Dq); + adept::set_values(jac_a.data(), dim*dim, jac_inv); + // start recording + stack.new_recording(); + // create vector of active output variables + std::vector grad_a(dim); + // run algorithm + calcGrad(Dq_a.data(), jac_a.data(), grad_a.data()); + // identify independent and dependent variables + stack.independent(Dq_a.data(), Dq_a.size()); + stack.dependent(grad_a.data(), grad_a.size()); + // compute and store jacobian in jac_inv + mfem::Vector work(dim*(dim+3)*dim); + stack.jacobian_reverse(work.GetData()); + for (int i = 0; i < dim; ++i) + { + jac_grad[i] = (work.GetData() + i*(dim+3)*dim); + } +} + } // namespace mach #endif diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp index 56c63f0e..7c56d7d0 100644 --- a/src/physics/fluidflow/rans_integ.hpp +++ b/src/physics/fluidflow/rans_integ.hpp @@ -15,28 +15,6 @@ using namespace std; /// TODO: this is polluting other headers! namespace mach { -/// Entropy-stable volume integrator for Navier-Stokes viscous terms -/// \tparam dim - number of spatial dimensions (1, 2, or 3) -/// \note This derived class uses the CRTP -template -class ESViscousSAIntegrator : public ESViscousIntegrator -{ -public: - /// Construct an entropy-stable viscous integrator - /// \param[in] diff_stack - for algorithmic differentiation - /// \param[in] Re_num - Reynolds number - /// \param[in] Pr_num - Prandtl number - /// \param[in] vis - nondimensional dynamic viscosity (use Sutherland if neg) - /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) - ESViscousSAIntegrator(adept::Stack &diff_stack, double Re_num, double Pr_num, - double vis = -1.0, double a = 1.0) - : ESViscousIntegrator ( - diff_stack, Re_num, Pr_num, vis, a) {} - -private: - -}; - /// Volume integrator for inviscid terms, including SA variable /// \tparam dim - number of spatial dimensions (1, 2, or 3) /// \note This derived class uses the CRTP @@ -112,26 +90,26 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator const mfem::Vector &elfun, mfem::DenseMatrix &elmat); - // Compute vorticity on an SBP element, needed for SA model terms - /// \param[in] q - the state over the element - /// \param[in] sbp - the sbp element whose shape functions we want - /// \param[in] Trans - defines the reference to physical element mapping - /// \param[out] curl - the curl of the velocity field at each node/int point - void calcVorticitySBP(const mfem::DenseMatrix &q, - const mfem::FiniteElement &fe, - mfem::ElementTransformation &Trans, - mfem::DenseMatrix curl); - - // Compute gradient for the turbulence variable on an SBP element, - // needed for SA model terms - /// \param[in] q - the state over the element - /// \param[in] sbp - the sbp element whose shape functions we want - /// \param[in] Trans - defines the reference to physical element mapping - /// \param[out] grad - the gradient of the turbulence variable at each node - void calcGradSBP(const mfem::DenseMatrix &q, - const mfem::FiniteElement &fe, - mfem::ElementTransformation &Trans, - mfem::DenseMatrix grad); + // // Compute vorticity on an SBP element, needed for SA model terms + // /// \param[in] q - the state over the element + // /// \param[in] sbp - the sbp element whose shape functions we want + // /// \param[in] Trans - defines the reference to physical element mapping + // /// \param[out] curl - the curl of the velocity field at each node/int point + // void calcVorticitySBP(const mfem::DenseMatrix &q, + // const mfem::FiniteElement &fe, + // mfem::ElementTransformation &Trans, + // mfem::DenseMatrix curl); + + // // Compute gradient for the turbulence variable on an SBP element, + // // needed for SA model terms + // /// \param[in] q - the state over the element + // /// \param[in] sbp - the sbp element whose shape functions we want + // /// \param[in] Trans - defines the reference to physical element mapping + // /// \param[out] grad - the gradient of the turbulence variable at each node + // void calcGradSBP(const mfem::DenseMatrix &q, + // const mfem::FiniteElement &fe, + // mfem::ElementTransformation &Trans, + // mfem::DenseMatrix grad); private: @@ -170,6 +148,194 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator #endif }; +/// Integrator for SA no-slip adiabatic-wall boundary condition +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \note This derived class uses the CRTP +template +class SANoSlipAdiabaticWallBC : public ViscousBoundaryIntegrator> +{ +public: + /// Constructs an integrator for a no-slip, adiabatic boundary flux + /// \param[in] diff_stack - for algorithmic differentiation + /// \param[in] fe_coll - used to determine the face elements + /// \param[in] Re_num - Reynolds number + /// \param[in] Pr_num - Prandtl number + /// \param[in] q_ref - a reference state (needed by penalty) + /// \param[in] vis - viscosity (if negative use Sutherland's law) + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + SANoSlipAdiabaticWallBC(adept::Stack &diff_stack, + const mfem::FiniteElementCollection *fe_coll, + double Re_num, double Pr_num, mfem::Vector sa_params, + const mfem::Vector &q_ref, double vis = -1.0, + double a = 1.0) + : ViscousBoundaryIntegrator>( + diff_stack, fe_coll, dim + 3, a), + Re(Re_num), Pr(Pr_num), sacs(sa_params), + qfs(q_ref), mu(vis), work_vec(dim + 3) {} + + /// converts conservative variables to entropy variables + /// \param[in] q - conservative variables that are to be converted + /// \param[out] w - entropy variables corresponding to `q` + /// \note a wrapper for the relevant function in `euler_fluxes.hpp` + void convertVars(const mfem::Vector &q, mfem::Vector &w) + { + calcEntropyVars(q.GetData(), w.GetData()); + w(dim+2) = q(dim+2); + } + + /// Compute the Jacobian of the mapping `convert` w.r.t. `u' + /// \param[in] q - conservative variables that are to be converted + /// \param[out] dwdu - Jacobian of entropy variables w.r.t. `u` + void convertVarsJacState(const mfem::Vector &q, mfem::DenseMatrix &dwdu) + { + dwdu = 0.0; + convertVarsJac(q, this->stack, dwdu); + dwdu(dim+2,dim+2) = 1.0; + } + + /// Compute entropy-stable, no-slip, adiabatic-wall boundary flux + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_vec - value of the flux + void calcFlux(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::Vector &flux_vec); + + /// Compute Jacobian of entropy-stable, no-slip, adiabatic-wall boundary flux + /// w.r.t states + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_jac - jacobian of the flux w.r.t. states + void calcFluxJacState(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::DenseMatrix &flux_jac); + + /// Compute Jacobian of entropy-stable, no-slip, adiabatic-wall boundary flux + /// w.r.t vector of entropy-variables' derivatives + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_jac - jacobian of the flux w.r.t. entropy-variables' derivatives + void calcFluxJacDw(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + std::vector &flux_jac); + +private: + /// Reynolds number + double Re; + /// Prandtl number + double Pr; + /// nondimensionalized dynamic viscosity + double mu; + /// Fixed state used to compute no-slip penalty matrix + mfem::Vector qfs; + /// work space for flux computations + mfem::Vector work_vec; + /// vector of SA model parameters + mfem::Vector sacs; +}; + +/// Integrator for SA viscous slip-wall boundary condition +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \note This derived class uses the CRTP +/// \note This is the same as the inviscid slip wall, but it provides the +/// necessary entropy-variable gradient flux. +template +class SAViscousSlipWallBC : public ViscousBoundaryIntegrator> +{ +public: + /// Constructs an integrator for a viscous slip-wall boundary + /// \param[in] diff_stack - for algorithmic differentiation + /// \param[in] fe_coll - used to determine the face elements + /// \param[in] Re_num - Reynolds number + /// \param[in] Pr_num - Prandtl number + /// \param[in] vis - viscosity (if negative use Sutherland's law) + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + SAViscousSlipWallBC(adept::Stack &diff_stack, + const mfem::FiniteElementCollection *fe_coll, + double Re_num, double Pr_num, mfem::Vector sa_params, + double vis = -1.0, double a = 1.0) + : ViscousBoundaryIntegrator>( + diff_stack, fe_coll, dim + 3, a), + Re(Re_num), Pr(Pr_num), mu(vis), + sacs(sa_params), work_vec(dim + 3) {} + + /// converts conservative variables to entropy variables + /// \param[in] q - conservative variables that are to be converted + /// \param[out] w - entropy variables corresponding to `q` + /// \note a wrapper for the relevant function in `euler_fluxes.hpp` + void convertVars(const mfem::Vector &q, mfem::Vector &w) + { + calcEntropyVars(q.GetData(), w.GetData()); + w(dim+2) = q(dim+2); + } + + /// Compute the Jacobian of the mapping `convert` w.r.t. `u` + /// \param[in] q - conservative variables that are to be converted + /// \param[out] dwdu - Jacobian of entropy variables w.r.t. `u` + void convertVarsJacState(const mfem::Vector &q, mfem::DenseMatrix &dwdu) + { + dwdu = 0.0; + convertVarsJac(q, this->stack, dwdu); + dwdu(dim+2,dim+2) = 1.0; + } + /// Compute flux corresponding to a viscous slip-wall boundary + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_vec - value of the flux + void calcFlux(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::Vector &flux_vec); + + /// Compute jacobian of flux corresponding to a viscous slip-wall boundary + /// w.r.t `states` + /// \param[in] x - coordinate location at which flux is evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables (not used yet) + /// \param[out] flux_jac - jacobian of the flux + void calcFluxJacState(const mfem::Vector &x, const mfem::Vector &dir, + double jac, const mfem::Vector &q, + const mfem::DenseMatrix &Dw, + mfem::DenseMatrix &flux_jac); + + /// Compute jacobian of flux corresponding to a viscous slip-wall boundary + /// w.r.t `entrpy-variables' derivatives` + /// \param[in] x - coordinate location at which flux is evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian (needed by no-slip penalty) + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] Dw - space derivatives of the entropy variables + /// \param[out] flux_jac - jacobian of the flux + void calcFluxJacDw(const mfem::Vector &x, const mfem::Vector &dir, + double jac, const mfem::Vector &q, + const mfem::DenseMatrix &Dw, + std::vector &flux_jac); + +private: + /// Reynolds number + double Re; + /// Prandtl number + double Pr; + /// nondimensionalized dynamic viscosity + double mu; + /// work space for flux computations + mfem::Vector work_vec; + /// vector of SA model parameters + mfem::Vector sacs; +}; /// Integrator for SA far-field boundary conditions /// \tparam dim - number of spatial dimensions (1, 2, or 3) @@ -514,6 +680,92 @@ class SALPSIntegrator : public LPSIntegrator< }; +/// Volume integrator for Navier-Stokes viscous terms with SA variable +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \note This derived class uses the CRTP +template +class SAViscousIntegrator : public SymmetricViscousIntegrator> +{ +public: + /// Construct an SA viscous integrator + /// \param[in] diff_stack - for algorithmic differentiation + /// \param[in] Re_num - Reynolds number + /// \param[in] Pr_num - Prandtl number + /// \param[in] vis - nondimensional dynamic viscosity (use Sutherland if neg) + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + SAViscousIntegrator(adept::Stack &diff_stack, double Re_num, double Pr_num, + mfem::Vector sa_params, double vis = -1.0, double a = 1.0) + : SymmetricViscousIntegrator>( + diff_stack, dim + 3, a), + Re(Re_num), Pr(Pr_num), mu(vis), sacs(sa_params) {} + + /// converts conservative variables to entropy variables + /// \param[in] q - conservative variables that are to be converted + /// \param[out] w - entropy variables corresponding to `q` + /// \note a wrapper for the relevant function in `euler_fluxes.hpp` + void convertVars(const mfem::Vector &q, mfem::Vector &w) + { + calcEntropyVars(q.GetData(), w.GetData()); + w(dim+2) = q(dim+2); + } + + /// Compute the Jacobian of the mapping `convert` w.r.t. `u` + /// \param[in] q - conservative variables that are to be converted + /// \param[out] dwdu - Jacobian of entropy variables w.r.t. `u` + void convertVarsJacState(const mfem::Vector &q, mfem::DenseMatrix &dwdu) + { + dwdu = 0.0; + convertVarsJac(q, this->stack, dwdu); + dwdu(dim+2, dim+2) = 1.0; + } + + /// applies symmetric matrices \f$ C_{d,:}(q) \f$ to input `Dw` + /// \param[in] d - index `d` in \f$ C_{d,:} \f$ matrices + /// \param[in] x - coordinate location at which scaling evaluated (not used) + /// \param[in] q - state at which the symmetric matrices `C` are evaluated + /// \param[in] Dw - `Du[:,d2]` stores derivative of `w` in direction `d2`. + /// \param[out] CDw - product of the multiplication between the `C` and `Dw`. + void applyScaling(int d, const mfem::Vector &x, const mfem::Vector &q, + const mfem::DenseMatrix &Dw, mfem::Vector &CDw); + + /// Computes the Jacobian of the product `C(q)*Dw` w.r.t. `q` + /// \param[in] d - index `d` in \f$ C_{d,:} \f$ matrices + /// \param[in] x - coordinate location at which scaling evaluated (not used) + /// \param[in] q - state at which the symmetric matrix `C` is evaluated + /// \param[in] Dw - vector that is being multiplied + /// \param[out] CDw_jac - Jacobian of product w.r.t. `q` + /// \note This uses the CRTP, so it wraps call to a func. in Derived. + void applyScalingJacState(int d, const mfem::Vector &x, + const mfem::Vector &q, + const mfem::DenseMatrix &Dw, + mfem::DenseMatrix &CDw_jac); + + /// Computes the Jacobian of the product `C(q)*Dw` w.r.t. `Dw` + /// \param[in] d - index `d` in \f$ C_{d,:} \f$ matrices + /// \param[in] x - coordinate location at which scaling evaluated (not used) + /// \param[in] q - state at which the symmetric matrix `C` is evaluated + /// \param[in] Dw - vector that is being multiplied + /// \param[out] CDw_jac - Jacobian of product w.r.t. `Dw` (i.e. `C`) + /// \note This uses the CRTP, so it wraps call to a func. in Derived. + void applyScalingJacDw(int d, const mfem::Vector &x, const mfem::Vector &q, + const mfem::DenseMatrix &Dw, + vector &CDw_jac); + + /// This allows the base class to access the number of dimensions + static const int ndim = dim; + +private: + /// Reynolds number + double Re; + /// Prandtl number + double Pr; + /// nondimensional dynamic viscosity + double mu; + /// vector of SA model parameters + mfem::Vector sacs; +}; + + #include "rans_integ_def.hpp" } // namespace mach diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index 2d1180e4..6c34efda 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -1,7 +1,3 @@ -//============================================================================== -// ESViscousSAIntegrator methods - - //============================================================================== // SASourceIntegrator methods template @@ -25,9 +21,21 @@ void SASourceIntegrator::AssembleElementVector( curl_i.SetSize(3); DenseMatrix u(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well DenseMatrix res(elvect.GetData(), num_nodes, num_states); + DenseMatrix Qu(num_nodes, num_states); + DenseMatrix HQu(num_nodes, num_states); + DenseMatrix Dui(num_states, dim); + + // get Du + for(int di = 0; di < dim; di++) + { + sbp.multWeakOperator(di, u, Qu); + sbp.multNormMatrix(Qu, HQu); + //dxi.CopyMN(HQu, q.Height(), dim, 1, dim+1, di*q.Height(), 0); + } + // precompute certain values - calcVorticitySBP(u, sbp, Trans, curl); - calcGradSBP(u, sbp, Trans, grad); + //calcVorticitySBP(u, sbp, Trans, curl); + //calcGradSBP(u, sbp, Trans, grad); elvect = 0.0; for (int i = 0; i < num_nodes; ++i) { @@ -36,33 +44,34 @@ void SASourceIntegrator::AssembleElementVector( Trans.SetIntPoint(&node); u.GetRow(i, ui); Trans.Transform(node, xi); + Dui.Set(1.0, (HQu.GetData() + i*dim*num_states)); // compute vorticity at node and take magnitude - curl.GetRow(i, curl_i); + calcVorticity(Dui.GetData(), Trans.InverseJacobian().GetData(), curl_i.GetData()); //curl.GetRow(i, curl_i); double S = curl_i.Norml2(); // compute gradient of turbulent viscosity at node - grad.GetRow(i, grad_i); + calcGrad(Dui.GetData(), Trans.InverseJacobian().GetData(), grad_i.GetData()); //curl.GetRow(i, curl_i); // get distance function value at node - double d = 1.0; + double d = xi(1); //temp solution, y distance from wall // accumulate source terms - double src = calcSASource( + double src = calcSASource( ui.GetData(), grad_i.GetData(), sacs.GetData()); // use negative model if turbulent viscosity is negative if (ui(dim+2) < 0) { - src += calcSANegativeProduction( + src += calcSANegativeProduction( ui.GetData(), S, sacs.GetData()); - src += calcSANegativeDestruction( + src += calcSANegativeDestruction( ui.GetData(), d, sacs.GetData()); } else { - src += calcSAProduction( + src += calcSAProduction( ui.GetData(), mu, d, S, sacs.GetData()); - src += calcSADestruction( + src += calcSADestruction( ui.GetData(), mu, d, S, sacs.GetData()); } @@ -76,28 +85,39 @@ void SASourceIntegrator::AssembleElementGrad( const mfem::Vector &elfun, mfem::DenseMatrix &elmat) { using namespace mfem; - using namespace std; const SBPFiniteElement &sbp = dynamic_cast(fe); int num_nodes = sbp.GetDof(); #ifdef MFEM_THREAD_SAFE Vector ui, xi, uj, grad_i, curl_i; DenseMatrix grad, curl; #endif + elvect.SetSize(num_states * num_nodes); ui.SetSize(num_states); xi.SetSize(dim); grad.SetSize(num_nodes, dim); curl.SetSize(num_nodes, 3); grad_i.SetSize(dim); curl_i.SetSize(3); - DenseMatrix u(elfun.GetData(), num_nodes, num_states); - - //precompute vorticity and derivatives - calcVorticitySBP(u, sbp, Trans, curl); + DenseMatrix u(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well + DenseMatrix Qu(num_nodes, num_states); + DenseMatrix HQu(num_nodes, num_states); + DenseMatrix Dui(num_states, dim); + vector jac_curl(dim); + vector jac_grad(dim); + for (int d = 0; d < dim; ++d) + { + jac_curl[d].SetSize(3, num_states); + jac_grad[d].SetSize(dim, num_states); + } - //precompute gradient and derivatives - calcGradSBP(u, sbp, Trans, grad); + // get Du + for(int di = 0; di < dim; di++) + { + sbp.multWeakOperator(di, u, Qu); + sbp.multNormMatrix(Qu, HQu); + //dxi.CopyMN(HQu, q.Height(), dim, 1, dim+1, di*q.Height(), 0); + } - elmat = 0.0; for (int i = 0; i < num_nodes; ++i) { // get the Jacobian (Trans.Weight) and cubature weight (node.weight) @@ -105,126 +125,43 @@ void SASourceIntegrator::AssembleElementGrad( Trans.SetIntPoint(&node); u.GetRow(i, ui); Trans.Transform(node, xi); + Dui.Set(1.0, (HQu.GetData() + i*dim*num_states)); // compute vorticity at node and take magnitude - curl.GetRow(i, curl_i); + calcVorticity(Dui.GetData(), Trans.InverseJacobian().GetData(), curl_i.GetData()); //curl.GetRow(i, curl_i); double S = curl_i.Norml2(); + // compute vorticity magnitude gradient + calcVorticityJacDw(Dui.GetData(), Trans.InverseJacobian().GetData(), jac_curl); // compute gradient of turbulent viscosity at node - grad.GetRow(i, grad_i); + calcGrad(Dui.GetData(), Trans.InverseJacobian().GetData(), grad_i.GetData()); //curl.GetRow(i, curl_i); + calcGradJacDw(Dui.GetData(), Trans.InverseJacobian().GetData(), jac_grad); // get distance function value at node - double d = 1.0; + double d = xi(1); //temp solution, y distance from wall // accumulate source terms - double src = calcSASource( + adouble src = calcSASource( ui.GetData(), grad_i.GetData(), sacs.GetData()); - double P; double D; // use negative model if turbulent viscosity is negative if (ui(dim+2) < 0) { - P = calcSANegativeProduction( + src += calcSANegativeProduction( ui.GetData(), S, sacs.GetData()); - D = calcSANegativeDestruction( + src += calcSANegativeDestruction( ui.GetData(), d, sacs.GetData()); } else { - P = calcSAProduction( + src += calcSAProduction( ui.GetData(), mu, d, S, sacs.GetData()); - D = calcSADestruction( + src += calcSADestruction( ui.GetData(), mu, d, S, sacs.GetData()); } - //res(i, dim+2) += alpha * Trans.Weight() * node.weight * src; + res(i, dim+2) += alpha * Trans.Weight() * node.weight * src; } // loop over element nodes i -} - -template -void SASourceIntegrator::calcVorticitySBP(const mfem::DenseMatrix &q, const mfem::FiniteElement &fe, - mfem::ElementTransformation &Trans, mfem::DenseMatrix curl) -{ - using namespace mfem; - const SBPFiniteElement &sbp = dynamic_cast(fe); - int num_nodes = sbp.GetDof(); - DenseMatrix dq(q.Height(), q.Width()); //contains state derivatives in reference space - DenseMatrix dxi(dim*q.Height(), dim); //contains velocity derivatives in reference space - dq = 0.0; - for(int di = 0; di < dim; di++) - { - sbp.multWeakOperator(di, q, dq); - //need to scale with 1/H, probably when looping over nodes - dxi.CopyMN(dq, q.Height(), dim, 1, dim+1, di*q.Height(), 0); - } - - DenseMatrix dx(dim, dim); //contains velocity derivatives in absolute space - DenseMatrix dxin(dim, dim); //contains velocity derivatives in reference space for a node - Vector dxrow(dim); Vector curln(dim); - for (int i = 0; i < num_nodes; ++i) - { - // get the Jacobian - IntegrationPoint &node = sbp.GetNodes().IntPoint(i); - Trans.SetIntPoint(&node); - - // store nodal derivatives in a single matrix - for(int di = 0; di < dim; di++) - { - dxi.GetRow(di*q.Height() + i, dxrow); - dxin.SetRow(di, dxrow); - } - - // compute absolute derivatives - MultAtB(Trans.InverseJacobian(), dxin, dx); - - // compute curl at node and append - if(dim == 2) - { - curl(i, 0) = 0; - curl(i, 1) = 0; - curl(i, 2) = dx(1,0) - dx(0,1); - } - if(dim == 3) - { - curl(i, 0) = dx(2,1) - dx(1,2); - curl(i, 1) = dx(0,2) - dx(2,0); - curl(i, 2) = dx(1,0) - dx(0,1); - } - } -} - -template -void SASourceIntegrator::calcGradSBP(const mfem::DenseMatrix &q, const mfem::FiniteElement &fe, - mfem::ElementTransformation &Trans, mfem::DenseMatrix grad) -{ - using namespace mfem; - const SBPFiniteElement &sbp = dynamic_cast(fe); - int num_nodes = sbp.GetDof(); - DenseMatrix dq(q.Height(), q.Width()); //contains state derivatives in reference space - DenseMatrix dnu(q.Height(), dim); //contains turb variable derivatives in reference space - dq = 0.0; - for(int di = 0; di < dim; di++) - { - sbp.multWeakOperator(di, q, dq); - //need to scale with 1/H - dnu.SetCol(di, dq.GetColumn(dim+2)); - } - - Vector dnurow(dim); Vector gradn(dim); - for (int i = 0; i < num_nodes; ++i) - { - // get the Jacobian - IntegrationPoint &node = sbp.GetNodes().IntPoint(i); - Trans.SetIntPoint(&node); - - // store nodal grad in a vector - dnu.GetRow(i, dnurow); - - // compute absolute derivatives - Trans.InverseJacobian().MultTranspose(dnurow, gradn); - - // append result - grad.SetRow(i, gradn); - } + elmat *= alpha; } //==================================================================================== @@ -303,6 +240,385 @@ void SAInviscidIntegrator::calcFluxJacStates( //==================================================================================== //SA Boundary Integrator Methods +//============================================================================== +//SA No-Slip Adiabatic Wall Integrator methods +template +void SANoSlipAdiabaticWallBC::calcFlux(const mfem::Vector &x, + const mfem::Vector &dir, double jac, + const mfem::Vector &q, + const mfem::DenseMatrix &Dw, + mfem::Vector &flux_vec) +{ + // Step 1: apply the EC slip wall flux + calcSlipWallFlux(x.GetData(), dir.GetData(), q.GetData(), + flux_vec.GetData()); + flux_vec(dim+2) = 0.0; + // Step 2: evaluate the adiabatic flux + double mu_Re = mu; + if (mu < 0.0) + { + mu_Re = calcSutherlandViscosity(q.GetData()); + } + mu_Re /= Re; + double fv1 = calcSACoefficient(q.GetData(), mu_Re, + sacs.GetData()); + mu_Re += q(dim+2)*q(0)*fv1; + calcAdiabaticWallFlux(dir.GetData(), mu_Re, Pr, q.GetData(), + Dw.GetData(), work_vec.GetData()); + flux_vec -= work_vec; // note the minus sign!!! + // evaluate wall normal eddy viscosity flux + double grad[dim]; + for (int di = 0; di < dim; di++) + grad[di] = Dw(dim+2, di); + double SAflux = dot(dir.GetData(), grad); + flux_vec(dim+2) -= (mu_Re/q(0) + q(dim+2))*SAflux/sacs(2); + // Step 3: evaluate the no-slip penalty + calcNoSlipPenaltyFlux(dir.GetData(), jac, mu_Re, Pr, qfs.GetData(), + q.GetData(), work_vec.GetData()); + flux_vec += work_vec; + double dnu = q(dim+2); + double dnuflux = (mu_Re/q(0) + q(dim+2))*dnu/sacs(2); + double fac = sqrt(dot(dir, dir))/jac; + flux_vec(dim+2) += dnuflux*fac; +} + +template +void SANoSlipAdiabaticWallBC::calcFluxJacState( + const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::DenseMatrix &flux_jac) +{ + // create containers for active double objects for each input + int Dw_size = Dw.Height() * Dw.Width(); + std::vector x_a(x.Size()); + std::vector q_a(q.Size()); + std::vector Dw_a(Dw_size); + std::vector work_vec_a(work_vec.Size()); + std::vector dir_a(dir.Size()); + std::vector qfs_a(qfs.Size()); + std::vector sacs_a(sacs.Size()); + // initialize active double containers with data from inputs + adept::set_values(q_a.data(), q.Size(), q.GetData()); + adept::set_values(dir_a.data(), dir.Size(), dir.GetData()); + adept::set_values(Dw_a.data(), Dw_size, Dw.GetData()); + adept::set_values(qfs_a.data(), qfs.Size(), qfs.GetData()); + adept::set_values(x_a.data(), x.Size(), x.GetData()); + adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); + // start new stack recording + this->stack.new_recording(); + // create container for active double flux output + std::vector flux_a(q.Size()); + // Step 1: apply the EC slip wall flux + mach::calcSlipWallFlux(x_a.data(), dir_a.data(), q_a.data(), + flux_a.data()); + // Step 2: evaluate the adiabatic flux + adouble mu_Re = mu; + if (mu < 0.0) + { + mu_Re = mach::calcSutherlandViscosity(q_a.data()); + } + mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + sacs_a.data()); + mu_Re += q_a[dim+2]*q_a[0]*fv1; + mach::calcAdiabaticWallFlux(dir_a.data(), mu_Re, Pr, q_a.data(), + Dw_a.data(), work_vec_a.data()); + for (int i = 0; i < flux_a.size(); ++i) + { + flux_a[i] -= work_vec_a[i]; // note the minus sign!!! + } + // evaluate wall normal eddy viscosity flux + adouble grad[dim]; + for (int di = 0; di < dim; di++) + grad[di] = Dw_a[dim+2 + di*(dim+3)]; + adouble SAflux = dot(dir_a.data(), grad); + flux_a[dim+2] -= (mu_Re/q_a[0] + q_a[dim+2])*SAflux/sacs_a[2]; + // Step 3: evaluate the no-slip penalty + mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re, Pr, qfs_a.data(), + q_a.data(), work_vec_a.data()); + for (int i = 0; i < flux_a.size(); ++i) + { + flux_a[i] += work_vec_a[i]; + } + adouble dnu = q_a[dim+2]; + adouble dnuflux = (mu_Re/q_a[0] + q_a[dim+2])*dnu/sacs_a[2]; + adouble fac = sqrt(dot(dir_a.data(), dir_a.data()))/jac; + flux_a[dim+2] += dnuflux*fac; + + this->stack.independent(q_a.data(), q.Size()); + this->stack.dependent(flux_a.data(), q.Size()); + this->stack.jacobian(flux_jac.GetData()); +} + +template +void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + vector &flux_jac) +{ + // create containers for active double objects for each input + int Dw_size = Dw.Height() * Dw.Width(); + std::vector x_a(x.Size()); + std::vector q_a(q.Size()); + std::vector Dw_a(Dw_size); + std::vector work_vec_a(work_vec.Size()); + std::vector dir_a(dir.Size()); + std::vector qfs_a(qfs.Size()); + std::vector sacs_a(sacs.Size()); + // initialize active double containers with data from inputs + adept::set_values(q_a.data(), q.Size(), q.GetData()); + adept::set_values(dir_a.data(), dir.Size(), dir.GetData()); + adept::set_values(Dw_a.data(), Dw_size, Dw.GetData()); + adept::set_values(qfs_a.data(), qfs.Size(), qfs.GetData()); + adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); + // start new stack recording + this->stack.new_recording(); + // create container for active double flux output + std::vector flux_a(q.Size()); + // Step 1: apply the EC slip wall flux + mach::calcSlipWallFlux(x_a.data(), dir_a.data(), q_a.data(), + flux_a.data()); + // Step 2: evaluate the adiabatic flux + adouble mu_Re = mu; + if (mu < 0.0) + { + mu_Re = mach::calcSutherlandViscosity(q_a.data()); + } + mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + sacs_a.data()); + mu_Re += q_a[dim+2]*q_a[0]*fv1; + mach::calcAdiabaticWallFlux(dir_a.data(), mu_Re, Pr, q_a.data(), + Dw_a.data(), work_vec_a.data()); + for (int i = 0; i < flux_a.size(); ++i) + { + flux_a[i] -= work_vec_a[i]; // note the minus sign!!! + } + // evaluate wall normal eddy viscosity flux + adouble grad[dim]; + for (int di = 0; di < dim; di++) + grad[di] = Dw_a[dim+2 + di*(dim+3)]; + adouble SAflux = dot(dir_a.data(), grad); + flux_a[dim+2] -= (mu_Re/q_a[0] + q_a[dim+2])*SAflux/sacs_a[2]; + // Step 3: evaluate the no-slip penalty + mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re, Pr, qfs_a.data(), + q_a.data(), work_vec_a.data()); + for (int i = 0; i < flux_a.size(); ++i) + { + flux_a[i] += work_vec_a[i]; + } + adouble dnu = q_a[dim+2]; + adouble dnuflux = (mu_Re/q_a[0] + q_a[dim+2])*dnu/sacs_a[2]; + adouble fac = sqrt(dot(dir_a.data(), dir_a.data()))/jac; + flux_a[dim+2] += dnuflux*fac; + + this->stack.independent(Dw_a.data(),Dw_size); + this->stack.dependent(flux_a.data(), q.Size()); + // compute and store jacobian in CDw_jac + mfem::Vector work(dim*this->num_states*this->num_states); + this->stack.jacobian(work.GetData()); + for (int i = 0; i < dim; ++i) + { + flux_jac[i] = (work.GetData() + i*this->num_states*this->num_states); + } +} + +//============================================================================== +//SA Viscous Slip-Wall Integrator methods +template +void SAViscousSlipWallBC::calcFlux(const mfem::Vector &x, + const mfem::Vector &dir, double jac, + const mfem::Vector &q, + const mfem::DenseMatrix &Dw, + mfem::Vector &flux_vec) +{ + // Part 1: apply the inviscid slip wall BCs + calcSlipWallFlux(x.GetData(), dir.GetData(), q.GetData(), + flux_vec.GetData()); + int Dw_size = Dw.Height() * Dw.Width(); + mfem::Vector Dw_work(Dw_size); + setZeroNormalDeriv(dir.GetData(), Dw.GetData(), + Dw_work.GetData()); + // SA treatment + double nrm[dim]; + double Dw_nrm = 0.0; + double fac = 1.0 / sqrt(dot(dir, dir)); + for (int i = 0; i < dim; ++i) + { + nrm[i] = dir(i) * fac; + Dw_nrm += Dw(dim+2, i)*nrm[i]; + } + for (int i = 0; i < dim; ++i) + { + Dw_work(dim+2 + i*(dim+3)) = Dw(dim+2, i) - nrm[i]*Dw_nrm; + } + + // Part 2: viscous BCs + double mu_Re = mu; + if (mu < 0.0) + { + mu_Re = calcSutherlandViscosity(q.GetData()); + } + mu_Re /= Re; + double fv1 = calcSACoefficient(q.GetData(), mu_Re, + sacs.GetData()); + mu_Re += q(dim+2)*q(0)*fv1; + for (int d = 0; d < dim; ++d) + { + work_vec = 0.0; + applyViscousScaling(d, mu_Re, Pr, q.GetData(), + Dw_work.GetData(), work_vec.GetData()); + work_vec *= dir(d); + flux_vec -= work_vec; + flux_vec(dim+2) -= (mu_Re/q(0) + q(dim+2))*Dw_work(dim+2 + d*(dim+3))/sacs(2); + } +} + +template +void SAViscousSlipWallBC::calcFluxJacState( + const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::DenseMatrix &flux_jac) +{ + // create containers for active double objects for each input + int Dw_size = Dw.Height() * Dw.Width(); + std::vector x_a(x.Size()); + std::vector q_a(q.Size()); + std::vector Dw_a(Dw_size); + std::vector work_vec_a(work_vec.Size()); + std::vector dir_a(dir.Size()); + std::vector sacs_a(sacs.Size()); + // initialize active double containers with data from inputs + adept::set_values(q_a.data(), q.Size(), q.GetData()); + adept::set_values(dir_a.data(), dir.Size(), dir.GetData()); + adept::set_values(Dw_a.data(), Dw_size, Dw.GetData()); + adept::set_values(x_a.data(), x.Size(), x.GetData()); + adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); + // start new stack recording + this->stack.new_recording(); + // create container for active double flux output + std::vector flux_a(q.Size()); + // Part 1: apply the inviscid slip wall BCs + calcSlipWallFlux(x_a.data(), dir_a.data(), q_a.data(), + flux_a.data()); + std::vector Dw_work_a(Dw_size); + setZeroNormalDeriv(dir_a.data(), Dw_a.data(), + Dw_work_a.data()); + // SA treatment + adouble nrm[dim]; + adouble Dw_nrm = 0.0; + adouble fac = 1.0 / sqrt(dot(dir_a.data(), dir_a.data())); + for (int i = 0; i < dim; ++i) + { + nrm[i] = dir_a[i] * fac; + Dw_nrm += Dw_a[dim+2 + i*(dim+3)]*nrm[i]; + } + for (int i = 0; i < dim; ++i) + { + Dw_work_a[dim+2 + i*(dim+3)] = Dw_a[dim+2 + i*(dim+3)] - nrm[i]*Dw_nrm; + } + + // Part 2: viscous BCs + adouble mu_Re = mu; + if (mu < 0.0) + { + mu_Re = calcSutherlandViscosity(q_a.data()); + } + mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + sacs_a.data()); + mu_Re += q_a[dim+2]*q_a[0]*fv1; + for (int d = 0; d < dim; ++d) + { + applyViscousScaling(d, mu_Re, Pr, q_a.data(), + Dw_work_a.data(), work_vec_a.data()); + for (int k = 0; k < dim+2; k++) + { + work_vec_a[k] *= dir_a[d]; + flux_a[k] -= work_vec_a[k]; + } + flux_a[dim+2] -= (mu_Re/q_a[0] + q_a[dim+2])*Dw_work_a[dim+2 + d*(dim+3)]/sacs_a[2]; + } + + this->stack.independent(q_a.data(), q.Size()); + this->stack.dependent(flux_a.data(), q.Size()); + this->stack.jacobian(flux_jac.GetData()); +} + +template +void SAViscousSlipWallBC::calcFluxJacDw(const mfem::Vector &x, const mfem::Vector &dir, double jac, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + vector &flux_jac) +{ + // create containers for active double objects for each input + int Dw_size = Dw.Height() * Dw.Width(); + std::vector x_a(x.Size()); + std::vector q_a(q.Size()); + std::vector Dw_a(Dw_size); + std::vector work_vec_a(work_vec.Size()); + std::vector dir_a(dir.Size()); + std::vector sacs_a(sacs.Size()); + // initialize active double containers with data from inputs + adept::set_values(q_a.data(), q.Size(), q.GetData()); + adept::set_values(dir_a.data(), dir.Size(), dir.GetData()); + adept::set_values(Dw_a.data(), Dw_size, Dw.GetData()); + adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); + // start new stack recording + this->stack.new_recording(); + // create container for active double flux output + std::vector flux_a(q.Size()); + // Part 1: apply the inviscid slip wall BCs + calcSlipWallFlux(x_a.data(), dir_a.data(), q_a.data(), + flux_a.data()); + std::vector Dw_work_a(Dw_size); + setZeroNormalDeriv(dir_a.data(), Dw_a.data(), + Dw_work_a.data()); + // SA treatment + adouble nrm[dim]; + adouble Dw_nrm = 0.0; + adouble fac = 1.0 / sqrt(dot(dir_a.data(), dir_a.data())); + for (int i = 0; i < dim; ++i) + { + nrm[i] = dir_a[i] * fac; + Dw_nrm += Dw_a[dim+2 + i*(dim+3)]*nrm[i]; + } + for (int i = 0; i < dim; ++i) + { + Dw_work_a[dim+2 + i*(dim+3)] = Dw_a[dim+2 + i*(dim+3)] - nrm[i]*Dw_nrm; + } + + // Part 2: viscous BCs + adouble mu_Re = mu; + if (mu < 0.0) + { + mu_Re = calcSutherlandViscosity(q_a.data()); + } + mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + sacs_a.data()); + mu_Re += q_a[dim+2]*q_a[0]*fv1; + for (int d = 0; d < dim; ++d) + { + applyViscousScaling(d, mu_Re, Pr, q_a.data(), + Dw_work_a.data(), work_vec_a.data()); + for (int k = 0; k < dim+2; k++) + { + work_vec_a[k] *= dir_a[d]; + flux_a[k] -= work_vec_a[k]; + } + flux_a[dim+2] -= (mu_Re/q_a[0] + q_a[dim+2])*Dw_work_a[dim+2 + d*(dim+3)]/sacs_a[2]; + } + + this->stack.independent(Dw_a.data(),Dw_size); + this->stack.dependent(flux_a.data(), q.Size()); + // compute and store jacobian in CDw_jac + mfem::Vector work(dim*this->num_states*this->num_states); + this->stack.jacobian(work.GetData()); + for (int i = 0; i < dim; ++i) + { + flux_jac[i] = (work.GetData() + i*this->num_states*this->num_states); + } +} + //==================================================================================== //SA Far Field Integrator Methods template @@ -586,4 +902,106 @@ void SALPSIntegrator::applyScalingJacV( this->stack.dependent(mat_vec_a.data(), q.Size()); // Calculate the jabobian this->stack.jacobian(mat_vec_jac.GetData()); +} + +//============================================================================== +// SAViscousIntegrator methods +template +void SAViscousIntegrator::applyScaling(int d, const mfem::Vector &x, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::Vector &CDw) +{ + double mu_Re = mu; + if (mu < 0.0) + { + mu_Re = calcSutherlandViscosity(q.GetData()); + } + mu_Re /= Re; + double fv1 = calcSACoefficient(q.GetData(), mu_Re, + sacs.GetData()); + mu_Re += q(dim+2)*q(0)*fv1; + applyViscousScaling(d, mu_Re, Pr, q.GetData(), Dw.GetData(), + CDw.GetData()); + CDw(dim+2) = (mu_Re/q(0) + q(dim+2))*Dw(dim+2, d)/sacs(2); +} + +template +void SAViscousIntegrator::applyScalingJacState(int d, const mfem::Vector &x, + const mfem::Vector &q, const mfem::DenseMatrix &Dw, + mfem::DenseMatrix &CDw_jac) +{ + // vector of active input variables + int Dw_size = Dw.Height() * Dw.Width(); + std::vector q_a(q.Size()); + std::vector Dw_a(Dw_size); + std::vector sacs_a(sacs.Size()); + // initialize adouble inputs + adept::set_values(q_a.data(), q.Size(), q.GetData()); + adept::set_values(Dw_a.data(), Dw_size, Dw.GetData()); + adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); + // start recording + this->stack.new_recording(); + // create vector of active output variables + std::vector CDw_a(q.Size()); + // run algorithm + adouble mu_Re = mu; + if (mu < 0.0) + { + mu_Re = calcSutherlandViscosity(q_a.data()); + } + mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + sacs_a.data()); + mu_Re += q_a[dim+2]*q_a[0]*fv1; + applyViscousScaling(d, mu_Re, Pr, q_a.data(), Dw_a.data(), + CDw_a.data()); + CDw_a[dim+2] = (mu_Re/q_a[0] + q_a[dim+2])*Dw_a[dim+2 + d*(dim+3)]/sacs_a[2]; + // identify independent and dependent variables + this->stack.independent(q_a.data(), q.Size()); + this->stack.dependent(CDw_a.data(), q.Size()); + // compute and store jacobian in CDw_jac + this->stack.jacobian(CDw_jac.GetData()); +} + +template +void SAViscousIntegrator::applyScalingJacDw( + int d, const mfem::Vector &x, const mfem::Vector &q, + const mfem::DenseMatrix &Dw, vector &CDw_jac) +{ + // vector of active input variables + int Dw_size = Dw.Height() * Dw.Width(); + std::vector q_a(q.Size()); + std::vector Dw_a(Dw_size); + std::vector sacs_a(sacs.Size()); + // initialize adouble inputs + adept::set_values(q_a.data(), q.Size(), q.GetData()); + adept::set_values(Dw_a.data(), Dw_size, Dw.GetData()); + adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); + // start recording + this->stack.new_recording(); + // create vector of active output variables + std::vector CDw_a(q.Size()); + // run algorithm + adouble mu_Re = mu; + if (mu < 0.0) + { + mu_Re = calcSutherlandViscosity(q_a.data()); + } + mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + sacs_a.data()); + mu_Re += q_a[dim+2]*q_a[0]*fv1; + applyViscousScaling(d, mu_Re, Pr, q_a.data(), Dw_a.data(), + CDw_a.data()); + CDw_a[dim+2] = (mu_Re/q_a[0] + q_a[dim+2])*Dw_a[dim+2 + d*(dim+3)]/sacs_a[2]; + // identify independent and dependent variables + this->stack.independent(Dw_a.data(), Dw_size); + this->stack.dependent(CDw_a.data(), q.Size()); + // compute and store jacobian in CDw_jac + mfem::Vector work(dim*this->num_states*this->num_states); + this->stack.jacobian(work.GetData()); + for (int i = 0; i < dim; ++i) + { + CDw_jac[i] = (work.GetData() + i*this->num_states*this->num_states); + } } \ No newline at end of file diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 31578a3c..c73bea1b 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -27,6 +27,7 @@ set(FLUID_TEST_SRCS test_utils test_thermal_integ test_res_integ + test_rans_fluxes test_rans_integ ) diff --git a/test/unit/euler_test_data.hpp b/test/unit/euler_test_data.hpp index 01f541b7..d9800f9b 100644 --- a/test/unit/euler_test_data.hpp +++ b/test/unit/euler_test_data.hpp @@ -63,6 +63,9 @@ extern double adjJ_data[9]; // Use this for spatial derivatives of entropy-variables extern double delw_data[15]; +// Spalart-Allmaras model parameters +const double sa_params[11] = {0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16}; + /// Returns a perturbed version of the baseline flow state /// \param[in] x - coordinates (not used) /// \param[out] u - pertrubed state variable diff --git a/test/unit/test_rans_fluxes.cpp b/test/unit/test_rans_fluxes.cpp index e69de29b..ffd9b0f0 100644 --- a/test/unit/test_rans_fluxes.cpp +++ b/test/unit/test_rans_fluxes.cpp @@ -0,0 +1,142 @@ +#include + +#include "catch.hpp" +#include "mfem.hpp" +#include "euler_integ.hpp" +#include "rans_fluxes.hpp" +#include "rans_integ.hpp" +#include "euler_test_data.hpp" + +TEMPLATE_TEST_CASE_SIG("SA calcVorticity Jacobian", "[SAVorticity]", + ((int dim), dim), 2, 3) +{ + using namespace euler_data; + mfem::DenseMatrix Dw(delw_data, dim+3, dim); + mfem::DenseMatrix trans_jac(adjJ_data, dim, dim); + mfem::Vector curl_plus(3); + mfem::Vector curl_minus(3); + mfem::Vector v(dim+3); + mfem::Vector jac_v(3); + double delta = 1e-5; + + for (int di = 0; di < dim; ++di) + { + Dw(dim+2, di) = 0.7; + } + // create perturbation vector + for (int di = 0; di < dim + 3; ++di) + { + v(di) = vec_pert[di]; + } + adept::Stack stack; + + DYNAMIC_SECTION("Jacobians w.r.t Dw is correct") + { + std::vector mat_vec_jac(dim); + for (int d = 0; d < dim; ++d) + { + mat_vec_jac[d].SetSize(3, dim+3); + } + // compute the jacobian + mach::calcVorticityJacDw(stack, Dw.GetData(), trans_jac.GetData(), + mat_vec_jac); + + for (int d = 0; d < dim; ++d) + { + // perturb one column of delw everytime + mfem::DenseMatrix Dw_plus(Dw), Dw_minus(Dw); + for (int s = 0; s < dim+3; ++s) + { + Dw_plus.GetColumn(d)[s] += v(s) * delta; + Dw_minus.GetColumn(d)[s] -= v(s) * delta; + } + // get perturbed states conv vector + mach::calcVorticity(Dw_plus.GetData(), trans_jac.GetData(), + curl_plus.GetData()); + mach::calcVorticity(Dw_minus.GetData(), trans_jac.GetData(), + curl_minus.GetData()); + + mat_vec_jac[d].Mult(v, jac_v); + + // finite difference jacobian + mfem::Vector jac_v_fd(3); + jac_v_fd = curl_plus; + jac_v_fd -= curl_minus; + jac_v_fd /= 2.0 * delta; + //std::cout << "viscous applyScaling jac Dw:" << std::endl; + for (int j = 0; j < 3; ++j) + { + std::cout << "FD " << j << " Deriv: " << jac_v_fd[j] << std::endl; + std::cout << "AN " << j << " Deriv: " << jac_v(j) << std::endl; + REQUIRE(jac_v(j) == Approx(jac_v_fd[j]).margin(1e-10)); + } + } + } +} + +TEMPLATE_TEST_CASE_SIG("SA calcGrad Jacobian", "[SAVorticity]", + ((int dim), dim), 2, 3) +{ + using namespace euler_data; + mfem::DenseMatrix Dw(delw_data, dim+3, dim); + mfem::DenseMatrix trans_jac(adjJ_data, dim, dim); + mfem::Vector grad_plus(dim); + mfem::Vector grad_minus(dim); + mfem::Vector v(dim+3); + mfem::Vector jac_v(dim); + double delta = 1e-5; + + for (int di = 0; di < dim; ++di) + { + Dw(dim+2, di) = 0.7; + } + // create perturbation vector + for (int di = 0; di < dim + 3; ++di) + { + v(di) = vec_pert[di]; + } + adept::Stack stack; + + DYNAMIC_SECTION("Jacobians w.r.t Dw is correct") + { + std::vector mat_vec_jac(dim); + for (int d = 0; d < dim; ++d) + { + mat_vec_jac[d].SetSize(dim, dim+3); + } + // compute the jacobian + mach::calcGradJacDw(stack, Dw.GetData(), trans_jac.GetData(), + mat_vec_jac); + + for (int d = 0; d < dim; ++d) + { + // perturb one column of delw everytime + mfem::DenseMatrix Dw_plus(Dw), Dw_minus(Dw); + for (int s = 0; s < dim+3; ++s) + { + Dw_plus.GetColumn(d)[s] += v(s) * delta; + Dw_minus.GetColumn(d)[s] -= v(s) * delta; + } + // get perturbed states conv vector + mach::calcGrad(Dw_plus.GetData(), trans_jac.GetData(), + grad_plus.GetData()); + mach::calcGrad(Dw_minus.GetData(), trans_jac.GetData(), + grad_minus.GetData()); + + mat_vec_jac[d].Mult(v, jac_v); + + // finite difference jacobian + mfem::Vector jac_v_fd(dim); + jac_v_fd = grad_plus; + jac_v_fd -= grad_minus; + jac_v_fd /= 2.0 * delta; + //std::cout << "viscous applyScaling jac Dw:" << std::endl; + for (int j = 0; j < dim; ++j) + { + std::cout << "FD " << j << " Deriv: " << jac_v_fd[j] << std::endl; + std::cout << "AN " << j << " Deriv: " << jac_v(j) << std::endl; + REQUIRE(jac_v(j) == Approx(jac_v_fd[j]).margin(1e-10)); + } + } + } +} \ No newline at end of file diff --git a/test/unit/test_rans_integ.cpp b/test/unit/test_rans_integ.cpp index 8626e2c4..0a1c2c01 100644 --- a/test/unit/test_rans_integ.cpp +++ b/test/unit/test_rans_integ.cpp @@ -7,6 +7,7 @@ #include "rans_integ.hpp" #include "euler_test_data.hpp" + TEMPLATE_TEST_CASE_SIG("SA Inviscid Flux Test", "[sa_inviscid_flux_test]", ((int dim), dim), 1, 2, 3) { @@ -38,8 +39,8 @@ TEMPLATE_TEST_CASE_SIG("SA Inviscid Flux Test", "[sa_inviscid_flux_test]", // check if euler variables are the same as before for(int n = 0; n < dim+2; n++) { - std::cout << "Ismail-Roe " << n << " Flux: " << flux1(n) << std::endl; - std::cout << "Spalart-Allmaras " << n << " Flux: " << flux2(n) << std::endl; + // std::cout << "Ismail-Roe " << n << " Flux: " << flux1(n) << std::endl; + // std::cout << "Spalart-Allmaras " << n << " Flux: " << flux2(n) << std::endl; REQUIRE(flux1(n) - flux2(n) == Approx(0.0)); } } @@ -160,7 +161,7 @@ TEMPLATE_TEST_CASE_SIG("SAFarFieldBC Jacobian", "[SAFarFieldBC]", if(di == 1) nrm *= -1.0; - DYNAMIC_SECTION("SA Far-Field BC safarfieldinteg jacobian is correct w.r.t state when di = "< sanoslipinteg(diff_stack, &fe_coll, Re, Pr, sacs, qfar); + // +ve perturbation + q_plus.Add(delta, v); + // -ve perturbation + q_minus.Add(-delta, v); + DYNAMIC_SECTION("SA No-Slip BC jacobian is correct w.r.t state") + { + + // get perturbed states flux vector + sanoslipinteg.calcFlux(x, nrm, 1.0, q_plus, Dw, flux_plus); + sanoslipinteg.calcFlux(x, nrm, 1.0, q_minus, Dw, flux_minus); + // compute the jacobian + sanoslipinteg.calcFluxJacState(x, nrm, 1.0, q, Dw, jac); + jac.Mult(v, jac_v); + // finite difference jacobian + mfem::Vector jac_v_fd(flux_plus); + jac_v_fd -= flux_minus; + jac_v_fd /= 2.0 * delta; + // compare each component of the matrix-vector products + for (int i = 0; i < dim + 3; ++i) + { + // std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + // std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; + REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); + } + } +} + +TEMPLATE_TEST_CASE_SIG("SANoSlipAdiabaticWallBC Dw Jacobian", "[SANoSlipAdiabaticWallBC]", + ((int dim), dim), 1, 2, 3) +{ + using namespace euler_data; + // copy the data into mfem vectors for convenience + mfem::Vector nrm(dim); mfem::Vector x(dim); + mfem::Vector q(dim + 3); + mfem::Vector qfar(dim + 3); + mfem::Vector flux(dim + 3); + mfem::Vector v(dim + 3); + mfem::Vector jac_v(dim + 3); + mfem::DenseMatrix Dw(delw_data, dim+3, dim); + mfem::DenseMatrix jac(dim + 3, dim + 3); + mfem::Vector sacs(11); + // create SA parameter vector + for (int di = 0; di < 11; ++di) + { + sacs(di) = sa_params[di]; + } + double delta = 1e-5; + + x = 0.0; + q(0) = rho; + q(dim + 1) = rhoe; + q(dim + 2) = 4; + for (int di = 0; di < dim; ++di) + { + q(di + 1) = rhou[di]; + Dw(dim+2, di) = 0.7; + } + qfar.Set(1.1, q); + // create direction vector + for (int di = 0; di < dim; ++di) + { + nrm(di) = dir[di]; + } + // create perturbation vector + for (int di = 0; di < dim + 3; ++di) + { + v(di) = vec_pert[di]; + } + // perturbed vectors + mfem::Vector flux_plus(q), flux_minus(q); + adept::Stack diff_stack; + mfem::H1_FECollection fe_coll(1); //dummy + double Re = 1.0; double Pr = 1.0; + mach::SANoSlipAdiabaticWallBC sanoslipinteg(diff_stack, &fe_coll, Re, Pr, sacs, qfar); + DYNAMIC_SECTION("SA No-Slip BC Dw jacobian is correct w.r.t state") + { + std::vector mat_vec_jac(dim); + for (int d = 0; d < dim; ++d) + { + mat_vec_jac[d].SetSize(dim+3); + } + // compute the jacobian + sanoslipinteg.calcFluxJacDw(x, nrm, 1.0, q, Dw, mat_vec_jac); + + for (int d = 0; d < dim; ++d) + { + // perturb one column of delw everytime + mfem::DenseMatrix Dw_plus(Dw), Dw_minus(Dw); + for (int s = 0; s < dim+3; ++s) + { + Dw_plus.GetColumn(d)[s] += v(s) * delta; + Dw_minus.GetColumn(d)[s] -= v(s) * delta; + } + // get perturbed states flux vector + sanoslipinteg.calcFlux(x, nrm, 1.0, q, Dw_plus, flux_plus); + sanoslipinteg.calcFlux(x, nrm, 1.0, q, Dw_minus, flux_minus); + + mat_vec_jac[d].Mult(v, jac_v); + // finite difference jacobian + mfem::Vector jac_v_fd(flux_plus); + jac_v_fd -= flux_minus; + jac_v_fd /= 2.0 * delta; + // compare each component of the matrix-vector products + for (int i = 0; i < dim + 3; ++i) + { + // std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + // std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; + REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); + } + } + } +} + +TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Jacobian", "[SASlipWallBC]", + ((int dim), dim), 1, 2, 3) +{ + using namespace euler_data; + // copy the data into mfem vectors for convenience + mfem::Vector nrm(dim); mfem::Vector x(dim); + mfem::Vector q(dim + 3); + mfem::Vector flux(dim + 3); + mfem::Vector v(dim + 3); + mfem::Vector jac_v(dim + 3); + mfem::DenseMatrix Dw(delw_data, dim+3, dim); + mfem::DenseMatrix jac(dim + 3, dim + 3); + mfem::Vector sacs(11); + // create SA parameter vector + for (int di = 0; di < 11; ++di) + { + sacs(di) = sa_params[di]; + } + double delta = 1e-5; + + x = 0.0; + q(0) = rho; + q(dim + 1) = rhoe; + q(dim + 2) = 4; + for (int di = 0; di < dim; ++di) + { + q(di + 1) = rhou[di]; + Dw(dim+2, di) = 0.7; + } + // create direction vector + for (int di = 0; di < dim; ++di) + { + nrm(di) = dir[di]; + } + // create perturbation vector + for (int di = 0; di < dim + 3; ++di) + { + v(di) = vec_pert[di]; + } + // perturbed vectors + mfem::Vector q_plus(q), q_minus(q); + mfem::Vector flux_plus(q), flux_minus(q); + adept::Stack diff_stack; + mfem::H1_FECollection fe_coll(1); //dummy + double Re = 1.0; double Pr = 1.0; + mach::SAViscousSlipWallBC saslipinteg(diff_stack, &fe_coll, Re, Pr, sacs); + // +ve perturbation + q_plus.Add(delta, v); + // -ve perturbation + q_minus.Add(-delta, v); + DYNAMIC_SECTION("SA Slip-Wall BC jacobian is correct w.r.t state") + { + + // get perturbed states flux vector + saslipinteg.calcFlux(x, nrm, 1.0, q_plus, Dw, flux_plus); + saslipinteg.calcFlux(x, nrm, 1.0, q_minus, Dw, flux_minus); + // compute the jacobian + saslipinteg.calcFluxJacState(x, nrm, 1.0, q, Dw, jac); + jac.Mult(v, jac_v); + // finite difference jacobian + mfem::Vector jac_v_fd(flux_plus); + jac_v_fd -= flux_minus; + jac_v_fd /= 2.0 * delta; + // compare each component of the matrix-vector products + for (int i = 0; i < dim + 3; ++i) + { + // std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + // std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; + REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); + } + } +} + +TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Dw Jacobian", "[SASlipWallBC]", + ((int dim), dim), 1, 2, 3) +{ + using namespace euler_data; + // copy the data into mfem vectors for convenience + mfem::Vector nrm(dim); mfem::Vector x(dim); + mfem::Vector q(dim + 3); + mfem::Vector flux(dim + 3); + mfem::Vector v(dim + 3); + mfem::Vector jac_v(dim + 3); + mfem::DenseMatrix Dw(delw_data, dim+3, dim); + mfem::DenseMatrix jac(dim + 3, dim + 3); + mfem::Vector sacs(11); + // create SA parameter vector + for (int di = 0; di < 11; ++di) + { + sacs(di) = sa_params[di]; + } + double delta = 1e-5; + + x = 0.0; + q(0) = rho; + q(dim + 1) = rhoe; + q(dim + 2) = 4; + for (int di = 0; di < dim; ++di) + { + q(di + 1) = rhou[di]; + Dw(dim+2, di) = 0.7; + } + // create direction vector + for (int di = 0; di < dim; ++di) + { + nrm(di) = dir[di]; + } + // create perturbation vector + for (int di = 0; di < dim + 3; ++di) + { + v(di) = vec_pert[di]; + } + // perturbed vectors + adept::Stack diff_stack; + mfem::H1_FECollection fe_coll(1); //dummy + double Re = 1.0; double Pr = 1.0; + mach::SAViscousSlipWallBC saslipinteg(diff_stack, &fe_coll, Re, Pr, sacs); + DYNAMIC_SECTION("SA Slip-Wall BC Dw jacobian is correct w.r.t state") + { + std::vector mat_vec_jac(dim); + for (int d = 0; d < dim; ++d) + { + mat_vec_jac[d].SetSize(dim+3); + } + // compute the jacobian + saslipinteg.calcFluxJacDw(x, nrm, 1.0, q, Dw, mat_vec_jac); + + for (int d = 0; d < dim; ++d) + { + // perturb one column of delw everytime + mfem::DenseMatrix Dw_plus(Dw), Dw_minus(Dw); + mfem::Vector flux_plus(q.Size()), flux_minus(q.Size()); + flux_plus = 0.0; flux_minus = 0.0; + for (int s = 0; s < dim+3; ++s) + { + Dw_plus.GetColumn(d)[s] += v(s) * delta; + Dw_minus.GetColumn(d)[s] -= v(s) * delta; + } + // get perturbed states flux vector + saslipinteg.calcFlux(x, nrm, 1.0, q, Dw_plus, flux_plus); + saslipinteg.calcFlux(x, nrm, 1.0, q, Dw_minus, flux_minus); + + mat_vec_jac[d].Mult(v, jac_v); + // finite difference jacobian + mfem::Vector jac_v_fd(flux_plus); + jac_v_fd -= flux_minus; + jac_v_fd /= 2.0 * delta; + // compare each component of the matrix-vector products + for (int i = 0; i < dim + 3; ++i) { std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; @@ -184,6 +504,198 @@ TEMPLATE_TEST_CASE_SIG("SAFarFieldBC Jacobian", "[SAFarFieldBC]", } } + +TEMPLATE_TEST_CASE_SIG("SAViscous Jacobian", "[SAViscous]", + ((int dim), dim), 1, 2, 3) +{ + using namespace euler_data; + // copy the data into mfem vectors for convenience + mfem::Vector q(dim + 3); + mfem::Vector conv(dim + 3); + mfem::Vector conv_plus(dim + 3); + mfem::Vector conv_minus(dim + 3); + mfem::Vector scale(dim + 3); + mfem::Vector scale_plus(dim + 3); + mfem::Vector scale_minus(dim + 3); + mfem::Vector scale_plus_2(dim + 3); + mfem::Vector scale_minus_2(dim + 3); + mfem::DenseMatrix Dw(delw_data, dim+3, dim); + mfem::Vector v(dim + 3); + mfem::DenseMatrix vm(dim, dim + 3); + mfem::Vector jac_v(dim + 3); + mfem::DenseMatrix adjJ(dim, dim); + mfem::DenseMatrix jac_conv(dim + 3, dim + 3); + mfem::DenseMatrix jac_scale(dim + 3, dim + 3); + double delta = 1e-5; + double Re = 1; double Pr = 1; + mfem::Vector sacs(11); + q(0) = rho; + q(dim + 1) = rhoe; + q(dim + 2) = 4; + for (int di = 0; di < dim; ++di) + { + q(di + 1) = rhou[di]; + Dw(dim+2, di) = 0.7; + } + // create SA parameter vector + for (int di = 0; di < 11; ++di) + { + sacs(di) = sa_params[di]; + } + // create perturbation vector + for (int di = 0; di < dim + 3; ++di) + { + v(di) = vec_pert[di]; + } + // create perturbation matrix + for (int di = 0; di < dim + 3; ++di) + { + for (int di2 = 0; di2 < dim; ++di2) + { + vm(di2, di) = (1.1 + 0.1*di2)*vec_pert[di]; + } + } + // perturbed vectors + mfem::Vector q_plus(q), q_minus(q); + adept::Stack diff_stack; + mach::SAViscousIntegrator saviscousinteg(diff_stack, Re, Pr, sacs); + for (int di = 0; di < dim; ++di) + { + DYNAMIC_SECTION("Jacobians w.r.t state is correct, dir"< saviscousinteg(diff_stack, Re, Pr, sacs); + + for (int di = 0; di < dim; ++di) + { + DYNAMIC_SECTION("Jacobians w.r.t Dw is correct, dir"< mat_vec_jac(dim); + for (int d = 0; d < dim; ++d) + { + mat_vec_jac[d].SetSize(dim+3); + } + // compute the jacobian + saviscousinteg.applyScalingJacDw(di, v, q, Dw, mat_vec_jac); + + for (int d = 0; d < dim; ++d) + { + // perturb one column of delw everytime + mfem::DenseMatrix Dw_plus(Dw), Dw_minus(Dw); + for (int s = 0; s < dim+3; ++s) + { + Dw_plus.GetColumn(d)[s] += v(s) * delta; + Dw_minus.GetColumn(d)[s] -= v(s) * delta; + } + // get perturbed states conv vector + saviscousinteg.applyScaling(di, v, q, Dw_plus, scale_plus_2); + saviscousinteg.applyScaling(di, v, q, Dw_minus, scale_minus_2); + + mat_vec_jac[d].Mult(v, jac_v); + // finite difference jacobian + mfem::Vector jac_v_fd(dim+3); + jac_v_fd = scale_plus_2; + jac_v_fd -= scale_minus_2; + jac_v_fd /= 2.0 * delta; + //std::cout << "viscous applyScaling jac Dw:" << std::endl; + for (int j = 0; j < dim + 3; ++j) + { + // std::cout << "FD " << j << " Deriv: " << jac_v_fd[j] << std::endl; + // std::cout << "AN " << j << " Deriv: " << jac_v(j) << std::endl; + REQUIRE(jac_v(j) == Approx(jac_v_fd[j]).margin(1e-10)); + } + } + } + } +} + + TEMPLATE_TEST_CASE_SIG("SALPS Jacobian", "[SALPS]", ((int dim), dim), 1, 2, 3) { @@ -249,11 +761,9 @@ TEMPLATE_TEST_CASE_SIG("SALPS Jacobian", "[SALPS]", jac_v_fd -= conv_minus; jac_v_fd /= 2.0 * delta; // compare each component of the matrix-vector products - std::cout << "convertVars jac:" << std::endl; + //std::cout << "convertVars jac:" << std::endl; for (int i = 0; i < dim + 3; ++i) { - std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; - std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); } @@ -262,11 +772,9 @@ TEMPLATE_TEST_CASE_SIG("SALPS Jacobian", "[SALPS]", jac_v_fd = scale_plus; jac_v_fd -= scale_minus; jac_v_fd /= 2.0 * delta; - std::cout << "applyScaling jac:" << std::endl; + //std::cout << "applyScaling jac:" << std::endl; for (int i = 0; i < dim + 3; ++i) { - std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; - std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); } @@ -275,16 +783,15 @@ TEMPLATE_TEST_CASE_SIG("SALPS Jacobian", "[SALPS]", jac_v_fd = scale_plus_2; jac_v_fd -= scale_minus_2; jac_v_fd /= 2.0 * delta; - std::cout << "applyScaling jac vec:" << std::endl; + //std::cout << "applyScaling jac vec:" << std::endl; for (int i = 0; i < dim + 3; ++i) { - std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; - std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); } } } +#if 0 TEST_CASE("SALPSIntegrator::AssembleElementGrad", "[SALPSIntegrator]") { using namespace mfem; @@ -342,7 +849,7 @@ TEST_CASE("SALPSIntegrator::AssembleElementGrad", "[SALPSIntegrator]") } } } - +#endif #if 0 TEMPLATE_TEST_CASE_SIG("SAInviscid Gradient", From 527e76436fbacf3a790207636505a57e0f425c73 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Thu, 13 Aug 2020 15:21:03 -0400 Subject: [PATCH 10/28] some derivatives implemented in source terms --- src/physics/fluidflow/rans_integ_def.hpp | 121 +++++++++++++++++++---- 1 file changed, 102 insertions(+), 19 deletions(-) diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index 6c34efda..29d34f2d 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -47,11 +47,11 @@ void SASourceIntegrator::AssembleElementVector( Dui.Set(1.0, (HQu.GetData() + i*dim*num_states)); // compute vorticity at node and take magnitude - calcVorticity(Dui.GetData(), Trans.InverseJacobian().GetData(), curl_i.GetData()); //curl.GetRow(i, curl_i); + calcVorticity(Dui.GetData(), Trans.InverseJacobian().GetData(), curl_i.GetData()); //curl.GetRow(i, curl_i); double S = curl_i.Norml2(); // compute gradient of turbulent viscosity at node - calcGrad(Dui.GetData(), Trans.InverseJacobian().GetData(), grad_i.GetData()); //curl.GetRow(i, curl_i); + calcGrad(Dui.GetData(), Trans.InverseJacobian().GetData(), grad_i.GetData()); //curl.GetRow(i, curl_i); // get distance function value at node double d = xi(1); //temp solution, y distance from wall @@ -91,7 +91,6 @@ void SASourceIntegrator::AssembleElementGrad( Vector ui, xi, uj, grad_i, curl_i; DenseMatrix grad, curl; #endif - elvect.SetSize(num_states * num_nodes); ui.SetSize(num_states); xi.SetSize(dim); grad.SetSize(num_nodes, dim); @@ -104,12 +103,21 @@ void SASourceIntegrator::AssembleElementGrad( DenseMatrix Dui(num_states, dim); vector jac_curl(dim); vector jac_grad(dim); + std::vector sacs_a(sacs.Size()); + adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); for (int d = 0; d < dim; ++d) { jac_curl[d].SetSize(3, num_states); jac_grad[d].SetSize(dim, num_states); } + // partial derivative terms + Vector dSdc(3); //partial vorticity mag (S) w.r.t. curl + Vector dSrcdu(num_states); //partial src w.r.t. u + Vector dSrcdS(1); //partial src w.r.t. S + Vector dSrcdgrad(dim); //partial src w.r.t grad + //need dgraddDu, dcdDu, dDudu + // get Du for(int di = 0; di < dim; di++) { @@ -126,40 +134,115 @@ void SASourceIntegrator::AssembleElementGrad( u.GetRow(i, ui); Trans.Transform(node, xi); Dui.Set(1.0, (HQu.GetData() + i*dim*num_states)); + int Dui_size = Dui.Height() * Dui.Width(); - // compute vorticity at node and take magnitude - calcVorticity(Dui.GetData(), Trans.InverseJacobian().GetData(), curl_i.GetData()); //curl.GetRow(i, curl_i); - double S = curl_i.Norml2(); - // compute vorticity magnitude gradient - calcVorticityJacDw(Dui.GetData(), Trans.InverseJacobian().GetData(), jac_curl); + // set adept inputs + std::vector ui_a(ui.Size()); + //std::vector Dui_a(Dui_size); + std::vector curl_i_a(curl_i.Size()); + std::vector grad_i_a(grad_i.Size()); + adouble S; + adouble mu_a = mu; + // initialize adouble inputs + adept::set_values(ui_a.data(), ui.Size(), ui.GetData()); + //adept::set_values(Dui_a.data(), Dw_size, Dui.GetData()); + adept::set_values(curl_i_a.data(), curl_i.Size(), curl_i.GetData()); + adept::set_values(grad_i_a.data(), grad_i.Size(), grad_i.GetData()); + + // compute vorticity at node + calcVorticity(Dui.GetData(), + Trans.InverseJacobian().GetData(), curl_i.GetData()); //curl.GetRow(i, curl_i); + calcVorticityJacDw(Dui.GetData(), + Trans.InverseJacobian().GetData(), jac_curl); // compute gradient of turbulent viscosity at node - calcGrad(Dui.GetData(), Trans.InverseJacobian().GetData(), grad_i.GetData()); //curl.GetRow(i, curl_i); - calcGradJacDw(Dui.GetData(), Trans.InverseJacobian().GetData(), jac_grad); + calcGrad(Dui.GetData(), + Trans.InverseJacobian().GetData(), grad_i.GetData()); //curl.GetRow(i, curl_i); + calcGradJacDw(Dui.GetData(), + Trans.InverseJacobian().GetData(), jac_grad); // get distance function value at node - double d = xi(1); //temp solution, y distance from wall + adouble d = xi(1); //temp solution, y distance from wall - // accumulate source terms + // vorticity magnitude deriv + this->stack.new_recording(); + S = sqrt(curl_i_a[0]*curl_i_a[0] + curl_i_a[1]*curl_i_a[1] +curl_i_a[2]*curl_i_a[2]); + this->stack.independent(curl_i_a.data(), curl_i.Size()); + this->stack.dependent(S); + this->stack.jacobian(dSdc.GetData()); + + // ui differentiation + this->stack.new_recording(); adouble src = calcSASource( - ui.GetData(), grad_i.GetData(), sacs.GetData()); + ui_a.data(), grad_i_a.data(), sacs_a.data()); // use negative model if turbulent viscosity is negative - if (ui(dim+2) < 0) + if (ui_a[dim+2] < 0) { src += calcSANegativeProduction( - ui.GetData(), S, sacs.GetData()); + ui_a.data(), S, sacs_a.data()); src += calcSANegativeDestruction( - ui.GetData(), d, sacs.GetData()); + ui_a.data(), d, sacs_a.data()); } else { src += calcSAProduction( - ui.GetData(), mu, d, S, sacs.GetData()); + ui_a.data(), mu_a, d, S, sacs_a.data()); src += calcSADestruction( - ui.GetData(), mu, d, S, sacs.GetData()); + ui_a.data(), mu_a, d, S, sacs_a.data()); } + this->stack.independent(ui_a.data(), ui.Size()); + this->stack.dependent(src); + this->stack.jacobian(dSrcdu.GetData()); + + // S differentiation + this->stack.new_recording(); + src = calcSASource( + ui_a.data(), grad_i_a.data(), sacs_a.data()); + // use negative model if turbulent viscosity is negative + if (ui_a[dim+2] < 0) + { + src += calcSANegativeProduction( + ui_a.data(), S, sacs_a.data()); + src += calcSANegativeDestruction( + ui_a.data(), d, sacs_a.data()); + } + else + { + src += calcSAProduction( + ui_a.data(), mu_a, d, S, sacs_a.data()); + src += calcSADestruction( + ui_a.data(), mu_a, d, S, sacs_a.data()); + } + this->stack.independent(S); + this->stack.dependent(src); + this->stack.jacobian(dSrcdS.GetData()); + + // grad differentiation + this->stack.new_recording(); + src = calcSASource( + ui_a.data(), grad_i_a.data(), sacs_a.data()); + // use negative model if turbulent viscosity is negative + if (ui_a[dim+2] < 0) + { + src += calcSANegativeProduction( + ui_a.data(), S, sacs_a.data()); + src += calcSANegativeDestruction( + ui_a.data(), d, sacs_a.data()); + } + else + { + src += calcSAProduction( + ui_a.data(), mu_a, d, S, sacs_a.data()); + src += calcSADestruction( + ui_a.data(), mu_a, d, S, sacs_a.data()); + } + this->stack.independent(grad_i_a.data(), grad_i_a.size()); + this->stack.dependent(src); + this->stack.jacobian(dSrcdgrad.GetData()); + + // Dui differentiation + - res(i, dim+2) += alpha * Trans.Weight() * node.weight * src; } // loop over element nodes i elmat *= alpha; } From bcb8b8d83a83e982bb2d7ead5327d68f03f9da84 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Mon, 24 Aug 2020 19:18:26 -0400 Subject: [PATCH 11/28] source term mostly differentiated, other slight change needed --- src/physics/fluidflow/rans_fluxes.hpp | 70 +++++---- src/physics/fluidflow/rans_integ.hpp | 175 +++++++++++++++++++++++ src/physics/fluidflow/rans_integ_def.hpp | 123 +++++++++++----- test/unit/CMakeLists.txt | 30 ++-- test/unit/euler_test_data.cpp | 25 ++++ test/unit/euler_test_data.hpp | 10 +- test/unit/test_rans_integ.cpp | 68 +++++++++ 7 files changed, 416 insertions(+), 85 deletions(-) diff --git a/src/physics/fluidflow/rans_fluxes.hpp b/src/physics/fluidflow/rans_fluxes.hpp index 82702840..a5a7f9d2 100644 --- a/src/physics/fluidflow/rans_fluxes.hpp +++ b/src/physics/fluidflow/rans_fluxes.hpp @@ -52,27 +52,6 @@ xdouble calcSALaminarSuppression(const xdouble *q, const xdouble mu, return ft2; } - - -/// Returns the modified vorticity in SA -/// \param[in] q - state used to define the destruction -/// \param[in] mu - **nondimensionalized** viscosity -/// \param[in] sacs - Spalart-Allmaras constants -/// \returns St - modified vorticity -/// \tparam xdouble - typically `double` or `adept::adouble` -/// \tparam dim - number of spatial dimensions (1, 2, or 3) -template -xdouble calcSAModifiedVorticity(const xdouble *q, const xdouble S, - const xdouble mu, const xdouble d, - const xdouble *sacs) -{ - xdouble nu_tilde = q[dim+2]; - xdouble kappa = sacs[3]; - xdouble fv2 = calcSAProductionCoefficient(q, mu, sacs); - xdouble St = S + nu_tilde*fv2/(kappa*kappa*d*d); - return St; -} - /// Returns the turbulent viscosity coefficient in SA /// \param[in] q - state used to define the destruction /// \param[in] mu - **nondimensionalized** viscosity @@ -106,11 +85,30 @@ xdouble calcSAProductionCoefficient(const xdouble *q, const xdouble mu, xdouble nu_tilde = q[dim+2]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; - xdouble fv1 = calcSACoefficient(q, mu, sacs); - xdouble fv2 = 1 - chi/(1 + chi*fv1); + xdouble fv1 = calcSACoefficient(q, mu, sacs); + xdouble fv2 = 1.0 - chi/(1.0 + chi*fv1); return fv2; } +/// Returns the modified vorticity in SA +/// \param[in] q - state used to define the destruction +/// \param[in] mu - **nondimensionalized** viscosity +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns St - modified vorticity +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSAModifiedVorticity(const xdouble *q, const xdouble S, + const xdouble mu, const xdouble d, + const xdouble *sacs) +{ + xdouble nu_tilde = q[dim+2]; + xdouble kappa = sacs[3]; + xdouble fv2 = calcSAProductionCoefficient(q, mu, sacs); + xdouble St = S + nu_tilde*fv2/(kappa*kappa*d*d); + return St; +} + /// Returns the destruction coefficient in SA /// \param[in] q - state used to define the destruction /// \param[in] mu - **nondimensionalized** viscosity @@ -129,13 +127,13 @@ xdouble calcSADestructionCoefficient(const xdouble *q, xdouble cw3 = sacs[5]; xdouble rlim = sacs[9]; - xdouble St = calcSAModifiedVorticity(q, S, mu, d, sacs); + xdouble St = calcSAModifiedVorticity(q, S, mu, d, sacs); xdouble work = nu_tilde/(St*kappa*kappa*d*d); xdouble r = std::min(work, rlim); - xdouble g = r + cw2*(std::pow(r, 6) - r); - xdouble work2 = (1 + std::pow(cw3, 6))/ - (std::pow(g, 6) + std::pow(cw3, 6)); - xdouble fw = g*std::pow(work2, (1/6)); + xdouble g = r + cw2*(pow(r, 6.0) - r); + xdouble work2 = (1.0 + pow(cw3, 6.0))/ + (pow(g, 6.0) + pow(cw3, 6.0)); + xdouble fw = g*pow(work2, (1.0/6.0)); return fw; } @@ -153,9 +151,9 @@ xdouble calcSAProduction(const xdouble *q, { xdouble cb1 = sacs[0]; xdouble nu_tilde = q[dim+2]; - xdouble St = calcSAModifiedVorticity(q, S, mu, d, sacs); - xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); - xdouble P = cb1*(1-ft2)*St*nu_tilde; + xdouble St = calcSAModifiedVorticity(q, S, mu, d, sacs); + xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); + xdouble P = cb1*(1.0-ft2)*St*nu_tilde; return P; } @@ -177,8 +175,8 @@ xdouble calcSADestruction(const xdouble *q, xdouble kappa = sacs[3]; xdouble chi_d = q[dim+2]/d; xdouble cw1 = cb1/(kappa*kappa) + (1+cb2)/sigma; - xdouble fw = calcSADestructionCoefficient(q, mu, d, S, sacs); - xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); + xdouble fw = calcSADestructionCoefficient(q, mu, d, S, sacs); + xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); xdouble D = (cw1*fw - (cb1/(kappa*kappa))*ft2)*chi_d*chi_d; return D; } @@ -197,7 +195,7 @@ xdouble calcSANegativeProduction(const xdouble *q, const xdouble S, xdouble cb1 = sacs[0]; xdouble ct3 = sacs[7]; xdouble nu_tilde = q[dim+2]; - xdouble Pn = cb1*(1-ct3)*S*nu_tilde; + xdouble Pn = cb1*(1.0-ct3)*S*nu_tilde; return Pn; } @@ -383,7 +381,7 @@ void calcVorticity(const xdouble *Dq, const xdouble *jac_inv, xdouble work = 0; for(int k = 0; k < dim; k++) { - work += Dq[di+k*(dim+3)]*jac_inv[k+d*dim]; + work += (Dq[di+k*(dim+3)])*jac_inv[k+d*dim]; } DqJ[di+d*(dim+3)] = work; } @@ -500,7 +498,7 @@ void calcGradJacDw(adept::Stack &stack, const double *Dq, const double *jac_inv, // identify independent and dependent variables stack.independent(Dq_a.data(), Dq_a.size()); stack.dependent(grad_a.data(), grad_a.size()); - // compute and store jacobian in jac_inv + // compute and store jacobian in jac_grad mfem::Vector work(dim*(dim+3)*dim); stack.jacobian_reverse(work.GetData()); for (int i = 0; i < dim; ++i) diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp index 7c56d7d0..44c18a4f 100644 --- a/src/physics/fluidflow/rans_integ.hpp +++ b/src/physics/fluidflow/rans_integ.hpp @@ -228,6 +228,41 @@ class SANoSlipAdiabaticWallBC : public ViscousBoundaryIntegrator &flux_jac); + /// Compute flux terms that are multiplied by test-function derivative + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] q - conservative variables at which to evaluate the fluxes + /// \param[out] flux_mat[:,di] - to be scaled by derivative `D_[di] v` + void calcFluxDv(const mfem::Vector &x, const mfem::Vector &dir, + const mfem::Vector &q, mfem::DenseMatrix &flux_mat) + { + flux_mat = 0.0; + } + + /// Compute the Jacobian of calcFluxDv w.r.t. state + /// \param[in] x - coordinate location at which fluxes are evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] flux_jac[di] - Jacobian of calcFluxDv[di] with respect to `q` + void calcFluxDvJacState(const mfem::Vector &x, const mfem::Vector dir, + const mfem::Vector &u, + std::vector &flux_jac) + { + for (int d = 0; d < dim; ++d) + flux_jac[d] = 0.0; + } + + /// Computes boundary node contribution to the surface force + /// \param[in] x - coordinate location at which function is evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian determinant (needed by some fluxes) + /// \param[in] u - state at which to evaluate the function + /// \param[in] Dw - `Dw[:,di]` is the derivative of `w` in direction `di` + /// \returns fun - stress at given point + double calcBndryFun(const mfem::Vector &x, const mfem::Vector &dir, + double jac, const mfem::Vector &u, + const mfem::DenseMatrix &Dw) {return 0.0;} + private: /// Reynolds number double Re; @@ -324,6 +359,41 @@ class SAViscousSlipWallBC : public ViscousBoundaryIntegrator &flux_jac); + /// Compute flux terms that are multiplied by test-function derivative + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] q - conservative variables at which to evaluate the fluxes + /// \param[out] flux_mat[:,di] - to be scaled by derivative `D_[di] v` + void calcFluxDv(const mfem::Vector &x, const mfem::Vector &dir, + const mfem::Vector &q, mfem::DenseMatrix &flux_mat) + { + flux_mat = 0.0; + } + + /// Compute the Jacobian of calcFluxDv w.r.t. state + /// \param[in] x - coordinate location at which fluxes are evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] flux_jac[di] - Jacobian of calcFluxDv[di] with respect to `q` + void calcFluxDvJacState(const mfem::Vector &x, const mfem::Vector dir, + const mfem::Vector &u, + std::vector &flux_jac) + { + for (int d = 0; d < dim; ++d) + flux_jac[d] = 0.0; + } + + /// Computes boundary node contribution to the surface force + /// \param[in] x - coordinate location at which function is evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian determinant (needed by some fluxes) + /// \param[in] u - state at which to evaluate the function + /// \param[in] Dw - `Dw[:,di]` is the derivative of `w` in direction `di` + /// \returns fun - stress at given point + double calcBndryFun(const mfem::Vector &x, const mfem::Vector &dir, + double jac, const mfem::Vector &u, + const mfem::DenseMatrix &Dw) {return 0.0;} + private: /// Reynolds number double Re; @@ -419,6 +489,41 @@ class SAFarFieldBC : public ViscousBoundaryIntegrator> const mfem::DenseMatrix &Dw, std::vector &flux_jac); + /// Compute flux terms that are multiplied by test-function derivative + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] q - conservative variables at which to evaluate the fluxes + /// \param[out] flux_mat[:,di] - to be scaled by derivative `D_[di] v` + void calcFluxDv(const mfem::Vector &x, const mfem::Vector &dir, + const mfem::Vector &q, mfem::DenseMatrix &flux_mat) + { + flux_mat = 0.0; + } + + /// Compute the Jacobian of calcFluxDv w.r.t. state + /// \param[in] x - coordinate location at which fluxes are evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] flux_jac[di] - Jacobian of calcFluxDv[di] with respect to `q` + void calcFluxDvJacState(const mfem::Vector &x, const mfem::Vector dir, + const mfem::Vector &u, + std::vector &flux_jac) + { + for (int d = 0; d < dim; ++d) + flux_jac[d] = 0.0; + } + + /// Computes boundary node contribution to the surface force + /// \param[in] x - coordinate location at which function is evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian determinant (needed by some fluxes) + /// \param[in] u - state at which to evaluate the function + /// \param[in] Dw - `Dw[:,di]` is the derivative of `w` in direction `di` + /// \returns fun - stress at given point + double calcBndryFun(const mfem::Vector &x, const mfem::Vector &dir, + double jac, const mfem::Vector &u, + const mfem::DenseMatrix &Dw) {return 0.0;} + private: /// Reynolds number double Re; @@ -512,6 +617,41 @@ class SAInflowBC : public ViscousBoundaryIntegrator> const mfem::DenseMatrix &Dw, vector &flux_jac); + /// Compute flux terms that are multiplied by test-function derivative + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] q - conservative variables at which to evaluate the fluxes + /// \param[out] flux_mat[:,di] - to be scaled by derivative `D_[di] v` + void calcFluxDv(const mfem::Vector &x, const mfem::Vector &dir, + const mfem::Vector &q, mfem::DenseMatrix &flux_mat) + { + flux_mat = 0.0; + } + + /// Compute the Jacobian of calcFluxDv w.r.t. state + /// \param[in] x - coordinate location at which fluxes are evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] flux_jac[di] - Jacobian of calcFluxDv[di] with respect to `q` + void calcFluxDvJacState(const mfem::Vector &x, const mfem::Vector dir, + const mfem::Vector &u, + std::vector &flux_jac) + { + for (int d = 0; d < dim; ++d) + flux_jac[d] = 0.0; + } + + /// Computes boundary node contribution to the surface force + /// \param[in] x - coordinate location at which function is evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian determinant (needed by some fluxes) + /// \param[in] u - state at which to evaluate the function + /// \param[in] Dw - `Dw[:,di]` is the derivative of `w` in direction `di` + /// \returns fun - stress at given point + double calcBndryFun(const mfem::Vector &x, const mfem::Vector &dir, + double jac, const mfem::Vector &u, + const mfem::DenseMatrix &Dw) {return 0.0;} + private: /// Reynolds number double Re; @@ -603,6 +743,41 @@ class SAOutflowBC : public ViscousBoundaryIntegrator> const mfem::DenseMatrix &Dw, std::vector &flux_jac); + /// Compute flux terms that are multiplied by test-function derivative + /// \param[in] x - coordinate location at which flux is evaluated (not used) + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] q - conservative variables at which to evaluate the fluxes + /// \param[out] flux_mat[:,di] - to be scaled by derivative `D_[di] v` + void calcFluxDv(const mfem::Vector &x, const mfem::Vector &dir, + const mfem::Vector &q, mfem::DenseMatrix &flux_mat) + { + flux_mat = 0.0; + } + + /// Compute the Jacobian of calcFluxDv w.r.t. state + /// \param[in] x - coordinate location at which fluxes are evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] q - conservative variables at which to evaluate the flux + /// \param[in] flux_jac[di] - Jacobian of calcFluxDv[di] with respect to `q` + void calcFluxDvJacState(const mfem::Vector &x, const mfem::Vector dir, + const mfem::Vector &u, + std::vector &flux_jac) + { + for (int d = 0; d < dim; ++d) + flux_jac[d] = 0.0; + } + + /// Computes boundary node contribution to the surface force + /// \param[in] x - coordinate location at which function is evaluated + /// \param[in] dir - vector normal to the boundary at `x` + /// \param[in] jac - mapping Jacobian determinant (needed by some fluxes) + /// \param[in] u - state at which to evaluate the function + /// \param[in] Dw - `Dw[:,di]` is the derivative of `w` in direction `di` + /// \returns fun - stress at given point + double calcBndryFun(const mfem::Vector &x, const mfem::Vector &dir, + double jac, const mfem::Vector &u, + const mfem::DenseMatrix &Dw) {return 0.0;} + private: /// Reynolds number double Re; diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index 29d34f2d..b891754b 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -20,41 +20,52 @@ void SASourceIntegrator::AssembleElementVector( grad_i.SetSize(dim); curl_i.SetSize(3); DenseMatrix u(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well + u.Transpose(); DenseMatrix res(elvect.GetData(), num_nodes, num_states); DenseMatrix Qu(num_nodes, num_states); DenseMatrix HQu(num_nodes, num_states); DenseMatrix Dui(num_states, dim); + Vector Duidi(num_states); - // get Du - for(int di = 0; di < dim; di++) - { - sbp.multWeakOperator(di, u, Qu); - sbp.multNormMatrix(Qu, HQu); - //dxi.CopyMN(HQu, q.Height(), dim, 1, dim+1, di*q.Height(), 0); - } + // convert momentum to velocity + // precompute certain values - //calcVorticitySBP(u, sbp, Trans, curl); - //calcGradSBP(u, sbp, Trans, grad); elvect = 0.0; for (int i = 0; i < num_nodes; ++i) { // get the Jacobian (Trans.Weight) and cubature weight (node.weight) - IntegrationPoint &node = fe.GetNodes().IntPoint(i); + const IntegrationPoint &node = fe.GetNodes().IntPoint(i); Trans.SetIntPoint(&node); - u.GetRow(i, ui); + u.GetColumn(i, ui); Trans.Transform(node, xi); - Dui.Set(1.0, (HQu.GetData() + i*dim*num_states)); + + // get Dui + Duidi = 0.0; Dui = 0.0; + for (int di = 0; di < dim; di++) + { + sbp.multStrongOperator(di, i, u, Duidi); + Dui.SetCol(di, Duidi); + } + + // get Dui () + Duidi = 0.0; Dui = 0.0; + for (int di = 0; di < dim; di++) + { + sbp.multStrongOperator(di, i, u, Duidi); + Dui.SetCol(di, Duidi); + } // compute vorticity at node and take magnitude calcVorticity(Dui.GetData(), Trans.InverseJacobian().GetData(), curl_i.GetData()); //curl.GetRow(i, curl_i); - double S = curl_i.Norml2(); + double S = sqrt(curl_i(0)*curl_i(0) + curl_i(1)*curl_i(1) +curl_i(2)*curl_i(2)); + S = curl_i(2); // compute gradient of turbulent viscosity at node calcGrad(Dui.GetData(), Trans.InverseJacobian().GetData(), grad_i.GetData()); //curl.GetRow(i, curl_i); // get distance function value at node - double d = xi(1); //temp solution, y distance from wall + double d = 1.0; //temp solution, y distance from wall // accumulate source terms double src = calcSASource( @@ -75,7 +86,9 @@ void SASourceIntegrator::AssembleElementVector( ui.GetData(), mu, d, S, sacs.GetData()); } - res(i, dim+2) += alpha * Trans.Weight() * node.weight * src; + //byNODES + elvect(i + num_nodes*(num_states-1)) = alpha * Trans.Weight() * node.weight * src; + //elvect((dim+2) + i*(num_states)) = alpha * Trans.Weight() * node.weight * src; } // loop over element nodes i } @@ -98,9 +111,11 @@ void SASourceIntegrator::AssembleElementGrad( grad_i.SetSize(dim); curl_i.SetSize(3); DenseMatrix u(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well + u.Transpose(); DenseMatrix Qu(num_nodes, num_states); DenseMatrix HQu(num_nodes, num_states); DenseMatrix Dui(num_states, dim); + Vector Duidi(num_states); vector jac_curl(dim); vector jac_grad(dim); std::vector sacs_a(sacs.Size()); @@ -116,26 +131,35 @@ void SASourceIntegrator::AssembleElementGrad( Vector dSrcdu(num_states); //partial src w.r.t. u Vector dSrcdS(1); //partial src w.r.t. S Vector dSrcdgrad(dim); //partial src w.r.t grad + DenseMatrix src_grad(dim, num_states); + DenseMatrix src_curl(3, num_states); + Vector work1(num_nodes*num_states); + Vector work2s(num_states); + Vector work2g(num_states); + Vector work3(num_nodes); + Vector dnu(num_nodes*num_states); //total derivative //need dgraddDu, dcdDu, dDudu - // get Du - for(int di = 0; di < dim; di++) - { - sbp.multWeakOperator(di, u, Qu); - sbp.multNormMatrix(Qu, HQu); - //dxi.CopyMN(HQu, q.Height(), dim, 1, dim+1, di*q.Height(), 0); - } - + elmat.SetSize(num_states*num_nodes); + elmat = 0.0; for (int i = 0; i < num_nodes; ++i) { + dnu = 0.0; // get the Jacobian (Trans.Weight) and cubature weight (node.weight) - IntegrationPoint &node = fe.GetNodes().IntPoint(i); + const IntegrationPoint &node = fe.GetNodes().IntPoint(i); Trans.SetIntPoint(&node); - u.GetRow(i, ui); + u.GetColumn(i, ui); Trans.Transform(node, xi); - Dui.Set(1.0, (HQu.GetData() + i*dim*num_states)); - int Dui_size = Dui.Height() * Dui.Width(); + // get Dui + Duidi = 0.0; Dui = 0.0; + for (int di = 0; di < dim; di++) + { + sbp.multStrongOperator(di, i, u, Duidi); + Dui.SetCol(di, Duidi); + } + + int Dui_size = Dui.Height() * Dui.Width(); // set adept inputs std::vector ui_a(ui.Size()); //std::vector Dui_a(Dui_size); @@ -146,27 +170,30 @@ void SASourceIntegrator::AssembleElementGrad( // initialize adouble inputs adept::set_values(ui_a.data(), ui.Size(), ui.GetData()); //adept::set_values(Dui_a.data(), Dw_size, Dui.GetData()); - adept::set_values(curl_i_a.data(), curl_i.Size(), curl_i.GetData()); - adept::set_values(grad_i_a.data(), grad_i.Size(), grad_i.GetData()); // compute vorticity at node calcVorticity(Dui.GetData(), Trans.InverseJacobian().GetData(), curl_i.GetData()); //curl.GetRow(i, curl_i); - calcVorticityJacDw(Dui.GetData(), + calcVorticityJacDw(this->stack, Dui.GetData(), Trans.InverseJacobian().GetData(), jac_curl); // compute gradient of turbulent viscosity at node calcGrad(Dui.GetData(), Trans.InverseJacobian().GetData(), grad_i.GetData()); //curl.GetRow(i, curl_i); - calcGradJacDw(Dui.GetData(), + calcGradJacDw(this->stack, Dui.GetData(), Trans.InverseJacobian().GetData(), jac_grad); + adept::set_values(curl_i_a.data(), curl_i.Size(), curl_i.GetData()); + adept::set_values(grad_i_a.data(), grad_i.Size(), grad_i.GetData()); + + // get distance function value at node - adouble d = xi(1); //temp solution, y distance from wall + adouble d = 1.0; //temp solution, y distance from wall // vorticity magnitude deriv this->stack.new_recording(); - S = sqrt(curl_i_a[0]*curl_i_a[0] + curl_i_a[1]*curl_i_a[1] +curl_i_a[2]*curl_i_a[2]); + //S = sqrt(curl_i_a[0]*curl_i_a[0] + curl_i_a[1]*curl_i_a[1] +curl_i_a[2]*curl_i_a[2]); + S = curl_i_a[2]; this->stack.independent(curl_i_a.data(), curl_i.Size()); this->stack.dependent(S); this->stack.jacobian(dSdc.GetData()); @@ -236,13 +263,43 @@ void SASourceIntegrator::AssembleElementGrad( src += calcSADestruction( ui_a.data(), mu_a, d, S, sacs_a.data()); } + this->stack.independent(grad_i_a.data(), grad_i_a.size()); this->stack.dependent(src); this->stack.jacobian(dSrcdgrad.GetData()); + // Assemble nu derivative + for(int ns = 0; ns < num_states; ns ++) + { + dnu(i + ns*num_nodes) = dSrcdu(ns); + } + // Dui differentiation + for (int di = 0; di < dim; di++) + { + work1 = 0.0; work2s = 0.0; work2g = 0.0; work3 = 0.0; + sbp.getStrongOperator(di, i, work3); + jac_curl[di].MultTranspose(dSdc, work2s); + work2s *= dSrcdS(0); + jac_grad[di].MultTranspose(dSrcdgrad, work2g); + //src_grad.GetRow(di, work2g); + for (int nn = 0; nn < num_nodes; nn++) + { + // assuming ordering BY NODES + for (int ns = 0; ns < num_states; ns++) + { + work1(nn + ns*num_nodes) = work3(nn)*(work2s(ns) + work2g(ns)); + } + } + dnu += work1; + } + dnu *= (Trans.Weight() * node.weight); + // Set elmat entry + //byNODES + //elmat.SetRow((num_states-1)+i*num_states, dnu); + elmat.SetRow(i+num_nodes*(num_states-1), dnu); } // loop over element nodes i elmat *= alpha; } diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 6368c215..ddf540ed 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -13,21 +13,21 @@ endfunction(create_tests) # group together all fluids related tests set(FLUID_TEST_SRCS - test_euler_fluxes - test_euler_integ - test_euler_assemble - test_euler_sens_integ - test_evolver - test_inexact_newton - test_navier_stokes_fluxes - test_navier_stokes_integ - test_navier_stokes_assemble - test_reconstruction - test_sbp_fe - test_viscous_integ - test_utils - test_thermal_integ - test_res_integ + #test_euler_fluxes + #test_euler_integ + #test_euler_assemble + #test_euler_sens_integ + #test_evolver + #test_inexact_newton + #test_navier_stokes_fluxes + #test_navier_stokes_integ + #test_navier_stokes_assemble + #test_reconstruction + #test_sbp_fe + #test_viscous_integ + #test_utils + #test_thermal_integ + #test_res_integ test_rans_fluxes test_rans_integ ) diff --git a/test/unit/euler_test_data.cpp b/test/unit/euler_test_data.cpp index 59455703..ab92c4d6 100644 --- a/test/unit/euler_test_data.cpp +++ b/test/unit/euler_test_data.cpp @@ -96,6 +96,31 @@ template void randBaselinePert<1, false>(const mfem::Vector &x, mfem::Vector &u) template void randBaselinePert<2, false>(const mfem::Vector &x, mfem::Vector &u); template void randBaselinePert<3, false>(const mfem::Vector &x, mfem::Vector &u); +template +void randBaselinePertSA(const mfem::Vector &x, mfem::Vector &u) +{ + const double scale = 0.01; + u(0) = rho * (1.0 + scale * uniform_rand(gen)); + u(dim + 1) = rhoe * (1.0 + scale * uniform_rand(gen)); + for (int di = 0; di < dim; ++di) + { + u(di + 1) = rhou[di] * (1.0 + scale * uniform_rand(gen)); + } + if (entvar) + { + mfem::Vector q(u); + mach::calcEntropyVars(q.GetData(), u.GetData()); + } + u(dim + 2) = 10.0 * scale * (uniform_rand(gen) - 0.1); +} +// explicit instantiation of the templated function above +template void randBaselinePertSA<1, true>(const mfem::Vector &x, mfem::Vector &u); +template void randBaselinePertSA<2, true>(const mfem::Vector &x, mfem::Vector &u); +template void randBaselinePertSA<3, true>(const mfem::Vector &x, mfem::Vector &u); +template void randBaselinePertSA<1, false>(const mfem::Vector &x, mfem::Vector &u); +template void randBaselinePertSA<2, false>(const mfem::Vector &x, mfem::Vector &u); +template void randBaselinePertSA<3, false>(const mfem::Vector &x, mfem::Vector &u); + void randState(const mfem::Vector &x, mfem::Vector &u) { for (int i = 0; i < u.Size(); ++i) diff --git a/test/unit/euler_test_data.hpp b/test/unit/euler_test_data.hpp index d9800f9b..8aa59c58 100644 --- a/test/unit/euler_test_data.hpp +++ b/test/unit/euler_test_data.hpp @@ -64,7 +64,7 @@ extern double adjJ_data[9]; extern double delw_data[15]; // Spalart-Allmaras model parameters -const double sa_params[11] = {0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16}; +const double sa_params[11] = {0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10.0, 16.0}; /// Returns a perturbed version of the baseline flow state /// \param[in] x - coordinates (not used) @@ -74,6 +74,14 @@ const double sa_params[11] = {0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7. template void randBaselinePert(const mfem::Vector &x, mfem::Vector &u); +/// Returns a perturbed version of the baseline flow state (Spalart-Allmaras) +/// \param[in] x - coordinates (not used) +/// \param[out] u - pertrubed state variable +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \tparam entvar - if true, returns entropy variables +template +void randBaselinePertSA(const mfem::Vector &x, mfem::Vector &u); + /// Returns a random state with entries uniformly distributed in [-1,1] /// \param[in] x - coordinates (not used) /// \param[out] u - rand state variable diff --git a/test/unit/test_rans_integ.cpp b/test/unit/test_rans_integ.cpp index 0a1c2c01..f1306748 100644 --- a/test/unit/test_rans_integ.cpp +++ b/test/unit/test_rans_integ.cpp @@ -791,6 +791,74 @@ TEMPLATE_TEST_CASE_SIG("SALPS Jacobian", "[SALPS]", } } +TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") +{ + using namespace mfem; + using namespace euler_data; + + const int dim = 2; // templating is hard here because mesh constructors + int num_state = dim + 3; + adept::Stack diff_stack; + double delta = 1e-5; + + mfem::Vector sacs(11); + // create SA parameter vector + for (int di = 0; di < 11; ++di) + { + sacs(di) = sa_params[di]; + } + + // generate a 2 element mesh + int num_edge = 2; + std::unique_ptr mesh(new Mesh(num_edge, num_edge, Element::TRIANGLE, + true /* gen. edges */, 1.0, 1.0, true)); + for (int p = 1; p <= 2; ++p) + { + DYNAMIC_SECTION("...for degree p = " << p) + { + std::unique_ptr fec( + new SBPCollection(p, dim)); + std::unique_ptr fes(new FiniteElementSpace( + mesh.get(), fec.get(), num_state, Ordering::byVDIM)); + + GridFunction dist; + NonlinearForm res(fes.get()); + res.AddDomainIntegrator(new mach::SASourceIntegrator(diff_stack, dist, sacs)); + + // initialize state; here we randomly perturb a constant state + GridFunction q(fes.get()); + VectorFunctionCoefficient pert(num_state, randBaselinePertSA<2>); + q.ProjectCoefficient(pert); + + // initialize the vector that the Jacobian multiplies + GridFunction v(fes.get()); + VectorFunctionCoefficient v_rand(num_state, randState); + v.ProjectCoefficient(v_rand); + + // evaluate the Ja;cobian and compute its product with v + Operator &Jac = res.GetGradient(q); + GridFunction jac_v(fes.get()); + Jac.Mult(v, jac_v); + + // now compute the finite-difference approximation... + GridFunction q_pert(q), r(fes.get()), jac_v_fd(fes.get()); + q_pert.Add(-delta, v); + res.Mult(q_pert, r); + q_pert.Add(2 * delta, v); + res.Mult(q_pert, jac_v_fd); + jac_v_fd -= r; + jac_v_fd /= (2 * delta); + + for (int i = 0; i < jac_v.Size(); ++i) + { + std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + std::cout << "AN " << i << " Deriv: " << jac_v(i) << std::endl; + REQUIRE(jac_v(i) == Approx(jac_v_fd(i)).margin(1e-10)); + } + } + } +} + #if 0 TEST_CASE("SALPSIntegrator::AssembleElementGrad", "[SALPSIntegrator]") { From 1791685376266b88d244765f1eb33b60c05d1164 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Tue, 25 Aug 2020 14:41:03 -0400 Subject: [PATCH 12/28] implemented velocity conversion for vorticity --- src/physics/fluidflow/rans_integ_def.hpp | 32 +++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index b891754b..d79d8ddf 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -28,7 +28,13 @@ void SASourceIntegrator::AssembleElementVector( Vector Duidi(num_states); // convert momentum to velocity - + for (int nn = 0; nn < num_nodes; nn++) + { + for (int di = 0; di < dim; di++) + { + u(di+1, nn) *= 1.0/u(0, nn); + } + } // precompute certain values elvect = 0.0; @@ -110,8 +116,9 @@ void SASourceIntegrator::AssembleElementGrad( curl.SetSize(num_nodes, 3); grad_i.SetSize(dim); curl_i.SetSize(3); - DenseMatrix u(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well - u.Transpose(); + DenseMatrix uc(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well + uc.Transpose(); + DenseMatrix u; u = uc; DenseMatrix Qu(num_nodes, num_states); DenseMatrix HQu(num_nodes, num_states); DenseMatrix Dui(num_states, dim); @@ -140,6 +147,15 @@ void SASourceIntegrator::AssembleElementGrad( Vector dnu(num_nodes*num_states); //total derivative //need dgraddDu, dcdDu, dDudu + // convert momentum to velocity + for (int nn = 0; nn < num_nodes; nn++) + { + for (int di = 0; di < dim; di++) + { + u(di+1, nn) *= 1.0/uc(0, nn); + } + } + elmat.SetSize(num_states*num_nodes); elmat = 0.0; for (int i = 0; i < num_nodes; ++i) @@ -295,6 +311,16 @@ void SASourceIntegrator::AssembleElementGrad( } dnu *= (Trans.Weight() * node.weight); + // account for momentum conversion + for (int nn = 0; nn < num_nodes; nn++) + { + for (int di = 0; di < dim; di++) + { + dnu(nn) -= dnu(nn + (di+1)*num_nodes)*uc(di+1,nn)/(uc(0, nn)*uc(0, nn)); + dnu(nn + (di+1)*num_nodes) *= 1.0/uc(0, nn); + } + } + // Set elmat entry //byNODES From 148b9ffcf6443f15c84ee30d8043738c7b1a311b Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Mon, 7 Sep 2020 21:15:25 -0400 Subject: [PATCH 13/28] fixed bugs and (I believe) have differentiation sorted with SA source terms. wall test problems show mesh dependence, and lack of convergence with small wall elements as a result --- sandbox/rans_freestream.cpp | 18 +- sandbox/rans_freestream_options.json | 19 +- sandbox/rans_walltest.cpp | 281 ++++++++++++++ sandbox/rans_walltest_options.json | 58 +++ src/physics/fluidflow/euler.cpp | 2 +- src/physics/fluidflow/rans.cpp | 49 ++- src/physics/fluidflow/rans.hpp | 21 +- src/physics/fluidflow/rans_fluxes.hpp | 110 ++++-- src/physics/fluidflow/rans_integ.hpp | 35 +- src/physics/fluidflow/rans_integ_def.hpp | 303 +++++++++------ src/physics/fluidflow/viscous_integ_def.hpp | 1 - src/physics/solver.cpp | 3 + test/unit/euler_test_data.cpp | 2 +- test/unit/euler_test_data.hpp | 2 +- test/unit/test_rans_fluxes.cpp | 391 ++++++++++++++++++- test/unit/test_rans_integ.cpp | 399 ++++++++++++++++++-- 16 files changed, 1451 insertions(+), 243 deletions(-) create mode 100644 sandbox/rans_walltest.cpp create mode 100644 sandbox/rans_walltest_options.json diff --git a/sandbox/rans_freestream.cpp b/sandbox/rans_freestream.cpp index 67044d4e..d3735848 100644 --- a/sandbox/rans_freestream.cpp +++ b/sandbox/rans_freestream.cpp @@ -14,7 +14,7 @@ using namespace mfem; using namespace mach; std::default_random_engine gen(std::random_device{}()); -std::uniform_real_distribution normal_rand(-1.0,1.0); +std::uniform_real_distribution uniform_rand(0.0,1.0); static double pert_fs; static double mu; @@ -80,7 +80,7 @@ int main(int argc, char *argv[]) iroll = file_options["flow-param"]["roll-axis"].template get(); ipitch = file_options["flow-param"]["pitch-axis"].template get(); chi_fs = file_options["flow-param"]["chi"].template get(); - // generate a simple tri mesh + // generate a simple tri mesh and a distance function std::unique_ptr smesh(new Mesh(nx, ny, Element::TRIANGLE, true /* gen. edges */, 1.0, 1.0, true)); @@ -132,14 +132,8 @@ void pert(const Vector &x, Vector& p) p.SetSize(5); for (int i = 0; i < 5; i++) { - p(i) = normal_rand(gen); + p(i) = 2.0 * uniform_rand(gen) - 1.0; } - - // p.SetSize(4); - // for (int i = 0; i < 4; i++) - // { - // p(i) = normal_rand(gen); - // } } // Exact solution; same as freestream bc @@ -155,7 +149,7 @@ void uexact(const Vector &x, Vector& q) u(1) = u(0)*mach_fs*cos(aoa_fs); u(2) = u(0)*mach_fs*sin(aoa_fs); u(3) = 1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; - u(4) = chi_fs*mu/u(0); + u(4) = chi_fs*mu; if (entvar == false) { @@ -170,8 +164,6 @@ void uexact(const Vector &x, Vector& q) // initial guess perturbed from exact void uinit_pert(const Vector &x, Vector& q) { - // q.SetSize(4); - // Vector u(4); q.SetSize(5); Vector u(5); @@ -180,7 +172,7 @@ void uinit_pert(const Vector &x, Vector& q) u(1) = u(0)*mach_fs*cos(aoa_fs); u(2) = u(0)*mach_fs*sin(aoa_fs); u(3) = pert_fs*1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; - u(4) = pert_fs*pert_fs*chi_fs*mu/u(0); + u(4) = pert_fs*chi_fs*mu; q = u; } diff --git a/sandbox/rans_freestream_options.json b/sandbox/rans_freestream_options.json index 8182f2c1..ffcf35c4 100644 --- a/sandbox/rans_freestream_options.json +++ b/sandbox/rans_freestream_options.json @@ -5,10 +5,10 @@ "roll-axis": 0, "pitch-axis": 1, "chi": 3, - "Re": 10.0, + "Re": 1000.0, "Pr": 0.75, "mu": 1.0, - "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16] + "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9] }, "space-dis": { "degree": 1, @@ -25,11 +25,12 @@ "dt": 1e6, "cfl": 1.0 }, - "newton":{ + "nonlin-solver": { + "abstol": 1e-12, + "maxiter": 100, "printlevel": 1, - "maxiter": 50, - "reltol": 1e-1, - "abstol": 1e-12 + "reltol": 1e-2, + "type": "newton" }, "lin-solver": { "reltol": 1e-2, @@ -40,5 +41,9 @@ "bcs": { "far-field": [1, 1, 1, 1] }, - "init-pert": 1e-6 + "wall-func": { + "type": "const", + "val": 1.0 + }, + "init-pert": 0.0 } diff --git a/sandbox/rans_walltest.cpp b/sandbox/rans_walltest.cpp new file mode 100644 index 00000000..3a37070b --- /dev/null +++ b/sandbox/rans_walltest.cpp @@ -0,0 +1,281 @@ +// set this const expression to true in order to use entropy variables for state (doesn't work for rans) +constexpr bool entvar = false; + +#include +#include "adept.h" + +#include "mfem.hpp" +#include "rans.hpp" +#include +#include + +using namespace std; +using namespace mfem; +using namespace mach; + +std::default_random_engine gen(std::random_device{}()); +std::uniform_real_distribution uniform_rand(0.0,1.0); + +static double pert_fs; +static double mu; +static double mach_fs; +static double aoa_fs; +static int iroll; +static int ipitch; +static double chi_fs; + +static double m_offset; +static double m_coeff; +static int m_x; +static int m_y; + +/// \brief Defines the random function for the jacobian check +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void pert(const Vector &x, Vector& p); + +/// \brief Defines the exact solution for the rans freestream problem +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void uexact(const Vector &x, Vector& u); + +/// \brief Defines a perturbed solution for the rans freestream problem +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void uinit_pert(const Vector &x, Vector& u); + +/// \brief Defines a pseudo wall-distance function +/// \param[in] x - coordinate of the point at which the distance is needed +double walldist(const Vector &x); + +/// Generate a quadrilateral wall mesh, more dense near the wall +/// \param[in] num_x - number of nodes in x +/// \param[in] num_y - number of nodes in y +std::unique_ptr buildWalledMesh(int num_x, + int num_y); + +int main(int argc, char *argv[]) +{ + const char *options_file = "rans_walltest_options.json"; + + // Initialize MPI + int num_procs, rank; + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + ostream *out = getOutStream(rank); + + // Parse command-line options + OptionsParser args(argc, argv); + int degree = 2.0; + int nx = 14; + int ny = 10; + args.AddOption(&nx, "-nx", "--numx", + "Number of elements in x direction"); + args.AddOption(&ny, "-ny", "--numy", + "Number of elements in y direction"); + args.Parse(); + if (!args.Good()) + { + args.PrintUsage(*out); + return 1; + } + + try + { + // construct the mesh + string opt_file_name(options_file); + nlohmann::json file_options; + std::ifstream opts(opt_file_name); + opts >> file_options; + pert_fs = 1.0 + file_options["init-pert"].template get(); + mu = file_options["flow-param"]["mu"].template get(); + mach_fs = file_options["flow-param"]["mach"].template get(); + aoa_fs = file_options["flow-param"]["aoa"].template get()*M_PI/180; + iroll = file_options["flow-param"]["roll-axis"].template get(); + ipitch = file_options["flow-param"]["pitch-axis"].template get(); + chi_fs = file_options["flow-param"]["chi"].template get(); + + m_offset = file_options["mesh"]["offset"].template get(); + m_coeff = file_options["mesh"]["coeff"].template get(); + m_x = file_options["mesh"]["num-x"].template get(); + m_y = file_options["mesh"]["num-y"].template get(); + + // generate the walled mesh, and its distance function + std::unique_ptr smesh = buildWalledMesh(m_x, m_y); + + // construct the solver and set initial conditions + auto solver = createSolver>(opt_file_name, + move(smesh)); + solver->setInitialCondition(uinit_pert); + solver->printSolution("rans_wall_init", 0); + + // get the initial density error + double l2_error_init = (static_cast&>(*solver) + .calcConservativeVarsL2Error(uexact, 0)); + *out << "\n|| rho_h - rho ||_{L^2} init = " << l2_error_init << endl; + + double res_error = solver->calcResidualNorm(); + *out << "\ninitial residual norm = " << res_error << endl; + solver->checkJacobian(pert); + solver->solveForState(); + solver->printSolution("rans_wall_final",0); + // get the final density error + double l2_error_final = (static_cast&>(*solver) + .calcConservativeVarsL2Error(uexact, 0)); + res_error = solver->calcResidualNorm(); + + *out << "\nfinal residual norm = " << res_error; + *out << "\n|| rho_h - rho ||_{L^2} final = " << l2_error_final << endl; + } + catch (MachException &exception) + { + exception.print_message(); + } + catch (std::exception &exception) + { + cerr << exception.what() << endl; + } + +#ifdef MFEM_USE_PETSC + MFEMFinalizePetsc(); +#endif + + MPI_Finalize(); +} + +// perturbation function used to check the jacobian in each iteration +void pert(const Vector &x, Vector& p) +{ + p.SetSize(5); + for (int i = 0; i < 5; i++) + { + p(i) = 2.0 * uniform_rand(gen) - 1.0; + } +} + +// Exact solution; same as freestream bc +void uexact(const Vector &x, Vector& q) +{ + // q.SetSize(4); + // Vector u(4); + q.SetSize(5); + Vector u(5); + + u = 0.0; + u(0) = 1.0; + u(1) = u(0)*mach_fs*cos(aoa_fs); + u(2) = u(0)*mach_fs*sin(aoa_fs); + u(3) = 1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; + u(4) = u(0)*chi_fs*mu; + + if (entvar == false) + { + q = u; + } + else + { + throw MachException("No entvar for this"); + } +} + +// initial guess perturbed from exact +void uinit_pert(const Vector &x, Vector& q) +{ + // q.SetSize(4); + // Vector u(4); + q.SetSize(5); + Vector u(5); + + u = 0.0; + u(0) = pert_fs*1.0; + u(1) = u(0)*mach_fs*cos(aoa_fs); + u(2) = u(0)*mach_fs*sin(aoa_fs); + u(3) = pert_fs*1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; + u(4) = pert_fs*chi_fs*abs(mu); + + // if(x(1) == 0.0) + // { + // u(1) = 1e-10; + // } + + q = u; +} + +std::unique_ptr buildWalledMesh(int num_x, int num_y) +{ + auto mesh_ptr = unique_ptr(new Mesh(num_x, num_y, + Element::TRIANGLE, true /* gen. edges */, + 2.33333, 1.0, true)); + // strategy: + // 1) generate a fes for Lagrange elements of desired degree + // 2) create a Grid Function using a VectorFunctionCoefficient + // 4) use mesh_ptr->NewNodes(nodes, true) to set the mesh nodes + + // Problem: fes does not own fec, which is generated in this function's scope + // Solution: the grid function can own both the fec and fes + H1_FECollection *fec = new H1_FECollection(1, 2 /* = dim */); + FiniteElementSpace *fes = new FiniteElementSpace(mesh_ptr.get(), fec, 2, + Ordering::byVDIM); + + // Lambda function increases element density towards wall + double offset = m_offset; + double coeff = m_coeff; + auto xy_fun = [coeff, offset, num_y](const Vector& rt, Vector &xy) + { + xy(0) = rt(0) - 0.33333; + xy(1) = rt(1); + + double c = 1.0/num_y; + double b = log(offset)/(c - 1.0); + double a = 1.0/exp(1.0*b); + + if(rt(1) > 0.0 && rt(1) < 1.0) + { + xy(1) = coeff*a*exp(b*rt(1)); + //std::cout << xy(1) << std::endl; + } + }; + VectorFunctionCoefficient xy_coeff(2, xy_fun); + GridFunction *xy = new GridFunction(fes); + xy->MakeOwner(fec); + xy->ProjectCoefficient(xy_coeff); + + mesh_ptr->NewNodes(*xy, true); + + // Assign extra boundary attribute + for (int i = 0; i < mesh_ptr->GetNBE(); ++i) + { + Element *elem = mesh_ptr->GetBdrElement(i); + + Array verts; + elem->GetVertices(verts); + + bool before = true; //before wall + for (int j = 0; j < 2; ++j) + { + auto vtx = mesh_ptr->GetVertex(verts[j]); + cout<< "B El: "<SetAttribute(5); + } + } + mesh_ptr->bdr_attributes.Append(5); + // mesh_ptr->SetAttributes(); + // mesh_ptr->Finalize(); + + cout << "Number of bdr attr " << mesh_ptr->bdr_attributes.Size() <<'\n'; + cout << "Number of elements " << mesh_ptr->GetNE() <<'\n'; + + return mesh_ptr; +} diff --git a/sandbox/rans_walltest_options.json b/sandbox/rans_walltest_options.json new file mode 100644 index 00000000..eb8862ed --- /dev/null +++ b/sandbox/rans_walltest_options.json @@ -0,0 +1,58 @@ +{ + "flow-param": { + "mach": 0.2, + "aoa": 0.0, + "roll-axis": 0, + "pitch-axis": 1, + "chi": 3.0, + "Re": 5000000.0, + "Pr": 0.75, + "mu": 1.0, + "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9] + }, + "space-dis": { + "degree": 1, + "lps-coeff": 1.0, + "basis-type": "csbp" + }, + "time-dis": { + "steady": true, + "steady-abstol": 1e-12, + "steady-restol": 1e-10, + "const-cfl": true, + "ode-solver": "PTC", + "t-final": 100, + "dt": 0.001, + "cfl": 1.0, + "res-exp": 2.0 + }, + "nonlin-solver": { + "abstol": 1e-12, + "maxiter": 100, + "printlevel": 1, + "reltol": 1e-2, + "type": "newton" + }, + "lin-solver": { + "reltol": 1e-2, + "abstol": 1e-12, + "printlevel": 0, + "maxiter": 100 + }, + "bcs": { + "far-field": [0, 1, 1, 1, 0], + "slip-wall": [0, 0, 0, 0, 1], + "no-slip-adiabatic": [1, 0, 0, 0, 0] + }, + "wall-func": { + "type": "y-dist" + }, + "mesh": { + "num-x": 25, + "num-y": 15, + "offset": 1e-3, + "coeff": 1.0 + }, + "init-pert": 0.0 + +} diff --git a/src/physics/fluidflow/euler.cpp b/src/physics/fluidflow/euler.cpp index 1c2412cb..e822e707 100644 --- a/src/physics/fluidflow/euler.cpp +++ b/src/physics/fluidflow/euler.cpp @@ -366,7 +366,7 @@ double EulerSolver::calcConservativeVarsL2Error( // This lambda function computes the error at a node // Beware: this is not particularly efficient, given the conditionals // Also **NOT thread safe!** - Vector qdiscrete(dim+2), qexact(dim+2); // define here to avoid reallocation + Vector qdiscrete(num_state), qexact(num_state); // define here to avoid reallocation auto node_error = [&](const Vector &discrete, const Vector &exact) -> double { if (entvar) diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index 10947bea..a9c69c4f 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -11,9 +11,9 @@ namespace mach { template -RANavierStokesSolver::RANavierStokesSolver(const string &opt_file_name, - unique_ptr smesh) - : NavierStokesSolver(opt_file_name, move(smesh)) +RANavierStokesSolver::RANavierStokesSolver(const nlohmann::json &json_options, + unique_ptr smesh) + : NavierStokesSolver(json_options, move(smesh)) { if (entvar) { @@ -24,8 +24,9 @@ RANavierStokesSolver::RANavierStokesSolver(const string &opt_file_n chi_fs = this->options["flow-param"]["chi"].template get(); mu = this->options["flow-param"]["mu"].template get(); vector sa = this->options["flow-param"]["sa-consts"].template get>(); - sacs.SetData(sa.data()); - + sacs.SetSize(13); + sacs = sa.data(); + getDistanceFunction(); } template @@ -38,8 +39,8 @@ void RANavierStokesSolver::addResVolumeIntegrators(double alpha) this->res->AddDomainIntegrator(new SAViscousIntegrator( //SAViscousIntegrator this->diff_stack, this->re_fs, this->pr_fs, sacs, mu, alpha)); // now add RANS integrators - // this->res->AddDomainIntegrator(new SASourceIntegrator( - // this->diff_stack, sacs, mu, alpha)); + this->res->AddDomainIntegrator(new SASourceIntegrator( + this->diff_stack, *dist, this->re_fs, sacs, mu, -alpha)); // add LPS stabilization double lps_coeff = this->options["space-dis"]["lps-coeff"].template get(); this->res->AddDomainIntegrator(new SALPSIntegrator( @@ -188,7 +189,7 @@ void RANavierStokesSolver::getFreeStreamState(mfem::Vector &q_ref) q_ref(this->ipitch+1) = q_ref(0)*this->mach_fs*sin(this->aoa_fs); } q_ref(dim+1) = 1/(euler::gamma*euler::gami) + 0.5*this->mach_fs*this->mach_fs; - q_ref(dim+2) = this->chi_fs*mu/q_ref(0); + q_ref(dim+2) = q_ref(0)*this->chi_fs*mu; } static void pert(const Vector &x, Vector& p); @@ -198,6 +199,38 @@ void RANavierStokesSolver::iterationHook(int iter, double t, double dt) { this->checkJacobian(pert); + this->printSolution("rans_wall_last", 0); +} + +template +void RANavierStokesSolver::getDistanceFunction() +{ + std::string wall_type = + this->options["wall-func"]["type"].template get(); + H1_FECollection *dfec = new H1_FECollection(1, dim); + FiniteElementSpace *dfes = new FiniteElementSpace(this->mesh.get(), dfec); + dist.reset(new GridFunction(dfes)); + + if (wall_type == "const") + { + double val = this->options["wall-func"]["val"].template get(); + ConstantCoefficient wall_coeff(val); + dist->ProjectCoefficient(wall_coeff); + } + if (wall_type == "y-dist") + { + double offset = + this->options["mesh"]["offset"].template get(); + auto walldist = [offset](const Vector &x) + { + if(x(1) == 0.0) + return 0.25*offset; + else + return x(1); + }; + FunctionCoefficient wall_coeff(walldist); + dist->ProjectCoefficient(wall_coeff); + } } std::default_random_engine gen(std::random_device{}()); diff --git a/src/physics/fluidflow/rans.hpp b/src/physics/fluidflow/rans.hpp index 077f021e..7ed8948b 100644 --- a/src/physics/fluidflow/rans.hpp +++ b/src/physics/fluidflow/rans.hpp @@ -16,6 +16,13 @@ template class RANavierStokesSolver : public NavierStokesSolver { public: + /// Class constructor. + /// \param[in] opt_file_name - file where options are stored + /// \param[in] smesh - if provided, defines the mesh for the problem + /// \param[in] dim - number of dimensions + RANavierStokesSolver(const nlohmann::json &json_options, + std::unique_ptr smesh = nullptr); + /// Sets `q_in` to the inflow conservative + SA variables void getRANSInflowState(mfem::Vector &q_in); @@ -33,12 +40,7 @@ class RANavierStokesSolver : public NavierStokesSolver } protected: - /// Class constructor. - /// \param[in] opt_file_name - file where options are stored - /// \param[in] smesh - if provided, defines the mesh for the problem - /// \param[in] dim - number of dimensions - RANavierStokesSolver(const std::string &opt_file_name, - std::unique_ptr smesh = nullptr); + /// Add volume/domain integrators to `res` based on `options` /// \param[in] alpha - scales the data; used to move terms to rhs or lhs @@ -56,11 +58,16 @@ class RANavierStokesSolver : public NavierStokesSolver virtual void iterationHook(int iter, double t, double dt) override; friend SolverPtr createSolver>( - const std::string &opt_file_name, + const nlohmann::json &json_options, std::unique_ptr smesh); + /// create mesh distance function from options + void getDistanceFunction(); + /// SA constants vector mfem::Vector sacs; + /// Gridfunction for wall distance function (unused for now) + std::unique_ptr dist; /// free-stream SA viscosity ratio (nu_tilde/nu_material) double chi_fs; /// material dynamic viscosity diff --git a/src/physics/fluidflow/rans_fluxes.hpp b/src/physics/fluidflow/rans_fluxes.hpp index a5a7f9d2..66d22dd0 100644 --- a/src/physics/fluidflow/rans_fluxes.hpp +++ b/src/physics/fluidflow/rans_fluxes.hpp @@ -23,6 +23,9 @@ /// sacs[9] = rlim /// sacs[10] = cn1 +/// sacs[11] = cv2 +/// sacs[12] = cv3 + namespace mach { @@ -45,7 +48,7 @@ xdouble calcSALaminarSuppression(const xdouble *q, const xdouble mu, { xdouble ct3 = sacs[7]; xdouble ct4 = sacs[8]; - xdouble nu_tilde = q[dim+2]; + xdouble nu_tilde = q[dim+2]/q[0]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble ft2 = ct3*exp(-ct4*chi*chi); @@ -64,7 +67,7 @@ xdouble calcSACoefficient(const xdouble *q, const xdouble mu, const xdouble *sacs) { xdouble cv1 = sacs[6]; - xdouble nu_tilde = q[dim+2]; + xdouble nu_tilde = q[dim+2]/q[0]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble fv1 = chi*chi*chi/(cv1*cv1*cv1 + chi*chi*chi); @@ -82,7 +85,7 @@ template xdouble calcSAProductionCoefficient(const xdouble *q, const xdouble mu, const xdouble *sacs) { - xdouble nu_tilde = q[dim+2]; + xdouble nu_tilde = q[dim+2]/q[0]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble fv1 = calcSACoefficient(q, mu, sacs); @@ -100,13 +103,20 @@ xdouble calcSAProductionCoefficient(const xdouble *q, const xdouble mu, template xdouble calcSAModifiedVorticity(const xdouble *q, const xdouble S, const xdouble mu, const xdouble d, - const xdouble *sacs) + const xdouble Re, const xdouble *sacs) { - xdouble nu_tilde = q[dim+2]; + xdouble nu_tilde = q[dim+2]/q[0]; xdouble kappa = sacs[3]; + xdouble cv2 = sacs[11]; + xdouble cv3 = sacs[12]; xdouble fv2 = calcSAProductionCoefficient(q, mu, sacs); - xdouble St = S + nu_tilde*fv2/(kappa*kappa*d*d); - return St; + xdouble work = fv2/(kappa*kappa*d*d); + xdouble St; + if (work < -cv2*S) + St = S + (S*(cv2*cv2*S + cv3*work))/((cv3-2*cv2)*S - work); + else + St = S + nu_tilde*work; + return Re*St; } /// Returns the destruction coefficient in SA @@ -119,20 +129,31 @@ xdouble calcSAModifiedVorticity(const xdouble *q, const xdouble S, template xdouble calcSADestructionCoefficient(const xdouble *q, const xdouble mu, const xdouble d, - const xdouble S, const xdouble *sacs) + const xdouble S, const xdouble Re, + const xdouble *sacs) { - xdouble nu_tilde = q[dim+2]; + using std::min; + using adept::min; + using std::pow; + using adept::pow; + xdouble nu_tilde = q[dim+2]/q[0]; xdouble kappa = sacs[3]; xdouble cw2 = sacs[4]; xdouble cw3 = sacs[5]; xdouble rlim = sacs[9]; - xdouble St = calcSAModifiedVorticity(q, S, mu, d, sacs); + xdouble St = calcSAModifiedVorticity(q, S, mu, d, Re, sacs); + //xdouble fv2 = calcSAProductionCoefficient(q, mu, sacs); + xdouble work = nu_tilde/(St*kappa*kappa*d*d); - xdouble r = std::min(work, rlim); - xdouble g = r + cw2*(pow(r, 6.0) - r); - xdouble work2 = (1.0 + pow(cw3, 6.0))/ - (pow(g, 6.0) + pow(cw3, 6.0)); + + xdouble r = min(work, rlim); + xdouble r6 = r*r*r*r*r*r; + xdouble g = r + cw2*(/*pow(r, 6.0)*/ r6 - r); + xdouble g6 = g*g*g*g*g*g; + xdouble cw36 = cw3*cw3*cw3*cw3*cw3*cw3; + xdouble work2 = (1.0 + /*pow(cw3, 6.0)*/ cw36)/ + (/*pow(g, 6.0)*/ g6 + /*pow(cw3, 6.0)*/ cw36); xdouble fw = g*pow(work2, (1.0/6.0)); return fw; } @@ -147,14 +168,15 @@ xdouble calcSADestructionCoefficient(const xdouble *q, template xdouble calcSAProduction(const xdouble *q, const xdouble mu, const xdouble d, - const xdouble S, const xdouble *sacs) + const xdouble S, const xdouble Re, + const xdouble *sacs) { xdouble cb1 = sacs[0]; - xdouble nu_tilde = q[dim+2]; - xdouble St = calcSAModifiedVorticity(q, S, mu, d, sacs); + xdouble nu_tilde = q[dim+2]/q[0]; + xdouble St = calcSAModifiedVorticity(q, S, mu, d, Re, sacs); xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); xdouble P = cb1*(1.0-ft2)*St*nu_tilde; - return P; + return q[0]*P; } /// Returns the destruction term in SA @@ -167,18 +189,19 @@ xdouble calcSAProduction(const xdouble *q, template xdouble calcSADestruction(const xdouble *q, const xdouble mu, const xdouble d, - const xdouble S, const xdouble *sacs) + const xdouble S, const xdouble Re, + const xdouble *sacs) { xdouble cb1 = sacs[0]; xdouble cb2 = sacs[1]; xdouble sigma = sacs[2]; xdouble kappa = sacs[3]; - xdouble chi_d = q[dim+2]/d; + xdouble chi_d = q[dim+2]/(d*q[0]); xdouble cw1 = cb1/(kappa*kappa) + (1+cb2)/sigma; - xdouble fw = calcSADestructionCoefficient(q, mu, d, S, sacs); + xdouble fw = calcSADestructionCoefficient(q, mu, d, S, Re, sacs); xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); xdouble D = (cw1*fw - (cb1/(kappa*kappa))*ft2)*chi_d*chi_d; - return D; + return -q[0]*D; } /// Returns the production term in negative SA @@ -194,7 +217,7 @@ xdouble calcSANegativeProduction(const xdouble *q, const xdouble S, { xdouble cb1 = sacs[0]; xdouble ct3 = sacs[7]; - xdouble nu_tilde = q[dim+2]; + xdouble nu_tilde = q[dim+2]/q[0]; xdouble Pn = cb1*(1.0-ct3)*S*nu_tilde; return Pn; } @@ -214,10 +237,10 @@ xdouble calcSANegativeDestruction(const xdouble *q, const xdouble d, xdouble cb2 = sacs[1]; xdouble sigma = sacs[2]; xdouble kappa = sacs[3]; - xdouble chi_d = q[dim+2]/d; + xdouble chi_d = q[dim+2]/(d*q[0]); xdouble cw1 = cb1/(kappa*kappa) + (1+cb2)/sigma; xdouble Dn = -cw1*chi_d*chi_d; - return Dn; + return -Dn; } /// Returns the turbulent viscosity coefficient in negative SA @@ -232,10 +255,12 @@ xdouble calcSANegativeCoefficient(const xdouble *q, const xdouble mu, const xdouble *sacs) { xdouble cn1 = sacs[10]; - xdouble nu_tilde = q[dim+2]; + xdouble nu_tilde = q[dim+2]/q[0]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble fn = (cn1 + chi*chi*chi)/(cn1 - chi*chi*chi); + if(q[dim+2] >= 0) + fn = 1.0; return fn; } @@ -253,6 +278,24 @@ xdouble calcSASource(const xdouble *q, const xdouble *dir, xdouble cb2 = sacs[1]; xdouble sigma = sacs[2]; xdouble Sr = (cb2/sigma)*dot(dir, dir); + return q[0]*Sr; +} + +/// Returns the viscous "source" term in SA +/// \param[in] q - state used to define the destruction +/// \param[in] dir - turbulent viscosity gradient +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns Sr - "source" term +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSASource2(const xdouble *q, const xdouble mu, const xdouble *dir, + const xdouble *dir2, const xdouble *sacs) +{ + xdouble nu_tilde = q[dim+2]/q[0]; + xdouble sigma = sacs[2]; + xdouble fn = calcSANegativeCoefficient(q, mu, sacs); + xdouble Sr = (1.0/sigma)*(mu/q[0] + fn*nu_tilde)*dot(dir, dir2); return Sr; } @@ -440,13 +483,14 @@ void calcVorticityJacDw(adept::Stack &stack, const double *Dq, const double *jac // Compute gradient for the turbulence variable on an element node, // needed for SA model terms +/// \param[in] i - the state variable to take /// \param[in] q - the derivative of the state at the node /// \param[in] jac_inv - transformation jacobian at node /// \param[out] grad - the gradient of the turbulence variable at each node /// \tparam xdouble - typically `double` or `adept::adouble` /// \tparam dim - number of spatial dimensions (1, 2, or 3) template -void calcGrad(const xdouble *Dq, const xdouble *jac_inv, +void calcGrad(const int i, const xdouble *Dq, const xdouble *jac_inv, xdouble *grad) { xdouble DqJ[dim*(dim+3)]; @@ -465,9 +509,9 @@ void calcGrad(const xdouble *Dq, const xdouble *jac_inv, } } - for (int i = 0; i < dim; ++i) + for (int j = 0; j < dim; ++j) { - grad[i] = DqJ[dim+2 + i*(dim+3)]; + grad[j] = DqJ[i + j*(dim+3)]; } } @@ -480,7 +524,7 @@ void calcGrad(const xdouble *Dq, const xdouble *jac_inv, /// \tparam xdouble - typically `double` or `adept::adouble` /// \tparam dim - number of spatial dimensions (1, 2, or 3) template -void calcGradJacDw(adept::Stack &stack, const double *Dq, const double *jac_inv, +void calcGradJacDw(adept::Stack &stack, const int i, const double *Dq, const double *jac_inv, std::vector &jac_grad) { // vector of active input variables @@ -494,16 +538,16 @@ void calcGradJacDw(adept::Stack &stack, const double *Dq, const double *jac_inv, // create vector of active output variables std::vector grad_a(dim); // run algorithm - calcGrad(Dq_a.data(), jac_a.data(), grad_a.data()); + calcGrad(i, Dq_a.data(), jac_a.data(), grad_a.data()); // identify independent and dependent variables stack.independent(Dq_a.data(), Dq_a.size()); stack.dependent(grad_a.data(), grad_a.size()); // compute and store jacobian in jac_grad mfem::Vector work(dim*(dim+3)*dim); stack.jacobian_reverse(work.GetData()); - for (int i = 0; i < dim; ++i) + for (int j = 0; j < dim; ++j) { - jac_grad[i] = (work.GetData() + i*(dim+3)*dim); + jac_grad[j] = (work.GetData() + j*(dim+3)*dim); } } diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp index 44c18a4f..ee627709 100644 --- a/src/physics/fluidflow/rans_integ.hpp +++ b/src/physics/fluidflow/rans_integ.hpp @@ -66,9 +66,10 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator /// \param[in] /// \param[in] vis - nondimensional dynamic viscosity (use Sutherland if neg) /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) - SASourceIntegrator(adept::Stack &diff_stack, mfem::GridFunction dist, + SASourceIntegrator(adept::Stack &diff_stack, mfem::GridFunction distance, double re_fs, mfem::Vector sa_params, double vis = -1.0, double a = 1.0) - : alpha(a), mu(vis), stack(diff_stack), num_states(dim+3), sacs(sa_params) {} + : alpha(a), mu(vis), stack(diff_stack), num_states(dim+3), Re(re_fs), sacs(sa_params), + dist(distance) {} /// Construct the element local residual /// \param[in] fe - the finite element whose residual we want @@ -90,26 +91,6 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator const mfem::Vector &elfun, mfem::DenseMatrix &elmat); - // // Compute vorticity on an SBP element, needed for SA model terms - // /// \param[in] q - the state over the element - // /// \param[in] sbp - the sbp element whose shape functions we want - // /// \param[in] Trans - defines the reference to physical element mapping - // /// \param[out] curl - the curl of the velocity field at each node/int point - // void calcVorticitySBP(const mfem::DenseMatrix &q, - // const mfem::FiniteElement &fe, - // mfem::ElementTransformation &Trans, - // mfem::DenseMatrix curl); - - // // Compute gradient for the turbulence variable on an SBP element, - // // needed for SA model terms - // /// \param[in] q - the state over the element - // /// \param[in] sbp - the sbp element whose shape functions we want - // /// \param[in] Trans - defines the reference to physical element mapping - // /// \param[out] grad - the gradient of the turbulence variable at each node - // void calcGradSBP(const mfem::DenseMatrix &q, - // const mfem::FiniteElement &fe, - // mfem::ElementTransformation &Trans, - // mfem::DenseMatrix grad); private: @@ -120,6 +101,8 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator int num_states; /// scales the terms; can be used to move to rhs/lhs double alpha; + /// Freestream Reynolds number + double Re; /// vector of SA model parameters mfem::Vector sacs; /// stack used for algorithmic differentiation @@ -129,8 +112,10 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator mfem::Vector xi; /// used to reference the states at node i mfem::Vector ui; - /// used to reference the gradient at node i - mfem::Vector grad_i; + /// used to reference the gradient of nu at node i + mfem::Vector grad_nu_i; + /// used to reference the gradient of rho at node i + mfem::Vector grad_rho_i; /// used to reference the curl at node i mfem::Vector curl_i; /// stores the result of calling the flux function @@ -145,6 +130,8 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator mfem::DenseMatrix elflux; /// used to store the residual in (num_states, Dof) format mfem::DenseMatrix elres; + /// wall distance function + mfem::GridFunction dist; #endif }; diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index d79d8ddf..600e9ec7 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -9,7 +9,7 @@ void SASourceIntegrator::AssembleElementVector( const SBPFiniteElement &sbp = dynamic_cast(fe); int num_nodes = sbp.GetDof(); #ifdef MFEM_THREAD_SAFE - Vector ui, xi, uj, grad_i, curl_i; + Vector ui, xi, uj, grad_nu_i, curl_i; DenseMatrix grad, curl; #endif elvect.SetSize(num_states * num_nodes); @@ -17,10 +17,12 @@ void SASourceIntegrator::AssembleElementVector( xi.SetSize(dim); grad.SetSize(num_nodes, dim); curl.SetSize(num_nodes, 3); - grad_i.SetSize(dim); + grad_nu_i.SetSize(dim); + grad_rho_i.SetSize(dim); curl_i.SetSize(3); - DenseMatrix u(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well - u.Transpose(); + DenseMatrix uc(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well + DenseMatrix u; u = uc; + u.Transpose(); //NOTE: Do not manipulate elfun directly DenseMatrix res(elvect.GetData(), num_nodes, num_states); DenseMatrix Qu(num_nodes, num_states); DenseMatrix HQu(num_nodes, num_states); @@ -34,6 +36,7 @@ void SASourceIntegrator::AssembleElementVector( { u(di+1, nn) *= 1.0/u(0, nn); } + u(dim+2, nn) *= 1.0/u(0, nn); } // precompute certain values @@ -54,44 +57,46 @@ void SASourceIntegrator::AssembleElementVector( Dui.SetCol(di, Duidi); } - // get Dui () - Duidi = 0.0; Dui = 0.0; - for (int di = 0; di < dim; di++) - { - sbp.multStrongOperator(di, i, u, Duidi); - Dui.SetCol(di, Duidi); - } - // compute vorticity at node and take magnitude calcVorticity(Dui.GetData(), Trans.InverseJacobian().GetData(), curl_i.GetData()); //curl.GetRow(i, curl_i); double S = sqrt(curl_i(0)*curl_i(0) + curl_i(1)*curl_i(1) +curl_i(2)*curl_i(2)); - S = curl_i(2); - + S = abs(curl_i(2)); // compute gradient of turbulent viscosity at node - calcGrad(Dui.GetData(), Trans.InverseJacobian().GetData(), grad_i.GetData()); //curl.GetRow(i, curl_i); + calcGrad(dim+2, Dui.GetData(), Trans.InverseJacobian().GetData(), grad_nu_i.GetData()); //curl.GetRow(i, curl_i); + // compute gradient of density at node + calcGrad(0, Dui.GetData(), Trans.InverseJacobian().GetData(), grad_rho_i.GetData()); //curl.GetRow(i, curl_i); // get distance function value at node - double d = 1.0; //temp solution, y distance from wall + double d = dist.GetValue(Trans.ElementNo, node); //temp solution, y distance from wall // accumulate source terms double src = calcSASource( - ui.GetData(), grad_i.GetData(), sacs.GetData()); + ui.GetData(), grad_nu_i.GetData(), sacs.GetData())/Re; + src -= calcSASource2( + ui.GetData(), mu, grad_nu_i.GetData(), grad_rho_i.GetData(), sacs.GetData())/Re; + ///std::cout << "grad:" << src << std::endl; // use negative model if turbulent viscosity is negative if (ui(dim+2) < 0) { src += calcSANegativeProduction( ui.GetData(), S, sacs.GetData()); src += calcSANegativeDestruction( - ui.GetData(), d, sacs.GetData()); + ui.GetData(), d, sacs.GetData())/Re; } else { src += calcSAProduction( - ui.GetData(), mu, d, S, sacs.GetData()); + ui.GetData(), mu, d, S, Re, sacs.GetData())/Re; + ///std::cout << "P+grad:" << src << std::endl; src += calcSADestruction( - ui.GetData(), mu, d, S, sacs.GetData()); + ui.GetData(), mu, d, S, Re, sacs.GetData())/Re; + ///std::cout << "P+D+grad:" << src << std::endl; } + // std::cout << "El: " << Trans.ElementNo << " P+D+grad: " << src << std::endl; + // std::cout << "Vorticity: " <::AssembleElementGrad( const SBPFiniteElement &sbp = dynamic_cast(fe); int num_nodes = sbp.GetDof(); #ifdef MFEM_THREAD_SAFE - Vector ui, xi, uj, grad_i, curl_i; + Vector ui, xi, uj, grad_nu_i, curl_i; DenseMatrix grad, curl; #endif ui.SetSize(num_states); xi.SetSize(dim); grad.SetSize(num_nodes, dim); curl.SetSize(num_nodes, 3); - grad_i.SetSize(dim); + grad_nu_i.SetSize(dim); + grad_rho_i.SetSize(dim); curl_i.SetSize(3); DenseMatrix uc(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well - uc.Transpose(); DenseMatrix u; u = uc; + u.Transpose(); DenseMatrix Qu(num_nodes, num_states); DenseMatrix HQu(num_nodes, num_states); DenseMatrix Dui(num_states, dim); Vector Duidi(num_states); vector jac_curl(dim); - vector jac_grad(dim); + vector jac_grad_nu(dim); + vector jac_grad_rho(dim); std::vector sacs_a(sacs.Size()); adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); for (int d = 0; d < dim; ++d) { jac_curl[d].SetSize(3, num_states); - jac_grad[d].SetSize(dim, num_states); + jac_grad_nu[d].SetSize(dim, num_states); + jac_grad_rho[d].SetSize(dim, num_states); } // partial derivative terms Vector dSdc(3); //partial vorticity mag (S) w.r.t. curl Vector dSrcdu(num_states); //partial src w.r.t. u Vector dSrcdS(1); //partial src w.r.t. S - Vector dSrcdgrad(dim); //partial src w.r.t grad + Vector dSrcdgradnu(dim); //partial src w.r.t grad + Vector dSrcdgradrho(dim); //partial src w.r.t grad DenseMatrix src_grad(dim, num_states); DenseMatrix src_curl(3, num_states); Vector work1(num_nodes*num_states); Vector work2s(num_states); - Vector work2g(num_states); + Vector work2gn(num_states); + Vector work2gr(num_states); Vector work3(num_nodes); Vector dnu(num_nodes*num_states); //total derivative //need dgraddDu, dcdDu, dDudu @@ -152,8 +162,9 @@ void SASourceIntegrator::AssembleElementGrad( { for (int di = 0; di < dim; di++) { - u(di+1, nn) *= 1.0/uc(0, nn); + u(di+1, nn) *= 1.0/u(0, nn); } + u(dim+2, nn) *= 1.0/u(0, nn); } elmat.SetSize(num_states*num_nodes); @@ -180,9 +191,11 @@ void SASourceIntegrator::AssembleElementGrad( std::vector ui_a(ui.Size()); //std::vector Dui_a(Dui_size); std::vector curl_i_a(curl_i.Size()); - std::vector grad_i_a(grad_i.Size()); + std::vector grad_nu_i_a(grad_nu_i.Size()); + std::vector grad_rho_i_a(grad_rho_i.Size()); adouble S; adouble mu_a = mu; + adouble Re_a = Re; // initialize adouble inputs adept::set_values(ui_a.data(), ui.Size(), ui.GetData()); //adept::set_values(Dui_a.data(), Dw_size, Dui.GetData()); @@ -194,95 +207,138 @@ void SASourceIntegrator::AssembleElementGrad( Trans.InverseJacobian().GetData(), jac_curl); // compute gradient of turbulent viscosity at node - calcGrad(Dui.GetData(), - Trans.InverseJacobian().GetData(), grad_i.GetData()); //curl.GetRow(i, curl_i); - calcGradJacDw(this->stack, Dui.GetData(), - Trans.InverseJacobian().GetData(), jac_grad); + calcGrad(dim+2, Dui.GetData(), + Trans.InverseJacobian().GetData(), grad_nu_i.GetData()); //curl.GetRow(i, curl_i); + calcGradJacDw(this->stack, dim+2, Dui.GetData(), + Trans.InverseJacobian().GetData(), jac_grad_nu); + // compute gradient of density at node + calcGrad(0, Dui.GetData(), + Trans.InverseJacobian().GetData(), grad_rho_i.GetData()); //curl.GetRow(i, curl_i); + calcGradJacDw(this->stack, 0, Dui.GetData(), + Trans.InverseJacobian().GetData(), jac_grad_rho); adept::set_values(curl_i_a.data(), curl_i.Size(), curl_i.GetData()); - adept::set_values(grad_i_a.data(), grad_i.Size(), grad_i.GetData()); - + adept::set_values(grad_nu_i_a.data(), grad_nu_i.Size(), grad_nu_i.GetData()); + adept::set_values(grad_rho_i_a.data(), grad_rho_i.Size(), grad_rho_i.GetData()); // get distance function value at node - adouble d = 1.0; //temp solution, y distance from wall + adouble d = dist.GetValue(Trans.ElementNo, node); //temp solution, y distance from wall // vorticity magnitude deriv this->stack.new_recording(); //S = sqrt(curl_i_a[0]*curl_i_a[0] + curl_i_a[1]*curl_i_a[1] +curl_i_a[2]*curl_i_a[2]); - S = curl_i_a[2]; + S = abs(curl_i_a[2]); this->stack.independent(curl_i_a.data(), curl_i.Size()); this->stack.dependent(S); this->stack.jacobian(dSdc.GetData()); + dSrcdu = 0.0; // ui differentiation this->stack.new_recording(); adouble src = calcSASource( - ui_a.data(), grad_i_a.data(), sacs_a.data()); + ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; + src -= calcSASource2( + ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; // use negative model if turbulent viscosity is negative if (ui_a[dim+2] < 0) { src += calcSANegativeProduction( ui_a.data(), S, sacs_a.data()); src += calcSANegativeDestruction( - ui_a.data(), d, sacs_a.data()); + ui_a.data(), d, sacs_a.data())/Re_a; } else { src += calcSAProduction( - ui_a.data(), mu_a, d, S, sacs_a.data()); + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; src += calcSADestruction( - ui_a.data(), mu_a, d, S, sacs_a.data()); + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; } this->stack.independent(ui_a.data(), ui.Size()); this->stack.dependent(src); this->stack.jacobian(dSrcdu.GetData()); + dSrcdS = 0.0; // S differentiation this->stack.new_recording(); + adouble S_a; + S_a.set_value(S.value()); src = calcSASource( - ui_a.data(), grad_i_a.data(), sacs_a.data()); + ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; + src -= calcSASource2( + ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; // use negative model if turbulent viscosity is negative if (ui_a[dim+2] < 0) { src += calcSANegativeProduction( ui_a.data(), S, sacs_a.data()); src += calcSANegativeDestruction( - ui_a.data(), d, sacs_a.data()); + ui_a.data(), d, sacs_a.data())/Re_a; } else { src += calcSAProduction( - ui_a.data(), mu_a, d, S, sacs_a.data()); + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; src += calcSADestruction( - ui_a.data(), mu_a, d, S, sacs_a.data()); + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; } - this->stack.independent(S); + this->stack.independent(S_a); this->stack.dependent(src); this->stack.jacobian(dSrcdS.GetData()); + dSrcdgradnu = 0.0; // grad differentiation this->stack.new_recording(); src = calcSASource( - ui_a.data(), grad_i_a.data(), sacs_a.data()); + ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; + src -= calcSASource2( + ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; // use negative model if turbulent viscosity is negative if (ui_a[dim+2] < 0) { src += calcSANegativeProduction( ui_a.data(), S, sacs_a.data()); src += calcSANegativeDestruction( - ui_a.data(), d, sacs_a.data()); + ui_a.data(), d, sacs_a.data())/Re_a; } else { src += calcSAProduction( - ui_a.data(), mu_a, d, S, sacs_a.data()); + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; src += calcSADestruction( - ui_a.data(), mu_a, d, S, sacs_a.data()); + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; } - this->stack.independent(grad_i_a.data(), grad_i_a.size()); + this->stack.independent(grad_nu_i_a.data(), grad_nu_i_a.size()); this->stack.dependent(src); - this->stack.jacobian(dSrcdgrad.GetData()); + this->stack.jacobian(dSrcdgradnu.GetData()); + + dSrcdgradrho = 0.0; + // grad differentiation + this->stack.new_recording(); + src = calcSASource( + ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; + src -= calcSASource2( + ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; + // use negative model if turbulent viscosity is negative + if (ui_a[dim+2] < 0) + { + src += calcSANegativeProduction( + ui_a.data(), S, sacs_a.data()); + src += calcSANegativeDestruction( + ui_a.data(), d, sacs_a.data())/Re_a; + } + else + { + src += calcSAProduction( + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; + src += calcSADestruction( + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; + } + + this->stack.independent(grad_rho_i_a.data(), grad_rho_i_a.size()); + this->stack.dependent(src); + this->stack.jacobian(dSrcdgradrho.GetData()); // Assemble nu derivative for(int ns = 0; ns < num_states; ns ++) @@ -293,18 +349,19 @@ void SASourceIntegrator::AssembleElementGrad( // Dui differentiation for (int di = 0; di < dim; di++) { - work1 = 0.0; work2s = 0.0; work2g = 0.0; work3 = 0.0; + work1 = 0.0; work2s = 0.0; work2gn = 0.0; work2gr = 0.0; work3 = 0.0; sbp.getStrongOperator(di, i, work3); jac_curl[di].MultTranspose(dSdc, work2s); work2s *= dSrcdS(0); - jac_grad[di].MultTranspose(dSrcdgrad, work2g); + jac_grad_nu[di].MultTranspose(dSrcdgradnu, work2gn); + jac_grad_rho[di].MultTranspose(dSrcdgradrho, work2gr); //src_grad.GetRow(di, work2g); for (int nn = 0; nn < num_nodes; nn++) { // assuming ordering BY NODES for (int ns = 0; ns < num_states; ns++) { - work1(nn + ns*num_nodes) = work3(nn)*(work2s(ns) + work2g(ns)); + work1(nn + ns*num_nodes) = work3(nn)*(work2s(ns) + work2gn(ns) + work2gr(ns)); } } dnu += work1; @@ -316,9 +373,11 @@ void SASourceIntegrator::AssembleElementGrad( { for (int di = 0; di < dim; di++) { - dnu(nn) -= dnu(nn + (di+1)*num_nodes)*uc(di+1,nn)/(uc(0, nn)*uc(0, nn)); - dnu(nn + (di+1)*num_nodes) *= 1.0/uc(0, nn); + dnu(nn) -= dnu(nn + (di+1)*num_nodes)*uc(nn, di+1)/(uc(nn, 0)*uc(nn, 0)); + dnu(nn + (di+1)*num_nodes) *= 1.0/uc(nn, 0); } + dnu(nn) -= dnu(nn + (dim+2)*num_nodes)*uc(nn, dim+2)/(uc(nn, 0)*uc(nn, 0)); + dnu(nn + (dim+2)*num_nodes) *= 1.0/uc(nn, 0); } // Set elmat entry @@ -352,6 +411,7 @@ void SAInviscidIntegrator::calcFlux(int di, const mfem::Vector &qL, } //add flux term for SA flux(dim+2) = 0.5*(qL(di+1)/qL(0) + qR(di+1)/qR(0))*0.5*(qL(dim+2) + qR(dim+2)); + //flux(dim+2) = (qL(di+1)/qL(0) + qR(di+1)/qR(0))*0.5*(qL(dim+2) + qR(dim+2)); } template @@ -425,11 +485,11 @@ void SANoSlipAdiabaticWallBC::calcFlux(const mfem::Vector &x, { mu_Re = calcSutherlandViscosity(q.GetData()); } - mu_Re /= Re; - double fv1 = calcSACoefficient(q.GetData(), mu_Re, + //mu_Re /= Re; + double fv1 = calcSACoefficient(q.GetData(), mu, sacs.GetData()); - mu_Re += q(dim+2)*q(0)*fv1; - calcAdiabaticWallFlux(dir.GetData(), mu_Re, Pr, q.GetData(), + double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; + calcAdiabaticWallFlux(dir.GetData(), mu_Re_SA, Pr, q.GetData(), Dw.GetData(), work_vec.GetData()); flux_vec -= work_vec; // note the minus sign!!! // evaluate wall normal eddy viscosity flux @@ -437,13 +497,15 @@ void SANoSlipAdiabaticWallBC::calcFlux(const mfem::Vector &x, for (int di = 0; di < dim; di++) grad[di] = Dw(dim+2, di); double SAflux = dot(dir.GetData(), grad); - flux_vec(dim+2) -= (mu_Re/q(0) + q(dim+2))*SAflux/sacs(2); + double fn = calcSANegativeCoefficient(q.GetData(), mu, + sacs.GetData()); + flux_vec(dim+2) -= (mu + fn*q(dim+2))*SAflux/(sacs(2)*Re); // Step 3: evaluate the no-slip penalty calcNoSlipPenaltyFlux(dir.GetData(), jac, mu_Re, Pr, qfs.GetData(), q.GetData(), work_vec.GetData()); flux_vec += work_vec; double dnu = q(dim+2); - double dnuflux = (mu_Re/q(0) + q(dim+2))*dnu/sacs(2); + double dnuflux = (mu + fn*q(dim+2))*dnu/(sacs(2)*Re); double fac = sqrt(dot(dir, dir))/jac; flux_vec(dim+2) += dnuflux*fac; } @@ -483,11 +545,11 @@ void SANoSlipAdiabaticWallBC::calcFluxJacState( { mu_Re = mach::calcSutherlandViscosity(q_a.data()); } - mu_Re /= Re; - adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + //mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); - mu_Re += q_a[dim+2]*q_a[0]*fv1; - mach::calcAdiabaticWallFlux(dir_a.data(), mu_Re, Pr, q_a.data(), + adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + mach::calcAdiabaticWallFlux(dir_a.data(), mu_Re_SA, Pr, q_a.data(), Dw_a.data(), work_vec_a.data()); for (int i = 0; i < flux_a.size(); ++i) { @@ -498,7 +560,9 @@ void SANoSlipAdiabaticWallBC::calcFluxJacState( for (int di = 0; di < dim; di++) grad[di] = Dw_a[dim+2 + di*(dim+3)]; adouble SAflux = dot(dir_a.data(), grad); - flux_a[dim+2] -= (mu_Re/q_a[0] + q_a[dim+2])*SAflux/sacs_a[2]; + adouble fn = calcSANegativeCoefficient(q_a.data(), mu, + sacs_a.data()); + flux_a[dim+2] -= (mu + fn*q_a[dim+2])*SAflux/(sacs_a[2]*Re); // Step 3: evaluate the no-slip penalty mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re, Pr, qfs_a.data(), q_a.data(), work_vec_a.data()); @@ -507,7 +571,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacState( flux_a[i] += work_vec_a[i]; } adouble dnu = q_a[dim+2]; - adouble dnuflux = (mu_Re/q_a[0] + q_a[dim+2])*dnu/sacs_a[2]; + adouble dnuflux = (mu + fn*q_a[dim+2])*dnu/(sacs_a[2]*Re); adouble fac = sqrt(dot(dir_a.data(), dir_a.data()))/jac; flux_a[dim+2] += dnuflux*fac; @@ -549,11 +613,11 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf { mu_Re = mach::calcSutherlandViscosity(q_a.data()); } - mu_Re /= Re; - adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + //mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); - mu_Re += q_a[dim+2]*q_a[0]*fv1; - mach::calcAdiabaticWallFlux(dir_a.data(), mu_Re, Pr, q_a.data(), + adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + mach::calcAdiabaticWallFlux(dir_a.data(), mu_Re_SA, Pr, q_a.data(), Dw_a.data(), work_vec_a.data()); for (int i = 0; i < flux_a.size(); ++i) { @@ -564,7 +628,9 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf for (int di = 0; di < dim; di++) grad[di] = Dw_a[dim+2 + di*(dim+3)]; adouble SAflux = dot(dir_a.data(), grad); - flux_a[dim+2] -= (mu_Re/q_a[0] + q_a[dim+2])*SAflux/sacs_a[2]; + adouble fn = calcSANegativeCoefficient(q_a.data(), mu, + sacs_a.data()); + flux_a[dim+2] -= (mu + fn*q_a[dim+2])*SAflux/(sacs_a[2]*Re); // Step 3: evaluate the no-slip penalty mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re, Pr, qfs_a.data(), q_a.data(), work_vec_a.data()); @@ -573,7 +639,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf flux_a[i] += work_vec_a[i]; } adouble dnu = q_a[dim+2]; - adouble dnuflux = (mu_Re/q_a[0] + q_a[dim+2])*dnu/sacs_a[2]; + adouble dnuflux = (mu + fn*q_a[dim+2])*dnu/(sacs_a[2]*Re); adouble fac = sqrt(dot(dir_a.data(), dir_a.data()))/jac; flux_a[dim+2] += dnuflux*fac; @@ -600,6 +666,7 @@ void SAViscousSlipWallBC::calcFlux(const mfem::Vector &x, // Part 1: apply the inviscid slip wall BCs calcSlipWallFlux(x.GetData(), dir.GetData(), q.GetData(), flux_vec.GetData()); + flux_vec(dim+2) = 0.0; int Dw_size = Dw.Height() * Dw.Width(); mfem::Vector Dw_work(Dw_size); setZeroNormalDeriv(dir.GetData(), Dw.GetData(), @@ -624,18 +691,20 @@ void SAViscousSlipWallBC::calcFlux(const mfem::Vector &x, { mu_Re = calcSutherlandViscosity(q.GetData()); } - mu_Re /= Re; - double fv1 = calcSACoefficient(q.GetData(), mu_Re, + //mu_Re /= Re; + double fv1 = calcSACoefficient(q.GetData(), mu, sacs.GetData()); - mu_Re += q(dim+2)*q(0)*fv1; + double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; for (int d = 0; d < dim; ++d) { work_vec = 0.0; - applyViscousScaling(d, mu_Re, Pr, q.GetData(), + applyViscousScaling(d, mu_Re_SA, Pr, q.GetData(), Dw_work.GetData(), work_vec.GetData()); work_vec *= dir(d); flux_vec -= work_vec; - flux_vec(dim+2) -= (mu_Re/q(0) + q(dim+2))*Dw_work(dim+2 + d*(dim+3))/sacs(2); + double fn = calcSANegativeCoefficient(q.GetData(), mu, + sacs.GetData()); + flux_vec(dim+2) -= (mu + fn*q(dim+2))*Dw_work(dim+2 + d*(dim+3))/(sacs(2)*Re); } } @@ -666,6 +735,7 @@ void SAViscousSlipWallBC::calcFluxJacState( // Part 1: apply the inviscid slip wall BCs calcSlipWallFlux(x_a.data(), dir_a.data(), q_a.data(), flux_a.data()); + flux_a[dim+2] = 0.0; std::vector Dw_work_a(Dw_size); setZeroNormalDeriv(dir_a.data(), Dw_a.data(), Dw_work_a.data()); @@ -689,22 +759,24 @@ void SAViscousSlipWallBC::calcFluxJacState( { mu_Re = calcSutherlandViscosity(q_a.data()); } - mu_Re /= Re; - adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + //mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); - mu_Re += q_a[dim+2]*q_a[0]*fv1; + adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; for (int d = 0; d < dim; ++d) { - applyViscousScaling(d, mu_Re, Pr, q_a.data(), + work_vec_a[dim+2] = 0.0; + applyViscousScaling(d, mu_Re_SA, Pr, q_a.data(), Dw_work_a.data(), work_vec_a.data()); for (int k = 0; k < dim+2; k++) { work_vec_a[k] *= dir_a[d]; flux_a[k] -= work_vec_a[k]; } - flux_a[dim+2] -= (mu_Re/q_a[0] + q_a[dim+2])*Dw_work_a[dim+2 + d*(dim+3)]/sacs_a[2]; + adouble fn = calcSANegativeCoefficient(q_a.data(), mu, + sacs_a.data()); + flux_a[dim+2] -= (mu + fn*q_a[dim+2])*Dw_work_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); } - this->stack.independent(q_a.data(), q.Size()); this->stack.dependent(flux_a.data(), q.Size()); this->stack.jacobian(flux_jac.GetData()); @@ -735,6 +807,7 @@ void SAViscousSlipWallBC::calcFluxJacDw(const mfem::Vector &x, const mfem:: // Part 1: apply the inviscid slip wall BCs calcSlipWallFlux(x_a.data(), dir_a.data(), q_a.data(), flux_a.data()); + flux_a[dim+2] = 0.0; std::vector Dw_work_a(Dw_size); setZeroNormalDeriv(dir_a.data(), Dw_a.data(), Dw_work_a.data()); @@ -758,20 +831,23 @@ void SAViscousSlipWallBC::calcFluxJacDw(const mfem::Vector &x, const mfem:: { mu_Re = calcSutherlandViscosity(q_a.data()); } - mu_Re /= Re; - adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + //mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); - mu_Re += q_a[dim+2]*q_a[0]*fv1; + adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; for (int d = 0; d < dim; ++d) { - applyViscousScaling(d, mu_Re, Pr, q_a.data(), + work_vec_a[dim+2] = 0.0; + applyViscousScaling(d, mu_Re_SA, Pr, q_a.data(), Dw_work_a.data(), work_vec_a.data()); for (int k = 0; k < dim+2; k++) { work_vec_a[k] *= dir_a[d]; flux_a[k] -= work_vec_a[k]; } - flux_a[dim+2] -= (mu_Re/q_a[0] + q_a[dim+2])*Dw_work_a[dim+2 + d*(dim+3)]/sacs_a[2]; + adouble fn = calcSANegativeCoefficient(q_a.data(), mu, + sacs_a.data()); + flux_a[dim+2] -= (mu + fn*q_a[dim+2])*Dw_work_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); } this->stack.independent(Dw_a.data(),Dw_size); @@ -802,12 +878,12 @@ void SAFarFieldBC::calcFlux(const mfem::Vector &x, const mfem::Vector &dir, { mu_Re = calcSutherlandViscosity(q.GetData()); } - mu_Re /= Re; - + //mu_Re /= Re; + adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; for (int d = 0; d < dim; ++d) { work_vec = 0.0; - applyViscousScaling(d, mu_Re, Pr, q.GetData(), Dw.GetData(), + applyViscousScaling(d, mu_Re_SA, Pr, q.GetData(), Dw.GetData(), work_vec.GetData()); work_vec *= dir[d]; flux_vec -= work_vec; @@ -865,13 +941,14 @@ void SAFarFieldBC::calcFluxJacState( mu_Re = mach::calcSutherlandViscosity(q_a.data()); } mu_Re /= Re; + adouble mu_Re_SA = mu_Re + q_a[dim+2]*fv1/Re; for (int d = 0; d < dim; ++d) { for (int i = 0; i < work_vec_a.size(); ++i) { work_vec_a[i] = 0.0; } - applyViscousScaling(d, mu_Re, Pr, q_a.data(), Dw_a.data(), + applyViscousScaling(d, mu_Re_SA, Pr, q_a.data(), Dw_a.data(), work_vec_a.data()); for (int i = 0; i < flux_a.size(); ++i) { @@ -1082,13 +1159,15 @@ void SAViscousIntegrator::applyScaling(int d, const mfem::Vector &x, { mu_Re = calcSutherlandViscosity(q.GetData()); } - mu_Re /= Re; - double fv1 = calcSACoefficient(q.GetData(), mu_Re, + //mu_Re /= Re; + double fv1 = calcSACoefficient(q.GetData(), mu, sacs.GetData()); - mu_Re += q(dim+2)*q(0)*fv1; - applyViscousScaling(d, mu_Re, Pr, q.GetData(), Dw.GetData(), + double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; + applyViscousScaling(d, mu_Re_SA, Pr, q.GetData(), Dw.GetData(), CDw.GetData()); - CDw(dim+2) = (mu_Re/q(0) + q(dim+2))*Dw(dim+2, d)/sacs(2); + double fn = calcSANegativeCoefficient(q.GetData(), mu, + sacs.GetData()); + CDw(dim+2) = (mu + fn*q(dim+2))*Dw(dim+2, d)/(sacs(2)*Re); } template @@ -1115,13 +1194,15 @@ void SAViscousIntegrator::applyScalingJacState(int d, const mfem::Vector &x { mu_Re = calcSutherlandViscosity(q_a.data()); } - mu_Re /= Re; - adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + //mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); - mu_Re += q_a[dim+2]*q_a[0]*fv1; - applyViscousScaling(d, mu_Re, Pr, q_a.data(), Dw_a.data(), + adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + applyViscousScaling(d, mu_Re_SA, Pr, q_a.data(), Dw_a.data(), CDw_a.data()); - CDw_a[dim+2] = (mu_Re/q_a[0] + q_a[dim+2])*Dw_a[dim+2 + d*(dim+3)]/sacs_a[2]; + adouble fn = calcSANegativeCoefficient(q_a.data(), mu, + sacs_a.data()); + CDw_a[dim+2] = (mu + fn*q_a[dim+2])*Dw_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); // identify independent and dependent variables this->stack.independent(q_a.data(), q.Size()); this->stack.dependent(CDw_a.data(), q.Size()); @@ -1153,13 +1234,15 @@ void SAViscousIntegrator::applyScalingJacDw( { mu_Re = calcSutherlandViscosity(q_a.data()); } - mu_Re /= Re; - adouble fv1 = calcSACoefficient(q_a.data(), mu_Re, + //mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); - mu_Re += q_a[dim+2]*q_a[0]*fv1; - applyViscousScaling(d, mu_Re, Pr, q_a.data(), Dw_a.data(), + adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + applyViscousScaling(d, mu_Re_SA, Pr, q_a.data(), Dw_a.data(), CDw_a.data()); - CDw_a[dim+2] = (mu_Re/q_a[0] + q_a[dim+2])*Dw_a[dim+2 + d*(dim+3)]/sacs_a[2]; + adouble fn = calcSANegativeCoefficient(q_a.data(), mu, + sacs_a.data()); + CDw_a[dim+2] = (mu + fn*q_a[dim+2])*Dw_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); // identify independent and dependent variables this->stack.independent(Dw_a.data(), Dw_size); this->stack.dependent(CDw_a.data(), q.Size()); diff --git a/src/physics/fluidflow/viscous_integ_def.hpp b/src/physics/fluidflow/viscous_integ_def.hpp index 2cd8f36c..d5bec70c 100644 --- a/src/physics/fluidflow/viscous_integ_def.hpp +++ b/src/physics/fluidflow/viscous_integ_def.hpp @@ -188,7 +188,6 @@ void SymmetricViscousIntegrator::AssembleElementGrad( jac_term2(sk, sj); } } - } // loop over space dimension d2 } // loop over the element nodes j } // loop over element nodes k diff --git a/src/physics/solver.cpp b/src/physics/solver.cpp index 44b97218..89f61a63 100644 --- a/src/physics/solver.cpp +++ b/src/physics/solver.cpp @@ -711,6 +711,9 @@ void AbstractSolver::printSolution(const std::string &file_name, } mesh->PrintVTK(sol_ofs, refine); u->SaveVTK(sol_ofs, "Solution", refine); + GridFunction r(u->FESpace()); + res->Mult(*u, r); + r.SaveVTK(sol_ofs, "Residual", refine); sol_ofs.close(); } diff --git a/test/unit/euler_test_data.cpp b/test/unit/euler_test_data.cpp index ab92c4d6..1983ce94 100644 --- a/test/unit/euler_test_data.cpp +++ b/test/unit/euler_test_data.cpp @@ -111,7 +111,7 @@ void randBaselinePertSA(const mfem::Vector &x, mfem::Vector &u) mfem::Vector q(u); mach::calcEntropyVars(q.GetData(), u.GetData()); } - u(dim + 2) = 10.0 * scale * (uniform_rand(gen) - 0.1); + u(dim + 2) = 3.0 * (uniform_rand(gen) - 0.1); } // explicit instantiation of the templated function above template void randBaselinePertSA<1, true>(const mfem::Vector &x, mfem::Vector &u); diff --git a/test/unit/euler_test_data.hpp b/test/unit/euler_test_data.hpp index 8aa59c58..7b90e19d 100644 --- a/test/unit/euler_test_data.hpp +++ b/test/unit/euler_test_data.hpp @@ -64,7 +64,7 @@ extern double adjJ_data[9]; extern double delw_data[15]; // Spalart-Allmaras model parameters -const double sa_params[11] = {0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10.0, 16.0}; +const double sa_params[13] = {0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10.0, 16.0, 0.7, 0.9}; /// Returns a perturbed version of the baseline flow state /// \param[in] x - coordinates (not used) diff --git a/test/unit/test_rans_fluxes.cpp b/test/unit/test_rans_fluxes.cpp index ffd9b0f0..c2fefea4 100644 --- a/test/unit/test_rans_fluxes.cpp +++ b/test/unit/test_rans_fluxes.cpp @@ -105,7 +105,7 @@ TEMPLATE_TEST_CASE_SIG("SA calcGrad Jacobian", "[SAVorticity]", mat_vec_jac[d].SetSize(dim, dim+3); } // compute the jacobian - mach::calcGradJacDw(stack, Dw.GetData(), trans_jac.GetData(), + mach::calcGradJacDw(stack, dim+2, Dw.GetData(), trans_jac.GetData(), mat_vec_jac); for (int d = 0; d < dim; ++d) @@ -118,9 +118,9 @@ TEMPLATE_TEST_CASE_SIG("SA calcGrad Jacobian", "[SAVorticity]", Dw_minus.GetColumn(d)[s] -= v(s) * delta; } // get perturbed states conv vector - mach::calcGrad(Dw_plus.GetData(), trans_jac.GetData(), + mach::calcGrad(dim+2, Dw_plus.GetData(), trans_jac.GetData(), grad_plus.GetData()); - mach::calcGrad(Dw_minus.GetData(), trans_jac.GetData(), + mach::calcGrad(dim+2, Dw_minus.GetData(), trans_jac.GetData(), grad_minus.GetData()); mat_vec_jac[d].Mult(v, jac_v); @@ -139,4 +139,387 @@ TEMPLATE_TEST_CASE_SIG("SA calcGrad Jacobian", "[SAVorticity]", } } } -} \ No newline at end of file +} + + +TEST_CASE("SA S derivatives", "[SASource]") +{ + using namespace euler_data; + const int dim = 2; + mfem::Vector q(dim+3); + mfem::Vector grad(dim); + double delta = 1e-5; + + grad(0) = 0.0; + grad(1) = 0.0; + + q(0) = rho; + q(dim + 1) = rhoe; + q(dim + 2) = 4; + for (int di = 0; di < dim; ++di) + { + q(di + 1) = rhou[di]; + } + + mfem::Vector sacs(13); + // create SA parameter vector + for (int di = 0; di < 13; ++di) + { + sacs(di) = sa_params[di]; + } + + adept::Stack stack; + std::vector sacs_a(sacs.Size()); + adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); + std::vector q_a(sacs.Size()); + adept::set_values(q_a.data(), q.Size(), q.GetData()); + std::vector grad_a(grad.Size()); + adept::set_values(grad_a.data(), grad.Size(), grad.GetData()); + + DYNAMIC_SECTION("Jacobians w.r.t S is correct") + { + // compute the jacobian + stack.new_recording(); + mfem::Vector dSrcdu(1); + adouble S_a; + S_a.set_value(0.000); + adouble d_a = 10.1; + adouble mu_a = 1.1; + adouble Re_a = 5000000; + adouble src;// = mach::calcSADestruction(q_a.data(), S_a, mu_a, d_a, Re_a, sacs_a.data()); + src = mach::calcSASource( + q_a.data(), grad_a.data(), sacs_a.data())/Re_a; + // use negative model if turbulent viscosity is negative + // if (q_a[dim+2] < 0) + // { + // src += mach::calcSANegativeProduction( + // q_a.data(), S_a, sacs_a.data()); + // src += mach::calcSANegativeDestruction( + // q_a.data(), d_a, sacs_a.data())/Re_a; + // } + // else + { + // src += mach::calcSAProduction( + // q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; + src += mach::calcSADestruction( + q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; + } + + stack.independent(S_a); + stack.dependent(src); + stack.jacobian(dSrcdu.GetData()); + + double Sp = 0.000 + delta; double Sm = 0.000 - delta; + double d = 10.1; + double mu = 1.1; + double Re = 5000000; + + // get perturbed states conv vector + //double srcp = mach::calcSADestruction(q.GetData(), Sp, mu, d, Re, sacs.GetData()); + //double srcm = mach::calcSADestruction(q.GetData(), Sm, mu, d, Re, sacs.GetData()); + + double srcp = mach::calcSASource( + q.GetData(), grad.GetData(), sacs.GetData())/Re; + // if (q(dim+2) < 0) + // { + // srcp += mach::calcSANegativeProduction( + // q.GetData(), Sp, sacs.GetData()); + // srcp += mach::calcSANegativeDestruction( + // q.GetData(), d, sacs.GetData())/Re; + // } + // else + { + // srcp += mach::calcSAProduction( + // q.GetData(), mu, d, Sp, Re, sacs.GetData())/Re; + srcp += mach::calcSADestruction( + q.GetData(), mu, d, Sp, Re, sacs.GetData())/Re; + } + + double srcm = mach::calcSASource( + q.GetData(), grad.GetData(), sacs.GetData())/Re; + // if (q(dim+2) < 0) + // { + // srcm += mach::calcSANegativeProduction( + // q.GetData(), Sm, sacs.GetData()); + // srcm += mach::calcSANegativeDestruction( + // q.GetData(), d, sacs.GetData())/Re; + // } + // else + { + // srcm += mach::calcSAProduction( + // q.GetData(), mu, d, Sm, Re, sacs.GetData())/Re; + srcm += mach::calcSADestruction( + q.GetData(), mu, d, Sm, Re, sacs.GetData())/Re; + } + + // finite difference jacobian + double jac_fd = srcp; + jac_fd -= srcm; + jac_fd /= 2.0 * delta; + + std::cout << "FD Deriv: " << jac_fd << std::endl; + std::cout << "AN Deriv: " << dSrcdu(0) << std::endl; + REQUIRE(dSrcdu(0) == Approx(jac_fd).margin(1e-10)); + + } +} + +TEST_CASE("SA grad derivatives", "[SAVorticity]") +{ + using namespace euler_data; + const int dim = 2; + mfem::Vector q(dim+3); + mfem::Vector grad(dim); + mfem::Vector v(dim); + double delta = 1e-5; + + grad(0) = 0.0; + grad(1) = 0.0; + + q(0) = rho; + q(dim + 1) = rhoe; + q(dim + 2) = 4; + for (int di = 0; di < dim; ++di) + { + q(di + 1) = rhou[di]; + } + + for (int di = 0; di < dim; ++di) + { + v(di) = vec_pert[di]; + } + + mfem::Vector sacs(13); + // create SA parameter vector + for (int di = 0; di < 13; ++di) + { + sacs(di) = sa_params[di]; + } + + adept::Stack stack; + DYNAMIC_SECTION("Jacobians w.r.t grad is correct") + { + // compute the jacobian + stack.new_recording(); + mfem::Vector dSrcdgrad(dim); + std::vector sacs_a(sacs.Size()); + adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); + std::vector q_a(sacs.Size()); + adept::set_values(q_a.data(), q.Size(), q.GetData()); + std::vector grad_a(grad.Size()); + adept::set_values(grad_a.data(), grad.Size(), grad.GetData()); + adouble S_a = 0.000; + adouble d_a = 10.1; + adouble mu_a = 1.1; + adouble Re_a = 5000000; + adouble src;// = mach::calcSADestruction(q_a.data(), S_a, mu_a, d_a, Re_a, sacs_a.data()); + src = mach::calcSASource( + q_a.data(), grad_a.data(), sacs_a.data())/Re_a; + // use negative model if turbulent viscosity is negative + if (q_a[dim+2] < 0) + { + src += mach::calcSANegativeProduction( + q_a.data(), S_a, sacs_a.data()); + src += mach::calcSANegativeDestruction( + q_a.data(), d_a, sacs_a.data())/Re_a; + } + else + { + src += mach::calcSAProduction( + q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; + src += mach::calcSADestruction( + q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; + } + + stack.independent(grad_a.data(), dim); + stack.dependent(src); + stack.jacobian(dSrcdgrad.GetData()); + + double S = 0.000; + double d = 10.1; + double mu = 1.1; + double Re = 5000000; + mfem::Vector gradp(grad); mfem::Vector gradm(grad); + gradp.Add(delta, v); gradm.Add(-delta, v); + + // get perturbed states conv vector + //double srcp = mach::calcSADestruction(q.GetData(), Sp, mu, d, Re, sacs.GetData()); + //double srcm = mach::calcSADestruction(q.GetData(), Sm, mu, d, Re, sacs.GetData()); + + double srcp = mach::calcSASource( + q.GetData(), gradp.GetData(), sacs.GetData())/Re; + if (q(dim+2) < 0) + { + srcp += mach::calcSANegativeProduction( + q.GetData(), S, sacs.GetData()); + srcp += mach::calcSANegativeDestruction( + q.GetData(), d, sacs.GetData())/Re; + } + else + { + srcp += mach::calcSAProduction( + q.GetData(), mu, d, S, Re, sacs.GetData())/Re; + srcp += mach::calcSADestruction( + q.GetData(), mu, d, S, Re, sacs.GetData())/Re; + } + + double srcm = mach::calcSASource( + q.GetData(), gradm.GetData(), sacs.GetData())/Re; + if (q(dim+2) < 0) + { + srcm += mach::calcSANegativeProduction( + q.GetData(), S, sacs.GetData()); + srcm += mach::calcSANegativeDestruction( + q.GetData(), d, sacs.GetData())/Re; + } + else + { + srcm += mach::calcSAProduction( + q.GetData(), mu, d, S, Re, sacs.GetData())/Re; + srcm += mach::calcSADestruction( + q.GetData(), mu, d, S, Re, sacs.GetData())/Re; + } + + // finite difference jacobian + double jac_fd = srcp; + jac_fd -= srcm; + jac_fd /= 2.0 * delta; + + double jac_v = dSrcdgrad*v; + + std::cout << "FD Deriv: " << jac_fd << std::endl; + std::cout << "AN Deriv: " << jac_v << std::endl; + REQUIRE(jac_v == Approx(jac_fd).margin(1e-10)); + + } +} + +TEST_CASE("SA q derivatives", "[SAVorticity]") +{ + using namespace euler_data; + const int dim = 2; + mfem::Vector q(dim+3); + mfem::Vector grad(dim); + mfem::Vector v(dim+3); + double delta = 1e-5; + + grad(0) = 0.0; + grad(1) = 0.0; + + q(0) = rho; + q(dim + 1) = rhoe; + q(dim + 2) = 4; + for (int di = 0; di < dim; ++di) + { + q(di + 1) = rhou[di]; + } + + for (int di = 0; di < dim+3; ++di) + { + v(di) = vec_pert[di]; + } + + mfem::Vector sacs(13); + // create SA parameter vector + for (int di = 0; di < 13; ++di) + { + sacs(di) = sa_params[di]; + } + + adept::Stack stack; + DYNAMIC_SECTION("Jacobians w.r.t grad is correct") + { + // compute the jacobian + stack.new_recording(); + mfem::Vector dSrcdq(dim+3); + std::vector sacs_a(sacs.Size()); + adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); + std::vector q_a(sacs.Size()); + adept::set_values(q_a.data(), q.Size(), q.GetData()); + std::vector grad_a(grad.Size()); + adept::set_values(grad_a.data(), grad.Size(), grad.GetData()); + adouble S_a = 0.000; + adouble d_a = 10.1; + adouble mu_a = 1.1; + adouble Re_a = 5000000; + adouble src;// = mach::calcSADestruction(q_a.data(), S_a, mu_a, d_a, Re_a, sacs_a.data()); + src = mach::calcSASource( + q_a.data(), grad_a.data(), sacs_a.data())/Re_a; + // use negative model if turbulent viscosity is negative + if (q_a[dim+2] < 0) + { + src += mach::calcSANegativeProduction( + q_a.data(), S_a, sacs_a.data()); + src += mach::calcSANegativeDestruction( + q_a.data(), d_a, sacs_a.data())/Re_a; + } + else + { + src += mach::calcSAProduction( + q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; + src += mach::calcSADestruction( + q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; + } + + stack.independent(q_a.data(), dim+3); + stack.dependent(src); + stack.jacobian(dSrcdq.GetData()); + + double S = 0.000; + double d = 10.1; + double mu = 1.1; + double Re = 5000000; + mfem::Vector qp(q); mfem::Vector qm(q); + qp.Add(delta, v); qm.Add(-delta, v); + + // get perturbed states conv vector + //double srcp = mach::calcSADestruction(q.GetData(), Sp, mu, d, Re, sacs.GetData()); + //double srcm = mach::calcSADestruction(q.GetData(), Sm, mu, d, Re, sacs.GetData()); + + double srcp = mach::calcSASource( + qp.GetData(), grad.GetData(), sacs.GetData())/Re; + if (qp(dim+2) < 0) + { + srcp += mach::calcSANegativeProduction( + qp.GetData(), S, sacs.GetData()); + srcp += mach::calcSANegativeDestruction( + qp.GetData(), d, sacs.GetData())/Re; + } + else + { + srcp += mach::calcSAProduction( + qp.GetData(), mu, d, S, Re, sacs.GetData())/Re; + srcp += mach::calcSADestruction( + qp.GetData(), mu, d, S, Re, sacs.GetData())/Re; + } + + double srcm = mach::calcSASource( + qm.GetData(), grad.GetData(), sacs.GetData())/Re; + if (qm(dim+2) < 0) + { + srcm += mach::calcSANegativeProduction( + qm.GetData(), S, sacs.GetData()); + srcm += mach::calcSANegativeDestruction( + qm.GetData(), d, sacs.GetData())/Re; + } + else + { + srcm += mach::calcSAProduction( + qm.GetData(), mu, d, S, Re, sacs.GetData())/Re; + srcm += mach::calcSADestruction( + qm.GetData(), mu, d, S, Re, sacs.GetData())/Re; + } + + // finite difference jacobian + double jac_fd = srcp; + jac_fd -= srcm; + jac_fd /= 2.0 * delta; + + double jac_v = dSrcdq*v; + + std::cout << "FD Deriv: " << jac_fd << std::endl; + std::cout << "AN Deriv: " << jac_v << std::endl; + REQUIRE(jac_v == Approx(jac_fd).margin(1e-10)); + + } +} \ No newline at end of file diff --git a/test/unit/test_rans_integ.cpp b/test/unit/test_rans_integ.cpp index f1306748..b530f3a6 100644 --- a/test/unit/test_rans_integ.cpp +++ b/test/unit/test_rans_integ.cpp @@ -7,6 +7,7 @@ #include "rans_integ.hpp" #include "euler_test_data.hpp" +void uinit(const mfem::Vector &x, mfem::Vector& u); TEMPLATE_TEST_CASE_SIG("SA Inviscid Flux Test", "[sa_inviscid_flux_test]", ((int dim), dim), 1, 2, 3) @@ -150,7 +151,7 @@ TEMPLATE_TEST_CASE_SIG("SAFarFieldBC Jacobian", "[SAFarFieldBC]", mfem::Vector flux_plus(q), flux_minus(q); adept::Stack diff_stack; mfem::H1_FECollection fe_coll(1); //dummy - double Re = 1.0; double Pr = 1.0; + double Re = 1000.0; double Pr = 1.0; mach::SAFarFieldBC safarfieldinteg(diff_stack, &fe_coll, Re, Pr, qfar, 1.0); // +ve perturbation q_plus.Add(delta, v); @@ -198,9 +199,9 @@ TEMPLATE_TEST_CASE_SIG("SANoSlipAdiabaticWallBC Jacobian", "[SANoSlipAdiabaticWa mfem::Vector jac_v(dim + 3); mfem::DenseMatrix Dw(delw_data, dim+3, dim); mfem::DenseMatrix jac(dim + 3, dim + 3); - mfem::Vector sacs(11); + mfem::Vector sacs(13); // create SA parameter vector - for (int di = 0; di < 11; ++di) + for (int di = 0; di < 13; ++di) { sacs(di) = sa_params[di]; } @@ -231,8 +232,8 @@ TEMPLATE_TEST_CASE_SIG("SANoSlipAdiabaticWallBC Jacobian", "[SANoSlipAdiabaticWa mfem::Vector flux_plus(q), flux_minus(q); adept::Stack diff_stack; mfem::H1_FECollection fe_coll(1); //dummy - double Re = 1.0; double Pr = 1.0; - mach::SANoSlipAdiabaticWallBC sanoslipinteg(diff_stack, &fe_coll, Re, Pr, sacs, qfar); + double Re = 1000.0; double Pr = 1.0; + mach::SANoSlipAdiabaticWallBC sanoslipinteg(diff_stack, &fe_coll, Re, Pr, sacs, qfar, 1.5); // +ve perturbation q_plus.Add(delta, v); // -ve perturbation @@ -273,9 +274,9 @@ TEMPLATE_TEST_CASE_SIG("SANoSlipAdiabaticWallBC Dw Jacobian", "[SANoSlipAdiabati mfem::Vector jac_v(dim + 3); mfem::DenseMatrix Dw(delw_data, dim+3, dim); mfem::DenseMatrix jac(dim + 3, dim + 3); - mfem::Vector sacs(11); + mfem::Vector sacs(13); // create SA parameter vector - for (int di = 0; di < 11; ++di) + for (int di = 0; di < 13; ++di) { sacs(di) = sa_params[di]; } @@ -305,8 +306,8 @@ TEMPLATE_TEST_CASE_SIG("SANoSlipAdiabaticWallBC Dw Jacobian", "[SANoSlipAdiabati mfem::Vector flux_plus(q), flux_minus(q); adept::Stack diff_stack; mfem::H1_FECollection fe_coll(1); //dummy - double Re = 1.0; double Pr = 1.0; - mach::SANoSlipAdiabaticWallBC sanoslipinteg(diff_stack, &fe_coll, Re, Pr, sacs, qfar); + double Re = 1000.0; double Pr = 1.0; + mach::SANoSlipAdiabaticWallBC sanoslipinteg(diff_stack, &fe_coll, Re, Pr, sacs, qfar, 1.5); DYNAMIC_SECTION("SA No-Slip BC Dw jacobian is correct w.r.t state") { std::vector mat_vec_jac(dim); @@ -351,6 +352,7 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Jacobian", "[SASlipWallBC]", { using namespace euler_data; // copy the data into mfem vectors for convenience + std::cout << "Slip-Wall State Jac" << std::endl; mfem::Vector nrm(dim); mfem::Vector x(dim); mfem::Vector q(dim + 3); mfem::Vector flux(dim + 3); @@ -358,9 +360,9 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Jacobian", "[SASlipWallBC]", mfem::Vector jac_v(dim + 3); mfem::DenseMatrix Dw(delw_data, dim+3, dim); mfem::DenseMatrix jac(dim + 3, dim + 3); - mfem::Vector sacs(11); + mfem::Vector sacs(13); // create SA parameter vector - for (int di = 0; di < 11; ++di) + for (int di = 0; di < 13; ++di) { sacs(di) = sa_params[di]; } @@ -373,7 +375,7 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Jacobian", "[SASlipWallBC]", for (int di = 0; di < dim; ++di) { q(di + 1) = rhou[di]; - Dw(dim+2, di) = 0.7; + Dw(dim+2, di) = 0.7+0.1*di; } // create direction vector for (int di = 0; di < dim; ++di) @@ -390,8 +392,8 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Jacobian", "[SASlipWallBC]", mfem::Vector flux_plus(q), flux_minus(q); adept::Stack diff_stack; mfem::H1_FECollection fe_coll(1); //dummy - double Re = 1.0; double Pr = 1.0; - mach::SAViscousSlipWallBC saslipinteg(diff_stack, &fe_coll, Re, Pr, sacs); + double Re = 1000.0; double Pr = 0.75; + mach::SAViscousSlipWallBC saslipinteg(diff_stack, &fe_coll, Re, Pr, sacs, 1.5); // +ve perturbation q_plus.Add(delta, v); // -ve perturbation @@ -412,8 +414,8 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Jacobian", "[SASlipWallBC]", // compare each component of the matrix-vector products for (int i = 0; i < dim + 3; ++i) { - // std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; - // std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; + std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); } } @@ -424,6 +426,7 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Dw Jacobian", "[SASlipWallBC]", { using namespace euler_data; // copy the data into mfem vectors for convenience + std::cout << "Slip-Wall Dw Jac" << std::endl; mfem::Vector nrm(dim); mfem::Vector x(dim); mfem::Vector q(dim + 3); mfem::Vector flux(dim + 3); @@ -431,9 +434,9 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Dw Jacobian", "[SASlipWallBC]", mfem::Vector jac_v(dim + 3); mfem::DenseMatrix Dw(delw_data, dim+3, dim); mfem::DenseMatrix jac(dim + 3, dim + 3); - mfem::Vector sacs(11); + mfem::Vector sacs(13); // create SA parameter vector - for (int di = 0; di < 11; ++di) + for (int di = 0; di < 13; ++di) { sacs(di) = sa_params[di]; } @@ -446,7 +449,7 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Dw Jacobian", "[SASlipWallBC]", for (int di = 0; di < dim; ++di) { q(di + 1) = rhou[di]; - Dw(dim+2, di) = 0.7; + Dw(dim+2, di) = 0.7+0.1*di; } // create direction vector for (int di = 0; di < dim; ++di) @@ -461,8 +464,8 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Dw Jacobian", "[SASlipWallBC]", // perturbed vectors adept::Stack diff_stack; mfem::H1_FECollection fe_coll(1); //dummy - double Re = 1.0; double Pr = 1.0; - mach::SAViscousSlipWallBC saslipinteg(diff_stack, &fe_coll, Re, Pr, sacs); + double Re = 1000.0; double Pr = 0.75; + mach::SAViscousSlipWallBC saslipinteg(diff_stack, &fe_coll, Re, Pr, sacs, 1.5); DYNAMIC_SECTION("SA Slip-Wall BC Dw jacobian is correct w.r.t state") { std::vector mat_vec_jac(dim); @@ -527,8 +530,8 @@ TEMPLATE_TEST_CASE_SIG("SAViscous Jacobian", "[SAViscous]", mfem::DenseMatrix jac_conv(dim + 3, dim + 3); mfem::DenseMatrix jac_scale(dim + 3, dim + 3); double delta = 1e-5; - double Re = 1; double Pr = 1; - mfem::Vector sacs(11); + double Re = 1000000; double Pr = 1; + mfem::Vector sacs(13); q(0) = rho; q(dim + 1) = rhoe; q(dim + 2) = 4; @@ -538,7 +541,7 @@ TEMPLATE_TEST_CASE_SIG("SAViscous Jacobian", "[SAViscous]", Dw(dim+2, di) = 0.7; } // create SA parameter vector - for (int di = 0; di < 11; ++di) + for (int di = 0; di < 13; ++di) { sacs(di) = sa_params[di]; } @@ -626,8 +629,8 @@ TEMPLATE_TEST_CASE_SIG("SAViscous Dw Jacobian", "[SAViscous]", mfem::DenseMatrix adjJ(dim, dim); mfem::DenseMatrix jac_scale_2(dim + 3, dim + 3); double delta = 1e-5; - double Re = 1; double Pr = 1; - mfem::Vector sacs(11); + double Re = 1000000; double Pr = 1; + mfem::Vector sacs(13); Dw = 0.5; q(0) = rho; q(dim + 1) = rhoe; @@ -638,7 +641,7 @@ TEMPLATE_TEST_CASE_SIG("SAViscous Dw Jacobian", "[SAViscous]", Dw(dim+2, di) = 0.7; } // create SA parameter vector - for (int di = 0; di < 11; ++di) + for (int di = 0; di < 13; ++di) { sacs(di) = sa_params[di]; } @@ -650,7 +653,7 @@ TEMPLATE_TEST_CASE_SIG("SAViscous Dw Jacobian", "[SAViscous]", // perturbed vectors mfem::DenseMatrix Dw_plus(Dw), Dw_minus(Dw); adept::Stack diff_stack; - mach::SAViscousIntegrator saviscousinteg(diff_stack, Re, Pr, sacs); + mach::SAViscousIntegrator saviscousinteg(diff_stack, Re, Pr, sacs, 1.0); for (int di = 0; di < dim; ++di) { @@ -791,19 +794,255 @@ TEMPLATE_TEST_CASE_SIG("SALPS Jacobian", "[SALPS]", } } -TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") +#if 0 +TEST_CASE("SAViscousIntegrator::AssembleElementGrad", "[SAViscousIntegrator]") +{ + using namespace mfem; + using namespace euler_data; + + const int dim = 2; // templating is hard here because mesh constructors + int num_state = dim + 3; + adept::Stack diff_stack; + double delta = 1e-5; + + mfem::Vector sacs(13); + // create SA parameter vector + for (int di = 0; di < 13; ++di) + { + sacs(di) = sa_params[di]; + } + + // generate a 2 element mesh + int num_edge = 2; + std::unique_ptr mesh(new Mesh(num_edge, num_edge, Element::TRIANGLE, + true /* gen. edges */, 1.0, 1.0, true)); + for (int p = 1; p <= 2; ++p) + { + DYNAMIC_SECTION("...for degree p = " << p) + { + std::unique_ptr fec( + new SBPCollection(p, dim)); + std::unique_ptr fes(new FiniteElementSpace( + mesh.get(), fec.get(), num_state, Ordering::byVDIM)); + + GridFunction dist; + NonlinearForm res(fes.get()); + res.AddDomainIntegrator(new mach::SAViscousIntegrator(diff_stack, 1000.0, 1.0, sacs)); + + // initialize state; here we randomly perturb a constant state + GridFunction q(fes.get()); + VectorFunctionCoefficient pert(num_state, randBaselinePertSA<2>); + q.ProjectCoefficient(pert); + + // initialize the vector that the Jacobian multiplies + GridFunction v(fes.get()); + VectorFunctionCoefficient v_rand(num_state, randState); + v.ProjectCoefficient(v_rand); + + // evaluate the Ja;cobian and compute its product with v + Operator &Jac = res.GetGradient(q); + GridFunction jac_v(fes.get()); + Jac.Mult(v, jac_v); + + // now compute the finite-difference approximation... + GridFunction q_pert(q), r(fes.get()), jac_v_fd(fes.get()); + q_pert.Add(-delta, v); + res.Mult(q_pert, r); + q_pert.Add(2 * delta, v); + res.Mult(q_pert, jac_v_fd); + jac_v_fd -= r; + jac_v_fd /= (2 * delta); + + for (int i = 0; i < jac_v.Size(); ++i) + { + // std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + // std::cout << "AN " << i << " Deriv: " << jac_v(i) << std::endl; + REQUIRE(jac_v(i) == Approx(jac_v_fd(i)).margin(1e-10)); + } + } + } +} +#endif + +#if 0 +TEST_CASE("SANoSlipAdiabaticWallBC::AssembleElementGrad", "[SANoSlipAdiabaticWallBC]") +{ + using namespace mfem; + using namespace euler_data; + + const int dim = 2; // templating is hard here because mesh constructors + int num_state = dim + 3; + mfem::Vector qfar(dim + 3); + adept::Stack diff_stack; + double delta = 1e-5; + + qfar(0) = rho; + qfar(dim + 1) = rhoe; + qfar(dim + 2) = 4; + for (int di = 0; di < dim; ++di) + { + qfar(di + 1) = rhou[di]; + } + + mfem::Vector sacs(13); + // create SA parameter vector + for (int di = 0; di < 13; ++di) + { + sacs(di) = sa_params[di]; + } + + // generate a 2 element mesh + int num_edge = 2; + std::unique_ptr mesh(new Mesh(num_edge, num_edge, Element::TRIANGLE, + true /* gen. edges */, 1.0, 1.0, true)); + for (int p = 1; p <= 2; ++p) + { + DYNAMIC_SECTION("...for degree p = " << p) + { + std::unique_ptr fec( + new SBPCollection(p, dim)); + std::unique_ptr fes(new FiniteElementSpace( + mesh.get(), fec.get(), num_state, Ordering::byVDIM)); + + GridFunction dist; + NonlinearForm res(fes.get()); + res.AddBdrFaceIntegrator(new mach::SANoSlipAdiabaticWallBC(diff_stack, fec.get(), 1000.0, 1.0, sacs, qfar, 1.0)); + + // initialize state; here we randomly perturb a constant state + GridFunction q(fes.get()); + VectorFunctionCoefficient pert(num_state, randBaselinePertSA<2>); + q.ProjectCoefficient(pert); + + // initialize the vector that the Jacobian multiplies + GridFunction v(fes.get()); + VectorFunctionCoefficient v_rand(num_state, randState); + v.ProjectCoefficient(v_rand); + + // evaluate the Ja;cobian and compute its product with v + Operator &Jac = res.GetGradient(q); + GridFunction jac_v(fes.get()); + Jac.Mult(v, jac_v); + + // now compute the finite-difference approximation... + GridFunction q_pert(q), r(fes.get()), jac_v_fd(fes.get()); + q_pert.Add(-delta, v); + res.Mult(q_pert, r); + q_pert.Add(2 * delta, v); + res.Mult(q_pert, jac_v_fd); + jac_v_fd -= r; + jac_v_fd /= (2 * delta); + + for (int i = 0; i < jac_v.Size(); ++i) + { + // std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + // std::cout << "AN " << i << " Deriv: " << jac_v(i) << std::endl; + REQUIRE(jac_v(i) == Approx(jac_v_fd(i)).margin(1e-10)); + } + } + } +} + +TEST_CASE("SAViscousSlipWallBC::AssembleElementGrad", "[SAViscousSlipWallBC]") +{ + using namespace mfem; + using namespace euler_data; + + const int dim = 2; // templating is hard here because mesh constructors + int num_state = dim + 3; + mfem::Vector qfar(dim + 3); + adept::Stack diff_stack; + double delta = 1e-5; + + qfar(0) = rho; + qfar(dim + 1) = rhoe; + qfar(dim + 2) = 4; + for (int di = 0; di < dim; ++di) + { + qfar(di + 1) = rhou[di]; + } + + mfem::Vector sacs(13); + // create SA parameter vector + for (int di = 0; di < 13; ++di) + { + sacs(di) = sa_params[di]; + } + + // generate a 2 element mesh + int num_edge = 2; + std::unique_ptr mesh(new Mesh(num_edge, num_edge, Element::TRIANGLE, + true /* gen. edges */, 1.0, 1.0, true)); + for (int p = 1; p <= 2; ++p) + { + DYNAMIC_SECTION("...for degree p = " << p) + { + std::unique_ptr fec( + new SBPCollection(p, dim)); + std::unique_ptr fes(new FiniteElementSpace( + mesh.get(), fec.get(), num_state, Ordering::byVDIM)); + + GridFunction dist; + NonlinearForm res(fes.get()); + res.AddBdrFaceIntegrator(new mach::SAViscousSlipWallBC(diff_stack, fec.get(), 1000.0, 0.75, sacs, 1.5)); + + // initialize state; here we randomly perturb a constant state + GridFunction q(fes.get()); + VectorFunctionCoefficient pert(num_state, randBaselinePertSA<2>); + q.ProjectCoefficient(pert); + + // initialize the vector that the Jacobian multiplies + GridFunction v(fes.get()); + VectorFunctionCoefficient v_rand(num_state, randState); + v.ProjectCoefficient(v_rand); + + // evaluate the Ja;cobian and compute its product with v + Operator &Jac = res.GetGradient(q); + GridFunction jac_v(fes.get()); + Jac.Mult(v, jac_v); + + // now compute the finite-difference approximation... + GridFunction q_pert(q), r(fes.get()), jac_v_fd(fes.get()); + q_pert.Add(-delta, v); + res.Mult(q_pert, r); + q_pert.Add(2 * delta, v); + res.Mult(q_pert, jac_v_fd); + jac_v_fd -= r; + jac_v_fd /= (2 * delta); + + for (int i = 0; i < jac_v.Size(); ++i) + { + std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + std::cout << "AN " << i << " Deriv: " << jac_v(i) << std::endl; + REQUIRE(jac_v(i) == Approx(jac_v_fd(i)).margin(1e-10)); + } + } + } +} +#endif + +#if 0 +TEST_CASE("SAFarFieldBC::AssembleElementGrad", "[SAFarFieldBC]") { using namespace mfem; using namespace euler_data; const int dim = 2; // templating is hard here because mesh constructors int num_state = dim + 3; + mfem::Vector qfar(dim + 3); adept::Stack diff_stack; double delta = 1e-5; - mfem::Vector sacs(11); + qfar(0) = rho; + qfar(dim + 1) = rhoe; + qfar(dim + 2) = 4; + for (int di = 0; di < dim; ++di) + { + qfar(di + 1) = rhou[di]; + } + + mfem::Vector sacs(13); // create SA parameter vector - for (int di = 0; di < 11; ++di) + for (int di = 0; di < 13; ++di) { sacs(di) = sa_params[di]; } @@ -823,7 +1062,7 @@ TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") GridFunction dist; NonlinearForm res(fes.get()); - res.AddDomainIntegrator(new mach::SASourceIntegrator(diff_stack, dist, sacs)); + res.AddBdrFaceIntegrator(new mach::SAFarFieldBC(diff_stack, fec.get(), 1000.0, 1.0, qfar, 1.0)); // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); @@ -858,6 +1097,83 @@ TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") } } } +#endif + +TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") +{ + using namespace mfem; + using namespace euler_data; + + const int dim = 2; // templating is hard here because mesh constructors + int num_state = dim + 3; + adept::Stack diff_stack; + double delta = 1e-5; + + mfem::Vector sacs(13); + // create SA parameter vector + for (int di = 0; di < 13; ++di) + { + sacs(di) = sa_params[di]; + } + + // generate a 2 element mesh + int num_edge = 2; + std::unique_ptr mesh(new Mesh(num_edge, num_edge, Element::TRIANGLE, + true /* gen. edges */, 1.0, 1.0, true)); + for (int p = 1; p <= 2; ++p) + { + DYNAMIC_SECTION("...for degree p = " << p) + { + std::unique_ptr fec( + new SBPCollection(p, dim)); + std::unique_ptr fes(new FiniteElementSpace( + mesh.get(), fec.get(), num_state, Ordering::byVDIM)); + + GridFunction dist(fes.get()); + auto walldist = [](const Vector &x) + { + return x(1) + 0.00001; + }; + FunctionCoefficient wall_coeff(walldist); + dist.ProjectCoefficient(wall_coeff); + dist = 0.00001; + NonlinearForm res(fes.get()); + double Re = 5000000.0; + res.AddDomainIntegrator(new mach::SASourceIntegrator(diff_stack, dist, Re, sacs, 1.0)); + + // initialize state; here we randomly perturb a constant state + GridFunction q(fes.get()); + VectorFunctionCoefficient pert(num_state, uinit);//randBaselinePertSA<2>); + q.ProjectCoefficient(pert); + + // initialize the vector that the Jacobian multiplies + GridFunction v(fes.get()); + VectorFunctionCoefficient v_rand(num_state, randState); + v.ProjectCoefficient(v_rand); + + // evaluate the Ja;cobian and compute its product with v + Operator &Jac = res.GetGradient(q); + GridFunction jac_v(fes.get()); + Jac.Mult(v, jac_v); + + // now compute the finite-difference approximation... + GridFunction q_pert(q), r(fes.get()), jac_v_fd(fes.get()); + q_pert.Add(-delta, v); + res.Mult(q_pert, r); + q_pert.Add(2 * delta, v); + res.Mult(q_pert, jac_v_fd); + jac_v_fd -= r; + jac_v_fd /= (2 * delta); + + for (int i = 0; i < jac_v.Size(); ++i) + { + std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + std::cout << "AN " << i << " Deriv: " << jac_v(i) << std::endl; + REQUIRE(jac_v(i) == Approx(jac_v_fd(i)).margin(1e-10)); + } + } + } +} #if 0 TEST_CASE("SALPSIntegrator::AssembleElementGrad", "[SALPSIntegrator]") @@ -984,4 +1300,21 @@ TEMPLATE_TEST_CASE_SIG("SAInviscid Gradient", } } } -#endif \ No newline at end of file +#endif + +void uinit(const mfem::Vector &x, mfem::Vector& q) +{ + // q.SetSize(4); + // Vector u(4); + q.SetSize(5); + mfem::Vector u(5); + + u = 0.0; + u(0) = 1.0; + u(1) = u(0)*1.5*cos(0.3*M_PI/180.0); + u(2) = u(0)*1.5*sin(0.3*M_PI/180.0); + u(3) = 1.0/(1.4) + 0.5*1.5*1.5; + u(4) = u(0)*3*(1.0e-5); + + q = u; +} \ No newline at end of file From 88af1b40ddebebd79e677eb4bb1022d50f6f76ef Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Sun, 13 Sep 2020 18:06:34 -0400 Subject: [PATCH 14/28] fixed initial residual error, now convergence stagnates --- sandbox/CMakeLists.txt | 2 + sandbox/ns_walltest.cpp | 271 ++++++++++++++++++ sandbox/ns_walltest_options.json | 59 ++++ sandbox/rans_walltest_options.json | 7 +- src/physics/fluidflow/navier_stokes.cpp | 24 ++ src/physics/fluidflow/navier_stokes.hpp | 2 + .../fluidflow/navier_stokes_fluxes.hpp | 2 +- src/physics/fluidflow/rans.cpp | 6 +- src/physics/fluidflow/rans_integ.hpp | 18 +- src/physics/fluidflow/rans_integ_def.hpp | 71 ++++- test/unit/test_rans_fluxes.cpp | 6 +- test/unit/test_rans_integ.cpp | 7 +- 12 files changed, 450 insertions(+), 25 deletions(-) create mode 100644 sandbox/ns_walltest.cpp create mode 100644 sandbox/ns_walltest_options.json diff --git a/sandbox/CMakeLists.txt b/sandbox/CMakeLists.txt index 36a32527..8a5f2949 100644 --- a/sandbox/CMakeLists.txt +++ b/sandbox/CMakeLists.txt @@ -26,7 +26,9 @@ set(SANDBOX_SRCS navier_stokes_mms thermal_square joule_wire + ns_walltest rans_freestream + rans_walltest ) create_sandbox("${SANDBOX_SRCS}") diff --git a/sandbox/ns_walltest.cpp b/sandbox/ns_walltest.cpp new file mode 100644 index 00000000..718cf3ee --- /dev/null +++ b/sandbox/ns_walltest.cpp @@ -0,0 +1,271 @@ +// set this const expression to true in order to use entropy variables for state (doesn't work for rans) +constexpr bool entvar = false; + +#include +#include "adept.h" + +#include "mfem.hpp" +#include "rans.hpp" +#include "navier_stokes.hpp" +#include +#include + +using namespace std; +using namespace mfem; +using namespace mach; + +std::default_random_engine gen(std::random_device{}()); +std::uniform_real_distribution uniform_rand(0.0,1.0); + +static double pert_fs; +static double mu; +static double mach_fs; +static double aoa_fs; +static int iroll; +static int ipitch; +static double chi_fs; + +static double m_offset; +static double m_coeff; +static int m_x; +static int m_y; + +/// \brief Defines the random function for the jacobian check +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void pert(const Vector &x, Vector& p); + +/// \brief Defines the exact solution for the rans freestream problem +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void uexact(const Vector &x, Vector& u); + +/// \brief Defines a perturbed solution for the rans freestream problem +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void uinit_pert(const Vector &x, Vector& u); + +/// \brief Defines a pseudo wall-distance function +/// \param[in] x - coordinate of the point at which the distance is needed +double walldist(const Vector &x); + +/// Generate a quadrilateral wall mesh, more dense near the wall +/// \param[in] num_x - number of nodes in x +/// \param[in] num_y - number of nodes in y +std::unique_ptr buildWalledMesh(int num_x, + int num_y); + +int main(int argc, char *argv[]) +{ + const char *options_file = "ns_walltest_options.json"; + + // Initialize MPI + int num_procs, rank; + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + ostream *out = getOutStream(rank); + + // Parse command-line options + OptionsParser args(argc, argv); + int degree = 2.0; + int nx = 14; + int ny = 10; + args.AddOption(&nx, "-nx", "--numx", + "Number of elements in x direction"); + args.AddOption(&ny, "-ny", "--numy", + "Number of elements in y direction"); + args.Parse(); + if (!args.Good()) + { + args.PrintUsage(*out); + return 1; + } + + try + { + // construct the mesh + string opt_file_name(options_file); + nlohmann::json file_options; + std::ifstream opts(opt_file_name); + opts >> file_options; + pert_fs = 1.0 + file_options["init-pert"].template get(); + mu = file_options["flow-param"]["mu"].template get(); + mach_fs = file_options["flow-param"]["mach"].template get(); + aoa_fs = file_options["flow-param"]["aoa"].template get()*M_PI/180; + iroll = file_options["flow-param"]["roll-axis"].template get(); + ipitch = file_options["flow-param"]["pitch-axis"].template get(); + chi_fs = file_options["flow-param"]["chi"].template get(); + + m_offset = file_options["mesh"]["offset"].template get(); + m_coeff = file_options["mesh"]["coeff"].template get(); + m_x = file_options["mesh"]["num-x"].template get(); + m_y = file_options["mesh"]["num-y"].template get(); + + // generate the walled mesh, and its distance function + std::unique_ptr smesh = buildWalledMesh(m_x, m_y); + + // construct the solver and set initial conditions + auto solver = createSolver>(opt_file_name, + move(smesh)); + solver->setInitialCondition(uinit_pert); + solver->printSolution("ns_wall_init", 0); + + // get the initial density error + double l2_error_init = (static_cast&>(*solver) + .calcConservativeVarsL2Error(uexact, 0)); + *out << "\n|| rho_h - rho ||_{L^2} init = " << l2_error_init << endl; + + double res_error = solver->calcResidualNorm(); + *out << "\ninitial residual norm = " << res_error << endl; + solver->checkJacobian(pert); + solver->solveForState(); + solver->printSolution("ns_wall_final",0); + // get the final density error + double l2_error_final = (static_cast&>(*solver) + .calcConservativeVarsL2Error(uexact, 0)); + res_error = solver->calcResidualNorm(); + + *out << "\nfinal residual norm = " << res_error; + *out << "\n|| rho_h - rho ||_{L^2} final = " << l2_error_final << endl; + } + catch (MachException &exception) + { + exception.print_message(); + } + catch (std::exception &exception) + { + cerr << exception.what() << endl; + } + +#ifdef MFEM_USE_PETSC + MFEMFinalizePetsc(); +#endif + + MPI_Finalize(); +} + +// perturbation function used to check the jacobian in each iteration +void pert(const Vector &x, Vector& p) +{ + p.SetSize(4); + for (int i = 0; i < 4; i++) + { + p(i) = 2.0 * uniform_rand(gen) - 1.0; + } +} + +// Exact solution; same as freestream bc +void uexact(const Vector &x, Vector& q) +{ + q.SetSize(4); + Vector u(4); + + u = 0.0; + u(0) = 1.0; + u(1) = u(0)*mach_fs*cos(aoa_fs); + u(2) = u(0)*mach_fs*sin(aoa_fs); + u(3) = 1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; + + if (entvar == false) + { + q = u; + } + else + { + throw MachException("No entvar for this"); + } +} + +// initial guess perturbed from exact +void uinit_pert(const Vector &x, Vector& q) +{ + q.SetSize(4); + Vector u(4); + + u = 0.0; + u(0) = pert_fs*1.0; + u(1) = u(0)*mach_fs*cos(aoa_fs); + u(2) = u(0)*mach_fs*sin(aoa_fs); + u(3) = pert_fs*1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; + + q = u; +} + +std::unique_ptr buildWalledMesh(int num_x, int num_y) +{ + auto mesh_ptr = unique_ptr(new Mesh(num_x, num_y, + Element::TRIANGLE, true /* gen. edges */, + 2.33333, 1.0, true)); + // strategy: + // 1) generate a fes for Lagrange elements of desired degree + // 2) create a Grid Function using a VectorFunctionCoefficient + // 4) use mesh_ptr->NewNodes(nodes, true) to set the mesh nodes + + // Problem: fes does not own fec, which is generated in this function's scope + // Solution: the grid function can own both the fec and fes + H1_FECollection *fec = new H1_FECollection(1, 2 /* = dim */); + FiniteElementSpace *fes = new FiniteElementSpace(mesh_ptr.get(), fec, 2, + Ordering::byVDIM); + + // Lambda function increases element density towards wall + double offset = m_offset; + double coeff = m_coeff; + auto xy_fun = [coeff, offset, num_y](const Vector& rt, Vector &xy) + { + xy(0) = rt(0) - 0.33333; + xy(1) = rt(1); + + double c = 1.0/num_y; + double b = log(offset)/(c - 1.0); + double a = 1.0/exp(1.0*b); + + if(rt(1) > 0.0 && rt(1) < 1.0) + { + xy(1) = coeff*a*exp(b*rt(1)); + //std::cout << xy(1) << std::endl; + } + }; + VectorFunctionCoefficient xy_coeff(2, xy_fun); + GridFunction *xy = new GridFunction(fes); + xy->MakeOwner(fec); + xy->ProjectCoefficient(xy_coeff); + + mesh_ptr->NewNodes(*xy, true); + + // Assign extra boundary attribute + for (int i = 0; i < mesh_ptr->GetNBE(); ++i) + { + Element *elem = mesh_ptr->GetBdrElement(i); + + Array verts; + elem->GetVertices(verts); + + bool before = true; //before wall + for (int j = 0; j < 2; ++j) + { + auto vtx = mesh_ptr->GetVertex(verts[j]); + cout<< "B El: "<SetAttribute(5); + } + } + mesh_ptr->bdr_attributes.Append(5); + // mesh_ptr->SetAttributes(); + // mesh_ptr->Finalize(); + + cout << "Number of bdr attr " << mesh_ptr->bdr_attributes.Size() <<'\n'; + cout << "Number of elements " << mesh_ptr->GetNE() <<'\n'; + + return mesh_ptr; +} diff --git a/sandbox/ns_walltest_options.json b/sandbox/ns_walltest_options.json new file mode 100644 index 00000000..3d433ff9 --- /dev/null +++ b/sandbox/ns_walltest_options.json @@ -0,0 +1,59 @@ +{ + "flow-param": { + "mach": 0.2, + "aoa": 0.0, + "roll-axis": 0, + "pitch-axis": 1, + "chi": 3.0, + "Re": 5000000.0, + "Pr": 0.75, + "mu": 1.0, + "viscous-mms": false, + "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9] + }, + "space-dis": { + "degree": 1, + "lps-coeff": 1.0, + "basis-type": "csbp" + }, + "time-dis": { + "steady": true, + "steady-abstol": 1e-12, + "steady-restol": 1e-10, + "const-cfl": true, + "ode-solver": "PTC", + "t-final": 100, + "dt": 0.1, + "cfl": 1.0, + "res-exp": 2.0 + }, + "nonlin-solver": { + "abstol": 1e-12, + "maxiter": 100, + "printlevel": 1, + "reltol": 1e-2, + "type": "newton" + }, + "lin-solver": { + "reltol": 1e-2, + "abstol": 1e-12, + "printlevel": 0, + "maxiter": 100 + }, + "bcs": { + "far-field": [0, 1, 1, 1, 0], + "slip-wall": [0, 0, 0, 0, 1], + "no-slip-adiabatic": [1, 0, 0, 0, 0] + }, + "wall-func": { + "type": "y-dist" + }, + "mesh": { + "num-x": 25, + "num-y": 15, + "offset": 1e-3, + "coeff": 1.0 + }, + "init-pert": 0.0 + +} diff --git a/sandbox/rans_walltest_options.json b/sandbox/rans_walltest_options.json index eb8862ed..05654852 100644 --- a/sandbox/rans_walltest_options.json +++ b/sandbox/rans_walltest_options.json @@ -4,7 +4,7 @@ "aoa": 0.0, "roll-axis": 0, "pitch-axis": 1, - "chi": 3.0, + "chi": 0.0, "Re": 5000000.0, "Pr": 0.75, "mu": 1.0, @@ -22,7 +22,7 @@ "const-cfl": true, "ode-solver": "PTC", "t-final": 100, - "dt": 0.001, + "dt": 1.0, "cfl": 1.0, "res-exp": 2.0 }, @@ -49,10 +49,11 @@ }, "mesh": { "num-x": 25, - "num-y": 15, + "num-y": 20, "offset": 1e-3, "coeff": 1.0 }, "init-pert": 0.0 } + diff --git a/src/physics/fluidflow/navier_stokes.cpp b/src/physics/fluidflow/navier_stokes.cpp index 316e413a..5c3e3df5 100644 --- a/src/physics/fluidflow/navier_stokes.cpp +++ b/src/physics/fluidflow/navier_stokes.cpp @@ -1,4 +1,5 @@ #include +#include #include "navier_stokes.hpp" #include "navier_stokes_integ.hpp" @@ -224,6 +225,16 @@ void NavierStokesSolver::addOutputs() } } +static void pert(const Vector &x, Vector& p); + +template +void NavierStokesSolver::iterationHook(int iter, + double t, double dt) +{ + this->checkJacobian(pert); + this->printSolution("ns_last", 0); +} + template void NavierStokesSolver::getViscousInflowState(Vector &q_in) { @@ -258,6 +269,19 @@ void NavierStokesSolver::getViscousOutflowState(Vector &q_out) } } +std::default_random_engine gen(std::random_device{}()); +std::uniform_real_distribution normal_rand(-1.0,1.0); + +// perturbation function used to check the jacobian in each iteration +void pert(const Vector &x, Vector& p) +{ + p.SetSize(4); + for (int i = 0; i < 4; i++) + { + p(i) = normal_rand(gen); + } +} + // explicit instantiation template class NavierStokesSolver<1>; template class NavierStokesSolver<2>; diff --git a/src/physics/fluidflow/navier_stokes.hpp b/src/physics/fluidflow/navier_stokes.hpp index 6fbfe343..032f0f83 100644 --- a/src/physics/fluidflow/navier_stokes.hpp +++ b/src/physics/fluidflow/navier_stokes.hpp @@ -44,6 +44,8 @@ class NavierStokesSolver : public EulerSolver /// Create `output` based on `options` and add approporiate integrators virtual void addOutputs() override; + virtual void iterationHook(int iter, double t, double dt) override; + /// Set the state corresponding to the inflow boundary /// \param[in] q_in - state corresponding to the inflow void getViscousInflowState(mfem::Vector &q_in); diff --git a/src/physics/fluidflow/navier_stokes_fluxes.hpp b/src/physics/fluidflow/navier_stokes_fluxes.hpp index f8d7e1ed..9b994046 100644 --- a/src/physics/fluidflow/navier_stokes_fluxes.hpp +++ b/src/physics/fluidflow/navier_stokes_fluxes.hpp @@ -235,7 +235,7 @@ template void calcNoSlipDualFlux(const xdouble *dir, xdouble mu, double Pr, const xdouble *q, xdouble *fluxes) { - int num_state = dim + 2; + int num_state = dim + 3; // zero out the fluxes, since applyCijMatrix accummulates for (int i = 0; i < num_state*dim; ++i) fluxes[i] = 0.0; diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index a9c69c4f..d813e757 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -233,8 +233,8 @@ void RANavierStokesSolver::getDistanceFunction() } } -std::default_random_engine gen(std::random_device{}()); -std::uniform_real_distribution normal_rand(-1.0,1.0); +std::default_random_engine gen_ns(std::random_device{}()); +std::uniform_real_distribution normal_rand_ns(-1.0,1.0); // perturbation function used to check the jacobian in each iteration void pert(const Vector &x, Vector& p) @@ -242,7 +242,7 @@ void pert(const Vector &x, Vector& p) p.SetSize(5); for (int i = 0; i < 5; i++) { - p(i) = normal_rand(gen); + p(i) = normal_rand_ns(gen_ns); } } diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp index ee627709..8aaf8388 100644 --- a/src/physics/fluidflow/rans_integ.hpp +++ b/src/physics/fluidflow/rans_integ.hpp @@ -221,10 +221,10 @@ class SANoSlipAdiabaticWallBC : public ViscousBoundaryIntegrator &flux_jac) - { - for (int d = 0; d < dim; ++d) - flux_jac[d] = 0.0; - } + std::vector &flux_jac); + // { + // for (int d = 0; d < dim; ++d) + // flux_jac[d] = 0.0; + // } /// Computes boundary node contribution to the surface force /// \param[in] x - coordinate location at which function is evaluated diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index 600e9ec7..4d540ff7 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -501,7 +501,7 @@ void SANoSlipAdiabaticWallBC::calcFlux(const mfem::Vector &x, sacs.GetData()); flux_vec(dim+2) -= (mu + fn*q(dim+2))*SAflux/(sacs(2)*Re); // Step 3: evaluate the no-slip penalty - calcNoSlipPenaltyFlux(dir.GetData(), jac, mu_Re, Pr, qfs.GetData(), + calcNoSlipPenaltyFlux(dir.GetData(), jac, mu_Re_SA, Pr, qfs.GetData(), q.GetData(), work_vec.GetData()); flux_vec += work_vec; double dnu = q(dim+2); @@ -564,7 +564,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacState( sacs_a.data()); flux_a[dim+2] -= (mu + fn*q_a[dim+2])*SAflux/(sacs_a[2]*Re); // Step 3: evaluate the no-slip penalty - mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re, Pr, qfs_a.data(), + mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re_SA, Pr, qfs_a.data(), q_a.data(), work_vec_a.data()); for (int i = 0; i < flux_a.size(); ++i) { @@ -632,7 +632,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf sacs_a.data()); flux_a[dim+2] -= (mu + fn*q_a[dim+2])*SAflux/(sacs_a[2]*Re); // Step 3: evaluate the no-slip penalty - mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re, Pr, qfs_a.data(), + mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re_SA, Pr, qfs_a.data(), q_a.data(), work_vec_a.data()); for (int i = 0; i < flux_a.size(); ++i) { @@ -654,6 +654,71 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf } } +template +void SANoSlipAdiabaticWallBC::calcFluxDv(const mfem::Vector &x, + const mfem::Vector &dir, + const mfem::Vector &q, + mfem::DenseMatrix &flux_mat) +{ + double mu_Re = mu; + if (mu < 0.0) + mu_Re = calcSutherlandViscosity(q.GetData()); + //mu_Re /= Re; + double fv1 = calcSACoefficient(q.GetData(), mu, + sacs.GetData()); + double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; + calcNoSlipDualFlux(dir.GetData(), mu_Re_SA, Pr, q.GetData(), + flux_mat.GetData()); + for (int di = 0; di < dim; di++) + flux_mat(dim+2, di) = 0.0; + +} + +template +void SANoSlipAdiabaticWallBC::calcFluxDvJacState( + const mfem::Vector &x, const mfem::Vector dir, const mfem::Vector &q, + std::vector &flux_jac) +{ + // create containers for active double objects for each input + int flux_size = dim*(dim+3); + std::vector q_a(q.Size()); + std::vector dir_a(dir.Size()); + std::vector sacs_a(sacs.Size()); + // initialize active double containers with data from inputs + adept::set_values(q_a.data(), q.Size(), q.GetData()); + adept::set_values(dir_a.data(), dir.Size(), dir.GetData()); + adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); + // start new stack recording + this->stack.new_recording(); + // create container for active double flux output + std::vector fluxes_a(flux_size); + // evaluate the fluxes + adouble mu_Re = mu; + if (mu < 0.0) + mu_Re = calcSutherlandViscosity(q_a.data()); + //mu_Re /= Re; + adouble fv1 = calcSACoefficient(q_a.data(), mu, + sacs_a.data()); + adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + calcNoSlipDualFlux(dir_a.data(), mu_Re_SA, Pr, q_a.data(), + fluxes_a.data()); + for (int di = 0; di < dim; di++) + fluxes_a[dim+2 + di*(dim+3)] = 0.0; + this->stack.independent(q_a.data(), q.Size()); + this->stack.dependent(fluxes_a.data(), flux_size); + // compute and store jacobian in flux_jac + mfem::Vector work(flux_size*(dim+3)); + this->stack.jacobian(work.GetData()); + for (int s = 0; s < dim+3; ++s) + { + for (int i = 0; i < dim; ++i) + { + flux_jac[i].SetCol(s, work.GetData() + (s*dim + i)*(dim+3)); + } + } +} + + //============================================================================== //SA Viscous Slip-Wall Integrator methods template diff --git a/test/unit/test_rans_fluxes.cpp b/test/unit/test_rans_fluxes.cpp index c2fefea4..a5505adb 100644 --- a/test/unit/test_rans_fluxes.cpp +++ b/test/unit/test_rans_fluxes.cpp @@ -148,7 +148,7 @@ TEST_CASE("SA S derivatives", "[SASource]") const int dim = 2; mfem::Vector q(dim+3); mfem::Vector grad(dim); - double delta = 1e-5; + double delta = 1e-6; grad(0) = 0.0; grad(1) = 0.0; @@ -182,7 +182,7 @@ TEST_CASE("SA S derivatives", "[SASource]") stack.new_recording(); mfem::Vector dSrcdu(1); adouble S_a; - S_a.set_value(0.000); + S_a.set_value(2e-5); adouble d_a = 10.1; adouble mu_a = 1.1; adouble Re_a = 5000000; @@ -209,7 +209,7 @@ TEST_CASE("SA S derivatives", "[SASource]") stack.dependent(src); stack.jacobian(dSrcdu.GetData()); - double Sp = 0.000 + delta; double Sm = 0.000 - delta; + double Sp = 2e-5 + delta; double Sm = 2e-5 - delta; double d = 10.1; double mu = 1.1; double Re = 5000000; diff --git a/test/unit/test_rans_integ.cpp b/test/unit/test_rans_integ.cpp index b530f3a6..9d4994a5 100644 --- a/test/unit/test_rans_integ.cpp +++ b/test/unit/test_rans_integ.cpp @@ -864,7 +864,7 @@ TEST_CASE("SAViscousIntegrator::AssembleElementGrad", "[SAViscousIntegrator]") } #endif -#if 0 + TEST_CASE("SANoSlipAdiabaticWallBC::AssembleElementGrad", "[SANoSlipAdiabaticWallBC]") { using namespace mfem; @@ -942,6 +942,7 @@ TEST_CASE("SANoSlipAdiabaticWallBC::AssembleElementGrad", "[SANoSlipAdiabaticWal } } +#if 0 TEST_CASE("SAViscousSlipWallBC::AssembleElementGrad", "[SAViscousSlipWallBC]") { using namespace mfem; @@ -1132,11 +1133,11 @@ TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") GridFunction dist(fes.get()); auto walldist = [](const Vector &x) { - return x(1) + 0.00001; + return 0.0001*x(1) + 0.00001; }; FunctionCoefficient wall_coeff(walldist); dist.ProjectCoefficient(wall_coeff); - dist = 0.00001; + //dist = 0.00001; NonlinearForm res(fes.get()); double Re = 5000000.0; res.AddDomainIntegrator(new mach::SASourceIntegrator(diff_stack, dist, Re, sacs, 1.0)); From 5d44c4702fdb4f9e0a208f579423cb822197f56e Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Fri, 16 Oct 2020 18:05:51 -0400 Subject: [PATCH 15/28] rans source terms are problematic --- sandbox/CMakeLists.txt | 25 +- sandbox/ns_walltest_options.json | 6 +- sandbox/rans_freestream_options.json | 6 +- sandbox/rans_walltest.cpp | 132 +++++++- sandbox/rans_walltest_options.json | 33 +- src/common/default_options.cpp | 5 +- src/common/inexact_newton.cpp | 7 +- src/common/sbp_fe.cpp | 10 +- src/physics/fluidflow/rans.cpp | 63 +++- src/physics/fluidflow/rans.hpp | 2 + src/physics/fluidflow/rans_fluxes.hpp | 126 ++++++-- src/physics/fluidflow/rans_integ.hpp | 26 +- src/physics/fluidflow/rans_integ_def.hpp | 364 +++++++++++++++-------- src/physics/solver.cpp | 4 + test/unit/euler_test_data.cpp | 9 +- test/unit/test_rans_fluxes.cpp | 157 +++++++++- test/unit/test_rans_integ.cpp | 126 +++++--- 17 files changed, 838 insertions(+), 263 deletions(-) diff --git a/sandbox/CMakeLists.txt b/sandbox/CMakeLists.txt index 8a5f2949..1fd3cf77 100644 --- a/sandbox/CMakeLists.txt +++ b/sandbox/CMakeLists.txt @@ -14,21 +14,22 @@ function(create_sandbox source_list) endfunction(create_sandbox) set(SANDBOX_SRCS - steady_vortex - steady_vortex_adjoint - unsteady_vortex - viscous_shock - airfoil_steady - airfoil_chaotic - magnetostatic_box - magnetostatic_motor - magnetostatic_wire - navier_stokes_mms - thermal_square - joule_wire + #steady_vortex + #steady_vortex_adjoint + #unsteady_vortex + #viscous_shock + #airfoil_steady + #airfoil_chaotic + #magnetostatic_box + #magnetostatic_motor + #magnetostatic_wire + #navier_stokes_mms + #thermal_square + #joule_wire ns_walltest rans_freestream rans_walltest + #gf_error ) create_sandbox("${SANDBOX_SRCS}") diff --git a/sandbox/ns_walltest_options.json b/sandbox/ns_walltest_options.json index 3d433ff9..05116837 100644 --- a/sandbox/ns_walltest_options.json +++ b/sandbox/ns_walltest_options.json @@ -23,7 +23,7 @@ "const-cfl": true, "ode-solver": "PTC", "t-final": 100, - "dt": 0.1, + "dt": 1.0, "cfl": 1.0, "res-exp": 2.0 }, @@ -54,6 +54,6 @@ "offset": 1e-3, "coeff": 1.0 }, - "init-pert": 0.0 - + "init-pert": 0.0, + "file-names": "ns_1e-2" } diff --git a/sandbox/rans_freestream_options.json b/sandbox/rans_freestream_options.json index ffcf35c4..5c3c287a 100644 --- a/sandbox/rans_freestream_options.json +++ b/sandbox/rans_freestream_options.json @@ -4,11 +4,12 @@ "aoa": 0.3, "roll-axis": 0, "pitch-axis": 1, - "chi": 3, + "chi": 3.0, "Re": 1000.0, "Pr": 0.75, "mu": 1.0, - "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9] + "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9], + "sa-srcs": [1.0, 1.0] }, "space-dis": { "degree": 1, @@ -45,5 +46,6 @@ "type": "const", "val": 1.0 }, + "file-names": "rans_free", "init-pert": 0.0 } diff --git a/sandbox/rans_walltest.cpp b/sandbox/rans_walltest.cpp index 3a37070b..02911dad 100644 --- a/sandbox/rans_walltest.cpp +++ b/sandbox/rans_walltest.cpp @@ -34,6 +34,11 @@ static int m_y; /// \param[out] u - conservative + SA variables stored as a 5-vector void pert(const Vector &x, Vector& p); +/// \brief Defines the random function for the jacobian check +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative variables stored as a 4-vector +void pert_ns(const Vector &x, Vector& p); + /// \brief Defines the exact solution for the rans freestream problem /// \param[in] x - coordinate of the point at which the state is needed /// \param[out] u - conservative + SA variables stored as a 5-vector @@ -44,6 +49,11 @@ void uexact(const Vector &x, Vector& u); /// \param[out] u - conservative + SA variables stored as a 5-vector void uinit_pert(const Vector &x, Vector& u); +/// \brief Defines a perturbed solution for the ns freestream problem +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative variables stored as a 4-vector +void uinit_pert_ns(const Vector &x, Vector& u); + /// \brief Defines a pseudo wall-distance function /// \param[in] x - coordinate of the point at which the distance is needed double walldist(const Vector &x); @@ -96,37 +106,89 @@ int main(int argc, char *argv[]) ipitch = file_options["flow-param"]["pitch-axis"].template get(); chi_fs = file_options["flow-param"]["chi"].template get(); - m_offset = file_options["mesh"]["offset"].template get(); + m_offset = file_options["mesh"]["offset"].template get(); //thickness of elements on wall m_coeff = file_options["mesh"]["coeff"].template get(); m_x = file_options["mesh"]["num-x"].template get(); m_y = file_options["mesh"]["num-y"].template get(); + string file = file_options["file-names"].template get(); + stringstream fileinit; stringstream filefinal; + fileinit << file << "_init"; + filefinal << file << "_final"; + + std::vector u_ns(1); + std::vector u_rans(1); + // generate the walled mesh, and its distance function std::unique_ptr smesh = buildWalledMesh(m_x, m_y); - + + if (file_options["try-rans"].template get()) + { // construct the solver and set initial conditions auto solver = createSolver>(opt_file_name, move(smesh)); solver->setInitialCondition(uinit_pert); - solver->printSolution("rans_wall_init", 0); - - // get the initial density error - double l2_error_init = (static_cast&>(*solver) - .calcConservativeVarsL2Error(uexact, 0)); - *out << "\n|| rho_h - rho ||_{L^2} init = " << l2_error_init << endl; + solver->printSolution(fileinit.str(), 0); double res_error = solver->calcResidualNorm(); - *out << "\ninitial residual norm = " << res_error << endl; - solver->checkJacobian(pert); + *out << "\ninitial rans residual norm = " << res_error << endl; + //solver->checkJacobian(pert); solver->solveForState(); - solver->printSolution("rans_wall_final",0); + solver->printSolution(filefinal.str(),0); // get the final density error - double l2_error_final = (static_cast&>(*solver) - .calcConservativeVarsL2Error(uexact, 0)); res_error = solver->calcResidualNorm(); - *out << "\nfinal residual norm = " << res_error; - *out << "\n|| rho_h - rho ||_{L^2} final = " << l2_error_final << endl; + *out << "\nfinal rans residual norm = " << res_error; + u_rans = solver->getFields(); + } + + if (file_options["try-ns"].template get()) + { + + // construct the solver and set initial conditions + std::unique_ptr smesh2 = buildWalledMesh(m_x, m_y); + auto solver2 = createSolver>(opt_file_name, + move(smesh2)); + solver2->setInitialCondition(uinit_pert_ns); + fileinit << "_ns"; + filefinal << "_ns"; + solver2->printSolution(fileinit.str(), 0); + + double res_error2 = solver2->calcResidualNorm(); + *out << "\ninitial ns residual norm = " << res_error2 << endl; + solver2->checkJacobian(pert_ns); + solver2->solveForState(); + solver2->printSolution(filefinal.str(),0); + res_error2 = solver2->calcResidualNorm(); + + *out << "\nfinal ns residual norm = " << res_error2; + u_ns = solver2->getFields(); + } + + //if (file_options["compare"].template get()) + //{ + + *out << "\n Before Reording " << endl; + mfem::Vector u_rans_comp(u_ns[0]->Size()); + for(int i = 0; i < u_rans[0]->Size()/5; i++) + { + for(int j = 0; j < 4; j++) + { + u_rans_comp(j + i*4) = u_rans[0]->Elem(j + i*5); + } + } + + // u_rans[0]->ReorderByNodes(); + // u_ns[0]->ReorderByNodes(); + *out << "\n After Reording = " << endl; + + //u_rans[0]->SetSize(u_ns[0]->Size()); + + *u_ns[0] -= u_rans_comp; + + *out << "\n ns-rans result norm = " << u_ns[0]->Norml2() << endl; + //} + } catch (MachException &exception) { @@ -154,6 +216,16 @@ void pert(const Vector &x, Vector& p) } } +// perturbation function used to check the jacobian in each iteration +void pert_ns(const Vector &x, Vector& p) +{ + p.SetSize(4); + for (int i = 0; i < 4; i++) + { + p(i) = 2.0 * uniform_rand(gen) - 1.0; + } +} + // Exact solution; same as freestream bc void uexact(const Vector &x, Vector& q) { @@ -202,6 +274,27 @@ void uinit_pert(const Vector &x, Vector& q) q = u; } +// initial guess perturbed from exact +void uinit_pert_ns(const Vector &x, Vector& q) +{ + // q.SetSize(4); + // Vector u(4); + q.SetSize(4); + Vector u(4); + + u = 0.0; + u(0) = pert_fs*1.0; + u(1) = u(0)*mach_fs*cos(aoa_fs); + u(2) = u(0)*mach_fs*sin(aoa_fs); + u(3) = pert_fs*1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; + // if(x(1) == 0.0) + // { + // u(1) = 1e-10; + // } + + q = u; +} + std::unique_ptr buildWalledMesh(int num_x, int num_y) { auto mesh_ptr = unique_ptr(new Mesh(num_x, num_y, @@ -230,11 +323,19 @@ std::unique_ptr buildWalledMesh(int num_x, int num_y) double b = log(offset)/(c - 1.0); double a = 1.0/exp(1.0*b); + ///Condense mesh near wall if(rt(1) > 0.0 && rt(1) < 1.0) { xy(1) = coeff*a*exp(b*rt(1)); //std::cout << xy(1) << std::endl; } + + ///TODO: condense mesh near wall transition as well + //double cx1 = 2.0/ + // if(rt(0) > 0.0 && rt(0) < 2.0) + // { + // xy(1) = coeff*a*exp(b*rt(0)); + // } }; VectorFunctionCoefficient xy_coeff(2, xy_fun); GridFunction *xy = new GridFunction(fes); @@ -255,7 +356,6 @@ std::unique_ptr buildWalledMesh(int num_x, int num_y) for (int j = 0; j < 2; ++j) { auto vtx = mesh_ptr->GetVertex(verts[j]); - cout<< "B El: "< +#include #include "utils.hpp" using namespace std; @@ -145,6 +146,10 @@ void InexactNewton::Mult(const Vector &b, Vector &x) const } jac = &oper->GetGradient(x); + // stringstream nssolname; + // nssolname << "jac_" <PrintMatlab(matlab); // std::cout << "Get the jacobian matrix.\n"; prec->SetOperator(*jac); //std::cout << "jac is set as one operator.\n"; diff --git a/src/common/sbp_fe.cpp b/src/common/sbp_fe.cpp index 9d840e86..b4f5be8f 100644 --- a/src/common/sbp_fe.cpp +++ b/src/common/sbp_fe.cpp @@ -783,11 +783,11 @@ void SBPTriangleElement::CalcShape(const IntegrationPoint &ip, Vector &shape) const { int ipIdx; - try - { - ipIdx = ipIdxMap.at(&ip); - } - catch (const std::out_of_range& oor) + // try + // { + // ipIdx = ipIdxMap.at(&ip); + // } + // catch (const std::out_of_range& oor) // error handling code to handle cases where the pointer to ip is not // in the map. Problems arise in GridFunction::SaveVTK() (specifically // GridFunction::GetValues()), which calls CalcShape() with an diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index d813e757..d92372a0 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -33,14 +33,16 @@ template void RANavierStokesSolver::addResVolumeIntegrators(double alpha) { // add Navier Stokes integrators - cout << "Inside RANS add volume integrators" << endl; + vector srcs = this->options["flow-param"]["sa-srcs"].template get>(); + Vector q_ref(dim+3); + this->getFreeStreamState(q_ref); this->res->AddDomainIntegrator(new SAInviscidIntegrator( //Inviscid term this->diff_stack, alpha)); this->res->AddDomainIntegrator(new SAViscousIntegrator( //SAViscousIntegrator this->diff_stack, this->re_fs, this->pr_fs, sacs, mu, alpha)); // now add RANS integrators this->res->AddDomainIntegrator(new SASourceIntegrator( - this->diff_stack, *dist, this->re_fs, sacs, mu, -alpha)); + this->diff_stack, *dist, this->re_fs, sacs, mu, -alpha, srcs[0], srcs[1])); // add LPS stabilization double lps_coeff = this->options["space-dis"]["lps-coeff"].template get(); this->res->AddDomainIntegrator(new SALPSIntegrator( @@ -51,7 +53,6 @@ void RANavierStokesSolver::addResVolumeIntegrators(double alpha) template void RANavierStokesSolver::addResBoundaryIntegrators(double alpha) { - // add base class integrators auto &bcs = this->options["bcs"]; double mu = this->options["flow-param"]["mu"].template get(); @@ -62,6 +63,9 @@ void RANavierStokesSolver::addResBoundaryIntegrators(double alpha) vector tmp = bcs["slip-wall"].template get>(); this->bndry_marker[idx].SetSize(tmp.size(), 0); this->bndry_marker[idx].Assign(tmp.data()); + + Vector q_ref(dim+3); + this->getFreeStreamState(q_ref); this->res->AddBdrFaceIntegrator( new SAViscousSlipWallBC(this->diff_stack, this->fec.get(), this->re_fs, this->pr_fs, sacs, mu, alpha), @@ -70,7 +74,7 @@ void RANavierStokesSolver::addResBoundaryIntegrators(double alpha) } if (bcs.find("no-slip-adiabatic") != bcs.end()) - { + { // no-slip-wall boundary condition vector tmp = bcs["no-slip-adiabatic"].template get>(); this->bndry_marker[idx].SetSize(tmp.size(), 0); this->bndry_marker[idx].Assign(tmp.data()); @@ -198,10 +202,46 @@ template void RANavierStokesSolver::iterationHook(int iter, double t, double dt) { + string file = this->options["file-names"].template get(); + stringstream filename; + filename << file <<"_last"; + this->checkJacobian(pert); - this->printSolution("rans_wall_last", 0); + this->printSolution(filename.str(), 0); + + stringstream solname; + solname << file <<"_last.gf"; + ofstream ssol(solname.str()); ssol.precision(16); + this->u->Save(ssol); + + stringstream rsolname; + rsolname << file <<"_last_res.gf"; + ofstream rsol(rsolname.str()); rsol.precision(16); + GridFunction r(this->u->FESpace()); + this->res->Mult(*this->u, r); + r.Save(rsol); + + cout << "Iter "<calcResidualNorm()< +void RANavierStokesSolver::terminalHook(int iter, double t_final) +{ + // double entropy = ent->GetEnergy(*u); + // entropylog << t_final << ' ' << entropy << endl; + // entropylog.close(); + + string file = this->options["file-names"].template get(); + stringstream filename; + filename << file <<"_final"; + stringstream solname; + solname << file <<"_final.gf"; + ofstream ssol(solname.str()); ssol.precision(30); + this->u->Save(ssol); +} + + template void RANavierStokesSolver::getDistanceFunction() { @@ -211,26 +251,27 @@ void RANavierStokesSolver::getDistanceFunction() FiniteElementSpace *dfes = new FiniteElementSpace(this->mesh.get(), dfec); dist.reset(new GridFunction(dfes)); - if (wall_type == "const") + if (wall_type == "const") // uniform d value { double val = this->options["wall-func"]["val"].template get(); ConstantCoefficient wall_coeff(val); dist->ProjectCoefficient(wall_coeff); } - if (wall_type == "y-dist") + if (wall_type == "y-dist") // y distance from the origin { double offset = this->options["mesh"]["offset"].template get(); auto walldist = [offset](const Vector &x) { - if(x(1) == 0.0) - return 0.25*offset; - else + // if(x(1) == 0.0) + // return 0.25*offset; //// not going to do it this way + // else return x(1); }; FunctionCoefficient wall_coeff(walldist); dist->ProjectCoefficient(wall_coeff); - } + } + ///TODO: Add option for proper wall distance function } std::default_random_engine gen_ns(std::random_device{}()); diff --git a/src/physics/fluidflow/rans.hpp b/src/physics/fluidflow/rans.hpp index 7ed8948b..4e3366c6 100644 --- a/src/physics/fluidflow/rans.hpp +++ b/src/physics/fluidflow/rans.hpp @@ -57,6 +57,8 @@ class RANavierStokesSolver : public NavierStokesSolver virtual void iterationHook(int iter, double t, double dt) override; + virtual void terminalHook(int iter, double t_final) override; + friend SolverPtr createSolver>( const nlohmann::json &json_options, std::unique_ptr smesh); diff --git a/src/physics/fluidflow/rans_fluxes.hpp b/src/physics/fluidflow/rans_fluxes.hpp index 66d22dd0..54e7d3cd 100644 --- a/src/physics/fluidflow/rans_fluxes.hpp +++ b/src/physics/fluidflow/rans_fluxes.hpp @@ -23,6 +23,7 @@ /// sacs[9] = rlim /// sacs[10] = cn1 +///NOTE: These are related only to preventing negative St /// sacs[11] = cv2 /// sacs[12] = cv3 @@ -59,7 +60,7 @@ xdouble calcSALaminarSuppression(const xdouble *q, const xdouble mu, /// \param[in] q - state used to define the destruction /// \param[in] mu - **nondimensionalized** viscosity /// \param[in] sacs - Spalart-Allmaras constants -/// \returns fn - turbulent viscosity coefficient +/// \returns fv1 - turbulent viscosity coefficient /// \tparam xdouble - typically `double` or `adept::adouble` /// \tparam dim - number of spatial dimensions (1, 2, or 3) template @@ -95,7 +96,10 @@ xdouble calcSAProductionCoefficient(const xdouble *q, const xdouble mu, /// Returns the modified vorticity in SA /// \param[in] q - state used to define the destruction +/// \param[in] S - vorticity magnitude /// \param[in] mu - **nondimensionalized** viscosity +/// \param[in] d - wall distance value +/// \param[in] Re - Reynold's number, if needed /// \param[in] sacs - Spalart-Allmaras constants /// \returns St - modified vorticity /// \tparam xdouble - typically `double` or `adept::adouble` @@ -110,18 +114,21 @@ xdouble calcSAModifiedVorticity(const xdouble *q, const xdouble S, xdouble cv2 = sacs[11]; xdouble cv3 = sacs[12]; xdouble fv2 = calcSAProductionCoefficient(q, mu, sacs); - xdouble work = fv2/(kappa*kappa*d*d); + xdouble work = nu_tilde*fv2/(kappa*kappa*d*d); xdouble St; if (work < -cv2*S) - St = S + (S*(cv2*cv2*S + cv3*work))/((cv3-2*cv2)*S - work); + St = S + (S*(cv2*cv2*S + cv3*work))/((cv3-2*cv2)*S - work); else - St = S + nu_tilde*work; - return Re*St; + St = S + work; + return St; } /// Returns the destruction coefficient in SA /// \param[in] q - state used to define the destruction /// \param[in] mu - **nondimensionalized** viscosity +/// \param[in] d - wall distance value +/// \param[in] S - vorticity magnitude +/// \param[in] Re - Reynold's number, if needed /// \param[in] sacs - Spalart-Allmaras constants /// \returns fw - destruction coefficient /// \tparam xdouble - typically `double` or `adept::adouble` @@ -145,7 +152,7 @@ xdouble calcSADestructionCoefficient(const xdouble *q, xdouble St = calcSAModifiedVorticity(q, S, mu, d, Re, sacs); //xdouble fv2 = calcSAProductionCoefficient(q, mu, sacs); - xdouble work = nu_tilde/(St*kappa*kappa*d*d); + xdouble work = nu_tilde/(Re*St*kappa*kappa*d*d); xdouble r = min(work, rlim); xdouble r6 = r*r*r*r*r*r; @@ -160,7 +167,9 @@ xdouble calcSADestructionCoefficient(const xdouble *q, /// Returns the production term in SA /// \param[in] q - state used to define the production +/// \param[in] d - wall distance value /// \param[in] S - vorticity magnitude (how to compute in h_grad space?) +/// \param[in] Re - Reynold's number, if needed /// \param[in] sacs - Spalart-Allmaras constants /// \returns P - production term /// \tparam xdouble - typically `double` or `adept::adouble` @@ -219,7 +228,7 @@ xdouble calcSANegativeProduction(const xdouble *q, const xdouble S, xdouble ct3 = sacs[7]; xdouble nu_tilde = q[dim+2]/q[0]; xdouble Pn = cb1*(1.0-ct3)*S*nu_tilde; - return Pn; + return q[0]*Pn; } /// Returns the destruction term in negative SA @@ -240,7 +249,7 @@ xdouble calcSANegativeDestruction(const xdouble *q, const xdouble d, xdouble chi_d = q[dim+2]/(d*q[0]); xdouble cw1 = cb1/(kappa*kappa) + (1+cb2)/sigma; xdouble Dn = -cw1*chi_d*chi_d; - return -Dn; + return -q[0]*Dn; } /// Returns the turbulent viscosity coefficient in negative SA @@ -435,8 +444,11 @@ void calcVorticity(const xdouble *Dq, const xdouble *jac_inv, { curl[0] = 0; curl[1] = 0; - curl[2] = DqJ[2] - DqJ[1 + dim+3]; + curl[2] = DqJ[2] - DqJ[1 + dim+3]; } + ////!!!!!!! + //curl[2] = 1.0;// min(abs(curl[2]), 0.0); + ////!!!!!!! if(dim == 3) { curl[0] = DqJ[3 + dim+3] - DqJ[2 + 2*(dim+3)]; @@ -447,10 +459,9 @@ void calcVorticity(const xdouble *Dq, const xdouble *jac_inv, // Compute vorticity jacobian w.r.t. state on an SBP element, needed for SA model terms /// \param[in] stack - adept stack -/// \param[in] q - the state over the element -/// \param[in] sbp - the sbp element whose shape functions we want -/// \param[in] Trans - defines the reference to physical element mapping -/// \param[out] curl - the curl of the velocity field at each node/int point +/// \param[in] Dq - the state gradient Dq +/// \param[in] jac_inv - inverse jacobian at node +/// \param[out] jac_curl - the curl jacobian /// \tparam xdouble - typically `double` or `adept::adouble` /// \tparam dim - number of spatial dimensions (1, 2, or 3) template @@ -484,7 +495,7 @@ void calcVorticityJacDw(adept::Stack &stack, const double *Dq, const double *jac // Compute gradient for the turbulence variable on an element node, // needed for SA model terms /// \param[in] i - the state variable to take -/// \param[in] q - the derivative of the state at the node +/// \param[in] Dq - the state gradient Dq /// \param[in] jac_inv - transformation jacobian at node /// \param[out] grad - the gradient of the turbulence variable at each node /// \tparam xdouble - typically `double` or `adept::adouble` @@ -517,10 +528,10 @@ void calcGrad(const int i, const xdouble *Dq, const xdouble *jac_inv, // Compute grad jacobian w.r.t. state on an SBP element, needed for SA model terms /// \param[in] stack - adept stack -/// \param[in] q - the state over the element -/// \param[in] sbp - the sbp element whose shape functions we want -/// \param[in] Trans - defines the reference to physical element mapping -/// \param[out] curl - the curl of the velocity field at each node/int point +/// \param[in] i - the state variable to take +/// \param[in] Dq - the state gradient Dq +/// \param[in] jac_inv - transformation jacobian at node +/// \param[out] jac_grad - the gradient jacobian /// \tparam xdouble - typically `double` or `adept::adouble` /// \tparam dim - number of spatial dimensions (1, 2, or 3) template @@ -551,6 +562,85 @@ void calcGradJacDw(adept::Stack &stack, const int i, const double *Dq, const dou } } +/// Computes the dual-consistent term for the no-slip wall penalty (temporary SA version) +/// \param[in] dir - desired (scaled) normal vector to the wall +/// \param[in] mu - nondimensionalized dynamic viscosity +/// \param[in] mu2 - SA transport viscosity +/// \param[in] Pr - Prandtl number +/// \param[in] q - state at the wall location +/// \param[out] fluxes - fluxes to be scaled by Dw (column major) +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +void calcNoSlipDualFluxSA(const xdouble *dir, xdouble mu, xdouble mu2, + double Pr, const xdouble *q, xdouble *fluxes) +{ + int num_state = dim + 3; + // zero out the fluxes, since applyCijMatrix accummulates + for (int i = 0; i < num_state*dim; ++i) + fluxes[i] = 0.0; + // evaluate the difference w - w_bc, where w_bc = [w[0], 0, 0, ...,w[dim+1], 0] + xdouble dw[num_state]; + dw[0] = 0.0; + dw[dim+1] = 0.0; + xdouble p = pressure(q); + for (int d = 0; d < dim; ++d) + { + dw[d+1] = q[d+1]/p; + } + dw[dim+2] = q[dim+2]; + // loop over the normal components + for (int d = 0; d < dim; ++d) + { + // loop over the derivative directions + for (int d2 = 0; d2 < dim; ++d2) + { + // we "sneak" dir[d] into the computation via mu + // !!!! we also sneak the sign into the computation here + applyCijMatrix(d2, d, -mu * dir[d], Pr, q, dw, + fluxes + (d2 * num_state)); + fluxes[dim+2 + d2*num_state] = -mu2 * dir[d] * dw[dim+2]; + } + } +#if 1 + // The following appears to be redundant (as far as the error goes) + // Project out the normal component from the last variable + xdouble nrm[dim]; + xdouble fac = 1.0 / sqrt(dot(dir, dir)); + xdouble flux_nrm = 0.0; + for (int d = 0; d < dim; ++d) + { + nrm[d] = dir[d] * fac; + flux_nrm += fluxes[d*num_state + dim+1]*nrm[d]; + } + for (int d = 0; d < dim; ++d) + fluxes[d*num_state + dim+1] -= flux_nrm*nrm[d]; +#endif +} + +/// Compute the product \f$ \sum_{d'=0}^{dim} C_{d,d'} D_{d'} w \f$ +/// Applies over conservative vars only +/// \param[in] d - desired space component of the flux +/// \param[in] mu - nondimensionalized dynamic viscosity +/// \param[in] Pr - Prandtl number +/// \param[in] q - state used to evaluate the \f$ C_d,d'} \f$ matrices +/// \param[in] Dw - derivatives of entropy varaibles, stored column-major +/// \param[out] mat_vec - stores the resulting matrix vector product +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +void applyViscousScalingSA(int d, xdouble mu, double Pr, const xdouble *q, + const xdouble *Dw, xdouble *mat_vec) +{ + for (int k = 0; k < dim+3; ++k) + { + mat_vec[k] = 0.0; + } + for (int d2 = 0; d2 < dim; ++d2) { + applyCijMatrix(d, d2, mu, Pr, q, Dw+(d2*(dim+3)), mat_vec); + } +} + } // namespace mach #endif diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp index 8aaf8388..1984e49a 100644 --- a/src/physics/fluidflow/rans_integ.hpp +++ b/src/physics/fluidflow/rans_integ.hpp @@ -10,7 +10,7 @@ #include "rans_fluxes.hpp" using adept::adouble; -using namespace std; /// TODO: this is polluting other headers! +using namespace std; namespace mach { @@ -26,11 +26,10 @@ class SAInviscidIntegrator : public DyadicFluxIntegrator< /// Construct an inviscid integrator with SA terms /// \param[in] diff_stack - for algorithmic differentiation /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) - SAInviscidIntegrator(adept::Stack &diff_stack, double a = 1.0) + SAInviscidIntegrator(adept::Stack &diff_stack, double a = 1.0) : DyadicFluxIntegrator>( diff_stack, dim+3, a) {} - /// Ismail-Roe two-point (dyadic) flux function with additional variable /// \param[in] di - physical coordinate direction in which flux is wanted /// \param[in] qL - state variables at "left" state @@ -51,10 +50,10 @@ class SAInviscidIntegrator : public DyadicFluxIntegrator< mfem::DenseMatrix &jacL, mfem::DenseMatrix &jacR); private: - + mfem::Vector qfs; }; -/// Integrator for RANS SA Production term +/// Integrator for RANS SA Production, Destruction, Viscous-Like terms /// \tparam dim - number of spatial dimensions (1, 2, or 3) /// \note This derived class uses the CRTP template @@ -63,12 +62,15 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator public: /// Construct an integrator for RANS SA Production term /// \param[in] diff_stack - for algorithmic differentiation - /// \param[in] + /// \param[in] distance - wall distance function projected onto a space + /// \param[in] re_fs - freestream reynolds number + /// \param[in] sa_params - Spalart-Allmaras model constants /// \param[in] vis - nondimensional dynamic viscosity (use Sutherland if neg) /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + /// \param[in] P, D - control production and destruction terms for debugging SASourceIntegrator(adept::Stack &diff_stack, mfem::GridFunction distance, double re_fs, - mfem::Vector sa_params, double vis = -1.0, double a = 1.0) - : alpha(a), mu(vis), stack(diff_stack), num_states(dim+3), Re(re_fs), sacs(sa_params), + mfem::Vector sa_params, double vis = -1.0, double a = 1.0, double P = 1.0, double D = 1.0) + : alpha(a), prod(P), dest(D), mu(vis), stack(diff_stack), num_states(dim+3), Re(re_fs), sacs(sa_params), dist(distance) {} /// Construct the element local residual @@ -107,6 +109,8 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator mfem::Vector sacs; /// stack used for algorithmic differentiation adept::Stack &stack; + /// activate production and destruction terms + double prod; double dest; #ifndef MFEM_THREAD_SAFE /// the coordinates of node i mfem::Vector xi; @@ -283,7 +287,7 @@ class SAViscousSlipWallBC : public ViscousBoundaryIntegrator>( diff_stack, fe_coll, dim + 3, a), @@ -392,6 +396,8 @@ class SAViscousSlipWallBC : public ViscousBoundaryIntegrator::AssembleElementVector( elvect.SetSize(num_states * num_nodes); ui.SetSize(num_states); xi.SetSize(dim); - grad.SetSize(num_nodes, dim); - curl.SetSize(num_nodes, 3); grad_nu_i.SetSize(dim); grad_rho_i.SetSize(dim); curl_i.SetSize(3); - DenseMatrix uc(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well + DenseMatrix uc(elfun.GetData(), num_nodes, num_states); DenseMatrix u; u = uc; u.Transpose(); //NOTE: Do not manipulate elfun directly - DenseMatrix res(elvect.GetData(), num_nodes, num_states); - DenseMatrix Qu(num_nodes, num_states); - DenseMatrix HQu(num_nodes, num_states); DenseMatrix Dui(num_states, dim); Vector Duidi(num_states); @@ -58,44 +53,62 @@ void SASourceIntegrator::AssembleElementVector( } // compute vorticity at node and take magnitude - calcVorticity(Dui.GetData(), Trans.InverseJacobian().GetData(), curl_i.GetData()); //curl.GetRow(i, curl_i); + calcVorticity(Dui.GetData(), Trans.InverseJacobian().GetData(), curl_i.GetData()); double S = sqrt(curl_i(0)*curl_i(0) + curl_i(1)*curl_i(1) +curl_i(2)*curl_i(2)); - S = abs(curl_i(2)); + //S = abs(curl_i(2)); // compute gradient of turbulent viscosity at node - calcGrad(dim+2, Dui.GetData(), Trans.InverseJacobian().GetData(), grad_nu_i.GetData()); //curl.GetRow(i, curl_i); + calcGrad(dim+2, Dui.GetData(), Trans.InverseJacobian().GetData(), grad_nu_i.GetData()); // compute gradient of density at node - calcGrad(0, Dui.GetData(), Trans.InverseJacobian().GetData(), grad_rho_i.GetData()); //curl.GetRow(i, curl_i); + calcGrad(0, Dui.GetData(), Trans.InverseJacobian().GetData(), grad_rho_i.GetData()); // get distance function value at node double d = dist.GetValue(Trans.ElementNo, node); //temp solution, y distance from wall // accumulate source terms double src = calcSASource( - ui.GetData(), grad_nu_i.GetData(), sacs.GetData())/Re; + ui.GetData(), grad_nu_i.GetData(), sacs.GetData())/Re; src -= calcSASource2( - ui.GetData(), mu, grad_nu_i.GetData(), grad_rho_i.GetData(), sacs.GetData())/Re; - ///std::cout << "grad:" << src << std::endl; - // use negative model if turbulent viscosity is negative + ui.GetData(), mu, grad_nu_i.GetData(), grad_rho_i.GetData(), sacs.GetData())/Re; + + //use negative model if turbulent viscosity is negative + double d0 = 1e-4; + if (fabs(d) > 1e-12) + { if (ui(dim+2) < 0) { - src += calcSANegativeProduction( + src += prod*calcSANegativeProduction( ui.GetData(), S, sacs.GetData()); - src += calcSANegativeDestruction( + src += dest*calcSANegativeDestruction( ui.GetData(), d, sacs.GetData())/Re; + //cout << "Negative!"<( - ui.GetData(), mu, d, S, Re, sacs.GetData())/Re; + src += prod*calcSAProduction( + ui.GetData(), mu, d, S, Re, sacs.GetData());///Re; ///std::cout << "P+grad:" << src << std::endl; - src += calcSADestruction( + src += dest*calcSADestruction( ui.GetData(), mu, d, S, Re, sacs.GetData())/Re; ///std::cout << "P+D+grad:" << src << std::endl; } - - // std::cout << "El: " << Trans.ElementNo << " P+D+grad: " << src << std::endl; - // std::cout << "Vorticity: " <( + ui.GetData(), d0, sacs.GetData())/Re; + } + else + { + src += dest*calcSADestruction( + ui.GetData(), mu, d0, S, Re, sacs.GetData())/Re; + } + } + // if (d < 1e-12) + // d = d0; + src = prod*(ui(dim+2)-3); ///!!!!!!!!!!!!!!!!!!!!!!!!!!! + //cout << src << endl; //byNODES elvect(i + num_nodes*(num_states-1)) = alpha * Trans.Weight() * node.weight * src; @@ -113,20 +126,15 @@ void SASourceIntegrator::AssembleElementGrad( int num_nodes = sbp.GetDof(); #ifdef MFEM_THREAD_SAFE Vector ui, xi, uj, grad_nu_i, curl_i; - DenseMatrix grad, curl; #endif ui.SetSize(num_states); xi.SetSize(dim); - grad.SetSize(num_nodes, dim); - curl.SetSize(num_nodes, 3); grad_nu_i.SetSize(dim); grad_rho_i.SetSize(dim); curl_i.SetSize(3); - DenseMatrix uc(elfun.GetData(), num_nodes, num_states); // send u into function to compute curl, gradient of nu, send transformation as well + DenseMatrix uc(elfun.GetData(), num_nodes, num_states); DenseMatrix u; u = uc; u.Transpose(); - DenseMatrix Qu(num_nodes, num_states); - DenseMatrix HQu(num_nodes, num_states); DenseMatrix Dui(num_states, dim); Vector Duidi(num_states); vector jac_curl(dim); @@ -134,6 +142,8 @@ void SASourceIntegrator::AssembleElementGrad( vector jac_grad_rho(dim); std::vector sacs_a(sacs.Size()); adept::set_values(sacs_a.data(), sacs.Size(), sacs.GetData()); + + // each matrix represents d/dx, d/dy, d/dz for (int d = 0; d < dim; ++d) { jac_curl[d].SetSize(3, num_states); @@ -147,15 +157,12 @@ void SASourceIntegrator::AssembleElementGrad( Vector dSrcdS(1); //partial src w.r.t. S Vector dSrcdgradnu(dim); //partial src w.r.t grad Vector dSrcdgradrho(dim); //partial src w.r.t grad - DenseMatrix src_grad(dim, num_states); - DenseMatrix src_curl(3, num_states); Vector work1(num_nodes*num_states); Vector work2s(num_states); Vector work2gn(num_states); Vector work2gr(num_states); Vector work3(num_nodes); Vector dnu(num_nodes*num_states); //total derivative - //need dgraddDu, dcdDu, dDudu // convert momentum to velocity for (int nn = 0; nn < num_nodes; nn++) @@ -178,7 +185,7 @@ void SASourceIntegrator::AssembleElementGrad( u.GetColumn(i, ui); Trans.Transform(node, xi); - // get Dui + // get Dui, insert each spatial derivative into column matrix Duidi = 0.0; Dui = 0.0; for (int di = 0; di < dim; di++) { @@ -193,22 +200,19 @@ void SASourceIntegrator::AssembleElementGrad( std::vector curl_i_a(curl_i.Size()); std::vector grad_nu_i_a(grad_nu_i.Size()); std::vector grad_rho_i_a(grad_rho_i.Size()); - adouble S; adouble mu_a = mu; adouble Re_a = Re; - // initialize adouble inputs adept::set_values(ui_a.data(), ui.Size(), ui.GetData()); - //adept::set_values(Dui_a.data(), Dw_size, Dui.GetData()); // compute vorticity at node calcVorticity(Dui.GetData(), - Trans.InverseJacobian().GetData(), curl_i.GetData()); //curl.GetRow(i, curl_i); + Trans.InverseJacobian().GetData(), curl_i.GetData()); calcVorticityJacDw(this->stack, Dui.GetData(), Trans.InverseJacobian().GetData(), jac_curl); // compute gradient of turbulent viscosity at node calcGrad(dim+2, Dui.GetData(), - Trans.InverseJacobian().GetData(), grad_nu_i.GetData()); //curl.GetRow(i, curl_i); + Trans.InverseJacobian().GetData(), grad_nu_i.GetData()); calcGradJacDw(this->stack, dim+2, Dui.GetData(), Trans.InverseJacobian().GetData(), jac_grad_nu); // compute gradient of density at node @@ -217,17 +221,21 @@ void SASourceIntegrator::AssembleElementGrad( calcGradJacDw(this->stack, 0, Dui.GetData(), Trans.InverseJacobian().GetData(), jac_grad_rho); + // set adept outputs adept::set_values(curl_i_a.data(), curl_i.Size(), curl_i.GetData()); adept::set_values(grad_nu_i_a.data(), grad_nu_i.Size(), grad_nu_i.GetData()); adept::set_values(grad_rho_i_a.data(), grad_rho_i.Size(), grad_rho_i.GetData()); // get distance function value at node - adouble d = dist.GetValue(Trans.ElementNo, node); //temp solution, y distance from wall + adouble d = dist.GetValue(Trans.ElementNo, node); //evaluate the distance gridfunction at the node + adouble d0 = 1e-4; //temporary solution for nodes on wall + // if (d < 1e-12) + // d = d0; // vorticity magnitude deriv this->stack.new_recording(); - //S = sqrt(curl_i_a[0]*curl_i_a[0] + curl_i_a[1]*curl_i_a[1] +curl_i_a[2]*curl_i_a[2]); - S = abs(curl_i_a[2]); + adouble S = sqrt(curl_i_a[0]*curl_i_a[0] + curl_i_a[1]*curl_i_a[1] +curl_i_a[2]*curl_i_a[2]); + //S = abs(curl_i_a[2]); this->stack.independent(curl_i_a.data(), curl_i.Size()); this->stack.dependent(S); this->stack.jacobian(dSdc.GetData()); @@ -236,24 +244,40 @@ void SASourceIntegrator::AssembleElementGrad( // ui differentiation this->stack.new_recording(); adouble src = calcSASource( - ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; + ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; src -= calcSASource2( - ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; - // use negative model if turbulent viscosity is negative + ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; + if (fabs(d) > 1e-12) + { if (ui_a[dim+2] < 0) { - src += calcSANegativeProduction( + src += prod*calcSANegativeProduction( ui_a.data(), S, sacs_a.data()); - src += calcSANegativeDestruction( + src += dest*calcSANegativeDestruction( ui_a.data(), d, sacs_a.data())/Re_a; } else { - src += calcSAProduction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; - src += calcSADestruction( + src += prod*calcSAProduction( + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data());///Re_a; + src += dest*calcSADestruction( ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; } + } + else + { + if (ui_a[dim+2] < 0) + { + src += dest*calcSANegativeDestruction( + ui_a.data(), d0, sacs_a.data())/Re_a; + } + else + { + src += dest*calcSADestruction( + ui_a.data(), mu_a, d0, S, Re_a, sacs_a.data())/Re_a; + } + } + src = prod*(ui_a[dim+2]-3); ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this->stack.independent(ui_a.data(), ui.Size()); this->stack.dependent(src); this->stack.jacobian(dSrcdu.GetData()); @@ -261,92 +285,137 @@ void SASourceIntegrator::AssembleElementGrad( dSrcdS = 0.0; // S differentiation this->stack.new_recording(); - adouble S_a; - S_a.set_value(S.value()); src = calcSASource( - ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; + ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; src -= calcSASource2( - ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; - // use negative model if turbulent viscosity is negative + ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; + if (fabs(d) > 1e-12) + { if (ui_a[dim+2] < 0) { - src += calcSANegativeProduction( + src += prod*calcSANegativeProduction( ui_a.data(), S, sacs_a.data()); - src += calcSANegativeDestruction( + src += dest*calcSANegativeDestruction( ui_a.data(), d, sacs_a.data())/Re_a; } else { - src += calcSAProduction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; - src += calcSADestruction( + src += prod*calcSAProduction( + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data());///Re_a; + src += dest*calcSADestruction( ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; } - this->stack.independent(S_a); + } + else + { + if (ui_a[dim+2] < 0) + { + src += dest*calcSANegativeDestruction( + ui_a.data(), d0, sacs_a.data())/Re_a; + } + else + { + src += dest*calcSADestruction( + ui_a.data(), mu_a, d0, S, Re_a, sacs_a.data())/Re_a; + } + } + src = prod*(ui_a[dim+2]-3); ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + this->stack.independent(S); this->stack.dependent(src); this->stack.jacobian(dSrcdS.GetData()); dSrcdgradnu = 0.0; - // grad differentiation + // grad nu differentiation this->stack.new_recording(); src = calcSASource( - ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; + ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; src -= calcSASource2( - ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; - // use negative model if turbulent viscosity is negative + ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; + if (fabs(d) > 1e-12) + { if (ui_a[dim+2] < 0) { - src += calcSANegativeProduction( + src += prod*calcSANegativeProduction( ui_a.data(), S, sacs_a.data()); - src += calcSANegativeDestruction( + src += dest*calcSANegativeDestruction( ui_a.data(), d, sacs_a.data())/Re_a; } else { - src += calcSAProduction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; - src += calcSADestruction( + src += prod*calcSAProduction( + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data());///Re_a; + src += dest*calcSADestruction( ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; } - + } + else + { + if (ui_a[dim+2] < 0) + { + src += dest*calcSANegativeDestruction( + ui_a.data(), d0, sacs_a.data())/Re_a; + } + else + { + src += dest*calcSADestruction( + ui_a.data(), mu_a, d0, S, Re_a, sacs_a.data())/Re_a; + } + } + src = prod*(ui_a[dim+2]-3); ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this->stack.independent(grad_nu_i_a.data(), grad_nu_i_a.size()); this->stack.dependent(src); this->stack.jacobian(dSrcdgradnu.GetData()); dSrcdgradrho = 0.0; - // grad differentiation + // grad rho differentiation this->stack.new_recording(); src = calcSASource( ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; src -= calcSASource2( ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; - // use negative model if turbulent viscosity is negative + if (fabs(d) > 1e-12) + { if (ui_a[dim+2] < 0) { - src += calcSANegativeProduction( + src += prod*calcSANegativeProduction( ui_a.data(), S, sacs_a.data()); - src += calcSANegativeDestruction( + src += dest*calcSANegativeDestruction( ui_a.data(), d, sacs_a.data())/Re_a; } else { - src += calcSAProduction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; - src += calcSADestruction( + src += prod*calcSAProduction( + ui_a.data(), mu_a, d, S, Re_a, sacs_a.data());///Re_a; + src += dest*calcSADestruction( ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; } - + } + else + { + if (ui_a[dim+2] < 0) + { + src += dest*calcSANegativeDestruction( + ui_a.data(), d0, sacs_a.data())/Re_a; + } + else + { + src += dest*calcSADestruction( + ui_a.data(), mu_a, d0, S, Re_a, sacs_a.data())/Re_a; + } + } + src = prod*(ui_a[dim+2]-3); ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this->stack.independent(grad_rho_i_a.data(), grad_rho_i_a.size()); this->stack.dependent(src); this->stack.jacobian(dSrcdgradrho.GetData()); // Assemble nu derivative + // Direct ui sensitivities for(int ns = 0; ns < num_states; ns ++) { dnu(i + ns*num_nodes) = dSrcdu(ns); } - // Dui differentiation + // Dui sensitivities for (int di = 0; di < dim; di++) { work1 = 0.0; work2s = 0.0; work2gn = 0.0; work2gr = 0.0; work3 = 0.0; @@ -355,7 +424,6 @@ void SASourceIntegrator::AssembleElementGrad( work2s *= dSrcdS(0); jac_grad_nu[di].MultTranspose(dSrcdgradnu, work2gn); jac_grad_rho[di].MultTranspose(dSrcdgradrho, work2gr); - //src_grad.GetRow(di, work2g); for (int nn = 0; nn < num_nodes; nn++) { // assuming ordering BY NODES @@ -366,6 +434,8 @@ void SASourceIntegrator::AssembleElementGrad( } dnu += work1; } + + // node weighting dnu *= (Trans.Weight() * node.weight); // account for momentum conversion @@ -397,8 +467,6 @@ void SAInviscidIntegrator::calcFlux(int di, const mfem::Vector &qL, const mfem::Vector &qR, mfem::Vector &flux) { - ///NOTE: Differentiate this function with adept directly, check euler/navier stokes integrators - //operate on original state variables if (entvar) { calcIsmailRoeFluxUsingEntVars(di, qL.GetData(), qR.GetData(), @@ -410,8 +478,8 @@ void SAInviscidIntegrator::calcFlux(int di, const mfem::Vector &qL, flux.GetData()); } //add flux term for SA + //flux(dim+2) = 0.5*(qfs(di+1)/qfs(0) + qfs(di+1)/qfs(0))*0.5*(qL(dim+2) + qR(dim+2)); flux(dim+2) = 0.5*(qL(di+1)/qL(0) + qR(di+1)/qR(0))*0.5*(qL(dim+2) + qR(dim+2)); - //flux(dim+2) = (qL(di+1)/qL(0) + qR(di+1)/qR(0))*0.5*(qL(dim+2) + qR(dim+2)); } template @@ -453,14 +521,15 @@ void SAInviscidIntegrator::calcFluxJacStates( jacL.CopyCols(jac, 0, dim + 2); // retrieve the jacobian w.r.t right state jacR.CopyCols(jac, dim + 3, 2 * (dim + 3) - 1); - // add SA variable contributions + // add SA variable contributions ///NOTE: no real need to do it this way jacL(dim+2,0) = -0.25*(qL(di+1)/(qL(0)*qL(0)))*(qL(dim+2) + qR(dim+2)); jacR(dim+2,0) = -0.25*(qR(di+1)/(qR(0)*qR(0)))*(qL(dim+2) + qR(dim+2)); jacL(dim+2,di+1) = 0.25*(1.0/qL(0))*(qL(dim+2) + qR(dim+2)); - jacR(dim+2,di+1) = 0.25*(1.0/qR(0))*(qL(dim+2) + qR(dim+2)); + jacR(dim+2,di+1) = 0.25*(1.0/qR(0))*(qL(dim+2) + qR(dim+2)); jacL(dim+2,dim+2) = 0.25*(qL(di+1)/qL(0) + qR(di+1)/qR(0)); jacR(dim+2,dim+2) = 0.25*(qL(di+1)/qL(0) + qR(di+1)/qR(0)); - + // jacL(dim+2,dim+2) = 0.5*(qfs(di+1)/qfs(0) + qfs(di+1)/qfs(0)); + // jacR(dim+2,dim+2) = 0.5*(qfs(di+1)/qfs(0) + qfs(di+1)/qfs(0)); } //==================================================================================== @@ -485,12 +554,14 @@ void SANoSlipAdiabaticWallBC::calcFlux(const mfem::Vector &x, { mu_Re = calcSutherlandViscosity(q.GetData()); } - //mu_Re /= Re; double fv1 = calcSACoefficient(q.GetData(), mu, sacs.GetData()); + if (q(dim+2)<0) + fv1 = 0.0; double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; calcAdiabaticWallFlux(dir.GetData(), mu_Re_SA, Pr, q.GetData(), Dw.GetData(), work_vec.GetData()); + work_vec(dim+2) = 0.0; flux_vec -= work_vec; // note the minus sign!!! // evaluate wall normal eddy viscosity flux double grad[dim]; @@ -503,10 +574,11 @@ void SANoSlipAdiabaticWallBC::calcFlux(const mfem::Vector &x, // Step 3: evaluate the no-slip penalty calcNoSlipPenaltyFlux(dir.GetData(), jac, mu_Re_SA, Pr, qfs.GetData(), q.GetData(), work_vec.GetData()); + work_vec(dim+2) = 0.0; flux_vec += work_vec; double dnu = q(dim+2); double dnuflux = (mu + fn*q(dim+2))*dnu/(sacs(2)*Re); - double fac = sqrt(dot(dir, dir))/jac; + double fac = 100*sqrt(dot(dir, dir))/jac; flux_vec(dim+2) += dnuflux*fac; } @@ -539,6 +611,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacState( // Step 1: apply the EC slip wall flux mach::calcSlipWallFlux(x_a.data(), dir_a.data(), q_a.data(), flux_a.data()); + flux_a[dim+2] = 0.0; // Step 2: evaluate the adiabatic flux adouble mu_Re = mu; if (mu < 0.0) @@ -548,9 +621,12 @@ void SANoSlipAdiabaticWallBC::calcFluxJacState( //mu_Re /= Re; adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); + if (q_a[dim+2]<0) + fv1 = 0.0; adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; mach::calcAdiabaticWallFlux(dir_a.data(), mu_Re_SA, Pr, q_a.data(), Dw_a.data(), work_vec_a.data()); + work_vec_a[dim+2] = 0.0; for (int i = 0; i < flux_a.size(); ++i) { flux_a[i] -= work_vec_a[i]; // note the minus sign!!! @@ -566,13 +642,14 @@ void SANoSlipAdiabaticWallBC::calcFluxJacState( // Step 3: evaluate the no-slip penalty mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re_SA, Pr, qfs_a.data(), q_a.data(), work_vec_a.data()); + work_vec_a[dim+2] = 0.0; for (int i = 0; i < flux_a.size(); ++i) { flux_a[i] += work_vec_a[i]; } adouble dnu = q_a[dim+2]; adouble dnuflux = (mu + fn*q_a[dim+2])*dnu/(sacs_a[2]*Re); - adouble fac = sqrt(dot(dir_a.data(), dir_a.data()))/jac; + adouble fac = 100*sqrt(dot(dir_a.data(), dir_a.data()))/jac; flux_a[dim+2] += dnuflux*fac; this->stack.independent(q_a.data(), q.Size()); @@ -607,6 +684,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf // Step 1: apply the EC slip wall flux mach::calcSlipWallFlux(x_a.data(), dir_a.data(), q_a.data(), flux_a.data()); + flux_a[dim+2] = 0.0; // Step 2: evaluate the adiabatic flux adouble mu_Re = mu; if (mu < 0.0) @@ -616,9 +694,12 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf //mu_Re /= Re; adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); + if (q_a[dim+2]<0) + fv1 = 0.0; adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; mach::calcAdiabaticWallFlux(dir_a.data(), mu_Re_SA, Pr, q_a.data(), Dw_a.data(), work_vec_a.data()); + work_vec_a[dim+2] = 0.0; for (int i = 0; i < flux_a.size(); ++i) { flux_a[i] -= work_vec_a[i]; // note the minus sign!!! @@ -634,13 +715,14 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf // Step 3: evaluate the no-slip penalty mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re_SA, Pr, qfs_a.data(), q_a.data(), work_vec_a.data()); + work_vec_a[dim+2] = 0.0; for (int i = 0; i < flux_a.size(); ++i) { flux_a[i] += work_vec_a[i]; } adouble dnu = q_a[dim+2]; adouble dnuflux = (mu + fn*q_a[dim+2])*dnu/(sacs_a[2]*Re); - adouble fac = sqrt(dot(dir_a.data(), dir_a.data()))/jac; + adouble fac = 100*sqrt(dot(dir_a.data(), dir_a.data()))/jac; flux_a[dim+2] += dnuflux*fac; this->stack.independent(Dw_a.data(),Dw_size); @@ -666,12 +748,14 @@ void SANoSlipAdiabaticWallBC::calcFluxDv(const mfem::Vector &x, //mu_Re /= Re; double fv1 = calcSACoefficient(q.GetData(), mu, sacs.GetData()); + if (q(dim+2)<0) + fv1 = 0.0; double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; - calcNoSlipDualFlux(dir.GetData(), mu_Re_SA, Pr, q.GetData(), + double fn = calcSANegativeCoefficient(q.GetData(), mu, + sacs.GetData()); + double mu_2 = (mu_Re + q(dim+2)*fn)/Re; + calcNoSlipDualFluxSA(dir.GetData(), mu_Re_SA, mu_2, Pr, q.GetData(), flux_mat.GetData()); - for (int di = 0; di < dim; di++) - flux_mat(dim+2, di) = 0.0; - } template @@ -699,11 +783,15 @@ void SANoSlipAdiabaticWallBC::calcFluxDvJacState( //mu_Re /= Re; adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); + if (q_a[dim+2]<0) + fv1 = 0.0; adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; - calcNoSlipDualFlux(dir_a.data(), mu_Re_SA, Pr, q_a.data(), + adouble fn = calcSANegativeCoefficient(q_a.data(), mu, + sacs_a.data()); + adouble mu_2 = (mu_Re + q_a[dim+2]*fn)/Re; + calcNoSlipDualFluxSA(dir_a.data(), mu_Re_SA, mu_2, Pr, q_a.data(), fluxes_a.data()); - for (int di = 0; di < dim; di++) - fluxes_a[dim+2 + di*(dim+3)] = 0.0; + this->stack.independent(q_a.data(), q.Size()); this->stack.dependent(fluxes_a.data(), flux_size); // compute and store jacobian in flux_jac @@ -759,11 +847,13 @@ void SAViscousSlipWallBC::calcFlux(const mfem::Vector &x, //mu_Re /= Re; double fv1 = calcSACoefficient(q.GetData(), mu, sacs.GetData()); + if (q(dim+2)<0) + fv1 = 0.0; double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; for (int d = 0; d < dim; ++d) { work_vec = 0.0; - applyViscousScaling(d, mu_Re_SA, Pr, q.GetData(), + applyViscousScalingSA(d, mu_Re_SA, Pr, q.GetData(), Dw_work.GetData(), work_vec.GetData()); work_vec *= dir(d); flux_vec -= work_vec; @@ -827,11 +917,13 @@ void SAViscousSlipWallBC::calcFluxJacState( //mu_Re /= Re; adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); + if (q_a[dim+2]<0) + fv1 = 0.0; adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; for (int d = 0; d < dim; ++d) { work_vec_a[dim+2] = 0.0; - applyViscousScaling(d, mu_Re_SA, Pr, q_a.data(), + applyViscousScalingSA(d, mu_Re_SA, Pr, q_a.data(), Dw_work_a.data(), work_vec_a.data()); for (int k = 0; k < dim+2; k++) { @@ -899,11 +991,13 @@ void SAViscousSlipWallBC::calcFluxJacDw(const mfem::Vector &x, const mfem:: //mu_Re /= Re; adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); + if (q_a[dim+2]<0) + fv1 = 0.0; adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; for (int d = 0; d < dim; ++d) { work_vec_a[dim+2] = 0.0; - applyViscousScaling(d, mu_Re_SA, Pr, q_a.data(), + applyViscousScalingSA(d, mu_Re_SA, Pr, q_a.data(), Dw_work_a.data(), work_vec_a.data()); for (int k = 0; k < dim+2; k++) { @@ -948,7 +1042,7 @@ void SAFarFieldBC::calcFlux(const mfem::Vector &x, const mfem::Vector &dir, for (int d = 0; d < dim; ++d) { work_vec = 0.0; - applyViscousScaling(d, mu_Re_SA, Pr, q.GetData(), Dw.GetData(), + applyViscousScalingSA(d, mu_Re_SA, Pr, q.GetData(), Dw.GetData(), work_vec.GetData()); work_vec *= dir[d]; flux_vec -= work_vec; @@ -958,13 +1052,16 @@ void SAFarFieldBC::calcFlux(const mfem::Vector &x, const mfem::Vector &dir, //flux_vec(dim+2) = 0; // handle SA variable double Unrm = dot(dir.GetData(), qfs.GetData()+1); + double fac = 1.0;//sqrt(dot(dir, dir)); if (Unrm > 0.0) { - flux_vec(dim+2) = Unrm*q(dim+2)/q(0); + + flux_vec(dim+2) = Unrm*q(dim+2)/(q(0)*fac); } else { - flux_vec(dim+2) = Unrm*qfs(dim+2)/qfs(0); + //double dq = q(dim+2) - qfs(dim+2); + flux_vec(dim+2) = Unrm*qfs(dim+2)/(qfs(0)*fac);//Unrm*dq/fac; } } @@ -1013,7 +1110,7 @@ void SAFarFieldBC::calcFluxJacState( { work_vec_a[i] = 0.0; } - applyViscousScaling(d, mu_Re_SA, Pr, q_a.data(), Dw_a.data(), + applyViscousScalingSA(d, mu_Re_SA, Pr, q_a.data(), Dw_a.data(), work_vec_a.data()); for (int i = 0; i < flux_a.size(); ++i) { @@ -1023,28 +1120,41 @@ void SAFarFieldBC::calcFluxJacState( } #endif - this->stack.independent(q_a.data(), q.Size()); - this->stack.dependent(flux_a.data(), q.Size()); - this->stack.jacobian(flux_jac.GetData()); - - // handle SA variable - double Unrm = dot(dir.GetData(), qfs.GetData()+1); + adouble Unrm = dot(dir_a.data(), qfs_a.data()+1); + adouble fac = 1.0;//sqrt(dot(dir_a.data(), dir_a.data())); if (Unrm > 0.0) { - flux_jac(dim+2, 0) = -Unrm*q(dim+2)/(q(0)*q(0)); - // for(int di = 0; di < dim; di++) - // { - // flux_jac(dim+2, di+1) = dir(di)*q(dim+2)/q(0); - // } - flux_jac(dim+2, dim+2) = Unrm/q(0); + flux_a[dim+2] = Unrm*q_a[dim+2]/(q_a[0]*fac); } else { - // for(int di = 0; di < dim; di++) - // { - // flux_jac(dim+2, di+1) = dir(di)*qfs(dim+2)/qfs(0); - // } + //adouble dq = q_a[dim+2] - qfs_a[dim+2]; + flux_a[dim+2] = Unrm*qfs_a[dim+2]/(qfs_a[0]*fac); //Unrm*dq/fac; } + + this->stack.independent(q_a.data(), q.Size()); + this->stack.dependent(flux_a.data(), q.Size()); + this->stack.jacobian(flux_jac.GetData()); + + // handle SA variable + // double Unrm = dot(dir.GetData(), qfs.GetData()+1); + // double fac = sqrt(dot(dir, dir)); + // if (Unrm > 0.0) + // { + // flux_jac(dim+2, 0) = -Unrm*q(dim+2)/(q(0)*q(0)*fac); + // for(int di = 0; di < dim; di++) + // { + // flux_jac(dim+2, di+1) = dir(di)*q(dim+2)/(q(0)*fac); + // } + // flux_jac(dim+2, dim+2) = Unrm/(q(0)*fac); + // } + // else + // { + // // for(int di = 0; di < dim; di++) + // // { + // // flux_jac(dim+2, di+1) = dir(di)*qfs(dim+2)/qfs(0); + // // } + // } } template @@ -1227,12 +1337,16 @@ void SAViscousIntegrator::applyScaling(int d, const mfem::Vector &x, //mu_Re /= Re; double fv1 = calcSACoefficient(q.GetData(), mu, sacs.GetData()); + if (q(dim+2)<0) + fv1 = 0.0; double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; - applyViscousScaling(d, mu_Re_SA, Pr, q.GetData(), Dw.GetData(), + applyViscousScalingSA(d, mu_Re_SA, Pr, q.GetData(), Dw.GetData(), CDw.GetData()); double fn = calcSANegativeCoefficient(q.GetData(), mu, sacs.GetData()); CDw(dim+2) = (mu + fn*q(dim+2))*Dw(dim+2, d)/(sacs(2)*Re); + // if (mu + fn*q(dim+2) < 0) + // cout << "Negative diffusion!: " < @@ -1262,8 +1376,10 @@ void SAViscousIntegrator::applyScalingJacState(int d, const mfem::Vector &x //mu_Re /= Re; adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); + if (q_a[dim+2]<0) + fv1 = 0.0; adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; - applyViscousScaling(d, mu_Re_SA, Pr, q_a.data(), Dw_a.data(), + applyViscousScalingSA(d, mu_Re_SA, Pr, q_a.data(), Dw_a.data(), CDw_a.data()); adouble fn = calcSANegativeCoefficient(q_a.data(), mu, sacs_a.data()); @@ -1302,8 +1418,10 @@ void SAViscousIntegrator::applyScalingJacDw( //mu_Re /= Re; adouble fv1 = calcSACoefficient(q_a.data(), mu, sacs_a.data()); + if (q_a[dim+2]<0) + fv1 = 0.0; adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; - applyViscousScaling(d, mu_Re_SA, Pr, q_a.data(), Dw_a.data(), + applyViscousScalingSA(d, mu_Re_SA, Pr, q_a.data(), Dw_a.data(), CDw_a.data()); adouble fn = calcSANegativeCoefficient(q_a.data(), mu, sacs_a.data()); diff --git a/src/physics/solver.cpp b/src/physics/solver.cpp index 89f61a63..f692b1b0 100644 --- a/src/physics/solver.cpp +++ b/src/physics/solver.cpp @@ -1193,6 +1193,10 @@ unique_ptr AbstractSolver::constructNonlinearSolver( { nonlin_solver.reset(new mfem::NewtonSolver(comm)); } + else if (solver_type == "inexact-newton") + { + nonlin_solver.reset(new InexactNewton(comm)); + } else { throw MachException("Unsupported nonlinear solver type!\n" diff --git a/test/unit/euler_test_data.cpp b/test/unit/euler_test_data.cpp index 1983ce94..99926343 100644 --- a/test/unit/euler_test_data.cpp +++ b/test/unit/euler_test_data.cpp @@ -104,14 +104,14 @@ void randBaselinePertSA(const mfem::Vector &x, mfem::Vector &u) u(dim + 1) = rhoe * (1.0 + scale * uniform_rand(gen)); for (int di = 0; di < dim; ++di) { - u(di + 1) = rhou[di] * (1.0 + scale * uniform_rand(gen)); + u(di + 1) = rhou[di]* (1.0 + scale * uniform_rand(gen)); } if (entvar) { mfem::Vector q(u); mach::calcEntropyVars(q.GetData(), u.GetData()); } - u(dim + 2) = 3.0 * (uniform_rand(gen) - 0.1); + u(dim + 2) = 3.0 *(1.0 + scale*(uniform_rand(gen) - 0.1)); } // explicit instantiation of the templated function above template void randBaselinePertSA<1, true>(const mfem::Vector &x, mfem::Vector &u); @@ -125,7 +125,10 @@ void randState(const mfem::Vector &x, mfem::Vector &u) { for (int i = 0; i < u.Size(); ++i) { - u(i) = 2.0 * uniform_rand(gen) - 1.0; + if(u.Size() > 3 && (i == 1 || i == 2)) + u(i) = 0.01*(2.0 * uniform_rand(gen) - 1.0); + else + u(i) = 2.0 * uniform_rand(gen) - 1.0; } } diff --git a/test/unit/test_rans_fluxes.cpp b/test/unit/test_rans_fluxes.cpp index a5505adb..6a9a3bec 100644 --- a/test/unit/test_rans_fluxes.cpp +++ b/test/unit/test_rans_fluxes.cpp @@ -7,6 +7,113 @@ #include "rans_integ.hpp" #include "euler_test_data.hpp" +//rotational vortex state +void uvort(const mfem::Vector &x, mfem::Vector& u); + +//exact vorticity magnitude field +double s_proj(const mfem::Vector &x); + +TEMPLATE_TEST_CASE_SIG("SA calcVorticity Accuracy", "[SAVorticity]", + ((bool entvar), entvar), false) +{ + using namespace mfem; + using namespace euler_data; + + const int dim = 2; // templating is hard here because mesh constructors + int num_state = dim + 3; + + // generate a mesh + int num_edge = 1; + std::unique_ptr mesh(new Mesh(num_edge, num_edge, Element::TRIANGLE, + true /* gen. edges */, 1.0, 1.0, true)); + + for (int p = 1; p <= 2; ++p) + { + DYNAMIC_SECTION("Vorticity correct for degree p = " << p) + { + std::unique_ptr fec( + new SBPCollection(p, dim)); + std::unique_ptr fes(new FiniteElementSpace( + mesh.get(), fec.get(), num_state, Ordering::byVDIM)); + std::unique_ptr fess(new FiniteElementSpace( + mesh.get(), fec.get())); + + // state gridfunction + GridFunction u(fes.get()); + + // vorticity magnitude gridfunctions + GridFunction s_exact(fess.get()); + GridFunction s_comp(fess.get()); + + FunctionCoefficient s_coeff(*s_proj); + VectorFunctionCoefficient u_coeff(dim+3, *uvort); + s_exact.ProjectCoefficient(s_coeff); + u.ProjectCoefficient(u_coeff); + + Array vdofs, vdofs2; + Vector el_x, el_y; + const FiniteElement *fe; + ElementTransformation *T; + + // element loop + for (int i = 0; i < fes->GetNE(); i++) + { + + fe = fes->GetFE(i); + fes->GetElementVDofs(i, vdofs); + fess->GetElementVDofs(i, vdofs2); + T = fes->GetElementTransformation(i); + const SBPFiniteElement &sbp = dynamic_cast(*fe); + int num_nodes = sbp.GetDof(); + int num_states = dim+3; + u.GetSubVector(vdofs, el_x); + s_comp.GetSubVector(vdofs2, el_y); + + Vector curl(3); + Vector Duidi(num_states); + DenseMatrix Dui(num_states, dim); + DenseMatrix uc(el_x.GetData(), num_nodes, num_states); + DenseMatrix ucc; ucc = uc; + ucc.Transpose(); + + // dof loop + for (int j = 0; j < num_nodes; j++) + { + const IntegrationPoint &node = fe->GetNodes().IntPoint(j); + T->SetIntPoint(&node); + + Duidi = 0.0; Dui = 0.0; + for (int di = 0; di < dim; di++) + { + sbp.multStrongOperator(di, j, ucc, Duidi); + Dui.SetCol(di, Duidi); + } + + // get vorticity at dof + mach::calcVorticity(Dui.GetData(), T->InverseJacobian().GetData(), + curl.GetData()); + + double S = curl.Norml2(); + + el_y(j) = S; + } + // insert into vorticity gridfunction + s_comp.AddElementVector(vdofs2, el_y); + + } + + // compare vorticity + for (int j = 0; j < s_comp.Size(); j++) + { + std::cout << "Computed: " << s_comp(j) << std::endl; + std::cout << "Exact: " << s_exact(j) << std::endl; + //REQUIRE(s_comp(j) == Approx(s_exact(j)).margin(1e-10)); + } + } + } +} + + TEMPLATE_TEST_CASE_SIG("SA calcVorticity Jacobian", "[SAVorticity]", ((int dim), dim), 2, 3) { @@ -148,7 +255,7 @@ TEST_CASE("SA S derivatives", "[SASource]") const int dim = 2; mfem::Vector q(dim+3); mfem::Vector grad(dim); - double delta = 1e-6; + double delta = 1e-5; grad(0) = 0.0; grad(1) = 0.0; @@ -176,14 +283,16 @@ TEST_CASE("SA S derivatives", "[SASource]") std::vector grad_a(grad.Size()); adept::set_values(grad_a.data(), grad.Size(), grad.GetData()); + double sval = 0.0; + DYNAMIC_SECTION("Jacobians w.r.t S is correct") { // compute the jacobian stack.new_recording(); mfem::Vector dSrcdu(1); adouble S_a; - S_a.set_value(2e-5); - adouble d_a = 10.1; + S_a.set_value(sval); + adouble d_a = 1.001; adouble mu_a = 1.1; adouble Re_a = 5000000; adouble src;// = mach::calcSADestruction(q_a.data(), S_a, mu_a, d_a, Re_a, sacs_a.data()); @@ -199,8 +308,8 @@ TEST_CASE("SA S derivatives", "[SASource]") // } // else { - // src += mach::calcSAProduction( - // q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; + src += mach::calcSAProduction( + q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; src += mach::calcSADestruction( q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; } @@ -209,8 +318,8 @@ TEST_CASE("SA S derivatives", "[SASource]") stack.dependent(src); stack.jacobian(dSrcdu.GetData()); - double Sp = 2e-5 + delta; double Sm = 2e-5 - delta; - double d = 10.1; + double Sp = sval + delta; double Sm = sval - delta; + double d = 1.001; double mu = 1.1; double Re = 5000000; @@ -229,8 +338,8 @@ TEST_CASE("SA S derivatives", "[SASource]") // } // else { - // srcp += mach::calcSAProduction( - // q.GetData(), mu, d, Sp, Re, sacs.GetData())/Re; + srcp += mach::calcSAProduction( + q.GetData(), mu, d, Sp, Re, sacs.GetData())/Re; srcp += mach::calcSADestruction( q.GetData(), mu, d, Sp, Re, sacs.GetData())/Re; } @@ -246,8 +355,8 @@ TEST_CASE("SA S derivatives", "[SASource]") // } // else { - // srcm += mach::calcSAProduction( - // q.GetData(), mu, d, Sm, Re, sacs.GetData())/Re; + srcm += mach::calcSAProduction( + q.GetData(), mu, d, Sm, Re, sacs.GetData())/Re; srcm += mach::calcSADestruction( q.GetData(), mu, d, Sm, Re, sacs.GetData())/Re; } @@ -522,4 +631,28 @@ TEST_CASE("SA q derivatives", "[SAVorticity]") REQUIRE(jac_v == Approx(jac_fd).margin(1e-10)); } -} \ No newline at end of file +} + +void uvort(const mfem::Vector &x, mfem::Vector& q) +{ + // q.SetSize(4); + // Vector u(4); + q.SetSize(5); + mfem::Vector u(5); + double om = 4; + u = 0.0; + u(0) = 1.0; + u(1) = -om*x(1); + u(2) = om*x(0); + u(3) = 1.0/(1.4) + 0.5*1.5*1.5; + u(4) = u(0)*3; + + q = u; +} + +double s_proj(const mfem::Vector &x) +{ + double om = 4; + + return 2*om; +} \ No newline at end of file diff --git a/test/unit/test_rans_integ.cpp b/test/unit/test_rans_integ.cpp index 9d4994a5..6d56a4bc 100644 --- a/test/unit/test_rans_integ.cpp +++ b/test/unit/test_rans_integ.cpp @@ -1,5 +1,5 @@ #include - +#include #include "catch.hpp" #include "mfem.hpp" #include "euler_integ.hpp" @@ -352,7 +352,7 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Jacobian", "[SASlipWallBC]", { using namespace euler_data; // copy the data into mfem vectors for convenience - std::cout << "Slip-Wall State Jac" << std::endl; +// std::cout << "Slip-Wall State Jac" << std::endl; mfem::Vector nrm(dim); mfem::Vector x(dim); mfem::Vector q(dim + 3); mfem::Vector flux(dim + 3); @@ -414,8 +414,8 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Jacobian", "[SASlipWallBC]", // compare each component of the matrix-vector products for (int i = 0; i < dim + 3; ++i) { - std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; - std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; + // std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + // std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); } } @@ -426,7 +426,7 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Dw Jacobian", "[SASlipWallBC]", { using namespace euler_data; // copy the data into mfem vectors for convenience - std::cout << "Slip-Wall Dw Jac" << std::endl; +// std::cout << "Slip-Wall Dw Jac" << std::endl; mfem::Vector nrm(dim); mfem::Vector x(dim); mfem::Vector q(dim + 3); mfem::Vector flux(dim + 3); @@ -499,8 +499,8 @@ TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Dw Jacobian", "[SASlipWallBC]", // compare each component of the matrix-vector products for (int i = 0; i < dim + 3; ++i) { - std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; - std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; + // std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + // std::cout << "AN " << i << " Deriv: " << jac_v[i] << std::endl; REQUIRE(jac_v[i] == Approx(jac_v_fd[i]).margin(1e-10)); } } @@ -620,11 +620,14 @@ TEMPLATE_TEST_CASE_SIG("SAViscous Dw Jacobian", "[SAViscous]", using namespace euler_data; // copy the data into mfem vectors for convenience mfem::Vector q(dim + 3); + mfem::Vector qr(dim + 2); mfem::Vector scale(dim + 3); mfem::Vector scale_plus_2(dim + 3); mfem::Vector scale_minus_2(dim + 3); mfem::DenseMatrix Dw(delw_data, dim+3, dim); + mfem::DenseMatrix Dwr(delw_data, dim+2, dim); mfem::Vector v(dim + 3); + mfem::Vector vr(dim + 2); mfem::Vector jac_v(dim + 3); mfem::DenseMatrix adjJ(dim, dim); mfem::DenseMatrix jac_scale_2(dim + 3, dim + 3); @@ -632,12 +635,16 @@ TEMPLATE_TEST_CASE_SIG("SAViscous Dw Jacobian", "[SAViscous]", double Re = 1000000; double Pr = 1; mfem::Vector sacs(13); Dw = 0.5; + Dwr = 0.5; q(0) = rho; + qr(0) = rho; q(dim + 1) = rhoe; + qr(dim + 1) = rhoe; q(dim + 2) = 4; for (int di = 0; di < dim; ++di) { q(di + 1) = rhou[di]; + qr(di + 1) = rhou[di]; Dw(dim+2, di) = 0.7; } // create SA parameter vector @@ -646,27 +653,41 @@ TEMPLATE_TEST_CASE_SIG("SAViscous Dw Jacobian", "[SAViscous]", sacs(di) = sa_params[di]; } // create perturbation vector - for (int di = 0; di < dim + 3; ++di) + for (int di = 0; di < dim + 2; ++di) { v(di) = vec_pert[di]; + vr(di) = vec_pert[di]; } + v(dim+2) = vec_pert[dim+2]; // perturbed vectors mfem::DenseMatrix Dw_plus(Dw), Dw_minus(Dw); adept::Stack diff_stack; mach::SAViscousIntegrator saviscousinteg(diff_stack, Re, Pr, sacs, 1.0); - + mach::ESViscousIntegrator esviscousinteg(diff_stack, Re, Pr, 1.0); + + for (int di = 0; di < dim; ++di) { DYNAMIC_SECTION("Jacobians w.r.t Dw is correct, dir"< mat_vec_jac(dim); + std::vector mat_vec_jac_ns(dim); for (int d = 0; d < dim; ++d) { mat_vec_jac[d].SetSize(dim+3); + mat_vec_jac_ns[d].SetSize(dim+2); } // compute the jacobian saviscousinteg.applyScalingJacDw(di, v, q, Dw, mat_vec_jac); - + esviscousinteg.applyScalingJacDw(di, vr, qr, Dwr, mat_vec_jac_ns); + + stringstream nssolname; stringstream ranssolname; + nssolname << "visc_jac_rans"; ranssolname << "visc_jac_ns"; + std::ofstream matlabns(nssolname.str()); matlabns.precision(15); + std::ofstream matlabrans(ranssolname.str()); matlabrans.precision(15); + mat_vec_jac[0].PrintMatlab(matlabrans); + mat_vec_jac_ns[0].PrintMatlab(matlabns); + for (int d = 0; d < dim; ++d) { // perturb one column of delw everytime @@ -794,7 +815,6 @@ TEMPLATE_TEST_CASE_SIG("SALPS Jacobian", "[SALPS]", } } -#if 0 TEST_CASE("SAViscousIntegrator::AssembleElementGrad", "[SAViscousIntegrator]") { using namespace mfem; @@ -862,7 +882,6 @@ TEST_CASE("SAViscousIntegrator::AssembleElementGrad", "[SAViscousIntegrator]") } } } -#endif TEST_CASE("SANoSlipAdiabaticWallBC::AssembleElementGrad", "[SANoSlipAdiabaticWallBC]") @@ -942,7 +961,6 @@ TEST_CASE("SANoSlipAdiabaticWallBC::AssembleElementGrad", "[SANoSlipAdiabaticWal } } -#if 0 TEST_CASE("SAViscousSlipWallBC::AssembleElementGrad", "[SAViscousSlipWallBC]") { using namespace mfem; @@ -1012,16 +1030,16 @@ TEST_CASE("SAViscousSlipWallBC::AssembleElementGrad", "[SAViscousSlipWallBC]") for (int i = 0; i < jac_v.Size(); ++i) { - std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; - std::cout << "AN " << i << " Deriv: " << jac_v(i) << std::endl; + // std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + // std::cout << "AN " << i << " Deriv: " << jac_v(i) << std::endl; REQUIRE(jac_v(i) == Approx(jac_v_fd(i)).margin(1e-10)); } } } } -#endif -#if 0 + + TEST_CASE("SAFarFieldBC::AssembleElementGrad", "[SAFarFieldBC]") { using namespace mfem; @@ -1091,14 +1109,13 @@ TEST_CASE("SAFarFieldBC::AssembleElementGrad", "[SAFarFieldBC]") for (int i = 0; i < jac_v.Size(); ++i) { - std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; - std::cout << "AN " << i << " Deriv: " << jac_v(i) << std::endl; + // std::cout << "FD " << i << " Deriv: " << jac_v_fd[i] << std::endl; + // std::cout << "AN " << i << " Deriv: " << jac_v(i) << std::endl; REQUIRE(jac_v(i) == Approx(jac_v_fd(i)).margin(1e-10)); } } } } -#endif TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") { @@ -1117,11 +1134,14 @@ TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") sacs(di) = sa_params[di]; } - // generate a 2 element mesh - int num_edge = 2; + // generate a 8 element mesh + int num_edge = 3; std::unique_ptr mesh(new Mesh(num_edge, num_edge, Element::TRIANGLE, true /* gen. edges */, 1.0, 1.0, true)); - for (int p = 1; p <= 2; ++p) + + + + for (int p = 1; p <= 1; ++p) { DYNAMIC_SECTION("...for degree p = " << p) { @@ -1129,22 +1149,33 @@ TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") new SBPCollection(p, dim)); std::unique_ptr fes(new FiniteElementSpace( mesh.get(), fec.get(), num_state, Ordering::byVDIM)); + std::unique_ptr fesx(new FiniteElementSpace( + mesh.get(), fec.get(), 2, Ordering::byVDIM)); + mesh->EnsureNodes(); + GridFunction dist(fes.get()); - auto walldist = [](const Vector &x) - { - return 0.0001*x(1) + 0.00001; - }; - FunctionCoefficient wall_coeff(walldist); + GridFunction x_nodes(fesx.get()); + auto walldist = [](const Vector &x) + { + return x(1); + }; + auto coord = [](const Vector &x, Vector &u) + { + u = x; + }; + FunctionCoefficient wall_coeff(walldist); + VectorFunctionCoefficient x_coord(2, coord); dist.ProjectCoefficient(wall_coeff); - //dist = 0.00001; + x_nodes.ProjectCoefficient(x_coord); + //dist = 1.0000; NonlinearForm res(fes.get()); double Re = 5000000.0; - res.AddDomainIntegrator(new mach::SASourceIntegrator(diff_stack, dist, Re, sacs, 1.0)); + res.AddDomainIntegrator(new mach::SASourceIntegrator(diff_stack, dist, Re, sacs, 1.0, -1.0, 1.0, 1.0)); // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, uinit);//randBaselinePertSA<2>); + VectorFunctionCoefficient pert(num_state, randBaselinePertSA<2>); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies @@ -1152,8 +1183,14 @@ TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") VectorFunctionCoefficient v_rand(num_state, randState); v.ProjectCoefficient(v_rand); - // evaluate the Ja;cobian and compute its product with v + // evaluate the Jacobian and compute its product with v Operator &Jac = res.GetGradient(q); + + // stringstream nssolname; + // nssolname << "jac_test"; + // std::ofstream matlab(nssolname.str()); matlab.precision(15); + // Jac.PrintMatlab(matlab); + GridFunction jac_v(fes.get()); Jac.Mult(v, jac_v); @@ -1166,17 +1203,33 @@ TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") jac_v_fd -= r; jac_v_fd /= (2 * delta); + GridFunction error(fes.get()); + //jac_v.Size()); + error = jac_v; + error -= jac_v_fd; + + // res.Mult(q, r); + // ofstream sol_ofs("why.vtk"); + // sol_ofs.precision(14); + // mesh->PrintVTK(sol_ofs, 1); + // r.SaveVTK(sol_ofs, "jac_error", 1); + + std::cout.precision(17); + std::cout << "Error Norm: " << error.Norml2() << std::endl; for (int i = 0; i < jac_v.Size(); ++i) { + int n = i/5; + std::cout << "Node Coord: "<< x_nodes(0+2*n) <<", "<< x_nodes(1+2*n) < Date: Mon, 19 Oct 2020 15:21:05 -0400 Subject: [PATCH 16/28] reverted some CMakelists --- sandbox/CMakeLists.txt | 26 +++++++++++++------------- test/unit/CMakeLists.txt | 30 +++++++++++++++--------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/sandbox/CMakeLists.txt b/sandbox/CMakeLists.txt index 1fd3cf77..2cbb2ddb 100644 --- a/sandbox/CMakeLists.txt +++ b/sandbox/CMakeLists.txt @@ -14,22 +14,22 @@ function(create_sandbox source_list) endfunction(create_sandbox) set(SANDBOX_SRCS - #steady_vortex - #steady_vortex_adjoint - #unsteady_vortex - #viscous_shock - #airfoil_steady - #airfoil_chaotic - #magnetostatic_box - #magnetostatic_motor - #magnetostatic_wire - #navier_stokes_mms - #thermal_square - #joule_wire + steady_vortex + steady_vortex_adjoint + unsteady_vortex + viscous_shock + airfoil_steady + airfoil_chaotic + magnetostatic_box + magnetostatic_motor + magnetostatic_wire + navier_stokes_mms + thermal_square + joule_wire ns_walltest rans_freestream rans_walltest - #gf_error + gf_error ) create_sandbox("${SANDBOX_SRCS}") diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index ddf540ed..6368c215 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -13,21 +13,21 @@ endfunction(create_tests) # group together all fluids related tests set(FLUID_TEST_SRCS - #test_euler_fluxes - #test_euler_integ - #test_euler_assemble - #test_euler_sens_integ - #test_evolver - #test_inexact_newton - #test_navier_stokes_fluxes - #test_navier_stokes_integ - #test_navier_stokes_assemble - #test_reconstruction - #test_sbp_fe - #test_viscous_integ - #test_utils - #test_thermal_integ - #test_res_integ + test_euler_fluxes + test_euler_integ + test_euler_assemble + test_euler_sens_integ + test_evolver + test_inexact_newton + test_navier_stokes_fluxes + test_navier_stokes_integ + test_navier_stokes_assemble + test_reconstruction + test_sbp_fe + test_viscous_integ + test_utils + test_thermal_integ + test_res_integ test_rans_fluxes test_rans_integ ) From 8824e740fd29184796dae3aa0c9de36efa9312e0 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Mon, 19 Oct 2020 15:26:48 -0400 Subject: [PATCH 17/28] reverted some ns changes --- src/physics/fluidflow/navier_stokes.cpp | 24 ------------------- src/physics/fluidflow/navier_stokes.hpp | 2 -- .../fluidflow/navier_stokes_fluxes.hpp | 2 +- 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/src/physics/fluidflow/navier_stokes.cpp b/src/physics/fluidflow/navier_stokes.cpp index 5c3e3df5..316e413a 100644 --- a/src/physics/fluidflow/navier_stokes.cpp +++ b/src/physics/fluidflow/navier_stokes.cpp @@ -1,5 +1,4 @@ #include -#include #include "navier_stokes.hpp" #include "navier_stokes_integ.hpp" @@ -225,16 +224,6 @@ void NavierStokesSolver::addOutputs() } } -static void pert(const Vector &x, Vector& p); - -template -void NavierStokesSolver::iterationHook(int iter, - double t, double dt) -{ - this->checkJacobian(pert); - this->printSolution("ns_last", 0); -} - template void NavierStokesSolver::getViscousInflowState(Vector &q_in) { @@ -269,19 +258,6 @@ void NavierStokesSolver::getViscousOutflowState(Vector &q_out) } } -std::default_random_engine gen(std::random_device{}()); -std::uniform_real_distribution normal_rand(-1.0,1.0); - -// perturbation function used to check the jacobian in each iteration -void pert(const Vector &x, Vector& p) -{ - p.SetSize(4); - for (int i = 0; i < 4; i++) - { - p(i) = normal_rand(gen); - } -} - // explicit instantiation template class NavierStokesSolver<1>; template class NavierStokesSolver<2>; diff --git a/src/physics/fluidflow/navier_stokes.hpp b/src/physics/fluidflow/navier_stokes.hpp index 032f0f83..6fbfe343 100644 --- a/src/physics/fluidflow/navier_stokes.hpp +++ b/src/physics/fluidflow/navier_stokes.hpp @@ -44,8 +44,6 @@ class NavierStokesSolver : public EulerSolver /// Create `output` based on `options` and add approporiate integrators virtual void addOutputs() override; - virtual void iterationHook(int iter, double t, double dt) override; - /// Set the state corresponding to the inflow boundary /// \param[in] q_in - state corresponding to the inflow void getViscousInflowState(mfem::Vector &q_in); diff --git a/src/physics/fluidflow/navier_stokes_fluxes.hpp b/src/physics/fluidflow/navier_stokes_fluxes.hpp index 9b994046..f8d7e1ed 100644 --- a/src/physics/fluidflow/navier_stokes_fluxes.hpp +++ b/src/physics/fluidflow/navier_stokes_fluxes.hpp @@ -235,7 +235,7 @@ template void calcNoSlipDualFlux(const xdouble *dir, xdouble mu, double Pr, const xdouble *q, xdouble *fluxes) { - int num_state = dim + 3; + int num_state = dim + 2; // zero out the fluxes, since applyCijMatrix accummulates for (int i = 0; i < num_state*dim; ++i) fluxes[i] = 0.0; From f45640429398bd6b1e7148c3a4446c190ac8ff27 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Mon, 19 Oct 2020 16:06:55 -0400 Subject: [PATCH 18/28] fixing tests after merge 1 --- sandbox/CMakeLists.txt | 1 + sandbox/steady_vortex.cpp | 7 +- sandbox/steady_vortex_adjoint.cpp | 2 +- sandbox/steady_vortex_options.json | 2 +- sandbox/surface_distance.cpp | 54 + sandbox/surface_distance.mesh | 1793 +++++++++++++++++++++ sandbox/surface_distance_options.json | 2 + src/common/CMakeLists.txt | 1 + src/common/default_options.cpp | 2 +- src/common/surface.hpp | 99 ++ src/common/surface_def.hpp | 305 ++++ src/physics/fluidflow/euler_fluxes.hpp | 4 +- src/physics/res_integ.cpp | 33 +- src/physics/solver.cpp | 17 +- src/physics/thermal/temp_integ.cpp | 63 +- src/physics/thermal/temp_integ.hpp | 19 +- src/utils/kdtree.hpp | 318 ++++ src/utils/mach_types.hpp | 2 +- test/regression/test_steady_vortex.cpp | 30 +- test/unit/CMakeLists.txt | 1 + test/unit/euler_test_data.cpp | 30 +- test/unit/euler_test_data.hpp | 4 +- test/unit/test_euler_assemble.cpp | 56 +- test/unit/test_euler_sens_integ.cpp | 8 +- test/unit/test_navier_stokes_assemble.cpp | 32 +- test/unit/test_rans_integ.cpp | 18 +- test/unit/test_res_integ.cpp | 8 +- test/unit/test_surface.cpp | 452 ++++++ test/unit/test_thermal_integ.cpp | 2 +- 29 files changed, 3215 insertions(+), 150 deletions(-) create mode 100644 sandbox/surface_distance.cpp create mode 100644 sandbox/surface_distance.mesh create mode 100644 sandbox/surface_distance_options.json create mode 100644 src/common/surface.hpp create mode 100644 src/common/surface_def.hpp create mode 100644 src/utils/kdtree.hpp create mode 100644 test/unit/test_surface.cpp diff --git a/sandbox/CMakeLists.txt b/sandbox/CMakeLists.txt index 2cbb2ddb..14b6f31f 100644 --- a/sandbox/CMakeLists.txt +++ b/sandbox/CMakeLists.txt @@ -25,6 +25,7 @@ set(SANDBOX_SRCS magnetostatic_wire navier_stokes_mms thermal_square + surface_distance joule_wire ns_walltest rans_freestream diff --git a/sandbox/steady_vortex.cpp b/sandbox/steady_vortex.cpp index c78dd9e9..7d36e477 100644 --- a/sandbox/steady_vortex.cpp +++ b/sandbox/steady_vortex.cpp @@ -71,7 +71,7 @@ int main(int argc, char *argv[]) // Parse command-line options OptionsParser args(argc, argv); - int degree = 2.0; + int degree = 2; int nx = 1; int ny = 1; args.AddOption(&options_file, "-o", "--options", @@ -118,6 +118,7 @@ int main(int argc, char *argv[]) double drag = abs(solver->calcOutput("drag") - (-1 / mach::euler::gamma)); double entropy = solver->calcOutput("entropy"); + out->precision(15); *out << "\nfinal residual norm = " << res_error; *out << "\n|| rho_h - rho ||_{L^2} = " << l2_error << endl; *out << "\nDrag error = " << drag << endl; @@ -192,8 +193,8 @@ void uexact(const Vector &x, Vector& q) double a = sqrt(euler::gamma*press/rho); u(0) = rho; - u(1) = rho*a*Ma*sin(theta); - u(2) = -rho*a*Ma*cos(theta); + u(1) = -rho*a*Ma*sin(theta); + u(2) = rho*a*Ma*cos(theta); u(3) = press/euler::gami + 0.5*rho*a*a*Ma*Ma; if (entvar == false) diff --git a/sandbox/steady_vortex_adjoint.cpp b/sandbox/steady_vortex_adjoint.cpp index 488f1111..a0e3fc80 100644 --- a/sandbox/steady_vortex_adjoint.cpp +++ b/sandbox/steady_vortex_adjoint.cpp @@ -44,7 +44,7 @@ int main(int argc, char *argv[]) // Parse command-line options OptionsParser args(argc, argv); - int map_degree = 2.0; + int map_degree = 2; int nx = 1; int ny = 1; args.AddOption(&options_file, "-o", "--options", diff --git a/sandbox/steady_vortex_options.json b/sandbox/steady_vortex_options.json index 3f73ae4c..10f4a537 100644 --- a/sandbox/steady_vortex_options.json +++ b/sandbox/steady_vortex_options.json @@ -54,7 +54,7 @@ "saveresults":false, "outputs": { - "entropy": true, + "entropy": true, "drag": [0, 0, 0, 1] } } diff --git a/sandbox/surface_distance.cpp b/sandbox/surface_distance.cpp new file mode 100644 index 00000000..fa8825dc --- /dev/null +++ b/sandbox/surface_distance.cpp @@ -0,0 +1,54 @@ +/// Solve the steady isentropic vortex problem on a quarter annulus +#include +#include + +#include "mfem.hpp" +#include "surface.hpp" + +int main(int argc, char *argv[]) +{ + using namespace std; + using namespace mfem; + using namespace mach; + + // Initialize MPI + MPI_Init(&argc, &argv); + + // import the airfoil mesh + const char *mesh_file = "surface_distance.mesh"; + Mesh mesh(mesh_file, 1, 1); + + // Construct the surface object + Array bdr_attr_marker(4); + bdr_attr_marker[0] = 0; + bdr_attr_marker[1] = 0; + bdr_attr_marker[2] = 1; + bdr_attr_marker[3] = 1; + Surface<2> surf(mesh, bdr_attr_marker); + + // This lambda function transforms from (r,\theta) space to (x,y) space + auto dist_fun = [&surf](const Vector &x) + { + return surf.calcDistance(x); + }; + + // Needed just to construct the FunctionCoefficient + auto dist_fun_rev = [](const Vector &x, double dist_bar, Vector &x_bar) + { + }; + + // + int order = 1; + int dim = 2; + H1_FECollection fec(order, dim); + FiniteElementSpace fes(&mesh, &fec); + GridFunction u(&fes); + FunctionCoefficient dist(dist_fun, dist_fun_rev); + u.ProjectCoefficient(dist); + + ofstream sol_ofs("surface_distance.vtk"); + sol_ofs.precision(14); + mesh.PrintVTK(sol_ofs, 1); + u.SaveVTK(sol_ofs, "distance", 1); + sol_ofs.close(); +} diff --git a/sandbox/surface_distance.mesh b/sandbox/surface_distance.mesh new file mode 100644 index 00000000..d362de05 --- /dev/null +++ b/sandbox/surface_distance.mesh @@ -0,0 +1,1793 @@ +MFEM mesh v1.0 + +# +# MFEM Geometry Types (see mesh/geom.hpp): +# +# POINT = 0 +# SEGMENT = 1 +# TRIANGLE = 2 +# SQUARE = 3 +# TETRAHEDRON = 4 +# CUBE = 5 +# PRISM = 6 +# + +dimension +2 + +elements +1102 +1 2 0 1 76 +1 2 18 149 150 +1 2 7 8 78 +1 2 150 75 18 +1 2 8 9 80 +1 2 18 19 149 +1 2 9 10 82 +1 2 148 75 150 +1 2 10 11 84 +1 2 1 2 85 +1 2 28 99 147 +1 2 147 27 28 +1 2 146 74 148 +1 2 145 73 146 +1 2 17 0 90 +1 2 144 72 145 +1 2 92 19 20 +1 2 93 20 21 +1 2 143 71 144 +1 2 71 143 70 +1 2 142 70 143 +1 2 11 12 97 +1 2 70 142 69 +1 2 141 69 142 +1 2 69 141 68 +1 2 140 13 134 +1 2 12 13 140 +1 2 139 68 141 +1 2 68 139 67 +1 2 138 67 139 +1 2 67 138 66 +1 2 137 66 138 +1 2 66 137 65 +1 2 136 65 137 +1 2 65 136 64 +1 2 64 135 63 +1 2 134 14 87 +1 2 13 14 134 +1 2 133 63 135 +1 2 62 63 133 +1 2 132 62 133 +1 2 61 62 132 +1 2 132 131 61 +1 2 60 61 131 +1 2 60 131 130 +1 2 59 60 130 +1 2 59 130 129 +1 2 129 58 59 +1 2 58 129 128 +1 2 128 57 58 +1 2 57 128 127 +1 2 57 127 56 +1 2 56 127 126 +1 2 56 126 55 +1 2 55 126 125 +1 2 55 125 54 +1 2 54 125 124 +1 2 54 124 53 +1 2 53 124 123 +1 2 53 123 52 +1 2 135 64 136 +1 2 52 123 122 +1 2 52 122 51 +1 2 51 122 121 +1 2 51 121 50 +1 2 50 121 120 +1 2 50 120 49 +1 2 3 118 119 +1 2 2 3 119 +1 2 72 144 71 +1 2 73 145 72 +1 2 74 146 73 +1 2 4 101 118 +1 2 148 74 75 +1 2 92 149 19 +1 2 3 4 118 +1 2 1 85 76 +1 2 117 45 46 +1 2 77 23 24 +1 2 24 83 77 +1 2 78 8 80 +1 2 116 45 117 +1 2 79 21 22 +1 2 22 94 79 +1 2 80 9 82 +1 2 116 44 45 +1 2 6 7 81 +1 2 81 7 78 +1 2 10 84 82 +1 2 115 44 116 +1 2 83 24 25 +1 2 25 95 83 +1 2 11 97 84 +1 2 115 43 44 +1 2 2 119 85 +1 2 114 43 115 +1 2 47 48 86 +1 2 86 48 98 +1 2 14 15 87 +1 2 87 15 88 +1 2 15 16 88 +1 2 88 16 89 +1 2 16 17 89 +1 2 89 17 90 +1 2 0 76 90 +1 2 114 42 43 +1 2 5 6 91 +1 2 91 6 81 +1 2 20 93 92 +1 2 113 42 114 +1 2 21 79 93 +1 2 113 41 42 +1 2 94 22 23 +1 2 23 77 94 +1 2 95 25 26 +1 2 26 96 95 +1 2 96 26 27 +1 2 27 147 96 +1 2 12 140 97 +1 2 112 41 113 +1 2 49 98 48 +1 2 49 120 98 +1 2 99 28 29 +1 2 29 100 99 +1 2 100 29 30 +1 2 30 102 100 +1 2 4 5 101 +1 2 5 91 101 +1 2 30 31 102 +1 2 31 103 102 +1 2 31 32 103 +1 2 104 103 32 +1 2 32 33 104 +1 2 104 33 105 +1 2 33 34 105 +1 2 105 34 106 +1 2 35 106 34 +1 2 106 35 107 +1 2 107 35 36 +1 2 107 36 108 +1 2 108 36 37 +1 2 108 37 109 +1 2 109 37 38 +1 2 109 38 110 +1 2 110 38 39 +1 2 110 39 111 +1 2 111 39 40 +1 2 111 40 112 +1 2 112 40 41 +1 2 144 206 143 +1 2 82 210 211 +1 2 80 82 211 +1 2 203 90 197 +1 2 85 178 202 +1 2 145 201 144 +1 2 196 200 148 +1 2 92 204 199 +1 2 84 208 210 +1 2 76 202 197 +1 2 176 46 47 +1 2 200 209 146 +1 2 97 194 208 +1 2 97 140 194 +1 2 143 193 142 +1 2 192 141 142 +1 2 191 139 141 +1 2 190 138 139 +1 2 140 134 189 +1 2 188 137 138 +1 2 187 136 137 +1 2 186 135 207 +1 2 185 132 133 +1 2 134 87 184 +1 2 183 131 132 +1 2 182 130 131 +1 2 207 136 187 +1 2 206 144 201 +1 2 79 152 205 +1 2 205 93 79 +1 2 93 205 204 +1 2 204 92 93 +1 2 89 90 203 +1 2 76 85 202 +1 2 201 145 209 +1 2 200 146 148 +1 2 119 177 178 +1 2 119 118 177 +1 2 46 176 117 +1 2 118 101 175 +1 2 92 199 149 +1 2 174 115 116 +1 2 198 80 211 +1 2 173 113 114 +1 2 78 80 198 +1 2 172 111 112 +1 2 207 135 136 +1 2 90 76 197 +1 2 110 171 109 +1 2 199 196 149 +1 2 170 107 108 +1 2 169 106 107 +1 2 168 105 106 +1 2 149 196 150 +1 2 99 162 195 +1 2 99 195 147 +1 2 102 165 164 +1 2 140 189 194 +1 2 193 143 206 +1 2 192 142 193 +1 2 191 141 192 +1 2 190 139 191 +1 2 189 134 184 +1 2 188 138 190 +1 2 84 97 208 +1 2 187 137 188 +1 2 146 209 145 +1 2 186 133 135 +1 2 82 84 210 +1 2 185 133 186 +1 2 184 87 155 +1 2 185 183 132 +1 2 196 148 150 +1 2 131 183 182 +1 2 130 182 181 +1 2 130 181 129 +1 2 129 181 180 +1 2 83 151 77 +1 2 83 154 151 +1 2 152 79 94 +1 2 94 159 152 +1 2 81 78 153 +1 2 153 78 198 +1 2 95 154 83 +1 2 95 160 154 +1 2 87 88 155 +1 2 155 88 156 +1 2 88 89 156 +1 2 156 89 203 +1 2 47 86 157 +1 2 129 180 128 +1 2 91 81 158 +1 2 158 81 153 +1 2 159 94 77 +1 2 77 151 159 +1 2 96 160 95 +1 2 96 161 160 +1 2 147 161 96 +1 2 147 195 161 +1 2 100 162 99 +1 2 100 164 162 +1 2 101 91 163 +1 2 163 91 158 +1 2 128 180 179 +1 2 102 164 100 +1 2 103 165 102 +1 2 166 165 103 +1 2 104 166 103 +1 2 166 104 167 +1 2 105 167 104 +1 2 167 105 168 +1 2 168 106 169 +1 2 179 127 128 +1 2 169 107 170 +1 2 85 119 178 +1 2 118 175 177 +1 2 101 163 175 +1 2 326 332 265 +1 2 264 330 331 +1 2 452 448 391 +1 2 265 272 201 +1 2 237 271 200 +1 2 270 206 272 +1 2 244 116 117 +1 2 175 245 177 +1 2 170 108 222 +1 2 246 168 242 +1 2 266 176 47 +1 2 269 187 254 +1 2 222 109 171 +1 2 177 247 178 +1 2 202 178 248 +1 2 170 243 169 +1 2 242 168 169 +1 2 208 260 268 +1 2 197 262 267 +1 2 203 197 267 +1 2 47 157 266 +1 2 271 265 209 +1 2 263 237 199 +1 2 210 268 264 +1 2 204 213 263 +1 2 204 263 199 +1 2 228 235 120 +1 2 249 251 182 +1 2 202 248 262 +1 2 252 185 186 +1 2 253 189 184 +1 2 234 155 156 +1 2 233 203 267 +1 2 254 187 188 +1 2 197 202 262 +1 2 255 194 189 +1 2 261 191 258 +1 2 256 207 269 +1 2 194 255 260 +1 2 257 188 190 +1 2 259 193 270 +1 2 258 191 192 +1 2 258 192 259 +1 2 193 259 192 +1 2 257 190 261 +1 2 194 260 208 +1 2 164 241 226 +1 2 261 190 191 +1 2 166 225 165 +1 2 163 224 175 +1 2 223 166 167 +1 2 222 108 109 +1 2 256 186 207 +1 2 255 189 253 +1 2 254 188 257 +1 2 253 184 216 +1 2 252 186 256 +1 2 176 244 117 +1 2 210 264 211 +1 2 182 251 181 +1 2 252 250 185 +1 2 265 201 209 +1 2 250 183 185 +1 2 250 249 183 +1 2 183 249 182 +1 2 178 247 248 +1 2 177 245 247 +1 2 246 167 168 +1 2 219 127 179 +1 2 175 224 245 +1 2 174 116 244 +1 2 208 268 210 +1 2 242 169 243 +1 2 269 207 187 +1 2 225 241 165 +1 2 270 193 206 +1 2 237 200 196 +1 2 271 209 200 +1 2 165 241 164 +1 2 201 272 206 +1 2 154 236 240 +1 2 154 240 151 +1 2 152 212 205 +1 2 152 239 212 +1 2 205 213 204 +1 2 205 212 213 +1 2 214 198 211 +1 2 214 211 264 +1 2 162 215 195 +1 2 162 226 215 +1 2 216 184 155 +1 2 216 155 234 +1 2 181 217 180 +1 2 181 251 217 +1 2 180 218 179 +1 2 217 218 180 +1 2 127 219 126 +1 2 218 219 179 +1 2 125 220 124 +1 2 159 238 239 +1 2 123 221 122 +1 2 398 452 365 +1 2 159 239 152 +1 2 151 240 238 +1 2 246 223 167 +1 2 238 159 151 +1 2 163 227 224 +1 2 196 199 237 +1 2 223 225 166 +1 2 160 230 236 +1 2 160 236 154 +1 2 164 226 162 +1 2 158 227 163 +1 2 227 158 232 +1 2 121 228 120 +1 2 268 321 330 +1 2 195 229 161 +1 2 195 215 229 +1 2 161 230 160 +1 2 161 229 230 +1 2 231 153 198 +1 2 231 198 214 +1 2 232 158 153 +1 2 232 153 231 +1 2 120 235 98 +1 2 156 203 233 +1 2 234 156 233 +1 2 235 86 98 +1 2 329 254 320 +1 2 307 233 291 +1 2 328 259 322 +1 2 308 327 157 +1 2 289 271 237 +1 2 305 304 225 +1 2 224 275 303 +1 2 170 302 243 +1 2 301 222 171 +1 2 125 300 220 +1 2 212 299 213 +1 2 213 299 298 +1 2 239 277 297 +1 2 289 326 271 +1 2 325 270 272 +1 2 309 242 243 +1 2 302 170 222 +1 2 245 310 247 +1 2 324 258 328 +1 2 323 261 324 +1 2 322 270 325 +1 2 309 312 242 +1 2 313 223 246 +1 2 260 317 321 +1 2 126 300 125 +1 2 320 257 323 +1 2 318 319 256 +1 2 318 256 269 +1 2 233 267 291 +1 2 255 315 317 +1 2 248 311 290 +1 2 316 250 252 +1 2 315 255 253 +1 2 281 314 249 +1 2 263 298 289 +1 2 315 253 279 +1 2 314 251 249 +1 2 319 316 252 +1 2 312 313 246 +1 2 317 260 255 +1 2 312 246 242 +1 2 318 269 329 +1 2 319 252 256 +1 2 267 262 288 +1 2 306 287 226 +1 2 320 254 257 +1 2 247 310 311 +1 2 260 321 268 +1 2 247 311 248 +1 2 322 259 270 +1 2 245 303 310 +1 2 323 257 261 +1 2 302 309 243 +1 2 324 261 258 +1 2 282 308 235 +1 2 86 308 157 +1 2 281 249 250 +1 2 307 234 233 +1 2 279 216 294 +1 2 325 272 332 +1 2 304 306 241 +1 2 326 265 271 +1 2 241 306 226 +1 2 327 266 157 +1 2 313 305 223 +1 2 235 308 86 +1 2 275 227 296 +1 2 328 258 259 +1 2 176 274 244 +1 2 329 269 254 +1 2 223 305 225 +1 2 268 330 264 +1 2 225 304 241 +1 2 264 331 214 +1 2 224 303 245 +1 2 332 272 265 +1 2 244 273 174 +1 2 274 273 244 +1 2 302 222 301 +1 2 126 219 300 +1 2 212 297 299 +1 2 227 275 224 +1 2 236 276 240 +1 2 236 280 276 +1 2 238 277 239 +1 2 238 278 277 +1 2 240 278 238 +1 2 240 276 278 +1 2 213 298 263 +1 2 279 253 216 +1 2 230 280 236 +1 2 230 285 280 +1 2 316 281 250 +1 2 239 297 212 +1 2 282 235 228 +1 2 296 232 283 +1 2 283 232 231 +1 2 283 231 284 +1 2 284 231 214 +1 2 284 214 331 +1 2 229 285 230 +1 2 229 286 285 +1 2 215 286 229 +1 2 215 287 286 +1 2 232 296 227 +1 2 226 287 215 +1 2 262 290 288 +1 2 295 217 251 +1 2 263 289 237 +1 2 314 295 251 +1 2 294 234 307 +1 2 262 248 290 +1 2 291 267 288 +1 2 294 216 234 +1 2 292 219 218 +1 2 293 292 218 +1 2 293 218 217 +1 2 295 293 217 +1 2 391 325 332 +1 2 288 290 361 +1 2 390 328 383 +1 2 317 379 389 +1 2 388 330 385 +1 2 121 345 228 +1 2 308 371 387 +1 2 308 387 327 +1 2 359 358 281 +1 2 299 365 364 +1 2 335 357 304 +1 2 386 325 391 +1 2 311 368 356 +1 2 321 389 385 +1 2 347 301 171 +1 2 367 302 301 +1 2 343 176 266 +1 2 310 368 311 +1 2 374 369 313 +1 2 173 114 346 +1 2 370 275 334 +1 2 384 324 390 +1 2 383 322 386 +1 2 382 323 384 +1 2 381 320 382 +1 2 378 380 318 +1 2 315 377 379 +1 2 381 378 329 +1 2 377 315 279 +1 2 319 376 316 +1 2 303 375 310 +1 2 302 372 309 +1 2 312 374 313 +1 2 309 373 312 +1 2 372 373 309 +1 2 367 372 302 +1 2 373 374 312 +1 2 347 110 111 +1 2 308 282 371 +1 2 303 370 375 +1 2 346 114 115 +1 2 122 221 345 +1 2 380 376 319 +1 2 344 294 339 +1 2 377 279 344 +1 2 275 370 303 +1 2 369 305 313 +1 2 329 378 318 +1 2 379 317 315 +1 2 176 343 274 +1 2 219 292 342 +1 2 318 380 319 +1 2 301 347 366 +1 2 320 381 329 +1 2 310 375 368 +1 2 323 382 320 +1 2 297 363 365 +1 2 322 383 328 +1 2 297 365 299 +1 2 324 384 323 +1 2 343 338 274 +1 2 321 385 330 +1 2 386 322 325 +1 2 358 337 314 +1 2 299 364 298 +1 2 277 354 363 +1 2 335 304 305 +1 2 277 363 297 +1 2 334 296 336 +1 2 330 388 331 +1 2 362 288 361 +1 2 389 321 317 +1 2 122 345 121 +1 2 328 390 324 +1 2 346 115 174 +1 2 392 391 332 +1 2 347 111 172 +1 2 332 326 392 +1 2 307 291 333 +1 2 333 291 362 +1 2 291 288 362 +1 2 296 334 275 +1 2 369 335 305 +1 2 290 356 361 +1 2 283 336 296 +1 2 336 283 351 +1 2 357 360 306 +1 2 337 295 314 +1 2 360 287 306 +1 2 338 273 274 +1 2 339 294 307 +1 2 339 307 333 +1 2 295 340 293 +1 2 337 340 295 +1 2 293 341 292 +1 2 340 341 293 +1 2 376 359 316 +1 2 316 359 281 +1 2 327 343 266 +1 2 281 358 314 +1 2 357 306 304 +1 2 344 279 294 +1 2 345 282 228 +1 2 290 311 356 +1 2 273 346 174 +1 2 280 352 355 +1 2 171 110 347 +1 2 508 443 442 +1 2 348 286 287 +1 2 360 348 287 +1 2 349 285 286 +1 2 348 349 286 +1 2 350 284 331 +1 2 350 331 388 +1 2 351 283 284 +1 2 284 350 351 +1 2 352 280 285 +1 2 285 349 352 +1 2 276 353 278 +1 2 276 355 353 +1 2 278 354 277 +1 2 278 353 354 +1 2 355 276 280 +1 2 445 451 383 +1 2 361 356 424 +1 2 451 390 383 +1 2 425 334 395 +1 2 426 423 358 +1 2 359 426 358 +1 2 378 450 380 +1 2 390 449 384 +1 2 292 341 420 +1 2 452 391 392 +1 2 419 339 411 +1 2 447 381 382 +1 2 446 385 443 +1 2 433 417 357 +1 2 345 416 282 +1 2 448 445 386 +1 2 365 452 364 +1 2 364 392 394 +1 2 417 414 360 +1 2 423 413 337 +1 2 340 412 341 +1 2 448 386 391 +1 2 356 368 429 +1 2 430 301 366 +1 2 444 384 449 +1 2 389 442 443 +1 2 435 431 373 +1 2 370 425 432 +1 2 442 389 379 +1 2 418 433 335 +1 2 219 399 300 +1 2 380 441 376 +1 2 416 371 282 +1 2 447 440 381 +1 2 372 367 434 +1 2 376 439 359 +1 2 372 435 373 +1 2 375 432 436 +1 2 438 379 377 +1 2 431 437 374 +1 2 355 402 408 +1 2 438 377 397 +1 2 409 407 349 +1 2 441 439 376 +1 2 406 345 221 +1 2 350 404 405 +1 2 381 440 378 +1 2 437 369 374 +1 2 450 441 380 +1 2 375 436 368 +1 2 379 438 442 +1 2 434 435 372 +1 2 443 385 389 +1 2 433 357 335 +1 2 384 444 382 +1 2 370 432 375 +1 2 445 383 386 +1 2 346 400 173 +1 2 385 446 388 +1 2 219 342 399 +1 2 447 382 444 +1 2 373 431 374 +1 2 367 301 430 +1 2 397 377 344 +1 2 368 436 429 +1 2 395 336 410 +1 2 449 390 451 +1 2 393 343 327 +1 2 440 450 378 +1 2 289 298 394 +1 2 338 343 427 +1 2 364 452 392 +1 2 439 426 359 +1 2 326 289 394 +1 2 398 448 452 +1 2 327 387 393 +1 2 334 425 370 +1 2 298 364 394 +1 2 392 326 394 +1 2 356 429 424 +1 2 336 395 334 +1 2 354 396 363 +1 2 403 396 354 +1 2 397 344 419 +1 2 358 423 337 +1 2 363 398 365 +1 2 396 398 363 +1 2 300 399 220 +1 2 362 361 422 +1 2 401 346 273 +1 2 400 346 401 +1 2 338 401 273 +1 2 421 362 422 +1 2 352 402 355 +1 2 352 407 402 +1 2 353 403 354 +1 2 353 408 403 +1 2 388 404 350 +1 2 404 388 446 +1 2 333 362 421 +1 2 405 351 350 +1 2 292 420 342 +1 2 419 344 339 +1 2 437 418 369 +1 2 349 407 352 +1 2 418 335 369 +1 2 355 408 353 +1 2 409 349 348 +1 2 414 409 348 +1 2 351 410 336 +1 2 351 405 410 +1 2 339 333 411 +1 2 411 333 421 +1 2 413 412 340 +1 2 417 360 357 +1 2 345 406 416 +1 2 413 340 337 +1 2 387 371 415 +1 2 414 348 360 +1 2 480 410 463 +1 2 371 416 481 +1 2 411 458 479 +1 2 469 507 451 +1 2 482 395 480 +1 2 505 506 447 +1 2 505 444 504 +1 2 504 449 507 +1 2 473 483 423 +1 2 459 461 406 +1 2 123 459 221 +1 2 454 341 412 +1 2 503 450 440 +1 2 488 401 338 +1 2 471 456 393 +1 2 450 502 441 +1 2 484 419 479 +1 2 487 473 426 +1 2 501 442 438 +1 2 500 446 443 +1 2 493 486 433 +1 2 471 387 415 +1 2 498 487 439 +1 2 499 438 397 +1 2 441 498 439 +1 2 446 470 404 +1 2 436 432 497 +1 2 437 431 496 +1 2 465 468 396 +1 2 485 490 435 +1 2 469 451 445 +1 2 489 366 428 +1 2 436 497 491 +1 2 398 468 448 +1 2 424 429 495 +1 2 467 400 401 +1 2 493 433 418 +1 2 489 494 430 +1 2 494 434 367 +1 2 494 367 430 +1 2 403 465 396 +1 2 429 491 495 +1 2 490 496 431 +1 2 408 477 464 +1 2 432 455 497 +1 2 492 493 418 +1 2 502 498 441 +1 2 405 475 463 +1 2 499 397 484 +1 2 472 462 414 +1 2 399 459 220 +1 2 443 508 500 +1 2 438 499 501 +1 2 460 112 113 +1 2 503 502 450 +1 2 124 220 459 +1 2 496 492 437 +1 2 506 503 440 +1 2 172 112 460 +1 2 504 444 449 +1 2 400 460 173 +1 2 492 418 437 +1 2 456 343 393 +1 2 505 447 444 +1 2 506 440 447 +1 2 425 455 432 +1 2 396 468 398 +1 2 429 436 491 +1 2 507 449 451 +1 2 435 490 431 +1 2 442 501 508 +1 2 361 424 422 +1 2 366 489 430 +1 2 488 338 427 +1 2 124 459 123 +1 2 460 113 173 +1 2 342 420 453 +1 2 439 487 426 +1 2 486 417 433 +1 2 341 454 420 +1 2 455 425 482 +1 2 435 434 485 +1 2 397 419 484 +1 2 343 456 427 +1 2 483 413 423 +1 2 421 422 457 +1 2 458 411 421 +1 2 458 421 457 +1 2 459 399 461 +1 2 459 406 221 +1 2 428 460 400 +1 2 395 482 425 +1 2 371 481 415 +1 2 410 480 395 +1 2 419 411 479 +1 2 414 462 409 +1 2 413 483 478 +1 2 405 463 410 +1 2 478 412 413 +1 2 408 464 403 +1 2 464 465 403 +1 2 402 466 477 +1 2 407 466 402 +1 2 476 466 407 +1 2 428 400 467 +1 2 467 401 488 +1 2 465 469 468 +1 2 468 445 448 +1 2 468 469 445 +1 2 402 477 408 +1 2 470 446 500 +1 2 462 476 409 +1 2 409 476 407 +1 2 387 471 393 +1 2 472 414 417 +1 2 486 472 417 +1 2 404 470 475 +1 2 426 473 423 +1 2 342 453 474 +1 2 404 475 405 +1 2 547 544 553 +1 2 534 473 487 +1 2 546 553 503 +1 2 550 533 505 +1 2 552 465 532 +1 2 465 552 469 +1 2 500 551 470 +1 2 550 504 549 +1 2 536 478 483 +1 2 550 505 504 +1 2 537 513 535 +1 2 461 528 406 +1 2 562 582 533 +1 2 416 528 481 +1 2 537 472 486 +1 2 460 428 521 +1 2 536 479 509 +1 2 552 549 507 +1 2 534 536 473 +1 2 552 507 469 +1 2 514 485 434 +1 2 541 524 493 +1 2 537 482 513 +1 2 548 500 508 +1 2 545 534 487 +1 2 553 502 503 +1 2 460 521 172 +1 2 538 488 427 +1 2 547 508 501 +1 2 420 454 520 +1 2 491 543 540 +1 2 518 412 478 +1 2 546 503 506 +1 2 498 545 487 +1 2 424 495 519 +1 2 499 534 545 +1 2 501 499 544 +1 2 492 541 493 +1 2 502 544 498 +1 2 491 497 543 +1 2 490 543 496 +1 2 541 455 524 +1 2 543 497 541 +1 2 516 428 467 +1 2 543 492 496 +1 2 489 428 516 +1 2 544 499 545 +1 2 515 538 525 +1 2 498 544 545 +1 2 526 471 415 +1 2 533 546 506 +1 2 494 514 434 +1 2 553 544 502 +1 2 480 513 482 +1 2 501 544 547 +1 2 342 474 527 +1 2 508 547 548 +1 2 530 512 476 +1 2 511 475 517 +1 2 549 504 507 +1 2 542 490 485 +1 2 497 455 541 +1 2 551 548 571 +1 2 551 500 548 +1 2 527 528 461 +1 2 543 541 492 +1 2 495 491 540 +1 2 347 172 521 +1 2 456 525 538 +1 2 479 458 509 +1 2 466 510 477 +1 2 512 510 466 +1 2 456 538 427 +1 2 475 511 463 +1 2 455 482 537 +1 2 476 512 466 +1 2 513 480 529 +1 2 472 535 462 +1 2 537 486 524 +1 2 536 484 479 +1 2 473 536 483 +1 2 515 489 516 +1 2 537 535 472 +1 2 484 536 534 +1 2 470 517 475 +1 2 470 551 517 +1 2 499 484 534 +1 2 412 518 454 +1 2 457 422 519 +1 2 422 424 519 +1 2 420 520 453 +1 2 533 506 505 +1 2 428 366 521 +1 2 366 347 521 +1 2 464 531 532 +1 2 474 453 522 +1 2 458 457 523 +1 2 465 464 532 +1 2 455 537 524 +1 2 524 486 493 +1 2 471 525 456 +1 2 477 510 531 +1 2 526 415 481 +1 2 528 526 481 +1 2 399 527 461 +1 2 399 342 527 +1 2 406 528 416 +1 2 477 531 464 +1 2 463 529 480 +1 2 463 511 529 +1 2 462 530 476 +1 2 535 530 462 +1 2 515 569 570 +1 2 556 525 471 +1 2 457 519 568 +1 2 515 570 489 +1 2 569 515 554 +1 2 454 557 520 +1 2 564 551 587 +1 2 516 467 566 +1 2 566 467 488 +1 2 528 567 526 +1 2 569 539 570 +1 2 538 515 566 +1 2 565 494 539 +1 2 517 564 511 +1 2 566 488 538 +1 2 514 494 565 +1 2 494 570 539 +1 2 551 564 517 +1 2 563 550 549 +1 2 510 561 531 +1 2 563 562 550 +1 2 562 533 550 +1 2 560 485 514 +1 2 510 559 561 +1 2 560 542 485 +1 2 512 559 510 +1 2 518 557 454 +1 2 567 554 556 +1 2 471 526 556 +1 2 511 564 555 +1 2 565 578 581 +1 2 554 525 556 +1 2 494 489 570 +1 2 453 520 558 +1 2 453 558 522 +1 2 515 525 554 +1 2 566 515 516 +1 2 526 567 556 +1 2 580 561 559 +1 2 529 511 555 +1 2 580 587 562 +1 2 561 563 576 +1 2 563 549 576 +1 2 576 549 552 +1 2 575 557 518 +1 2 520 579 558 +1 2 579 573 558 +1 2 564 577 555 +1 2 574 495 540 +1 2 530 577 512 +1 2 561 576 531 +1 2 573 522 558 +1 2 531 576 532 +1 2 518 509 575 +1 2 520 557 579 +1 2 557 578 579 +1 2 559 564 580 +1 2 577 564 559 +1 2 574 542 560 +1 2 581 514 565 +1 2 542 574 540 +1 2 580 563 561 +1 2 578 573 579 +1 2 573 565 539 +1 2 576 552 532 +1 2 580 562 563 +1 2 522 569 572 +1 2 577 559 512 +1 2 573 578 565 +1 2 582 546 533 +1 2 474 522 572 +1 2 523 557 575 +1 2 560 514 581 +1 2 535 513 586 +1 2 529 586 513 +1 2 535 586 530 +1 2 587 551 571 +1 2 572 567 585 +1 2 528 527 585 +1 2 577 586 555 +1 2 474 572 585 +1 2 584 560 581 +1 2 568 574 584 +1 2 585 567 528 +1 2 587 571 582 +1 2 583 582 571 +1 2 583 553 546 +1 2 582 583 546 +1 2 548 583 571 +1 2 527 474 585 +1 2 574 560 584 +1 2 578 584 581 +1 2 586 577 530 +1 2 583 548 547 +1 2 580 564 587 +1 2 586 529 555 +1 2 547 553 583 +1 2 562 587 582 +1 2 578 588 584 +1 2 557 588 578 +1 2 568 584 588 +1 2 588 457 568 +1 2 557 523 588 +1 2 457 588 523 +1 2 519 574 568 +1 2 540 543 542 +1 2 569 573 539 +1 2 518 536 509 +1 2 575 458 523 +1 2 573 569 522 +1 2 574 519 495 +1 2 569 567 572 +1 2 458 575 509 +1 2 536 518 478 +1 2 567 569 554 +1 2 542 543 490 + +boundary +76 +1 1 0 1 +1 1 7 8 +4 1 75 18 +1 1 8 9 +3 1 18 19 +2 1 9 10 +2 1 10 11 +1 1 1 2 +3 1 27 28 +2 1 17 0 +3 1 19 20 +3 1 20 21 +4 1 70 71 +2 1 11 12 +4 1 69 70 +4 1 68 69 +2 1 12 13 +4 1 67 68 +4 1 66 67 +4 1 65 66 +4 1 64 65 +4 1 63 64 +2 1 13 14 +4 1 62 63 +4 1 61 62 +4 1 60 61 +4 1 59 60 +4 1 58 59 +4 1 57 58 +4 1 56 57 +4 1 55 56 +4 1 54 55 +4 1 53 54 +4 1 52 53 +4 1 51 52 +4 1 50 51 +4 1 49 50 +1 1 2 3 +4 1 71 72 +4 1 72 73 +4 1 73 74 +4 1 74 75 +1 1 3 4 +3 1 45 46 +3 1 23 24 +3 1 21 22 +3 1 44 45 +1 1 6 7 +3 1 24 25 +3 1 43 44 +4 1 47 48 +2 1 14 15 +2 1 15 16 +2 1 16 17 +3 1 42 43 +1 1 5 6 +3 1 41 42 +3 1 22 23 +3 1 25 26 +3 1 26 27 +4 1 48 49 +3 1 28 29 +3 1 29 30 +1 1 4 5 +3 1 30 31 +3 1 31 32 +3 1 32 33 +3 1 33 34 +3 1 34 35 +3 1 35 36 +3 1 36 37 +3 1 37 38 +3 1 38 39 +3 1 39 40 +3 1 40 41 +3 1 46 47 + +vertices +589 +2 +30 -7.3478808e-15 +28.190779 10.260604 +22.981333 19.283628 +15 25.980762 +5.2094453 29.544233 +-5.2094453 29.544233 +-15 25.980762 +-22.981333 19.283628 +-28.190779 10.260604 +-30 3.6739404e-15 +-28.190779 -10.260604 +-22.981333 -19.283628 +-15 -25.980762 +-5.2094453 -29.544233 +5.2094453 -29.544233 +15 -25.980762 +22.981333 -19.283628 +28.190779 -10.260604 +0 0 +0.00030951465 0.0030920588 +0.0010688278 0.0057437099 +0.0029095357 0.0093867356 +0.0067001887 0.014067863 +0.013549658 0.01967445 +0.024786408 0.026044923 +0.042093008 0.033005223 +0.067609932 0.040295784 +0.10387301 0.04746843 +0.15347929 0.053798759 +0.21837132 0.058311474 +0.29877851 0.060007003 +0.39218584 0.05828196 +0.49303622 0.05329995 +0.59374585 0.045987626 +0.68679864 0.037630818 +0.76680744 0.029398807 +0.83145051 0.022079438 +0.88110181 0.01604522 +0.9177763 0.011346706 +0.94409137 0.0078421154 +0.96258329 0.005309663 +0.97538825 0.0035210785 +0.98416532 0.0022781516 +0.99013961 0.001424089 +0.99418682 0.0008417611 +0.99691972 0.00044683168 +0.99876111 0.00017993209 +1 -3.3306691e-17 +0.99691972 -0.00044683168 +0.99418682 -0.0008417611 +0.99013961 -0.001424089 +0.98416532 -0.0022781516 +0.97538825 -0.0035210785 +0.96258329 -0.005309663 +0.94409137 -0.0078421154 +0.9177763 -0.011346706 +0.88110181 -0.01604522 +0.83145051 -0.022079438 +0.76680744 -0.029398807 +0.68679864 -0.037630818 +0.59374585 -0.045987626 +0.49303622 -0.05329995 +0.39218584 -0.05828196 +0.29877851 -0.060007003 +0.21837132 -0.058311474 +0.15347929 -0.053798759 +0.10387301 -0.04746843 +0.067609932 -0.040295784 +0.042093008 -0.033005223 +0.024786408 -0.026044923 +0.013549658 -0.01967445 +0.0067001887 -0.014067863 +0.0029095357 -0.0093867356 +0.0010688278 -0.0057437099 +0.00030951465 -0.0030920588 +5.1126916e-05 -0.0012504947 +20.380763 3.5934376 +0.013401152 0.033196327 +-17.910551 10.340624 +0.00060773987 0.015281824 +-20.36717 3.5912724 +-13.293628 15.842556 +-20.367174 -3.5912749 +0.027111807 0.045389705 +-17.910556 -10.340625 +17.921376 10.346119 +0.99886745 -0.0026449929 +7.076084 -19.437542 +13.300236 -15.848879 +17.921376 -10.34612 +20.380763 -3.5934377 +-7.0731939 19.433329 +-0.001926315 0.0051676507 +-0.0013326884 0.009354501 +0.0050925181 0.023197303 +0.048244957 0.059889089 +0.079297623 0.076603899 +-13.293629 -15.842556 +0.99599279 -0.0030942602 +0.18198111 0.11318899 +0.25710609 0.12904898 +0.00070138625 20.681469 +0.34692448 0.13948406 +0.44678454 0.14201812 +0.54954473 0.1357545 +0.64739171 0.1218505 +0.73394993 0.10306507 +0.80579862 0.082853872 +0.86243445 0.064279333 +0.90431856 0.047578813 +0.93457801 0.034054559 +0.95609338 0.023949907 +0.97091826 0.016395029 +0.98118783 0.011207673 +0.98808657 0.0074543345 +0.99283126 0.0049567503 +0.99599185 0.0031885493 +0.99811831 0.0020577134 +7.0760836 19.437541 +13.300235 15.848878 +0.99287961 -0.0047824034 +0.98814384 -0.0074720193 +0.98113582 -0.011113746 +0.97100462 -0.01649339 +0.95603672 -0.023752168 +0.93435677 -0.033000809 +0.90552985 -0.047749838 +0.86329911 -0.064793252 +0.80554863 -0.082640272 +0.73394732 -0.1030518 +0.64739171 -0.1218505 +0.54954473 -0.1357545 +0.44678454 -0.14201812 +0.34692448 -0.13948406 +0.00070147136 -20.681469 +0.25710609 -0.12904898 +0.18198111 -0.11318899 +0.12306142 -0.094880385 +0.079297623 -0.076603899 +0.048244957 -0.059889089 +-7.073194 -19.433329 +0.027111807 -0.045389705 +0.013401152 -0.033196327 +0.0050926067 -0.023197205 +0.00061018188 -0.015279659 +-0.0013039545 -0.0093390924 +-0.00174884 -0.0051648861 +0.12306142 0.094880385 +-0.0015857957 -0.0023514112 +-0.0019753452 0.0017959092 +-0.0012715137 -0.00055958834 +0.0086982143 0.052286028 +-0.0042061023 0.023370028 +-10.856695 9.1087845 +0.023788042 0.072811259 +7.1222686 -12.307514 +10.922109 -9.1514865 +1.0014699 -0.0021945342 +-7.0854655 12.269607 +7.6437198e-05 0.03589998 +0.047758148 0.097874672 +0.083743606 0.12732331 +0.20455811 0.19169125 +-2.4568643 13.952032 +0.2921614 0.21821498 +0.39438875 0.23376999 +0.50390973 0.23450397 +0.61139389 0.21997099 +0.70828479 0.19337984 +0.789078 0.16060418 +0.8561563 0.13134363 +0.93200366 0.069757949 +0.97015805 0.033123332 +0.98809506 0.015407989 +0.99606826 0.0067280097 +2.474943 13.967116 +1.0005756 0.0022850049 +7.1222143 12.307441 +10.92205 9.1514184 +0.84835157 -0.12455095 +0.78915103 -0.15967348 +0.70829954 -0.19335143 +0.61139389 -0.21997099 +0.50390973 -0.23450397 +2.4749621 -13.967146 +0.39438875 -0.23376999 +0.2921614 -0.21821498 +0.13514573 -0.15970604 +0.083743606 -0.12732331 +-2.456863 -13.952034 +0.047758148 -0.097874672 +0.023788042 -0.072811259 +0.0086982146 -0.052286028 +7.6659536e-05 -0.035899662 +-7.0854679 -12.269606 +0.13514573 0.15970604 +-0.0035478639 -0.00011320666 +14.273683 -1.3722064e-06 +-13.318103 4.8471415 +-0.0048745659 0.0034586463 +-0.0044600797 -0.0032858325 +-0.0056463993 -0.014146156 +13.408768 4.8748236 +13.408791 -4.874847 +-0.0055958793 0.0078933226 +-0.0057050217 0.014202726 +-0.0041998942 -0.023362992 +0.20455811 -0.19169125 +-10.856711 -9.1087841 +-0.0053463698 -0.007703223 +-13.318141 -4.8471575 +-14.173025 -1.1016008e-05 +-0.013152217 0.020117704 +-0.011421961 0.010944975 +-9.4423926 1.6644446 +0.13939105 0.24173674 +3.3464063 -9.0465228 +0.78090457 -0.25303058 +0.85779874 -0.1962322 +0.91926878 -0.11548745 +0.95456357 -0.047234553 +0.98109641 -0.022633558 +0.90662974 0.10302862 +0.57146597 0.32938024 +0.026886337 9.5841374 +0.44843409 0.3382163 +0.22310283 0.28825429 +-3.2740244 8.9939422 +0.99311132 -0.0097640111 +0.078597132 0.19244631 +0.037690785 0.14687245 +-8.302896 4.7915114 +-6.162069 7.3363769 +8.478927 -4.8638452 +6.2829846 -7.418742 +0.99718823 -0.0061617236 +0.011896018 0.10817057 +-0.007093995 0.00044003829 +-0.010911887 0.052143397 +-0.01339911 0.033502396 +-0.0032496295 0.076877911 +0.32845778 0.32316128 +0.78024627 0.2532712 +0.84780041 0.20428138 +0.99866414 0.0047537808 +3.3460421 9.0460316 +0.68515317 0.29874677 +6.2823445 7.4179461 +8.4785292 4.8633914 +0.57146597 -0.32938024 +0.44843409 -0.3382163 +0.68516578 -0.29875249 +0.32845778 -0.32316128 +0.02695946 -9.5842069 +0.078597132 -0.19244631 +-3.2740228 -8.9939433 +0.22310283 -0.28825429 +0.037690785 -0.14687245 +-0.0032496289 -0.076877911 +-0.010911816 -0.052143088 +-6.1620865 -7.3363697 +0.011896018 -0.10817057 +9.6543322 1.6928465 +-0.0092614608 0.0050189371 +-9.4424227 -1.6644999 +-0.011175551 -0.01063747 +1.0025168 0.00014073281 +9.6543845 -1.6929334 +-8.3029497 -4.791509 +0.13939105 -0.24173674 +-0.013394848 -0.033495506 +-0.0088347486 -0.0042761466 +-0.013098385 -0.020050183 +0.99926824 0.0085582076 +1.0016801 0.005416826 +-1.0869661 6.3110899 +-0.025477704 0.10694692 +-0.02964694 0.045126042 +-0.03056636 0.071778769 +1.2116382 -6.3468161 +-0.012074725 0.15209336 +0.51836618 -0.45098357 +0.99802387 -0.011972942 +-4.9257721 4.1220067 +-6.0428553 2.1960774 +0.014728967 0.20862691 +0.062039531 0.27500222 +0.13693557 0.34473376 +6.7744068 -9.0275655e-05 +-0.012111298 0.00072021485 +6.3593841 2.2694763 +6.3598102 -2.2704506 +0.94880302 -0.21400751 +0.87085562 -0.29572621 +3.3873798 -5.6396771 +0.77632424 -0.36681965 +-3.2126083 5.544538 +-0.025499996 0.026523592 +-0.015718575 0.0059853354 +-0.020557621 0.013891946 +0.93555845 -0.062890493 +0.9620958 0.11006167 +0.92653934 0.18771932 +1.2107155 6.3461444 +0.37410563 0.44496346 +0.51836619 0.45098359 +0.2425302 0.40605794 +5.1764249 -4.2351514 +1.0017644 -0.0060830985 +0.86813452 0.29048737 +3.3851473 5.6370159 +5.1743104 4.2324315 +0.77627039 0.36623075 +0.65748696 0.42233512 +0.65747962 -0.42235641 +-1.0868387 -6.3111476 +0.37410563 -0.44496346 +-3.2126165 -5.544531 +0.13693557 -0.34473376 +0.2425302 -0.40605794 +0.014728967 -0.20862691 +-4.9258225 -4.1219863 +-0.029646713 -0.045129355 +-0.012074725 -0.15209336 +-0.025477463 -0.10694705 +-0.025477484 -0.026500949 +-0.015364776 -0.0052165152 +1.0043575 -0.0022295007 +-0.030565746 -0.071781176 +0.062039531 -0.27500222 +-6.042906 -2.1961124 +-6.431423 -4.9419288e-05 +-0.020381011 -0.013658562 +4.1035684 -2.2406037 +-1.4421913 3.983943 +0.4405483 0.58522315 +-2.7508578 3.2472746 +0.7697169 -0.50763022 +1.0039362 0.0086817763 +3.061735 -3.3695468 +0.89160583 -0.42043467 +0.99041901 -0.32576076 +1.009934 -0.13675813 +1.0052237 0.0028520826 +1.674615 -4.0585194 +0.99006357 -0.016781809 +0.99410722 0.011315013 +0.95964122 0.054954393 +0.033100838 0.38120751 +-0.024836606 0.28687877 +-4.2134133 0.73943823 +-3.7054082 2.1258917 +-0.05252676 0.20589372 +-0.061500194 0.094201764 +-0.055375571 0.057493307 +-0.061781938 0.14228887 +4.1039921 2.2373327 +0.27133187 0.5507548 +0.61520422 -0.56901306 +0.44055008 -0.58522331 +0.13139571 0.47596303 +4.6716523 0.78610137 +4.6702665 -0.78529582 +-0.044955702 0.031667276 +-0.024919973 0.005030597 +-0.034314963 0.015120881 +0.99116044 0.069609875 +1.0183137 0.17706546 +3.0583232 3.365287 +0.61521264 0.56899934 +0.098153972 4.2664576 +1.004371 -0.012132964 +0.97667104 0.29581412 +0.89057759 0.41380174 +0.76989562 0.50708586 +1.6706885 4.0565446 +0.27133243 -0.55075481 +0.099353121 -4.2667924 +0.033101023 -0.38120752 +-1.4420698 -3.9839593 +0.13139627 -0.47596305 +-0.024836607 -0.28687877 +-0.05252676 -0.20589372 +-0.055342377 -0.057615482 +-0.061780795 -0.14228922 +-3.7054661 -2.125887 +-0.044955454 -0.031883141 +1.0069534 -0.0062210628 +-4.2134392 -0.73952923 +-2.750889 -3.24725 +-0.061480657 -0.094223132 +-0.034294338 -0.015442357 +-0.02476542 -0.0049181728 +1.0083855 -0.0018670729 +-0.018172062 0.00044346452 +-1.4049295 2.4003473 +-0.074196054 0.033132265 +0.70729991 -2.7854436 +-0.054076183 0.011419917 +0.97404481 -0.07575261 +0.99509183 0.021035403 +1.0007831 0.013838043 +-0.11773402 0.18290328 +-0.093314965 0.069477648 +-2.8242461 5.9744127e-05 +-2.6551288 0.95418287 +0.99168368 -0.030010605 +-0.11575014 0.27273151 +-0.10739809 0.11796251 +-0.085443607 0.38909326 +-2.1710762 1.7875778 +2.6156137 -1.9027792 +1.0456171 -0.47208715 +0.9128409 -0.57996563 +-0.00736597 0.52276708 +1.01031 -0.011227181 +1.0005474 -0.021196497 +0.1309391 0.64764383 +0.54437628 0.74180036 +1.7662109 -2.4993822 +1.0622984 -0.23224188 +3.1821057 -1.034104 +3.4045468 0.010742804 +0.75120116 -0.68372817 +3.2061695 1.0461353 +-0.41101866 2.7465129 +0.5444354 -0.74181425 +1.0086264 0.0074471943 +1.0022186 0.040216885 +2.6202533 1.9018716 +1.0231267 0.10751313 +0.91490857 0.57403358 +0.70326665 2.7845669 +0.32362067 0.72867911 +1.0982714 0.2709833 +1.0318049 0.43168047 +1.7611151 2.4964148 +0.75139698 0.68339419 +-0.41010296 -2.746626 +0.32363692 -0.72868005 +-0.085442741 -0.38909343 +0.13095513 -0.64764429 +-1.4048615 -2.4003316 +-2.171045 -1.7874672 +-0.11773111 -0.18290832 +-0.073727993 -0.03480413 +-2.6550307 -0.95403376 +-0.11574922 -0.27273195 +-0.0541164 -0.012986219 +-0.10733489 -0.1179983 +-0.0073576945 -0.52276732 +-0.092787049 -0.070123284 +-0.037710594 -0.00080260689 +1.1058991 -0.14678504 +1.1636144 -0.35518393 +0.19082126 1.772102 +1.0115089 0.002519648 +2.4151379 -0.3115739 +2.1188177 -0.94999334 +0.97444143 -0.037817885 +0.98380703 0.025976191 +0.99425056 -0.048168093 +-0.17133654 0.528722 +-1.6210397 0.91219041 +-0.14576596 0.079833566 +-0.12024322 0.025252931 +-0.20208438 0.2296202 +1.0054791 0.022646821 +-0.081342192 -0.0026672794 +-0.113971 -0.032882197 +-1.8338954 -0.3122475 +1.0141765 -0.0055155347 +-0.055328765 0.73050064 +0.72031221 -0.90423487 +1.0544589 -0.087566018 +-1.8371942 0.31753468 +-0.21028176 0.358831 +-0.17431324 0.1403309 +1.0649024 -0.66336897 +1.6806171 -1.4698908 +-1.2088744 1.379486 +1.0091807 -0.019161812 +-0.58986652 1.6908828 +0.9228893 -0.77345264 +0.99952159 -1.7603124 +1.2032163 0.40839827 +0.15150559 0.88440828 +0.42335194 -0.94269017 +1.0079166 0.013232287 +1.0412685 0.06367793 +1.0916846 0.62890918 +1.6842049 1.4693107 +0.71980729 0.90403709 +0.42311586 0.94266409 +1.1182246 0.13291156 +2.2005825 1.006518 +0.92541867 0.77036886 +0.99447806 1.7590386 +0.15165497 -0.8844144 +0.19308272 -1.772351 +-1.6175804 -0.90885404 +-0.58941478 -1.6909007 +-0.055212602 -0.73050424 +-0.17129369 -0.52872506 +-0.17420332 -0.14049436 +-0.20197968 -0.22963338 +-0.21018923 -0.35883807 +-0.14465062 -0.08022692 +-1.2086719 -1.3792855 +1.5417145 -0.87795586 +-0.27329406 0.15759835 +-1.1639525 0.41368542 +-0.32841096 0.28442676 +-0.61696883 1.0042456 +1.2513976 0.23442616 +1.0399073 0.020832744 +1.0202155 0.032786796 +-1.2891234 0.019128993 +1.2777922 -0.58440623 +2.5340337 0.40570009 +1.201118 -0.22387537 +0.98137142 0.044260628 +1.123701 -0.071152751 +1.841092 -0.45908781 +0.23736663 1.1763983 +1.0220978 0.0025755469 +1.0177174 -0.017195544 +1.0142102 -0.072279848 +1.0138086 -0.03606554 +-0.96968873 0.76780411 +-0.34739641 0.47022519 +-0.21603551 0.0793957 +-0.17583792 0.031788819 +-0.32599766 -0.28466109 +0.60321556 -1.2372058 +-0.30284029 0.72959609 +1.103961 -1.0244385 +-0.12664955 1.1294764 +1.0159428 0.010633983 +1.1516208 0.050517845 +1.583417 0.87455488 +0.60154536 1.2369687 +1.3484021 0.60916917 +1.1078933 1.0237496 +-0.12599226 -1.1295057 +0.23801242 -1.1764478 +-0.34614182 -0.47042247 +-0.61666192 -1.0041536 +-0.96487752 -0.76464382 +-0.21531304 -0.080915486 +-0.27194298 -0.15773421 +-1.1076412 -0.36312875 +-0.1683699 -0.026869341 +-0.30263886 -0.72960268 +1.0411716 -0.0024499956 +-0.7624737 0.43943969 +1.0249032 -0.0092430062 +1.4345647 -0.31998443 +1.1973338 -0.12352385 +-0.43427751 0.16124328 +1.4399308 0.35053657 +-0.31499794 0.05883178 +-0.41628418 -0.16461622 +-0.31375062 -0.058547087 +-0.78071933 0.10669499 +1.2689004 0.091901433 +1.0159434 0.01901519 +1.0382403 -0.024622091 +2.0384024 0.12343414 +1.0925271 -0.0056367094 +1.0847235 0.04840815 +-0.73762427 -0.44203474 +1.065133 -0.047257282 +1.2334902 -0.02873952 +1.8041578 0.50261325 +1.6019056 -0.59879319 +-0.23414236 -0.0035290394 +-0.52132368 0.34467776 +1.4725658 -0.029125564 +1.313292 -0.15125219 +-0.46909669 7.1269316e-05 +1.4293037 0.16139353 +-0.50240049 -0.35009376 +-0.5639599 -0.60155638 +1.6752958 0.16580958 +1.0359354 -0.051765316 +-0.56659025 0.60133954 +-0.65159765 -0.17905681 +1.8069363 -0.13971066 diff --git a/sandbox/surface_distance_options.json b/sandbox/surface_distance_options.json new file mode 100644 index 00000000..4404dcdf --- /dev/null +++ b/sandbox/surface_distance_options.json @@ -0,0 +1,2 @@ +# This file is not used, but is needed because the sandbox examples expect each +# to have an options file \ No newline at end of file diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 45af63bc..829a12a4 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -19,6 +19,7 @@ target_sources(mach sbp_fe.hpp spline.cpp spline.hpp + surface.hpp ) target_include_directories(mach diff --git a/src/common/default_options.cpp b/src/common/default_options.cpp index 9df51ba3..04fce734 100644 --- a/src/common/default_options.cpp +++ b/src/common/default_options.cpp @@ -70,7 +70,7 @@ const nlohmann::json default_options { {"type", "hypreilu"}, // default preconditioner {"lev-fill", 1}, // ILU(k) fill level - {"ilu-type", 10}, // ILU type (see mfem HypreILU doc) + {"ilu-type", 0}, // ILU type (see mfem HypreILU doc) {"ilu-reorder", 1}, // 0 = no reordering, 1 = RCM {"printlevel", 0} // 0 = none, 1 = setup, 2 = solve, 3 = setup+solve }}, diff --git a/src/common/surface.hpp b/src/common/surface.hpp new file mode 100644 index 00000000..99409c40 --- /dev/null +++ b/src/common/surface.hpp @@ -0,0 +1,99 @@ +#ifndef MACH_SURFACE +#define MACH_SURFACE + +#include "mfem.hpp" + +#include "kdtree.hpp" +#include "utils.hpp" + +namespace mach +{ + +/// A geometry representation for distance calculations +/// \tparam dim - number of spatial dimensions (only `dim=2` is tested) +/// \warning The class has only been tested thoroughly for spatial degree +/// `dim=2` and degree p=1 and p=2 meshes. Higher degree meshes pose changes +/// because the number of local minimizers increases (i.e. more than one point +/// on the surface satisfies the first-order optimality conditions). For +/// dimension `dim=3`, the code is mostly ready, but we need to handle cases +/// where the closest point is on an edge and the optimization becomes +/// constrained. +/// \note When we need to differentiation this code, we will need to think +/// about how to account for the sensitivities with respect to the mesh nodes +/// (the sensitivity with respect to the points we provide to `calcDistance` is +/// straightforward). One option is to expand this class to act as the +/// underlying geometry parameterization. +template +class Surface +{ +public: + /// Construct a Surface object using a copy of an existing surface mesh + /// \param[in] ext_mesh - external mesh that defines surface directly + /// For this constructor, the spatial dimension must be one dimension + /// greater than the parametric dimension. + Surface(mfem::Mesh &ext_mesh); + + /// Construct a Surface object by extracting surface mesh from volume mesh + /// \param[in] vol_mesh - volume mesh to extract surface mesh from + /// \param[in] bdr_attr_marker - extract boundary elements with nonzero attr. + /// \note The definition was adapted from the mesh-explorer.cpp miniapp + Surface(mfem::Mesh &vol_mesh, mfem::Array &bdr_attr_marker); + + /// Destructor, which deletes the surface mesh object + ~Surface() { delete mesh; } + + /// Find the distance from `x` to the surface + /// \param[in] x - the point whose distance to the surface we want + /// \returns the distance + double calcDistance(const mfem::Vector &x); + + /// Find the distance from `x` to element defined by `trans`. + /// \param[in] trans - coordinate transformation for an element + /// \param[in] x - the point whose distance to the surface we want + /// \returns the distance + double solveDistance(mfem::ElementTransformation &trans, + const mfem::Vector &x); + +private: + // Some aliases to high template parameters + using Point = point; + using KDTree = kdtree; + + /// The surface is defined using an mfem Mesh object. + mfem::Mesh *mesh; + /// kd-tree used to narrow search of candidate elements + KDTree tree; + +#ifndef MFEM_THREAD_SAFE + /// reference space integration point used in Newton's method + mfem::IntegrationPoint ip; + /// reference space integration point used in Newton's method + mfem::IntegrationPoint ip_new; + /// The difference between the surface point and the target point + mfem::Vector res; + /// The gradient of the least-squares objective + mfem::Vector gradient; + /// The Newton step to update the parametric coordinates + mfem::Vector step; + /// Hessian vector product; here, Hessian refers to the coordinate transform + mfem::Vector hess_vec; + /// Storage for the reference coordinates + mfem::Vector xi; + /// Storage for the Hessian + mfem::DenseMatrix Hess; +#endif + + /// Constructs the kd-tree data structure based on stored *mesh object + void buildKDTree(); + + double solveConstrained() + { + throw MachException("solveConstrained is not implemented yet!"); + } +}; + +#include "surface_def.hpp" + +} // namespace mach + +#endif \ No newline at end of file diff --git a/src/common/surface_def.hpp b/src/common/surface_def.hpp new file mode 100644 index 00000000..c7d28e7f --- /dev/null +++ b/src/common/surface_def.hpp @@ -0,0 +1,305 @@ + +template +Surface::Surface(mfem::Mesh &ext_mesh) +{ + using namespace mfem; + if (dim != 2) + { + throw MachException("Surface class not tested for dim != 2."); + } + // check that this is a valid surface mesh, in terms of dimensions + MFEM_ASSERT(dim == ext_mesh.Dimension() + 1, "Invalid surface mesh."); + MFEM_ASSERT(dim == ext_mesh.SpaceDimension(), "Invalid surface mesh."); + mesh = new Mesh(ext_mesh); + // build out the kd-tree data structure + buildKDTree(); +} + +template +Surface::Surface(mfem::Mesh &vol_mesh, mfem::Array &bdr_attr_marker) +{ + using namespace mfem; + if (dim != 2) + { + throw MachException("Surface class not tested for dim != 2."); + } + // check that this is a valid volume mesh, in terms of dimensions + MFEM_ASSERT(dim == vol_mesh.Dimension(), "Invalid volume mesh."); + MFEM_ASSERT(dim == vol_mesh.SpaceDimension(), "Invalid volume mesh."); + + // Determine mapping from vertex to boundary vertex + Array v2v(vol_mesh.GetNV()); + v2v = -1; + int num_belem = 0; + for (int i = 0; i < vol_mesh.GetNBE(); i++) + { + Element *el = vol_mesh.GetBdrElement(i); + int bdr_attr = el->GetAttribute(); + if (bdr_attr_marker[bdr_attr-1] == 0) { continue; } + num_belem++; + int *v = el->GetVertices(); + int nv = el->GetNVertices(); + for (int j = 0; j < nv; j++) + { + v2v[v[j]] = 0; + } + } + // Count the number of unique boundary vertices and index them + int nbvt = 0; + for (int i = 0; i < v2v.Size(); i++) + { + if (v2v[i] == 0) + { + v2v[i] = nbvt++; + } + } + + // Create the new boundary mesh + mesh = new Mesh(vol_mesh.Dimension() - 1, nbvt, num_belem, 0, + vol_mesh.SpaceDimension()); + + // Copy vertices to the boundary mesh + nbvt = 0; + for (int i = 0; i < v2v.Size(); i++) + { + if (v2v[i] >= 0) + { + double *c = vol_mesh.GetVertex(i); + mesh->AddVertex(c); + nbvt++; + } + } + + // Copy elements to the boundary mesh + int bv[4]; + for (int i = 0; i < vol_mesh.GetNBE(); i++) + { + Element *el = vol_mesh.GetBdrElement(i); + int bdr_attr = el->GetAttribute(); + if (bdr_attr_marker[bdr_attr-1] == 0) { continue; } + + int *v = el->GetVertices(); + int nv = el->GetNVertices(); + + for (int j = 0; j < nv; j++) + { + bv[j] = v2v[v[j]]; + } + + switch (el->GetGeometryType()) + { + case Geometry::SEGMENT: + mesh->AddSegment(bv, el->GetAttribute()); + break; + case Geometry::TRIANGLE: + mesh->AddTriangle(bv, el->GetAttribute()); + break; + case Geometry::SQUARE: + mesh->AddQuad(bv, el->GetAttribute()); + break; + default: + break; /// This should not happen + } + + } + mesh->FinalizeTopology(); + + // Copy GridFunction describing nodes if present + if (vol_mesh.GetNodes()) + { + FiniteElementSpace *fes = vol_mesh.GetNodes()->FESpace(); + const FiniteElementCollection *fec = fes->FEColl(); + if (dynamic_cast(fec)) + { + FiniteElementCollection *fec_copy = + FiniteElementCollection::New(fec->Name()); + FiniteElementSpace *fes_copy = + new FiniteElementSpace(*fes, mesh, fec_copy); + GridFunction *bdr_nodes = new GridFunction(fes_copy); + bdr_nodes->MakeOwner(fec_copy); + + mesh->NewNodes(*bdr_nodes, true); + + Array vdofs; + Array bvdofs; + Vector v; + for (int i=0; iGetBdrElementVDofs(i, vdofs); + vol_mesh.GetNodes()->GetSubVector(vdofs, v); + + fes_copy->GetElementVDofs(i, bvdofs); + bdr_nodes->SetSubVector(bvdofs, v); + } + } + else + { + throw MachException("Discontinuous nodes not yet supported"); + } + } + // build out the kd-tree data structure + buildKDTree(); +} + +template +void Surface::buildKDTree() +{ + using namespace mfem; + if (mesh == nullptr) + { + throw MachException("Cannot call buildKDTree with empty mesh."); + } + mesh->EnsureNodes(); + mesh->GetNodes()->FESpace()->BuildDofToArrays(); + // loop over the mesh nodes, and add to the tree + GridFunction &nodes = *mesh->GetNodes(); + tree.set_size(nodes.FESpace()->GetNDofs()); + Vector node(mesh->SpaceDimension()); + for (int i = 0; i < nodes.FESpace()->GetNDofs(); ++i) + { + for (int d = 0; d < dim; d++) + { + node(d) = nodes(nodes.FESpace()->DofToVDof(i, d)); + } + tree.add_node(node, i); + } + tree.finalize(); +} + +template +double Surface::calcDistance(const mfem::Vector &x) +{ + using namespace mfem; + const FiniteElementSpace *fes = mesh->GetNodalFESpace(); + // search for the element whose center is closest to x + int i = tree.nearest(x); + int e = fes->GetElementForDof(i); + + // Apply Newton's method to find the closest point in ref space + ElementTransformation *trans = fes->GetElementTransformation(e); + double dist = solveDistance(*trans, x); + return dist; +} + +template +double Surface::solveDistance(mfem::ElementTransformation &trans, + const mfem::Vector &x) +{ + using namespace mfem; + +#ifdef MFEM_THREAD_SAFE + Vector res(dim), gradient(dim-1), step(dim-1), hess_vec(dim*(dim-1)/2); + Vector xi(dim-1); + IntegrationPoint ip, ip_new; + DenseMatrix Hess(dim-1); +#endif + res.SetSize(dim); + gradient.SetSize(dim-1); + step.SetSize(dim-1); + hess_vec.SetSize(dim*(dim-1)/2); + xi.SetSize(dim-1); + Hess.SetSize(dim-1); + + // use centroid for initial guess for reference coordinate + ip = Geometries.GetCenter(trans.GetGeometryType()); + trans.SetIntPoint(&ip); + + // evaluate the gradient of the least-squares objective + const DenseMatrix &Jac = trans.Jacobian(); + trans.Transform(ip, res); + res -= x; + Jac.MultTranspose(res, gradient); + +#ifdef MFEM_DEBUG + std::cout << std::endl; + std::cout << "Surface::solveDistance starting Newton iterations" + << std::endl; +#endif + + // Loop over Newton iterations + const int max_iter = 30; + const double tol = 1e-13; + bool hit_boundary = false; + for (int n = 0; n < max_iter; ++n) + { +#ifdef MFEM_DEBUG + std::cout << "\titeration " << n << ": optimality = " + << gradient.Norml2() << ": distance = " << res.Norml2() << std::endl; +#endif + // check for convergence + if (gradient.Norml2() < tol) + { + return res.Norml2(); + } + // compute the Hessian for the Newton step + MultAtB(Jac, Jac, Hess); + const DenseMatrix &d2Fdx2 = trans.Hessian(); + d2Fdx2.MultTranspose(res, hess_vec); + int idx = 0; + for (int i = 0; i < dim-1; ++i) + { + Hess(i,i) += hess_vec(idx); + ++idx; + for (int j = i+1; j < dim-1; ++j) + { + Hess(i,j) += hess_vec(idx); + Hess(j,i) += hess_vec(idx); + ++idx; + } + } + + // compute the Newton step + step.Set(-1.0, gradient); + bool success = LinearSolve(Hess, step.GetData()); + if (!success) + { + throw MachException("LinearSolve failed in Surface::solveDistance!"); + } + + // check that this is a descent direction; if not, use scaled negative + // gradient + if (InnerProduct(step, gradient) > 0.0) + { +#ifdef MFEM_DEBUG + std::cout << "\tNewton step was not a descent direction; " + << "switching to steepest descent." << std::endl; +#endif + step.Set(-0.1/gradient.Norml2(), gradient); + } + ip.Get(xi.GetData(), dim-1); + xi += step; + ip_new.Set(xi.GetData(), dim-1); + // check that point is not outside element geometry + if (!Geometries.ProjectPoint(trans.GetGeometryType(), ip, ip_new)) + { + // If we get here, ip_new was outside the element and had to be + // projected to boundary +#ifdef MFEM_DEBUG + std::cout << "\tNewton step was projected onto feasible space." + << std::endl; +#endif + if (hit_boundary) + { + return res.Norml2(); + // if this happens twice, we should move to constrained + // optimization for 3D (ie. surface embedded in 3D) + // solveConstrained(); + } + hit_boundary = true; + } + + // prepare for the next iteration + ip_new.Get(xi.GetData(), dim-1); + ip.Set(xi.GetData(), dim-1); + trans.SetIntPoint(&ip); + trans.Jacobian(); + trans.Transform(ip, res); + res -= x; + Jac.MultTranspose(res, gradient); + } + // If we get here, we exceeded the maximum number of Newton iterations + throw MachException("Newton solve failed in Surface::solveDistance!"); +} \ No newline at end of file diff --git a/src/physics/fluidflow/euler_fluxes.hpp b/src/physics/fluidflow/euler_fluxes.hpp index 822b0df5..ef60afc6 100644 --- a/src/physics/fluidflow/euler_fluxes.hpp +++ b/src/physics/fluidflow/euler_fluxes.hpp @@ -593,8 +593,8 @@ void calcIsentropicVortexState(const xdouble *x, xdouble *qbnd) xdouble a = sqrt(euler::gamma * press / rho); qbnd[0] = rho; - qbnd[1] = rho * a * Ma * sin(theta); - qbnd[2] = -rho * a * Ma * cos(theta); + qbnd[1] = -rho * a * Ma * sin(theta); + qbnd[2] = rho * a * Ma * cos(theta); qbnd[3] = press / euler::gami + 0.5 * rho * a * a * Ma * Ma; } diff --git a/src/physics/res_integ.cpp b/src/physics/res_integ.cpp index e719be05..77fbad7d 100644 --- a/src/physics/res_integ.cpp +++ b/src/physics/res_integ.cpp @@ -7,8 +7,9 @@ namespace mach { void DomainResIntegrator::AssembleElementVector(const FiniteElement &elx, - ElementTransformation &Trx, - const Vector &elfunx, Vector &elvect) + ElementTransformation &Trx, + const Vector &elfunx, + Vector &elvect) { /// get the proper element, transformation, and state vector Array vdofs; Vector elfun; Vector eladj; @@ -67,8 +68,9 @@ void DomainResIntegrator::AssembleElementVector(const FiniteElement &elx, } void MassResIntegrator::AssembleElementVector(const FiniteElement &elx, - ElementTransformation &Trx, - const Vector &elfunx, Vector &elvect) + ElementTransformation &Trx, + const Vector &elfunx, + Vector &elvect) { /// get the proper element, transformation, and state vector Array vdofs; Vector elfun; Vector eladj; @@ -131,8 +133,9 @@ void MassResIntegrator::AssembleElementVector(const FiniteElement &elx, } void DiffusionResIntegrator::AssembleElementVector(const FiniteElement &elx, - ElementTransformation &Trx, - const Vector &elfunx, Vector &elvect) + ElementTransformation &Trx, + const Vector &elfunx, + Vector &elvect) { /// get the proper element, transformation, and state vector Array vdofs; Vector elfun; Vector eladj; @@ -214,10 +217,12 @@ void DiffusionResIntegrator::AssembleElementVector(const FiniteElement &elx, } } -void BoundaryNormalResIntegrator::AssembleFaceVector(const FiniteElement &el1x, - const FiniteElement &el2x, - FaceElementTransformations &Trx, - const Vector &elfunx, Vector &elvect) +void BoundaryNormalResIntegrator::AssembleFaceVector( + const FiniteElement &el1x, + const FiniteElement &el2x, + FaceElementTransformations &Trx, + const Vector &elfunx, + Vector &elvect) { /// get the proper element, transformation, and state vector Array vdofs; Vector elfun; Vector eladj; @@ -286,8 +291,8 @@ void BoundaryNormalResIntegrator::AssembleFaceVector(const FiniteElement &el1x, #if 0 double DomainResIntegrator::GetElementEnergy(const FiniteElement &elx, - ElementTransformation &Trx, - const Vector &elfunx) + ElementTransformation &Trx, + const Vector &elfunx) { double Rpart = 0; @@ -330,8 +335,8 @@ double DomainResIntegrator::GetElementEnergy(const FiniteElement &elx, } double MassResIntegrator::GetElementEnergy(const FiniteElement &elx, - ElementTransformation &Trx, - const Vector &elfunx) + ElementTransformation &Trx, + const Vector &elfunx) { double Rpart = 0; diff --git a/src/physics/solver.cpp b/src/physics/solver.cpp index f692b1b0..076fd016 100644 --- a/src/physics/solver.cpp +++ b/src/physics/solver.cpp @@ -1149,10 +1149,16 @@ unique_ptr AbstractSolver::constructPreconditioner( { precond.reset(new HypreILU()); HypreILU *ilu = dynamic_cast(precond.get()); - ilu->SetType(_options["ilu-type"].get()); - ilu->SetLevelOfFill(_options["lev-fill"].get()); - ilu->SetLocalReordering(_options["ilu-reorder"].get()); - ilu->SetPrintLevel(_options["printlevel"].get()); + HYPRE_ILUSetType(*ilu, _options["ilu-type"].get()); + HYPRE_ILUSetLevelOfFill(*ilu, _options["lev-fill"].get()); + HYPRE_ILUSetLocalReordering(*ilu, _options["ilu-reorder"].get()); + HYPRE_ILUSetPrintLevel(*ilu, _options["printlevel"].get()); + cout << "Just after Hypre options" << endl; + // Just listing the options below in case we need them in the future + //HYPRE_ILUSetSchurMaxIter(ilu, schur_max_iter); + //HYPRE_ILUSetNSHDropThreshold(ilu, nsh_thres); needs type = 20,21 + //HYPRE_ILUSetDropThreshold(ilu, drop_thres); + //HYPRE_ILUSetMaxNnzPerRow(ilu, nz_max); } else if (prec_type == "hypreams") { @@ -1236,7 +1242,8 @@ double AbstractSolver::calcOutput(const std::string &fun) { if (output.find(fun) == output.end()) { - *out << "Did not find " << fun << " in output map?" << endl; + //*out << "Did not find " << fun << " in output map?" << endl; + throw MachException("Did not find " + fun + " in output map?"); } return output.at(fun).GetEnergy(*u); } diff --git a/src/physics/thermal/temp_integ.cpp b/src/physics/thermal/temp_integ.cpp index d760504b..bdc772ab 100644 --- a/src/physics/thermal/temp_integ.cpp +++ b/src/physics/thermal/temp_integ.cpp @@ -7,17 +7,16 @@ using namespace std; namespace mach { -AggregateIntegrator::AggregateIntegrator( - const mfem::FiniteElementSpace *fe_space, - const double r, - const mfem::Vector m, - mfem::GridFunction *temp) +AggregateIntegrator::AggregateIntegrator(const FiniteElementSpace *fe_space, + const double r, + const Vector m, + GridFunction *temp) : fes(fe_space), rho(r), max(m) { GetIEAggregate(temp); } -double AggregateIntegrator::GetIEAggregate(mfem::GridFunction *temp) +double AggregateIntegrator::GetIEAggregate(GridFunction *temp) { cout.flush(); Array dofs; @@ -61,9 +60,9 @@ double AggregateIntegrator::GetIEAggregate(mfem::GridFunction *temp) return J_; } -double AggregateIntegrator::GetElementEnergy(const mfem::FiniteElement &el, - mfem::ElementTransformation &Trans, - const mfem::Vector &elfun) +double AggregateIntegrator::GetElementEnergy(const FiniteElement &el, + ElementTransformation &Trans, + const Vector &elfun) { double Jpart = 0; // const int dof = el.GetDof(); @@ -85,9 +84,10 @@ double AggregateIntegrator::GetElementEnergy(const mfem::FiniteElement &el, return Jpart/denom_; } -void AggregateIntegrator::AssembleElementVector(const mfem::FiniteElement &el, - mfem::ElementTransformation &Trans, - const mfem::Vector &elfun, mfem::Vector &elvect) +void AggregateIntegrator::AssembleElementVector(const FiniteElement &el, + ElementTransformation &Trans, + const Vector &elfun, + Vector &elvect) { int dof = el.GetDof(); //, dim = el.GetDim(); elvect.SetSize(dof); @@ -114,14 +114,14 @@ void AggregateIntegrator::AssembleElementVector(const mfem::FiniteElement &el, } -TempIntegrator::TempIntegrator( const mfem::FiniteElementSpace *fe_space, - mfem::GridFunction *temp) +TempIntegrator::TempIntegrator(const FiniteElementSpace *fe_space, + GridFunction *temp) : fes(fe_space), temp_(temp) { //GetTemp(temp); } -double TempIntegrator::GetTemp(mfem::GridFunction *temp) +double TempIntegrator::GetTemp(GridFunction *temp) { cout.flush(); Array dofs; @@ -162,9 +162,10 @@ double TempIntegrator::GetTemp(mfem::GridFunction *temp) return J_; } -void TempIntegrator::AssembleElementVector(const mfem::FiniteElement &el, - mfem::ElementTransformation &Trans, - const mfem::Vector &elfun, mfem::Vector &elvect) +void TempIntegrator::AssembleElementVector(const FiniteElement &el, + ElementTransformation &Trans, + const Vector &elfun, + Vector &elvect) { int dof = el.GetDof(); //, dim = el.GetDim(); elvect.SetSize(dof); @@ -183,10 +184,11 @@ void TempIntegrator::AssembleElementVector(const mfem::FiniteElement &el, } } -void TempIntegrator::AssembleFaceVector(const mfem::FiniteElement &el1, - const mfem::FiniteElement &el2, - mfem::FaceElementTransformations &Trans, - const mfem::Vector &elfun, mfem::Vector &elvect) +void TempIntegrator::AssembleFaceVector(const FiniteElement &el1, + const FiniteElement &el2, + FaceElementTransformations &Trans, + const Vector &elfun, + Vector &elvect) { int dof = el1.GetDof(); //, dim = el1.GetDim(); elvect.SetSize(dof); @@ -210,16 +212,16 @@ void TempIntegrator::AssembleFaceVector(const mfem::FiniteElement &el1, AggregateResIntegrator::AggregateResIntegrator( - const mfem::FiniteElementSpace *fe_space, - const double r, - const mfem::Vector m, - mfem::GridFunction *temp) + const FiniteElementSpace *fe_space, + const double r, + const Vector m, + GridFunction *temp) : fes(fe_space), rho(r), max(m) { GetIEAggregate(temp); } -double AggregateResIntegrator::GetIEAggregate(mfem::GridFunction *temp) +double AggregateResIntegrator::GetIEAggregate(GridFunction *temp) { cout.flush(); Array dofs; @@ -264,9 +266,10 @@ double AggregateResIntegrator::GetIEAggregate(mfem::GridFunction *temp) return J_; } -void AggregateResIntegrator::AssembleElementVector(const mfem::FiniteElement &elx, - mfem::ElementTransformation &Trx, - const mfem::Vector &elfunx, mfem::Vector &elvect) +void AggregateResIntegrator::AssembleElementVector(const FiniteElement &elx, + ElementTransformation &Trx, + const Vector &elfunx, + Vector &elvect) { /// get the proper element, transformation, and state vector Array vdofs; Vector elfun; diff --git a/src/physics/thermal/temp_integ.hpp b/src/physics/thermal/temp_integ.hpp index ac9b0bc0..9e3ea865 100644 --- a/src/physics/thermal/temp_integ.hpp +++ b/src/physics/thermal/temp_integ.hpp @@ -74,21 +74,24 @@ class TempIntegrator : public mfem::NonlinearFormIntegrator /// Overloaded, precomputes for use in adjoint TempIntegrator(const mfem::FiniteElementSpace *fe_space, - mfem::GridFunction *temp); + mfem::GridFunction *temp); + /// TODO: Turn this into a GetElementEnergy and GetFaceEnergy /// Computes the induced functional estimate for aggregated temperature double GetTemp(mfem::GridFunction *temp); /// Computes dJdu, for the adjoint - virtual void AssembleElementVector(const mfem::FiniteElement &el, - mfem::ElementTransformation &Trans, - const mfem::Vector &elfun, mfem::Vector &elvect); + void AssembleElementVector(const mfem::FiniteElement &el, + mfem::ElementTransformation &Trans, + const mfem::Vector &elfun, + mfem::Vector &elvect) override; /// Computes dJdu for the adjoint on the boundary - virtual void AssembleFaceVector(const mfem::FiniteElement &el1, - const mfem::FiniteElement &el2, - mfem::FaceElementTransformations &Trans, - const mfem::Vector &elfun, mfem::Vector &elvect); + void AssembleFaceVector(const mfem::FiniteElement &el1, + const mfem::FiniteElement &el2, + mfem::FaceElementTransformations &Trans, + const mfem::Vector &elfun, + mfem::Vector &elvect) override; private: /// used to integrate over appropriate elements diff --git a/src/utils/kdtree.hpp b/src/utils/kdtree.hpp new file mode 100644 index 00000000..a00f2cf9 --- /dev/null +++ b/src/utils/kdtree.hpp @@ -0,0 +1,318 @@ +#ifndef MACH_KDTREE +#define MACH_KDTREE + +#include +#include +#include +#include +#include +#include + +#include "mfem.hpp" + +namespace mach +{ + +/// Class for representing a point +/// \tparam coord_type - a numeric type +/// \tparam dim - number of spatial dim +template +class point +{ +public: + /// Copy constructor from mfem::Vector to point + point(const mfem::Vector &x) { + for (size_t i = 0; i < dim; ++i) coords_[i] = x(i); + } + + /// Copy construction from point to point + point(std::array c) : coords_(c) {} + + /// Initializer list based constructor + point(std::initializer_list list) + { + size_t n = std::min(dim, list.size()); + std::copy_n(list.begin(), n, coords_.begin()); + } + + /// Returns the coordinate in the given dimension. + /// \param[in] index - dimension index (zero based) + /// \returns coordinate in the given dimension + coord_type get(size_t index) const + { + return coords_[index]; + } + + /// Returns the distance squared from this point to another point + /// \param[in] pt - another point + /// \returns distance squared from this point to the other point + double distance(const point &pt) const + { + double dist = 0; + for (size_t i = 0; i < dim; ++i) + { + double d = get(i) - pt.get(i); + dist += d * d; + } + return dist; + } + +private: + std::array coords_; +}; + +template +std::ostream& operator<<(std::ostream& out, const point& pt) { + out << '('; + for (size_t i = 0; i < dim; ++i) { + if (i > 0) + out << ", "; + out << pt.get(i); + } + out << ')'; + return out; +} + +/// k-d tree implementation, based on the version at rosettacode.org +/// \tparam coord_type - a numeric type +/// \tparam dim - number of spatial dim +template +class kdtree +{ +public: + typedef point point_type; +private: + /// Defines a node in the tree + struct node { + /// Construct a leaf in the tree + /// \note this sets a default value of -1 for node_idx_ + node(const point_type &pt) : point_(pt), left_(nullptr), right_(nullptr), + mfem_idx_(-1) + { + } + /// Construct a leaf in a tree, including its index in the FE space. + node(const mfem::Vector &x, int mfem_idx) : point_(x), left_(nullptr), + right_(nullptr), + mfem_idx_(mfem_idx) + { + } + /// Get the `index` coordinate of the point + coord_type get(size_t index) const + { + return point_.get(index); + } + /// Get the distance between the node and `pt` + double distance(const point_type& pt) const + { + return point_.distance(pt); + } + point_type point_; + node *left_; + node *right_; + int mfem_idx_; /// maps back to node in mfem data structure + }; + /// root node in the kd tree + node *root_ = nullptr; + /// current best node + node *best_ = nullptr; + /// current best distance + double best_dist_ = 0; + /// stored number nodes visited since last call of nearest + size_t visited_ = 0; + /// container for all nodes in the tree + std::vector nodes_; + + /// Defines an operator to compare two nodes based on coordinate `index_` + struct node_cmp { + node_cmp(size_t index) : index_(index) {} + bool operator()(const node& n1, const node& n2) const { + return n1.point_.get(index_) < n2.point_.get(index_); + } + size_t index_; + }; + + /// Recursive function to build a tree + node *make_tree(size_t begin, size_t end, size_t index) + { + if (end <= begin) + return nullptr; + size_t n = begin + (end - begin) / 2; + std::nth_element(&nodes_[begin], &nodes_[n], &nodes_[0] + end, + node_cmp(index)); + index = (index + 1) % dim; + nodes_[n].left_ = make_tree(begin, n, index); + nodes_[n].right_ = make_tree(n + 1, end, index); + return &nodes_[n]; + } + + /// Find the nearest node in tree to `point` + void nearest(node* root, const point_type& point, size_t index) + { + if (root == nullptr) + return; + ++visited_; + double d = root->distance(point); + if (best_ == nullptr || d < best_dist_) + { + best_dist_ = d; + best_ = root; + } + if (best_dist_ == 0) + return; + double dx = root->get(index) - point.get(index); + index = (index + 1) % dim; + nearest(dx > 0 ? root->left_ : root->right_, point, index); + if (dx * dx >= best_dist_) + return; + nearest(dx > 0 ? root->right_ : root->left_, point, index); + } + +public: + /// Remove the copy constructor and assignment constructors + kdtree(const kdtree&) = delete; + kdtree& operator=(const kdtree&) = delete; + + /// Construct an empty tree; use with set_size and add_node, and finalize + kdtree() { } + + /// Constructor taking a pair of iterators. Adds each point in the range + /// [begin, end) to the tree. + /// \param[in] begin - start of range + /// \param[in] end - end of range + template + kdtree(iterator begin, iterator end) : nodes_(begin, end) + { + root_ = make_tree(0, nodes_.size(), 0); + } + + /// Constructor taking a function object that generates points. The function /// object will be called n times to populate the tree. + /// \param[in] f - function that returns a point + /// \param[in] n - number of points to add + template + kdtree(func&& f, size_t n) + { + nodes_.reserve(n); + for (size_t i = 0; i < n; ++i) + nodes_.emplace_back(f()); + root_ = make_tree(0, nodes_.size(), 0); + } + + /// Allocate memory for the tree + /// \param[in] n - number of nodes in the tree + void set_size(size_t n) + { + nodes_.reserve(n); + } + + /// Adds a new node to the tree, including its index + /// \param[in] x - the coordinates of the node + /// \param[in] mfem_idx - the index of the node in the finite-element space + void add_node(const mfem::Vector &x, int mfem_idx) + { + nodes_.emplace_back(node(x, mfem_idx)); + } + + /// Call this after adding all the nodes to the tree + void finalize() + { + root_ = make_tree(0, nodes_.size(), 0); + } + + /// Returns true if the tree is empty, false otherwise. + bool empty() const { return nodes_.empty(); } + + /// Returns the number of nodes visited by the last call to nearest() + size_t visited() const { return visited_; } + + /// Returns the distance between the input point and return value from the + /// last call to nearest() + double distance() const { return std::sqrt(best_dist_); } + + /// Returns the index of the closest node. + /// \warning based on the most recent call to nearest(). + //int get_node_index() const { return best_->node_idx_; } + + /// Returns the index of the element associated with the closest node. + /// \warning based on the most recent call to nearest(). + //int get_elem_index() const { return best_->elem_idx_; } + + /// Finds the nearest point in the tree to the given point. + /// \param[in] pt - a point whose distance to tree is sought + /// \returns the nearest node (index) in the tree to the given point + /// \note It is not valid to call this function if the tree is empty. + int nearest(const point_type &pt) + { + if (root_ == nullptr) + throw std::logic_error("tree is empty"); + best_ = nullptr; + visited_ = 0; + best_dist_ = 0; + nearest(root_, pt, 0); + return best_->mfem_idx_; + } +}; + +#if 0 +void test_wikipedia() { + typedef point point2d; + typedef kdtree tree2d; + + point2d points[] = { { 2, 3 }, { 5, 4 }, { 9, 6 }, { 4, 7 }, { 8, 1 }, { 7, 2 } }; + + tree2d tree(std::begin(points), std::end(points)); + point2d n = tree.nearest({ 9, 2 }); + + std::cout << "Wikipedia example data:\n"; + std::cout << "nearest point: " << n << '\n'; + std::cout << "distance: " << tree.distance() << '\n'; + std::cout << "nodes visited: " << tree.visited() << '\n'; +} + +typedef point point3d; +typedef kdtree tree3d; + +struct random_point_generator { + random_point_generator(double min, double max) + : engine_(std::random_device()()), distribution_(min, max) {} + + point3d operator()() { + double x = distribution_(engine_); + double y = distribution_(engine_); + double z = distribution_(engine_); + return point3d({x, y, z}); + } + + std::mt19937 engine_; + std::uniform_real_distribution distribution_; +}; + +void test_random(size_t count) { + random_point_generator rpg(0, 1); + tree3d tree(rpg, count); + point3d pt(rpg()); + point3d n = tree.nearest(pt); + + std::cout << "Random data (" << count << " points):\n"; + std::cout << "point: " << pt << '\n'; + std::cout << "nearest point: " << n << '\n'; + std::cout << "distance: " << tree.distance() << '\n'; + std::cout << "nodes visited: " << tree.visited() << '\n'; +} + +int main() { + try { + test_wikipedia(); + std::cout << '\n'; + test_random(1000); + std::cout << '\n'; + test_random(1000000); + } catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + } + return 0; +} +#endif + +} // namespace mach + +#endif \ No newline at end of file diff --git a/src/utils/mach_types.hpp b/src/utils/mach_types.hpp index bad51510..224de5d1 100644 --- a/src/utils/mach_types.hpp +++ b/src/utils/mach_types.hpp @@ -9,7 +9,7 @@ namespace mach // Aliases for various mfem types. // Originally used to distinguish between serial and parallel types #ifdef MFEM_USE_PUMI - using MeshType = mfem::ParPumiMesh; + using MeshType = mfem::ParMesh; #else using MeshType = mfem::ParMesh; #endif diff --git a/test/regression/test_steady_vortex.cpp b/test/regression/test_steady_vortex.cpp index c3b612e5..d097962c 100644 --- a/test/regression/test_steady_vortex.cpp +++ b/test/regression/test_steady_vortex.cpp @@ -94,10 +94,23 @@ TEMPLATE_TEST_CASE_SIG("Steady Vortex Solver Regression Test", auto uexact = !entvar ? qexact : wexact; int mesh_degree = options["space-dis"]["degree"].get() + 1; std::vector target_error; + std::vector target_drag_error; if (entvar) - target_error = {0.0690131081, 0.0224304871, 0.0107753424, 0.0064387612}; + { + target_error = {0.0901571779707352, 0.0311496716090168, + 0.0160317976193675, 0.0098390277746438}; + target_drag_error = {0.0149959859366922, 0.00323047181284186, + 0.00103944525942667, 0.000475003178886935}; + // target_error = {0.0690131081, 0.0224304871, 0.0107753424, 0.0064387612}; + } else - target_error = {0.0700148195, 0.0260625842, 0.0129909277, 0.0079317615}; + { + target_error = {0.0901571779707358, 0.0311496716080777, + 0.0160317976193642, 0.00983902777464837}; + target_drag_error = {0.0149959859366923, 0.00323047181325709, + 0.00103944525936905, 0.000475003178874278}; + // target_error = {0.0700148195, 0.0260625842, 0.0129909277, 0.0079317615}; + } for (int nx = 1; nx <= 4; ++nx) { @@ -110,8 +123,13 @@ TEMPLATE_TEST_CASE_SIG("Steady Vortex Solver Regression Test", solver->setInitialCondition(uexact); solver->solveForState(); - // Compute error and check against appropriate target - double l2_error = solver->calcL2Error(uexact, 0); + // Compute error and check against appropriate target: + // Using calcConservativeVarsL2Error, we should have the same error + // for both entvar=true and entvar=false + double l2_error = (static_cast&>(*solver) + .calcConservativeVarsL2Error(uexact, 0)); + //double l2_error = solver->calcL2Error(uexact, 0); + double drag_error = fabs(solver->calcOutput("drag") - (-1 /mach::euler::gamma)); REQUIRE(l2_error == Approx(target_error[nx - 1]).margin(1e-10)); } } @@ -144,8 +162,8 @@ void qexact(const Vector &x, Vector& q) double a = sqrt(euler::gamma*press/rho); q(0) = rho; - q(1) = rho*a*Ma*sin(theta); - q(2) = -rho*a*Ma*cos(theta); + q(1) = -rho*a*Ma*sin(theta); + q(2) = rho*a*Ma*cos(theta); q(3) = press/euler::gami + 0.5*rho*a*a*Ma*Ma; } diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 6368c215..e8d60030 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -25,6 +25,7 @@ set(FLUID_TEST_SRCS test_reconstruction test_sbp_fe test_viscous_integ + test_surface test_utils test_thermal_integ test_res_integ diff --git a/test/unit/euler_test_data.cpp b/test/unit/euler_test_data.cpp index 99926343..ce83cbc3 100644 --- a/test/unit/euler_test_data.cpp +++ b/test/unit/euler_test_data.cpp @@ -1,6 +1,8 @@ #include "euler_test_data.hpp" #include "euler_fluxes.hpp" +using namespace mfem; + namespace euler_data { @@ -73,7 +75,7 @@ static std::default_random_engine gen(std::random_device{}()); static std::uniform_real_distribution uniform_rand(0.0, 1.0); template -void randBaselinePert(const mfem::Vector &x, mfem::Vector &u) +void randBaselineVectorPert(const Vector &x, Vector &u) { const double scale = 0.01; u(0) = rho * (1.0 + scale * uniform_rand(gen)); @@ -84,17 +86,17 @@ void randBaselinePert(const mfem::Vector &x, mfem::Vector &u) } if (entvar) { - mfem::Vector q(u); + Vector q(u); mach::calcEntropyVars(q.GetData(), u.GetData()); } } // explicit instantiation of the templated function above -template void randBaselinePert<1, true>(const mfem::Vector &x, mfem::Vector &u); -template void randBaselinePert<2, true>(const mfem::Vector &x, mfem::Vector &u); -template void randBaselinePert<3, true>(const mfem::Vector &x, mfem::Vector &u); -template void randBaselinePert<1, false>(const mfem::Vector &x, mfem::Vector &u); -template void randBaselinePert<2, false>(const mfem::Vector &x, mfem::Vector &u); -template void randBaselinePert<3, false>(const mfem::Vector &x, mfem::Vector &u); +template void randBaselineVectorPert<1, true>(const Vector &x, Vector &u); +template void randBaselineVectorPert<2, true>(const Vector &x, Vector &u); +template void randBaselineVectorPert<3, true>(const Vector &x, Vector &u); +template void randBaselineVectorPert<1, false>(const Vector &x, Vector &u); +template void randBaselineVectorPert<2, false>(const Vector &x, Vector &u); +template void randBaselineVectorPert<3, false>(const Vector &x, Vector &u); template void randBaselinePertSA(const mfem::Vector &x, mfem::Vector &u) @@ -121,24 +123,24 @@ template void randBaselinePertSA<1, false>(const mfem::Vector &x, mfem::Vector & template void randBaselinePertSA<2, false>(const mfem::Vector &x, mfem::Vector &u); template void randBaselinePertSA<3, false>(const mfem::Vector &x, mfem::Vector &u); -void randState(const mfem::Vector &x, mfem::Vector &u) +void randVectorState(const Vector &x, Vector &u) { for (int i = 0; i < u.Size(); ++i) { - if(u.Size() > 3 && (i == 1 || i == 2)) - u(i) = 0.01*(2.0 * uniform_rand(gen) - 1.0); - else + // if(u.Size() > 3 && (i == 1 || i == 2)) + // u(i) = 0.01*(2.0 * uniform_rand(gen) - 1.0); + // else u(i) = 2.0 * uniform_rand(gen) - 1.0; } } -double randBaselinePert(const mfem::Vector &x) +double randBaselinePert(const Vector &x) { const double scale = 0.01; return 1.0 + scale * uniform_rand(gen); } -double randState(const mfem::Vector &x) +double randState(const Vector &x) { return 2.0 * uniform_rand(gen) - 1.0; } diff --git a/test/unit/euler_test_data.hpp b/test/unit/euler_test_data.hpp index 7b90e19d..cbc310a4 100644 --- a/test/unit/euler_test_data.hpp +++ b/test/unit/euler_test_data.hpp @@ -72,7 +72,7 @@ const double sa_params[13] = {0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7. /// \tparam dim - number of spatial dimensions (1, 2, or 3) /// \tparam entvar - if true, returns entropy variables template -void randBaselinePert(const mfem::Vector &x, mfem::Vector &u); +void randBaselineVectorPert(const mfem::Vector &x, mfem::Vector &u); /// Returns a perturbed version of the baseline flow state (Spalart-Allmaras) /// \param[in] x - coordinates (not used) @@ -85,7 +85,7 @@ void randBaselinePertSA(const mfem::Vector &x, mfem::Vector &u); /// Returns a random state with entries uniformly distributed in [-1,1] /// \param[in] x - coordinates (not used) /// \param[out] u - rand state variable -void randState(const mfem::Vector &x, mfem::Vector &u); +void randVectorState(const mfem::Vector &x, mfem::Vector &u); /// Returns a perturbed version of the baseline temperature state /// \param[in] x - coordinates (not used) diff --git a/test/unit/test_euler_assemble.cpp b/test/unit/test_euler_assemble.cpp index 7049bdc3..054e763b 100644 --- a/test/unit/test_euler_assemble.cpp +++ b/test/unit/test_euler_assemble.cpp @@ -34,12 +34,12 @@ TEST_CASE("EulerIntegrator::AssembleElementGrad", "[EulerIntegrator]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2>); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2>); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -74,12 +74,12 @@ TEST_CASE("EulerIntegrator::AssembleElementGrad", "[EulerIntegrator]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2>); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2>); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -133,12 +133,12 @@ TEST_CASE("SlipWallBC::AssembleFaceGrad", "[SlipWallBC]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -174,12 +174,12 @@ TEST_CASE("SlipWallBC::AssembleFaceGrad", "[SlipWallBC]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -238,12 +238,12 @@ TEST_CASE("PressureForce::AssembleVector", "[PressureForce]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); q.ProjectCoefficient(pert); // initialize the vector that dJdu multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate dJdu and compute its product with v @@ -274,12 +274,12 @@ TEST_CASE("PressureForce::AssembleVector", "[PressureForce]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); q.ProjectCoefficient(pert); // initialize the vector that dJdu multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate dJdu and compute its product with v @@ -330,12 +330,12 @@ TEMPLATE_TEST_CASE_SIG("DyadicFluxIntegrator::AssembleElementGrad", // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2,entvar>); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2,entvar>); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -370,12 +370,12 @@ TEMPLATE_TEST_CASE_SIG("DyadicFluxIntegrator::AssembleElementGrad", // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2, entvar>); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2, entvar>); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -433,12 +433,12 @@ TEMPLATE_TEST_CASE_SIG("DyadicFluxIntegrator::AssembleElementGrad", // // initialize state; here we randomly perturb a constant state // GridFunction q(fes.get()); -// VectorFunctionCoefficient pert(num_state, randBaselinePert); +// VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); // q.ProjectCoefficient(pert); // // initialize the vector that the Jacobian multiplies // GridFunction v(fes.get()); -// VectorFunctionCoefficient v_rand(num_state, randState); +// VectorFunctionCoefficient v_rand(num_state, randVectorState); // v.ProjectCoefficient(v_rand); // // evaluate the Jacobian and compute its product with v @@ -494,12 +494,12 @@ TEMPLATE_TEST_CASE_SIG("EntStableLPSIntegrator::AssembleElementGrad using entvar // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2, entvar>); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2, entvar>); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -534,12 +534,12 @@ TEMPLATE_TEST_CASE_SIG("EntStableLPSIntegrator::AssembleElementGrad using entvar // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2, entvar>); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2, entvar>); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -590,12 +590,12 @@ TEMPLATE_TEST_CASE_SIG("MassIntegrator::AssembleElementGrad", // initialize state; we randomly perturb a constant state GridFunction u(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2, entvar>); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2, entvar>); u.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); NonlinearForm res(fes.get()); @@ -631,12 +631,12 @@ TEMPLATE_TEST_CASE_SIG("MassIntegrator::AssembleElementGrad", // initialize state and k = du/dt; here we randomly perturb a constant state GridFunction u(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2, entvar>); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2, entvar>); u.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); NonlinearForm res(fes.get()); @@ -700,12 +700,12 @@ TEMPLATE_TEST_CASE_SIG("InviscidFaceIntegrator::AssembleFaceGrad", // initialize state; here we randomly perturb a constant state GridFunction w(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); w.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v diff --git a/test/unit/test_euler_sens_integ.cpp b/test/unit/test_euler_sens_integ.cpp index 6014e7db..5ef4b5db 100644 --- a/test/unit/test_euler_sens_integ.cpp +++ b/test/unit/test_euler_sens_integ.cpp @@ -41,7 +41,7 @@ TEMPLATE_TEST_CASE_SIG("IsmailRoeMeshSensIntegrator::AssembleElementVector", // initialize state and adjoint; here we randomly perturb a constant state GridFunction state(fes.get()), adjoint(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2,entvar>); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2,entvar>); state.ProjectCoefficient(pert); adjoint.ProjectCoefficient(pert); @@ -58,7 +58,7 @@ TEMPLATE_TEST_CASE_SIG("IsmailRoeMeshSensIntegrator::AssembleElementVector", // initialize the vector that we use to perturb the mesh nodes GridFunction v(mesh_fes); - VectorFunctionCoefficient v_rand(dim, randState); + VectorFunctionCoefficient v_rand(dim, randVectorState); v.ProjectCoefficient(v_rand); // contract dfdx with v @@ -117,7 +117,7 @@ TEMPLATE_TEST_CASE_SIG("SlipWallBCMeshSens::AssembleRHSElementVect", // initialize state and adjoint; here we randomly perturb a constant state GridFunction state(fes.get()), adjoint(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2,entvar>); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2,entvar>); state.ProjectCoefficient(pert); adjoint.ProjectCoefficient(pert); @@ -134,7 +134,7 @@ TEMPLATE_TEST_CASE_SIG("SlipWallBCMeshSens::AssembleRHSElementVect", // initialize the vector that we use to perturb the mesh nodes GridFunction v(mesh_fes); - VectorFunctionCoefficient v_rand(dim, randState); + VectorFunctionCoefficient v_rand(dim, randVectorState); v.ProjectCoefficient(v_rand); // contract dfdx with v diff --git a/test/unit/test_navier_stokes_assemble.cpp b/test/unit/test_navier_stokes_assemble.cpp index 24264de9..1a561577 100644 --- a/test/unit/test_navier_stokes_assemble.cpp +++ b/test/unit/test_navier_stokes_assemble.cpp @@ -38,12 +38,12 @@ TEST_CASE("ESViscousIntegrator::AssembleElementGrad", "[ESViscousIntegrator]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2>); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2>); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -109,12 +109,12 @@ TEST_CASE("NoSlipAdiabaticWallBC::AssembleFaceGrad", "[NoSlipBC]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -172,12 +172,12 @@ TEST_CASE("ViscousSlipWallBC::AssembleFaceGrad", "[VisSlipWallBC]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -243,12 +243,12 @@ TEST_CASE("ViscousInflowWallBC::AssembleFaceGrad", "[VisInflowBC]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -314,12 +314,12 @@ TEST_CASE("ViscousOutflowBC::AssembleFaceGrad", "[VisOutflowBC]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -385,12 +385,12 @@ TEST_CASE("ViscousFarFieldBC::AssembleFaceGrad", "[VisFarFieldBC]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -460,12 +460,12 @@ TEST_CASE("SurfaceForce::AssembleVector", "[Surface Force]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); q.ProjectCoefficient(pert); // initialize the vector that dJdu multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate dJdu and compute its product with v @@ -497,12 +497,12 @@ TEST_CASE("SurfaceForce::AssembleVector", "[Surface Force]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert); q.ProjectCoefficient(pert); // initialize the vector that dJdu multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate dJdu and compute its product with v diff --git a/test/unit/test_rans_integ.cpp b/test/unit/test_rans_integ.cpp index 6d56a4bc..7fb4c6a3 100644 --- a/test/unit/test_rans_integ.cpp +++ b/test/unit/test_rans_integ.cpp @@ -856,7 +856,7 @@ TEST_CASE("SAViscousIntegrator::AssembleElementGrad", "[SAViscousIntegrator]") // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Ja;cobian and compute its product with v @@ -934,7 +934,7 @@ TEST_CASE("SANoSlipAdiabaticWallBC::AssembleElementGrad", "[SANoSlipAdiabaticWal // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Ja;cobian and compute its product with v @@ -1011,7 +1011,7 @@ TEST_CASE("SAViscousSlipWallBC::AssembleElementGrad", "[SAViscousSlipWallBC]") // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Ja;cobian and compute its product with v @@ -1090,7 +1090,7 @@ TEST_CASE("SAFarFieldBC::AssembleElementGrad", "[SAFarFieldBC]") // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Ja;cobian and compute its product with v @@ -1180,7 +1180,7 @@ TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -1258,12 +1258,12 @@ TEST_CASE("SALPSIntegrator::AssembleElementGrad", "[SALPSIntegrator]") // initialize state; here we randomly perturb a constant state GridFunction q(fes.get()); - VectorFunctionCoefficient pert(num_state, randBaselinePert<2>); + VectorFunctionCoefficient pert(num_state, randBaselinePertSA<2>); q.ProjectCoefficient(pert); // initialize the vector that the Jacobian multiplies GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(num_state, randState); + VectorFunctionCoefficient v_rand(num_state, randVectorState); v.ProjectCoefficient(v_rand); // evaluate the Jacobian and compute its product with v @@ -1322,12 +1322,12 @@ TEMPLATE_TEST_CASE_SIG("SAInviscid Gradient", // initialize state ; here we randomly perturb a constant state GridFunction state(fes.get()) - VectorFunctionCoefficient v_rand(dim+3, randState); + VectorFunctionCoefficient v_rand(dim+3, randVectorState); state.ProjectCoefficient(pert); // initialize the vector that we use to perturb the mesh nodes GridFunction v(fes.get()); - VectorFunctionCoefficient v_rand(dim, randState); + VectorFunctionCoefficient v_rand(dim, randVectorState); v.ProjectCoefficient(v_rand); // evaluate df/dx and contract with v diff --git a/test/unit/test_res_integ.cpp b/test/unit/test_res_integ.cpp index b8f3ac84..2237badf 100644 --- a/test/unit/test_res_integ.cpp +++ b/test/unit/test_res_integ.cpp @@ -53,7 +53,7 @@ TEST_CASE("DomainResIntegrator::AssembleElementVector", // initialize the vector that we use to perturb the mesh nodes GridFunction v(mesh_fes); - VectorFunctionCoefficient v_rand(dim, randState); + VectorFunctionCoefficient v_rand(dim, randVectorState); v.ProjectCoefficient(v_rand); // evaluate df/dx and contract with v @@ -128,7 +128,7 @@ TEST_CASE("MassResIntegrator::AssembleElementVector", // initialize the vector that we use to perturb the mesh nodes GridFunction v(mesh_fes); - VectorFunctionCoefficient v_rand(dim, randState); + VectorFunctionCoefficient v_rand(dim, randVectorState); v.ProjectCoefficient(v_rand); // evaluate df/dx and contract with v @@ -207,7 +207,7 @@ TEST_CASE("DiffusionResIntegrator::AssembleElementVector", // initialize the vector that we use to perturb the mesh nodes GridFunction v(mesh_fes); - VectorFunctionCoefficient v_rand(dim, randState); + VectorFunctionCoefficient v_rand(dim, randVectorState); v.ProjectCoefficient(v_rand); // evaluate df/dx and contract with v @@ -288,7 +288,7 @@ TEST_CASE("BoundaryNormalResIntegrator::AssembleFaceVector", // initialize the vector that we use to perturb the mesh nodes GridFunction v(mesh_fes); - VectorFunctionCoefficient v_rand(dim, randState); + VectorFunctionCoefficient v_rand(dim, randVectorState); v.ProjectCoefficient(v_rand); // evaluate df/dx and contract with v diff --git a/test/unit/test_surface.cpp b/test/unit/test_surface.cpp new file mode 100644 index 00000000..08734275 --- /dev/null +++ b/test/unit/test_surface.cpp @@ -0,0 +1,452 @@ +#include +#include + +#include "catch.hpp" +#include "mfem.hpp" +#include "surface.hpp" + +/// This projects nodes onto the hypersphere +void SnapNodes(mfem::Mesh &mesh) +{ + using namespace mfem; + GridFunction &nodes = *mesh.GetNodes(); + Vector node(mesh.SpaceDimension()); + for (int i = 0; i < nodes.FESpace()->GetNDofs(); i++) + { + for (int d = 0; d < mesh.SpaceDimension(); d++) + { + node(d) = nodes(nodes.FESpace()->DofToVDof(i, d)); + } + node /= node.Norml2(); + for (int d = 0; d < mesh.SpaceDimension(); d++) + { + nodes(nodes.FESpace()->DofToVDof(i, d)) = node(d); + } + } +} + +TEST_CASE( "calc/solveDistance applied to linear segment", "[surface]") +{ + using namespace mfem; + + int order = 1; + int Nvert = 2; + int Nelem = 1; + Mesh *mesh = new Mesh(1, Nvert, Nelem, 0, 2); + const double seg_v[2][2] = + { + {1, 0}, {0, 1} + }; + const int seg_e[1][2] = + { + {0, 1} + }; + for (int j = 0; j < Nvert; j++) + { + mesh->AddVertex(seg_v[j]); + } + for (int j = 0; j < Nelem; j++) + { + int attribute = j + 1; + mesh->AddSegment(seg_e[j], attribute); + } + mesh->FinalizeMesh(); + + // Set the space for the high-order mesh nodes. + H1_FECollection fec(order, mesh->Dimension()); + FiniteElementSpace nodal_fes(mesh, &fec, mesh->SpaceDimension()); + mesh->SetNodalFESpace(&nodal_fes); + + // Snap nodes to the circle + SnapNodes(*mesh); + + // Construct the surface object + mach::Surface<2> surf(*mesh); + + // Get the relevant element transformation + ElementTransformation *trans = mesh->GetElementTransformation(0); + + // Test 1: Closest point on the interior of reference domain + Vector x(2); + x(0) = 3.0; + x(1) = 2.0; + double dist = surf.solveDistance(*trans, x); + //std::cout << "distance = " << dist << std::endl; + REQUIRE( dist == Approx(2*sqrt(2)).margin(1e-12) ); + REQUIRE( surf.calcDistance(x) == Approx(dist).margin(1e-14) ); + + // Test 2: Closest point on the edge of the reference domain + x(0) = 0.0; + x(1) = 2.0; + dist = surf.solveDistance(*trans, x); + //std::cout << "distance = " << dist << std::endl; + REQUIRE( dist == Approx(1.0).margin(1e-12) ); + REQUIRE( surf.calcDistance(x) == Approx(dist).margin(1e-14) ); + + delete mesh; +} + +TEST_CASE( "calc/solveDistance applied to quadratic segment", "[surface]") +{ + using namespace mfem; + + int order = 2; + int Nvert = 2; + int Nelem = 1; + Mesh *mesh = new Mesh(1, Nvert, Nelem, 0, 2); + const double seg_v[2][2] = + { + {1, 0}, {0, 1} + }; + const int seg_e[1][2] = + { + {0, 1} + }; + for (int j = 0; j < Nvert; j++) + { + mesh->AddVertex(seg_v[j]); + } + for (int j = 0; j < Nelem; j++) + { + int attribute = j + 1; + mesh->AddSegment(seg_e[j], attribute); + } + mesh->FinalizeMesh(); + + // Set the space for the high-order mesh nodes. + H1_FECollection fec(order, mesh->Dimension()); + FiniteElementSpace nodal_fes(mesh, &fec, mesh->SpaceDimension()); + mesh->SetNodalFESpace(&nodal_fes); + + // Snap nodes to the circle + SnapNodes(*mesh); + + // TEMP: visualize mesh + //std::ofstream sol_ofs("surface.vtk"); + //sol_ofs.precision(14); + //mesh->PrintVTK(sol_ofs, 20); + //sol_ofs.close(); + + // Construct the surface object + mach::Surface<2> surf(*mesh); + + // Get the relevant element transformation + ElementTransformation *trans = mesh->GetElementTransformation(0); + + // Test 1: Closest point on the interior of reference domain + Vector x(2); + x(0) = 2.0; + x(1) = 1.0; + double dist = surf.solveDistance(*trans, x); + //std::cout << std::setprecision(15); + //std::cout << "distance = " << dist << std::endl; + REQUIRE( dist == Approx(1.24261668385551).margin(1e-12) ); + REQUIRE( surf.calcDistance(x) == Approx(dist).margin(1e-14) ); + + // Test 2: Closest point on the edge of the reference domain + x(1) = -1.0; + dist = surf.solveDistance(*trans, x); + //std::cout << "distance = " << dist << std::endl; + REQUIRE( dist == Approx(sqrt(2.0)).margin(1e-12) ); + REQUIRE( surf.calcDistance(x) == Approx(dist).margin(1e-14) ); + + delete mesh; +} + +TEST_CASE( "calc/solveDistance applied to cubic segment", "[surface]") +{ + using namespace mfem; + + int order = 3; + int Nvert = 2; + int Nelem = 1; + Mesh *mesh = new Mesh(1, Nvert, Nelem, 0, 2); + const double seg_v[2][2] = + { + {-1, 0}, {1, 0} + }; + const int seg_e[1][2] = + { + {0, 1} + }; + for (int j = 0; j < Nvert; j++) + { + mesh->AddVertex(seg_v[j]); + } + for (int j = 0; j < Nelem; j++) + { + int attribute = j + 1; + mesh->AddSegment(seg_e[j], attribute); + } + mesh->FinalizeMesh(); + + // Set the space for the high-order mesh nodes. + H1_FECollection fec(order, mesh->Dimension()); + FiniteElementSpace nodal_fes(mesh, &fec, mesh->SpaceDimension()); + mesh->SetNodalFESpace(&nodal_fes); + + // Snap nodes to the circle + //SnapNodes(*mesh); + + // Snap the nodes to a oscillatory cubic function + GridFunction &nodes = *mesh->GetNodes(); + Vector node(2); + for (int i = 0; i < nodes.FESpace()->GetNDofs(); i++) + { + for (int d = 0; d < 2; d++) + { + node(d) = nodes(nodes.FESpace()->DofToVDof(i, d)); + } + node(1) = (node(0) + 0.9)*(node(0) - 1)*(node(0) + 1); + for (int d = 0; d < 2; d++) + { + nodes(nodes.FESpace()->DofToVDof(i, d)) = node(d); + } + } + + // TEMP: visualize mesh + //std::ofstream sol_ofs("surface.vtk"); + //sol_ofs.precision(14); + //mesh->PrintVTK(sol_ofs, 20); + //sol_ofs.close(); + + // Construct the surface object + mach::Surface<2> surf(*mesh); + + // Get the relevant element transformation + ElementTransformation *trans = mesh->GetElementTransformation(0); + + // Test 1: test that local maximizers can be avoided + Vector x(2); + x(0) = 0.0; + x(1) = 1.0; + double dist = surf.solveDistance(*trans, x); + //std::cout << std::setprecision(15); + //std::cout << "distance = " << dist << std::endl; + REQUIRE( dist == Approx(1.30292258016208).margin(1e-12) ); + REQUIRE( surf.calcDistance(x) == Approx(dist).margin(1e-14) ); + +#if 0 + // Test 2: Closest point on the edge of the reference domain + // This test currently fails, because we converge to a local minimizer + // rather than the global minimizer. + x(0) = 0.5; + x(1) = 1.0; + dist = surf.solveDistance(*trans, x); + std::cout << "distance = " << dist << std::endl; + REQUIRE(dist == Approx(0.5*sqrt(5.0)).margin(1e-12)); +#endif + + delete mesh; +} + +TEST_CASE( "calcDistance applied to linear mesh", "[surface]") +{ + using namespace mfem; + + int order = 1; + int Nvert = 4; + int Nelem = 4; + Mesh *mesh = new Mesh(1, Nvert, Nelem, 0, 2); + const double seg_v[4][2] = + { + {1, 0}, {0, 1}, {-1, 0}, {0, -1} + }; + const int seg_e[4][2] = + { + {0, 1}, {1, 2}, {2, 3}, {3, 0} + }; + for (int j = 0; j < Nvert; j++) + { + mesh->AddVertex(seg_v[j]); + } + for (int j = 0; j < Nelem; j++) + { + int attribute = j + 1; + mesh->AddSegment(seg_e[j], attribute); + } + mesh->FinalizeMesh(); + + // Set the space for the high-order mesh nodes. + H1_FECollection fec(order, mesh->Dimension()); + FiniteElementSpace nodal_fes(mesh, &fec, mesh->SpaceDimension()); + mesh->SetNodalFESpace(&nodal_fes); + + // Refine the mesh while snapping nodes to the sphere. + const int ref_levels = 4; + for (int l = 0; l <= ref_levels; l++) + { + if (l > 0) // for l == 0 just perform snapping + { + mesh->UniformRefinement(); + } + SnapNodes(*mesh); + } + + // TEMP: visualize mesh + //std::ofstream sol_ofs("surface.vtk"); + //sol_ofs.precision(14); + //mesh->PrintVTK(sol_ofs, 20); + //sol_ofs.close(); + + // Construct the surface object + mach::Surface<2> surf(*mesh); + + // Test 1: Closest point on a vertex + Vector x(2); + x(0) = 2.0; + x(1) = 2.0; + double dist = surf.calcDistance(x); + //std::cout << "distance = " << dist << std::endl; + REQUIRE( surf.calcDistance(x) == Approx(sqrt(8.0) - 1).margin(1e-12) ); + + // Test 2: Closest point inside a element + x(0) = 1.9975909124103448; + x(1) = 0.09813534865483603; + dist = surf.calcDistance(x); + //std::cout << "distance = " << dist << std::endl; + REQUIRE( dist == Approx(1.00120454379483).margin(1e-12) ); + + delete mesh; +} + +TEST_CASE( "calcDistance applied to quadratic mesh", "[surface]") +{ + using namespace mfem; + + int order = 2; + int Nvert = 4; + int Nelem = 4; + Mesh *mesh = new Mesh(1, Nvert, Nelem, 0, 2); + const double seg_v[4][2] = + { + {1, 0}, {0, 1}, {-1, 0}, {0, -1} + }; + const int seg_e[4][2] = + { + {0, 1}, {1, 2}, {2, 3}, {3, 0} + }; + for (int j = 0; j < Nvert; j++) + { + mesh->AddVertex(seg_v[j]); + } + for (int j = 0; j < Nelem; j++) + { + int attribute = j + 1; + mesh->AddSegment(seg_e[j], attribute); + } + mesh->FinalizeMesh(); + + // Set the space for the high-order mesh nodes. + H1_FECollection fec(order, mesh->Dimension()); + FiniteElementSpace nodal_fes(mesh, &fec, mesh->SpaceDimension()); + mesh->SetNodalFESpace(&nodal_fes); + + // Refine the mesh while snapping nodes to the sphere. + const int ref_levels = 2; + for (int l = 0; l <= ref_levels; l++) + { + if (l > 0) // for l == 0 just perform snapping + { + mesh->UniformRefinement(); + } + SnapNodes(*mesh); + } + + // TEMP: visualize mesh + //std::ofstream sol_ofs("surface.vtk"); + //sol_ofs.precision(14); + //mesh->PrintVTK(sol_ofs, 20); + //sol_ofs.close(); + + // Construct the surface object + mach::Surface<2> surf(*mesh); + + // Test 1: closest point inside an element + Vector x(2); + x(0) = -2.0; + x(1) = 1.0; + double dist = surf.calcDistance(x); + //std::cout << "distance = " << dist << std::endl; + REQUIRE( surf.calcDistance(x) == Approx(1.23626562072248).margin(1e-12) ); + + // Test 2: Closest point at a vertex + x(0) = 0.0; + x(1) = -2.0; + dist = surf.calcDistance(x); + //std::cout << "distance = " << dist << std::endl; + REQUIRE( dist == Approx(1.0).margin(1e-12) ); + + delete mesh; +} + +TEST_CASE( "calcDistance applied to cubic mesh", "[surface]") +{ + using namespace mfem; + + int order = 3; + int Nvert = 4; + int Nelem = 4; + Mesh *mesh = new Mesh(1, Nvert, Nelem, 0, 2); + const double seg_v[4][2] = + { + {1, 0}, {0, 1}, {-1, 0}, {0, -1} + }; + const int seg_e[4][2] = + { + {0, 1}, {1, 2}, {2, 3}, {3, 0} + }; + for (int j = 0; j < Nvert; j++) + { + mesh->AddVertex(seg_v[j]); + } + for (int j = 0; j < Nelem; j++) + { + int attribute = j + 1; + mesh->AddSegment(seg_e[j], attribute); + } + mesh->FinalizeMesh(); + + // Set the space for the high-order mesh nodes. + H1_FECollection fec(order, mesh->Dimension()); + FiniteElementSpace nodal_fes(mesh, &fec, mesh->SpaceDimension()); + mesh->SetNodalFESpace(&nodal_fes); + + // Refine the mesh while snapping nodes to the sphere. + const int ref_levels = 2; + for (int l = 0; l <= ref_levels; l++) + { + if (l > 0) // for l == 0 just perform snapping + { + mesh->UniformRefinement(); + } + SnapNodes(*mesh); + } + + // TEMP: visualize mesh + //std::ofstream sol_ofs("surface.vtk"); + //sol_ofs.precision(14); + //mesh->PrintVTK(sol_ofs, 20); + //sol_ofs.close(); + + // Construct the surface object + mach::Surface<2> surf(*mesh); + + // Test 1: closest point inside an element + Vector x(2); + x(0) = -2.0; + x(1) = 1.0; + double dist = surf.calcDistance(x); + //std::cout << "distance = " << dist << std::endl; + REQUIRE( surf.calcDistance(x) == Approx(1.23606070227677).margin(1e-12) ); + + // Test 2: Closest point at a vertex; have to go inside the "circle" because + // the snapped cubic mesh is slightly concave at the vertices. + x(0) = 0.0; + x(1) = -0.5; + dist = surf.calcDistance(x); + //std::cout << "distance = " << dist << std::endl; + REQUIRE( dist == Approx(0.5).margin(1e-12) ); + + delete mesh; +} diff --git a/test/unit/test_thermal_integ.cpp b/test/unit/test_thermal_integ.cpp index 1e8d15de..167332ba 100644 --- a/test/unit/test_thermal_integ.cpp +++ b/test/unit/test_thermal_integ.cpp @@ -181,7 +181,7 @@ TEST_CASE("AggregateResIntegrator::AssembleVector", "[AggregateResIntegrator]") // initialize the vector that dJdx multiplies GridFunction v(mesh_fes); - VectorFunctionCoefficient v_rand(dim, randState); + VectorFunctionCoefficient v_rand(dim, randVectorState); v.ProjectCoefficient(v_rand); // evaluate dJdx and compute its product with v From e05189cee9899c5014a0cf8b10b81d903af9dc42 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Mon, 19 Oct 2020 21:04:01 -0400 Subject: [PATCH 19/28] fixing dev merge 2 --- src/physics/solver.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/physics/solver.cpp b/src/physics/solver.cpp index 076fd016..9e735b53 100644 --- a/src/physics/solver.cpp +++ b/src/physics/solver.cpp @@ -711,9 +711,6 @@ void AbstractSolver::printSolution(const std::string &file_name, } mesh->PrintVTK(sol_ofs, refine); u->SaveVTK(sol_ofs, "Solution", refine); - GridFunction r(u->FESpace()); - res->Mult(*u, r); - r.SaveVTK(sol_ofs, "Residual", refine); sol_ofs.close(); } From 92fdc0baabea7cab78e3850a275e7415a82a9d1e Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Tue, 20 Oct 2020 12:31:15 -0400 Subject: [PATCH 20/28] resolved test issue, merge with dev complete --- src/common/sbp_fe.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common/sbp_fe.cpp b/src/common/sbp_fe.cpp index b4f5be8f..9d840e86 100644 --- a/src/common/sbp_fe.cpp +++ b/src/common/sbp_fe.cpp @@ -783,11 +783,11 @@ void SBPTriangleElement::CalcShape(const IntegrationPoint &ip, Vector &shape) const { int ipIdx; - // try - // { - // ipIdx = ipIdxMap.at(&ip); - // } - // catch (const std::out_of_range& oor) + try + { + ipIdx = ipIdxMap.at(&ip); + } + catch (const std::out_of_range& oor) // error handling code to handle cases where the pointer to ip is not // in the map. Problems arise in GridFunction::SaveVTK() (specifically // GridFunction::GetValues()), which calls CalcShape() with an From 36462b8bb6bf2d508e78819c632f032cd39a0565 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Tue, 20 Oct 2020 21:03:31 -0400 Subject: [PATCH 21/28] added gf_error files --- sandbox/gf_error.cpp | 261 ++++++++++++++++++++++++++++++++++ sandbox/gf_error_options.json | 60 ++++++++ 2 files changed, 321 insertions(+) create mode 100644 sandbox/gf_error.cpp create mode 100644 sandbox/gf_error_options.json diff --git a/sandbox/gf_error.cpp b/sandbox/gf_error.cpp new file mode 100644 index 00000000..8381b5d7 --- /dev/null +++ b/sandbox/gf_error.cpp @@ -0,0 +1,261 @@ +// set this const expression to true in order to use entropy variables for state (doesn't work for rans) +constexpr bool entvar = false; + +#include +#include "adept.h" + +#include "mfem.hpp" +#include "rans.hpp" +#include "mach.hpp" +#include +#include + +using namespace std; +using namespace mfem; +using namespace mach; + +std::default_random_engine gen(std::random_device{}()); +std::uniform_real_distribution uniform_rand(0.0,1.0); + +static double pert_fs; +static double mu; +static double mach_fs; +static double aoa_fs; +static int iroll; +static int ipitch; +static double chi_fs; + +static double m_offset; +static double m_coeff; +static int m_x; +static int m_y; + +/// \brief Defines the random function for the jacobian check +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void pert(const Vector &x, Vector& p); + +/// \brief Defines the exact solution for the rans freestream problem +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void uexact(const Vector &x, Vector& u); + +/// \brief Defines a perturbed solution for the rans freestream problem +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void uinit_pert(const Vector &x, Vector& u); + +/// \brief Defines a pseudo wall-distance function +/// \param[in] x - coordinate of the point at which the distance is needed +double walldist(const Vector &x); + +/// Generate a quadrilateral wall mesh, more dense near the wall +/// \param[in] num_x - number of nodes in x +/// \param[in] num_y - number of nodes in y +std::unique_ptr buildWalledMesh(int num_x, + int num_y); + +int main(int argc, char *argv[]) +{ + const char *options_file = "gf_error_options.json"; + + // Initialize MPI + int num_procs, rank; + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + ostream *out = getOutStream(rank); + + // Parse command-line options + OptionsParser args(argc, argv); + int degree = 2.0; + args.Parse(); + if (!args.Good()) + { + args.PrintUsage(*out); + return 1; + } + + // construct the mesh + string opt_file_name(options_file); + nlohmann::json file_options; + std::ifstream opts(opt_file_name); + opts >> file_options; + pert_fs = 1.0 + file_options["init-pert"].template get(); + mu = file_options["flow-param"]["mu"].template get(); + mach_fs = file_options["flow-param"]["mach"].template get(); + aoa_fs = file_options["flow-param"]["aoa"].template get()*M_PI/180; + iroll = file_options["flow-param"]["roll-axis"].template get(); + ipitch = file_options["flow-param"]["pitch-axis"].template get(); + chi_fs = file_options["flow-param"]["chi"].template get(); + + m_offset = file_options["mesh"]["offset"].template get(); + m_coeff = file_options["mesh"]["coeff"].template get(); + m_x = file_options["mesh"]["num-x"].template get(); + m_y = file_options["mesh"]["num-y"].template get(); + + string file = file_options["file-names-1"].template get(); + string file2 = file_options["file-names-2"].template get(); + + // generate the walled mesh, and its distance function + std::unique_ptr mesh = buildWalledMesh(m_x, m_y); + + stringstream nssolname; stringstream ranssolname; + nssolname << file <<".gf"; ranssolname << file2 <<".gf"; + std::ifstream sol_ns(nssolname.str()); std::ifstream sol_rans(ranssolname.str()); + // std::unique_ptr u_ns; + // std::unique_ptr u_rans; + // std::unique_ptr u_diff; + // u_ns.reset(new GridFunction(mesh.get(), sol_ns)); + // u_rans.reset(new GridFunction(mesh.get(), sol_rans)); + // u_diff.reset(new GridFunction(*u_ns)); + Vector u_ns; Vector u_rans; Vector u_diff; + u_ns.Load(sol_ns, 4); + u_rans.Load(sol_rans, 4); + u_diff = u_ns - u_rans; + //*u_diff -= *u_rans; + + cout << "Error Norm: "<< u_diff.Norml2()< buildWalledMesh(int num_x, int num_y) +{ + auto mesh_ptr = unique_ptr(new Mesh(num_x, num_y, + Element::TRIANGLE, true /* gen. edges */, + 2.33333, 1.0, true)); + // strategy: + // 1) generate a fes for Lagrange elements of desired degree + // 2) create a Grid Function using a VectorFunctionCoefficient + // 4) use mesh_ptr->NewNodes(nodes, true) to set the mesh nodes + + // Problem: fes does not own fec, which is generated in this function's scope + // Solution: the grid function can own both the fec and fes + H1_FECollection *fec = new H1_FECollection(1, 2 /* = dim */); + FiniteElementSpace *fes = new FiniteElementSpace(mesh_ptr.get(), fec, 2, + Ordering::byVDIM); + + // Lambda function increases element density towards wall + double offset = m_offset; + double coeff = m_coeff; + auto xy_fun = [coeff, offset, num_y](const Vector& rt, Vector &xy) + { + xy(0) = rt(0) - 0.33333; + xy(1) = rt(1); + + double c = 1.0/num_y; + double b = log(offset)/(c - 1.0); + double a = 1.0/exp(1.0*b); + + if(rt(1) > 0.0 && rt(1) < 1.0) + { + xy(1) = coeff*a*exp(b*rt(1)); + //std::cout << xy(1) << std::endl; + } + }; + VectorFunctionCoefficient xy_coeff(2, xy_fun); + GridFunction *xy = new GridFunction(fes); + xy->MakeOwner(fec); + xy->ProjectCoefficient(xy_coeff); + + mesh_ptr->NewNodes(*xy, true); + + // Assign extra boundary attribute + for (int i = 0; i < mesh_ptr->GetNBE(); ++i) + { + Element *elem = mesh_ptr->GetBdrElement(i); + + Array verts; + elem->GetVertices(verts); + + bool before = true; //before wall + for (int j = 0; j < 2; ++j) + { + auto vtx = mesh_ptr->GetVertex(verts[j]); + //cout<< "B El: "<SetAttribute(5); + } + } + mesh_ptr->bdr_attributes.Append(5); + // mesh_ptr->SetAttributes(); + // mesh_ptr->Finalize(); + + cout << "Number of bdr attr " << mesh_ptr->bdr_attributes.Size() <<'\n'; + cout << "Number of elements " << mesh_ptr->GetNE() <<'\n'; + + return mesh_ptr; +} diff --git a/sandbox/gf_error_options.json b/sandbox/gf_error_options.json new file mode 100644 index 00000000..62607166 --- /dev/null +++ b/sandbox/gf_error_options.json @@ -0,0 +1,60 @@ +{ + "flow-param": { + "mach": 0.2, + "aoa": 0.0, + "roll-axis": 0, + "pitch-axis": 1, + "chi": 0.0, + "Re": 5000000.0, + "Pr": 0.75, + "mu": 1.0, + "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9] + }, + "space-dis": { + "degree": 1, + "lps-coeff": 1.0, + "basis-type": "csbp" + }, + "time-dis": { + "steady": true, + "steady-abstol": 1e-12, + "steady-restol": 1e-10, + "const-cfl": true, + "ode-solver": "PTC", + "t-final": 100, + "dt": 100.0, + "cfl": 1.0, + "res-exp": 2.0 + }, + "nonlin-solver": { + "abstol": 1e-12, + "maxiter": 100, + "printlevel": 1, + "reltol": 1e-2, + "type": "newton" + }, + "lin-solver": { + "reltol": 1e-2, + "abstol": 1e-12, + "printlevel": 0, + "maxiter": 100 + }, + "bcs": { + "far-field": [0, 1, 1, 1, 0], + "slip-wall": [0, 0, 0, 0, 1], + "no-slip-adiabatic": [1, 0, 0, 0, 0] + }, + "wall-func": { + "type": "y-dist" + }, + "mesh": { + "num-x": 26, + "num-y": 18, + "offset": 1e-2, + "coeff": 1.0 + }, + "init-pert": 0.0, + "file-names-1": "ns_1e-2_final", + "file-names-2": "rans_noturb_1e-2_final" +} + From 7b1003f9bb22752f140d21177bfd4f76bdd2040f Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Thu, 29 Oct 2020 22:36:45 -0400 Subject: [PATCH 22/28] somewhat robust convergence now, but there are scaling hacks in mfem --- sandbox/rans_walltest.cpp | 120 ++++---- sandbox/rans_walltest_options.json | 7 +- src/common/inexact_newton.cpp | 143 +++++----- src/physics/fluidflow/rans.cpp | 47 +++- src/physics/fluidflow/rans.hpp | 3 +- src/physics/fluidflow/rans_fluxes.hpp | 106 +++++-- src/physics/fluidflow/rans_integ.hpp | 10 +- src/physics/fluidflow/rans_integ_def.hpp | 340 ++++++++--------------- 8 files changed, 387 insertions(+), 389 deletions(-) diff --git a/sandbox/rans_walltest.cpp b/sandbox/rans_walltest.cpp index 02911dad..b6a69e02 100644 --- a/sandbox/rans_walltest.cpp +++ b/sandbox/rans_walltest.cpp @@ -124,71 +124,26 @@ int main(int argc, char *argv[]) if (file_options["try-rans"].template get()) { - // construct the solver and set initial conditions - auto solver = createSolver>(opt_file_name, - move(smesh)); - solver->setInitialCondition(uinit_pert); - solver->printSolution(fileinit.str(), 0); - - double res_error = solver->calcResidualNorm(); - *out << "\ninitial rans residual norm = " << res_error << endl; - //solver->checkJacobian(pert); - solver->solveForState(); - solver->printSolution(filefinal.str(),0); - // get the final density error - res_error = solver->calcResidualNorm(); - - *out << "\nfinal rans residual norm = " << res_error; - u_rans = solver->getFields(); + // construct the solver and set initial conditions + auto solver = createSolver>(opt_file_name, + move(smesh)); + solver->setInitialCondition(uinit_pert); + solver->printSolution(fileinit.str(), 0); + + double res_error = solver->calcResidualNorm(); + *out << "\ninitial rans residual norm = " << res_error << endl; + //solver->checkJacobian(pert); + solver->solveForState(); + solver->printSolution(filefinal.str(),0); + // get the final density error + res_error = solver->calcResidualNorm(); + + out->precision(15); + *out << "\nfinal rans residual norm = " << res_error; + double drag = abs(solver->calcOutput("drag")); + *out << "\nDrag = " << drag << endl; } - if (file_options["try-ns"].template get()) - { - - // construct the solver and set initial conditions - std::unique_ptr smesh2 = buildWalledMesh(m_x, m_y); - auto solver2 = createSolver>(opt_file_name, - move(smesh2)); - solver2->setInitialCondition(uinit_pert_ns); - fileinit << "_ns"; - filefinal << "_ns"; - solver2->printSolution(fileinit.str(), 0); - - double res_error2 = solver2->calcResidualNorm(); - *out << "\ninitial ns residual norm = " << res_error2 << endl; - solver2->checkJacobian(pert_ns); - solver2->solveForState(); - solver2->printSolution(filefinal.str(),0); - res_error2 = solver2->calcResidualNorm(); - - *out << "\nfinal ns residual norm = " << res_error2; - u_ns = solver2->getFields(); - } - - //if (file_options["compare"].template get()) - //{ - - *out << "\n Before Reording " << endl; - mfem::Vector u_rans_comp(u_ns[0]->Size()); - for(int i = 0; i < u_rans[0]->Size()/5; i++) - { - for(int j = 0; j < 4; j++) - { - u_rans_comp(j + i*4) = u_rans[0]->Elem(j + i*5); - } - } - - // u_rans[0]->ReorderByNodes(); - // u_ns[0]->ReorderByNodes(); - *out << "\n After Reording = " << endl; - - //u_rans[0]->SetSize(u_ns[0]->Size()); - - *u_ns[0] -= u_rans_comp; - - *out << "\n ns-rans result norm = " << u_ns[0]->Norml2() << endl; - //} - } catch (MachException &exception) { @@ -314,7 +269,25 @@ std::unique_ptr buildWalledMesh(int num_x, int num_y) // Lambda function increases element density towards wall double offset = m_offset; double coeff = m_coeff; - auto xy_fun = [coeff, offset, num_y](const Vector& rt, Vector &xy) + // double s1 = m_s1; + // double s2 = m_s2; + + // double A = sqrt(s2)/sqrt(s1); + // double B = 1.0/sqrt(s1*s2); + + // // need to solve transcendental equation sinh(x)/x = B for x, use Newton's method + // double del = 5.0; + // double res = sinh(del) - B*del; + // double dres; + // while(res > 1e-15) + // { + // dres = cosh(del) - B; + // del = del - res/dres; + // res = sinh(del) - B*del; + // } + // double delta = del; + + auto xy_fun = [coeff, offset, num_y/*A, delta*/](const Vector& rt, Vector &xy) { xy(0) = rt(0) - 0.33333; xy(1) = rt(1); @@ -323,12 +296,19 @@ std::unique_ptr buildWalledMesh(int num_x, int num_y) double b = log(offset)/(c - 1.0); double a = 1.0/exp(1.0*b); - ///Condense mesh near wall - if(rt(1) > 0.0 && rt(1) < 1.0) - { - xy(1) = coeff*a*exp(b*rt(1)); - //std::cout << xy(1) << std::endl; - } + // double u = 0.5*(1 + tanh(delta*(rt(1) - 0.5))/tanh(delta))); + + // double s = u/(A + (1-A)*u); + + + //Condense mesh near wall + xy(1) = (1 + tanh(coeff*(rt(1) - 1))/tanh(coeff)); + + // if(rt(1) > 0.0 && rt(1) < 1.0) + // { + // xy(1) = coeff*a*exp(b*rt(1)); + // //std::cout << xy(1) << std::endl; + // } ///TODO: condense mesh near wall transition as well //double cx1 = 2.0/ diff --git a/sandbox/rans_walltest_options.json b/sandbox/rans_walltest_options.json index 16a84e97..d34760a1 100644 --- a/sandbox/rans_walltest_options.json +++ b/sandbox/rans_walltest_options.json @@ -10,7 +10,7 @@ "mu": 1.0, "viscous-mms": false, "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9], - "sa-srcs": [0.0, 1.0] + "sa-srcs": [1.0, 1.0] }, "space-dis": { "degree": 1, @@ -33,7 +33,7 @@ "maxiter": 100, "printlevel": 1, "reltol": 1e-2, - "type": "newton" + "type": "inexact-newton" }, "lin-solver": { "reltol": 1e-5, @@ -62,6 +62,9 @@ "offset": 1e-3, "coeff": 1.0 }, + "outputs": { + "drag": [1, 0, 0, 0, 0] + }, "init-pert": 0.0, "file-names": "rans_wturb_1e-3", "try-rans": true, diff --git a/src/common/inexact_newton.cpp b/src/common/inexact_newton.cpp index 72daf04b..c8242939 100755 --- a/src/common/inexact_newton.cpp +++ b/src/common/inexact_newton.cpp @@ -13,68 +13,68 @@ namespace mfem double InexactNewton::ComputeStepSize (const Vector &x, const Vector &b, const double norm) const { - double theta = 0.0; - double s = 1.0; - // p0, p1, and p0p are used for quadratic interpolation p(s) in [0,1]. - // p0 is the value of p(0), p0p is the derivative p'(0), and - // p1 is the value of p(1). */ - double p0, p1, p0p; - // A temporary vector for calculating p0p. - Vector temp(r.Size()); - - p0 = 0.5 * norm * norm; - // temp=F'(x_i)*r(x_i) - jac->Mult(r,temp); - // c is the negative inexact newton step size. - p0p = -Dot(c,temp); - //Calculate the new norm. - - add(x,-1.0,c,x_new); - oper->Mult(x_new,r); - const bool have_b = (b.Size()==Height()); - if (have_b) - { - r -= b; - } - double err_new = Norm(r); - - // Globalization start from here. - int itt=0; - while (err_new > (1 - t * (1 - theta) ) * norm) - { - p1 = 0.5*err_new*err_new; - // Quadratic interpolation between [0,1] - theta = quadInterp(0.0, p0, p0p, 1.0, p1); - theta = (theta > theta_min) ? theta : theta_min; - theta = (theta < theta_max) ? theta : theta_max; - // set the new trial step size. - s *= theta; - // update eta - eta = 1 - theta * (1- eta); - eta = (eta < eta_max) ? eta : eta_max; - // re-evaluate the error norm at new x. - add(x,-s,c,x_new); - oper->Mult(x_new, r); - if (have_b) - { - r-=b; - } - err_new = Norm(r); - - // Check the iteration counts. - itt ++; - if (itt > max_iter) - { - mfem::mfem_error("Fail to globalize: Exceed maximum iterations.\n"); - break; - } - } - if (print_level>=0) - { - mfem::out << " Globalization factors: theta= "<< s - << ", eta= " << eta <<'\n'; - } - return s; + // double theta = 0.0; + // double s = 1.0; + // // p0, p1, and p0p are used for quadratic interpolation p(s) in [0,1]. + // // p0 is the value of p(0), p0p is the derivative p'(0), and + // // p1 is the value of p(1). */ + // double p0, p1, p0p; + // // A temporary vector for calculating p0p. + // Vector temp(r.Size()); + + // p0 = 0.5 * norm * norm; + // // temp=F'(x_i)*r(x_i) + // jac->Mult(r,temp); + // // c is the negative inexact newton step size. + // p0p = -Dot(c,temp); + // //Calculate the new norm. + + // add(x,-1.0,c,x_new); + // oper->Mult(x_new,r); + // const bool have_b = (b.Size()==Height()); + // if (have_b) + // { + // r -= b; + // } + // double err_new = Norm(r); + + // // Globalization start from here. + // int itt=0; + // while (err_new > (1 - t * (1 - theta) ) * norm) + // { + // p1 = 0.5*err_new*err_new; + // // Quadratic interpolation between [0,1] + // theta = quadInterp(0.0, p0, p0p, 1.0, p1); + // theta = (theta > theta_min) ? theta : theta_min; + // theta = (theta < theta_max) ? theta : theta_max; + // // set the new trial step size. + // s *= theta; + // // update eta + // eta = 1 - theta * (1- eta); + // eta = (eta < eta_max) ? eta : eta_max; + // // re-evaluate the error norm at new x. + // add(x,-s,c,x_new); + // oper->Mult(x_new, r); + // if (have_b) + // { + // r-=b; + // } + // err_new = Norm(r); + + // // Check the iteration counts. + // itt ++; + // if (itt > max_iter) + // { + // mfem::mfem_error("Fail to globalize: Exceed maximum iterations.\n"); + // break; + // } + // } + // if (print_level>=0) + // { + // mfem::out << " Globalization factors: theta= "<< s + // << ", eta= " << eta <<'\n'; + // } + return 1.0;//s; } void InexactNewton::SetOperator(const Operator &op) @@ -146,10 +146,21 @@ void InexactNewton::Mult(const Vector &b, Vector &x) const } jac = &oper->GetGradient(x); - // stringstream nssolname; - // nssolname << "jac_" <PrintMatlab(matlab); + + // time to scale ? can't since newton expects the base Operator class + // double scale = 1e-4; + // for(int s = 0; s < r.Size()/5; s++) + // { + // r(4 + s*5) *= scale; + + // jac->ScaleRow(4 + s*5, scale); + // } + + + stringstream nssolname; + nssolname << "jac_" <PrintMatlab(matlab); // std::cout << "Get the jacobian matrix.\n"; prec->SetOperator(*jac); //std::cout << "jac is set as one operator.\n"; diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index d92372a0..eab0742e 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -41,8 +41,9 @@ void RANavierStokesSolver::addResVolumeIntegrators(double alpha) this->res->AddDomainIntegrator(new SAViscousIntegrator( //SAViscousIntegrator this->diff_stack, this->re_fs, this->pr_fs, sacs, mu, alpha)); // now add RANS integrators + double d0 = getZeroDistance(); this->res->AddDomainIntegrator(new SASourceIntegrator( - this->diff_stack, *dist, this->re_fs, sacs, mu, -alpha, srcs[0], srcs[1])); + this->diff_stack, *dist, this->re_fs, sacs, mu, -alpha, srcs[0], srcs[1], d0)); // add LPS stabilization double lps_coeff = this->options["space-dis"]["lps-coeff"].template get(); this->res->AddDomainIntegrator(new SALPSIntegrator( @@ -247,7 +248,8 @@ void RANavierStokesSolver::getDistanceFunction() { std::string wall_type = this->options["wall-func"]["type"].template get(); - H1_FECollection *dfec = new H1_FECollection(1, dim); + int fe_order = this->options["space-dis"]["degree"].template get(); + H1_FECollection *dfec = new H1_FECollection(fe_order, dim); FiniteElementSpace *dfes = new FiniteElementSpace(this->mesh.get(), dfec); dist.reset(new GridFunction(dfes)); @@ -274,6 +276,47 @@ void RANavierStokesSolver::getDistanceFunction() ///TODO: Add option for proper wall distance function } +template +double RANavierStokesSolver::getZeroDistance() +{ + std::string wall_type = + this->options["wall-func"]["type"].template get(); + // H1_FECollection *dfec = new H1_FECollection(1, dim); + // FiniteElementSpace *dfes = new FiniteElementSpace(this->mesh.get(), dfec); + // unique_ptr distcheck; + // distcheck.reset(new GridFunction(dfes)); + + double d0 = 0.0; + + if (wall_type == "const") // uniform d value + { + // doesn't make any difference + double val = this->options["wall-func"]["val"].template get(); + d0 = val; + } + if (wall_type == "y-dist") // y distance from the origin + { + + // this should probably work for any distance function + double work = 1e15; + // exhaustive search of the mesh for the smallest d value + for(int i = 0; i < dist->Size(); i++) + { + if(dist->Elem(i) < work && dist->Elem(i) > 0.0) + { + work = dist->Elem(i); + } + } + + // half the smallest computed distance from the wall + d0 = work/2; + } + ///TODO: Add option for proper wall distance function + + cout << "At-wall distance: "< normal_rand_ns(-1.0,1.0); diff --git a/src/physics/fluidflow/rans.hpp b/src/physics/fluidflow/rans.hpp index 4e3366c6..02d29516 100644 --- a/src/physics/fluidflow/rans.hpp +++ b/src/physics/fluidflow/rans.hpp @@ -65,7 +65,8 @@ class RANavierStokesSolver : public NavierStokesSolver /// create mesh distance function from options void getDistanceFunction(); - + /// get the distance value to calculate at the wall itself + double getZeroDistance(); /// SA constants vector mfem::Vector sacs; /// Gridfunction for wall distance function (unused for now) diff --git a/src/physics/fluidflow/rans_fluxes.hpp b/src/physics/fluidflow/rans_fluxes.hpp index 54e7d3cd..a00922af 100644 --- a/src/physics/fluidflow/rans_fluxes.hpp +++ b/src/physics/fluidflow/rans_fluxes.hpp @@ -49,7 +49,7 @@ xdouble calcSALaminarSuppression(const xdouble *q, const xdouble mu, { xdouble ct3 = sacs[7]; xdouble ct4 = sacs[8]; - xdouble nu_tilde = q[dim+2]/q[0]; + xdouble nu_tilde = q[dim+2];///q[0]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble ft2 = ct3*exp(-ct4*chi*chi); @@ -68,7 +68,7 @@ xdouble calcSACoefficient(const xdouble *q, const xdouble mu, const xdouble *sacs) { xdouble cv1 = sacs[6]; - xdouble nu_tilde = q[dim+2]/q[0]; + xdouble nu_tilde = q[dim+2];///q[0]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble fv1 = chi*chi*chi/(cv1*cv1*cv1 + chi*chi*chi); @@ -86,7 +86,7 @@ template xdouble calcSAProductionCoefficient(const xdouble *q, const xdouble mu, const xdouble *sacs) { - xdouble nu_tilde = q[dim+2]/q[0]; + xdouble nu_tilde = q[dim+2];///q[0]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble fv1 = calcSACoefficient(q, mu, sacs); @@ -109,18 +109,18 @@ xdouble calcSAModifiedVorticity(const xdouble *q, const xdouble S, const xdouble mu, const xdouble d, const xdouble Re, const xdouble *sacs) { - xdouble nu_tilde = q[dim+2]/q[0]; + xdouble nu_tilde = q[dim+2];///q[0]; xdouble kappa = sacs[3]; xdouble cv2 = sacs[11]; xdouble cv3 = sacs[12]; xdouble fv2 = calcSAProductionCoefficient(q, mu, sacs); xdouble work = nu_tilde*fv2/(kappa*kappa*d*d); xdouble St; - if (work < -cv2*S) - St = S + (S*(cv2*cv2*S + cv3*work))/((cv3-2*cv2)*S - work); + if (work/Re < -cv2*S) + St = S + (S*(cv2*cv2*S*Re + cv3*work))/((cv3-2*cv2)*S*Re - work); else - St = S + work; - return St; + St = S + work/Re; + return Re*St; } /// Returns the destruction coefficient in SA @@ -143,7 +143,7 @@ xdouble calcSADestructionCoefficient(const xdouble *q, using adept::min; using std::pow; using adept::pow; - xdouble nu_tilde = q[dim+2]/q[0]; + xdouble nu_tilde = q[dim+2];///q[0]; xdouble kappa = sacs[3]; xdouble cw2 = sacs[4]; xdouble cw3 = sacs[5]; @@ -152,7 +152,7 @@ xdouble calcSADestructionCoefficient(const xdouble *q, xdouble St = calcSAModifiedVorticity(q, S, mu, d, Re, sacs); //xdouble fv2 = calcSAProductionCoefficient(q, mu, sacs); - xdouble work = nu_tilde/(Re*St*kappa*kappa*d*d); + xdouble work = nu_tilde/(St*kappa*kappa*d*d); xdouble r = min(work, rlim); xdouble r6 = r*r*r*r*r*r; @@ -181,11 +181,11 @@ xdouble calcSAProduction(const xdouble *q, const xdouble *sacs) { xdouble cb1 = sacs[0]; - xdouble nu_tilde = q[dim+2]/q[0]; + xdouble nu_tilde = q[dim+2];///q[0]; xdouble St = calcSAModifiedVorticity(q, S, mu, d, Re, sacs); xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); xdouble P = cb1*(1.0-ft2)*St*nu_tilde; - return q[0]*P; + return /*q[0]*/P/Re; } /// Returns the destruction term in SA @@ -205,12 +205,12 @@ xdouble calcSADestruction(const xdouble *q, xdouble cb2 = sacs[1]; xdouble sigma = sacs[2]; xdouble kappa = sacs[3]; - xdouble chi_d = q[dim+2]/(d*q[0]); + xdouble chi_d = q[dim+2]/(d/*q[0]*/); xdouble cw1 = cb1/(kappa*kappa) + (1+cb2)/sigma; xdouble fw = calcSADestructionCoefficient(q, mu, d, S, Re, sacs); xdouble ft2 = calcSALaminarSuppression(q, mu, sacs); xdouble D = (cw1*fw - (cb1/(kappa*kappa))*ft2)*chi_d*chi_d; - return -q[0]*D; + return /*q[0]*/-D/Re; } /// Returns the production term in negative SA @@ -226,9 +226,9 @@ xdouble calcSANegativeProduction(const xdouble *q, const xdouble S, { xdouble cb1 = sacs[0]; xdouble ct3 = sacs[7]; - xdouble nu_tilde = q[dim+2]/q[0]; + xdouble nu_tilde = q[dim+2];///q[0]; xdouble Pn = cb1*(1.0-ct3)*S*nu_tilde; - return q[0]*Pn; + return /*q[0]*/Pn; } /// Returns the destruction term in negative SA @@ -240,16 +240,16 @@ xdouble calcSANegativeProduction(const xdouble *q, const xdouble S, /// \tparam dim - number of spatial dimensions (1, 2, or 3) template xdouble calcSANegativeDestruction(const xdouble *q, const xdouble d, - const xdouble *sacs) + const xdouble Re, const xdouble *sacs) { xdouble cb1 = sacs[0]; xdouble cb2 = sacs[1]; xdouble sigma = sacs[2]; xdouble kappa = sacs[3]; - xdouble chi_d = q[dim+2]/(d*q[0]); + xdouble chi_d = q[dim+2]/(d/*q[0]*/); xdouble cw1 = cb1/(kappa*kappa) + (1+cb2)/sigma; xdouble Dn = -cw1*chi_d*chi_d; - return -q[0]*Dn; + return /*q[0]*/-Dn/Re; } /// Returns the turbulent viscosity coefficient in negative SA @@ -264,7 +264,7 @@ xdouble calcSANegativeCoefficient(const xdouble *q, const xdouble mu, const xdouble *sacs) { xdouble cn1 = sacs[10]; - xdouble nu_tilde = q[dim+2]/q[0]; + xdouble nu_tilde = q[dim+2];///q[0]; xdouble nu_mat = mu/q[0]; xdouble chi = nu_tilde/nu_mat; xdouble fn = (cn1 + chi*chi*chi)/(cn1 - chi*chi*chi); @@ -287,7 +287,7 @@ xdouble calcSASource(const xdouble *q, const xdouble *dir, xdouble cb2 = sacs[1]; xdouble sigma = sacs[2]; xdouble Sr = (cb2/sigma)*dot(dir, dir); - return q[0]*Sr; + return /*q[0]*/Sr; } /// Returns the viscous "source" term in SA @@ -305,9 +305,71 @@ xdouble calcSASource2(const xdouble *q, const xdouble mu, const xdouble *dir, xdouble sigma = sacs[2]; xdouble fn = calcSANegativeCoefficient(q, mu, sacs); xdouble Sr = (1.0/sigma)*(mu/q[0] + fn*nu_tilde)*dot(dir, dir2); - return Sr; + return 0.0;//Sr; +} + + + +/// Returns the full sum of source terms +/// \param[in] q - state used to define the destruction +/// \param[in] mu - **nondimensionalized** viscosity +/// \param[in] d - wall distance value +/// \param[in] S - vorticity magnitude +/// \param[in] Re - Reynold's number, if needed +/// \param[in] d0 - at-wall distance value +/// \param[in] dir - turbulent viscosity gradient +/// \param[in] dir2 - density gradient +/// \param[in] sacs - Spalart-Allmaras constants +/// \returns src - full source term sum +/// \tparam xdouble - typically `double` or `adept::adouble` +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +template +xdouble calcSAFullSource(const xdouble *q, + const xdouble mu, const xdouble d, + const xdouble S, const xdouble Re, + const xdouble d0, const xdouble *dir, + const xdouble *dir2, const xdouble *sacs, + const xdouble prod, const xdouble dest) +{ + xdouble src = calcSASource( + q, dir, sacs)/Re; + src -= calcSASource2( + q, mu, dir, dir2, sacs)/Re; + if (fabs(d) > 1e-12) + { + if (q[dim+2] < 0) + { + src += prod*calcSANegativeProduction( + q, S, sacs); + src += dest*calcSANegativeDestruction( + q, d, Re, sacs); + } + else + { + src += prod*calcSAProduction( + q, mu, d, S, Re, sacs); + src += dest*calcSADestruction( + q, mu, d, S, Re, sacs); + } + } + else + { + if (q[dim+2] < 0) + { + src += dest*calcSANegativeDestruction( + q, d0, Re, sacs); + } + else + { + src += dest*calcSADestruction( + q, mu, d0, S, Re, sacs); + } + } + return src; } + + #if 0 // Compute vorticity on an SBP element, needed for SA model terms /// \param[in] q - the state over the element diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp index 1984e49a..0611a137 100644 --- a/src/physics/fluidflow/rans_integ.hpp +++ b/src/physics/fluidflow/rans_integ.hpp @@ -68,9 +68,9 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator /// \param[in] vis - nondimensional dynamic viscosity (use Sutherland if neg) /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) /// \param[in] P, D - control production and destruction terms for debugging - SASourceIntegrator(adept::Stack &diff_stack, mfem::GridFunction distance, double re_fs, - mfem::Vector sa_params, double vis = -1.0, double a = 1.0, double P = 1.0, double D = 1.0) - : alpha(a), prod(P), dest(D), mu(vis), stack(diff_stack), num_states(dim+3), Re(re_fs), sacs(sa_params), + SASourceIntegrator(adept::Stack &diff_stack, mfem::GridFunction distance, double re_fs, + mfem::Vector sa_params, double vis = -1.0, double a = 1.0, double P = 1.0, double D = 1.0, double dmin = 1e-4) + : alpha(a), d0(dmin), prod(P), dest(D), mu(vis), stack(diff_stack), num_states(dim+3), Re(re_fs), sacs(sa_params), dist(distance) {} /// Construct the element local residual @@ -105,6 +105,8 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator double alpha; /// Freestream Reynolds number double Re; + /// 0-wall distance to evaluate + double d0; /// vector of SA model parameters mfem::Vector sacs; /// stack used for algorithmic differentiation @@ -116,6 +118,8 @@ class SASourceIntegrator : public mfem::NonlinearFormIntegrator mfem::Vector xi; /// used to reference the states at node i mfem::Vector ui; + /// used to reference the conservative states at node i + mfem::Vector uci; /// used to reference the gradient of nu at node i mfem::Vector grad_nu_i; /// used to reference the gradient of rho at node i diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index d124612c..91621076 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -9,11 +9,12 @@ void SASourceIntegrator::AssembleElementVector( const SBPFiniteElement &sbp = dynamic_cast(fe); int num_nodes = sbp.GetDof(); #ifdef MFEM_THREAD_SAFE - Vector ui, xi, uj, grad_nu_i, curl_i; + Vector ui, uci, xi, uj, grad_nu_i, curl_i; DenseMatrix grad, curl; #endif elvect.SetSize(num_states * num_nodes); ui.SetSize(num_states); + uci.SetSize(num_states); xi.SetSize(dim); grad_nu_i.SetSize(dim); grad_rho_i.SetSize(dim); @@ -31,7 +32,7 @@ void SASourceIntegrator::AssembleElementVector( { u(di+1, nn) *= 1.0/u(0, nn); } - u(dim+2, nn) *= 1.0/u(0, nn); + //u(dim+2, nn) *= 1.0/u(0, nn); } // precompute certain values @@ -42,6 +43,7 @@ void SASourceIntegrator::AssembleElementVector( const IntegrationPoint &node = fe.GetNodes().IntPoint(i); Trans.SetIntPoint(&node); u.GetColumn(i, ui); + //uc.GetRow(i, uci); Trans.Transform(node, xi); // get Dui @@ -65,50 +67,11 @@ void SASourceIntegrator::AssembleElementVector( double d = dist.GetValue(Trans.ElementNo, node); //temp solution, y distance from wall // accumulate source terms - double src = calcSASource( - ui.GetData(), grad_nu_i.GetData(), sacs.GetData())/Re; - src -= calcSASource2( - ui.GetData(), mu, grad_nu_i.GetData(), grad_rho_i.GetData(), sacs.GetData())/Re; + double src = calcSAFullSource(ui.GetData(), + mu, d, S, Re, d0, grad_nu_i.GetData(), + grad_rho_i.GetData(), sacs.GetData(), + prod, dest); - //use negative model if turbulent viscosity is negative - double d0 = 1e-4; - if (fabs(d) > 1e-12) - { - if (ui(dim+2) < 0) - { - src += prod*calcSANegativeProduction( - ui.GetData(), S, sacs.GetData()); - src += dest*calcSANegativeDestruction( - ui.GetData(), d, sacs.GetData())/Re; - //cout << "Negative!"<( - ui.GetData(), mu, d, S, Re, sacs.GetData());///Re; - ///std::cout << "P+grad:" << src << std::endl; - src += dest*calcSADestruction( - ui.GetData(), mu, d, S, Re, sacs.GetData())/Re; - ///std::cout << "P+D+grad:" << src << std::endl; - } - } - else - { - if (ui(dim+2) < 0) - { - src += dest*calcSANegativeDestruction( - ui.GetData(), d0, sacs.GetData())/Re; - } - else - { - src += dest*calcSADestruction( - ui.GetData(), mu, d0, S, Re, sacs.GetData())/Re; - } - } - // if (d < 1e-12) - // d = d0; - src = prod*(ui(dim+2)-3); ///!!!!!!!!!!!!!!!!!!!!!!!!!!! - //cout << src << endl; //byNODES elvect(i + num_nodes*(num_states-1)) = alpha * Trans.Weight() * node.weight * src; @@ -125,9 +88,10 @@ void SASourceIntegrator::AssembleElementGrad( const SBPFiniteElement &sbp = dynamic_cast(fe); int num_nodes = sbp.GetDof(); #ifdef MFEM_THREAD_SAFE - Vector ui, xi, uj, grad_nu_i, curl_i; + Vector ui, uci, xi, uj, grad_nu_i, curl_i; #endif ui.SetSize(num_states); + uci.SetSize(num_states); xi.SetSize(dim); grad_nu_i.SetSize(dim); grad_rho_i.SetSize(dim); @@ -171,7 +135,7 @@ void SASourceIntegrator::AssembleElementGrad( { u(di+1, nn) *= 1.0/u(0, nn); } - u(dim+2, nn) *= 1.0/u(0, nn); + //u(dim+2, nn) *= 1.0/u(0, nn); } elmat.SetSize(num_states*num_nodes); @@ -183,6 +147,7 @@ void SASourceIntegrator::AssembleElementGrad( const IntegrationPoint &node = fe.GetNodes().IntPoint(i); Trans.SetIntPoint(&node); u.GetColumn(i, ui); + //uc.GetRow(i, uci); Trans.Transform(node, xi); // get Dui, insert each spatial derivative into column matrix @@ -228,7 +193,7 @@ void SASourceIntegrator::AssembleElementGrad( // get distance function value at node adouble d = dist.GetValue(Trans.ElementNo, node); //evaluate the distance gridfunction at the node - adouble d0 = 1e-4; //temporary solution for nodes on wall + adouble d0_a = d0; // if (d < 1e-12) // d = d0; @@ -239,45 +204,51 @@ void SASourceIntegrator::AssembleElementGrad( this->stack.independent(curl_i_a.data(), curl_i.Size()); this->stack.dependent(S); this->stack.jacobian(dSdc.GetData()); + adouble src; dSrcdu = 0.0; // ui differentiation this->stack.new_recording(); - adouble src = calcSASource( - ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; - src -= calcSASource2( - ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; - if (fabs(d) > 1e-12) - { - if (ui_a[dim+2] < 0) - { - src += prod*calcSANegativeProduction( - ui_a.data(), S, sacs_a.data()); - src += dest*calcSANegativeDestruction( - ui_a.data(), d, sacs_a.data())/Re_a; - } - else - { - src += prod*calcSAProduction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data());///Re_a; - src += dest*calcSADestruction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; - } - } - else - { - if (ui_a[dim+2] < 0) - { - src += dest*calcSANegativeDestruction( - ui_a.data(), d0, sacs_a.data())/Re_a; - } - else - { - src += dest*calcSADestruction( - ui_a.data(), mu_a, d0, S, Re_a, sacs_a.data())/Re_a; - } - } - src = prod*(ui_a[dim+2]-3); ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + src = calcSAFullSource(ui_a.data(), + mu_a, d, S, Re_a, d0_a, grad_nu_i_a.data(), + grad_rho_i_a.data(), sacs_a.data(), + prod, dest); + + // adouble src = calcSASource( + // ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; + // src -= calcSASource2( + // ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; + // if (fabs(d) > 1e-12) + // { + // if (ui_a[dim+2] < 0) + // { + // src += prod*calcSANegativeProduction( + // ui_a.data(), S, sacs_a.data()); + // src += dest*calcSANegativeDestruction( + // ui_a.data(), d, Re_a, sacs_a.data()); + // } + // else + // { + // src += prod*calcSAProduction( + // ui_a.data(), mu_a, d, S, Re_a, sacs_a.data()); + // src += dest*calcSADestruction( + // ui_a.data(), mu_a, d, S, Re_a, sacs_a.data()); + // } + // } + // else + // { + // if (ui_a[dim+2] < 0) + // { + // src += dest*calcSANegativeDestruction( + // ui_a.data(), d0_a, Re_a, sacs_a.data()); + // } + // else + // { + // src += dest*calcSADestruction( + // ui_a.data(), mu_a, d0_a, S, Re_a, sacs_a.data()); + // } + // } + //src = prod*(uci_a[dim+2]-3)*d; ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this->stack.independent(ui_a.data(), ui.Size()); this->stack.dependent(src); this->stack.jacobian(dSrcdu.GetData()); @@ -285,41 +256,11 @@ void SASourceIntegrator::AssembleElementGrad( dSrcdS = 0.0; // S differentiation this->stack.new_recording(); - src = calcSASource( - ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; - src -= calcSASource2( - ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; - if (fabs(d) > 1e-12) - { - if (ui_a[dim+2] < 0) - { - src += prod*calcSANegativeProduction( - ui_a.data(), S, sacs_a.data()); - src += dest*calcSANegativeDestruction( - ui_a.data(), d, sacs_a.data())/Re_a; - } - else - { - src += prod*calcSAProduction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data());///Re_a; - src += dest*calcSADestruction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; - } - } - else - { - if (ui_a[dim+2] < 0) - { - src += dest*calcSANegativeDestruction( - ui_a.data(), d0, sacs_a.data())/Re_a; - } - else - { - src += dest*calcSADestruction( - ui_a.data(), mu_a, d0, S, Re_a, sacs_a.data())/Re_a; - } - } - src = prod*(ui_a[dim+2]-3); ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + src = calcSAFullSource(ui_a.data(), + mu_a, d, S, Re_a, d0_a, grad_nu_i_a.data(), + grad_rho_i_a.data(), sacs_a.data(), + prod, dest); + //src = prod*(uci_a[dim+2]-3)*d; ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this->stack.independent(S); this->stack.dependent(src); this->stack.jacobian(dSrcdS.GetData()); @@ -327,41 +268,11 @@ void SASourceIntegrator::AssembleElementGrad( dSrcdgradnu = 0.0; // grad nu differentiation this->stack.new_recording(); - src = calcSASource( - ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; - src -= calcSASource2( - ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; - if (fabs(d) > 1e-12) - { - if (ui_a[dim+2] < 0) - { - src += prod*calcSANegativeProduction( - ui_a.data(), S, sacs_a.data()); - src += dest*calcSANegativeDestruction( - ui_a.data(), d, sacs_a.data())/Re_a; - } - else - { - src += prod*calcSAProduction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data());///Re_a; - src += dest*calcSADestruction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; - } - } - else - { - if (ui_a[dim+2] < 0) - { - src += dest*calcSANegativeDestruction( - ui_a.data(), d0, sacs_a.data())/Re_a; - } - else - { - src += dest*calcSADestruction( - ui_a.data(), mu_a, d0, S, Re_a, sacs_a.data())/Re_a; - } - } - src = prod*(ui_a[dim+2]-3); ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + src = calcSAFullSource(ui_a.data(), + mu_a, d, S, Re_a, d0_a, grad_nu_i_a.data(), + grad_rho_i_a.data(), sacs_a.data(), + prod, dest); + //src = prod*(uci_a[dim+2]-3)*d; ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this->stack.independent(grad_nu_i_a.data(), grad_nu_i_a.size()); this->stack.dependent(src); this->stack.jacobian(dSrcdgradnu.GetData()); @@ -369,51 +280,16 @@ void SASourceIntegrator::AssembleElementGrad( dSrcdgradrho = 0.0; // grad rho differentiation this->stack.new_recording(); - src = calcSASource( - ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; - src -= calcSASource2( - ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; - if (fabs(d) > 1e-12) - { - if (ui_a[dim+2] < 0) - { - src += prod*calcSANegativeProduction( - ui_a.data(), S, sacs_a.data()); - src += dest*calcSANegativeDestruction( - ui_a.data(), d, sacs_a.data())/Re_a; - } - else - { - src += prod*calcSAProduction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data());///Re_a; - src += dest*calcSADestruction( - ui_a.data(), mu_a, d, S, Re_a, sacs_a.data())/Re_a; - } - } - else - { - if (ui_a[dim+2] < 0) - { - src += dest*calcSANegativeDestruction( - ui_a.data(), d0, sacs_a.data())/Re_a; - } - else - { - src += dest*calcSADestruction( - ui_a.data(), mu_a, d0, S, Re_a, sacs_a.data())/Re_a; - } - } - src = prod*(ui_a[dim+2]-3); ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + src = calcSAFullSource(ui_a.data(), + mu_a, d, S, Re_a, d0_a, grad_nu_i_a.data(), + grad_rho_i_a.data(), sacs_a.data(), + prod, dest); + //src = prod*(uci_a[dim+2]-3)*d; ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this->stack.independent(grad_rho_i_a.data(), grad_rho_i_a.size()); this->stack.dependent(src); this->stack.jacobian(dSrcdgradrho.GetData()); // Assemble nu derivative - // Direct ui sensitivities - for(int ns = 0; ns < num_states; ns ++) - { - dnu(i + ns*num_nodes) = dSrcdu(ns); - } // Dui sensitivities for (int di = 0; di < dim; di++) @@ -435,6 +311,12 @@ void SASourceIntegrator::AssembleElementGrad( dnu += work1; } + // Direct ui sensitivities + for(int ns = 0; ns < num_states; ns ++) + { + dnu(i + ns*num_nodes) += dSrcdu(ns); + } + // node weighting dnu *= (Trans.Weight() * node.weight); @@ -446,9 +328,21 @@ void SASourceIntegrator::AssembleElementGrad( dnu(nn) -= dnu(nn + (di+1)*num_nodes)*uc(nn, di+1)/(uc(nn, 0)*uc(nn, 0)); dnu(nn + (di+1)*num_nodes) *= 1.0/uc(nn, 0); } - dnu(nn) -= dnu(nn + (dim+2)*num_nodes)*uc(nn, dim+2)/(uc(nn, 0)*uc(nn, 0)); - dnu(nn + (dim+2)*num_nodes) *= 1.0/uc(nn, 0); + //dnu(nn) -= dnu(nn + (dim+2)*num_nodes)*uc(nn, dim+2)/(uc(nn, 0)*uc(nn, 0)); + //dnu(nn + (dim+2)*num_nodes) *= 1.0/uc(nn, 0); } + // for (int nn = 0; nn < num_nodes; nn++) + // { + // for (int di = 0; di < dim; di++) + // { + // dnu(nn + (di+1)*num_nodes) *= 1.0/uc(nn, 0); + // dnu(nn + (di+1)*num_nodes) += dnu(nn)/u(di+1, nn); + // } + // dnu(nn + (dim+2)*num_nodes) *= 1.0/uc(nn, 0); + // dnu(nn + (dim+2)*num_nodes) += dnu(nn)/u(dim+2, nn); + // } + + // Set elmat entry @@ -558,7 +452,7 @@ void SANoSlipAdiabaticWallBC::calcFlux(const mfem::Vector &x, sacs.GetData()); if (q(dim+2)<0) fv1 = 0.0; - double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; + double mu_Re_SA = (mu_Re + q(0)*q(dim+2)*fv1)/Re; calcAdiabaticWallFlux(dir.GetData(), mu_Re_SA, Pr, q.GetData(), Dw.GetData(), work_vec.GetData()); work_vec(dim+2) = 0.0; @@ -570,15 +464,15 @@ void SANoSlipAdiabaticWallBC::calcFlux(const mfem::Vector &x, double SAflux = dot(dir.GetData(), grad); double fn = calcSANegativeCoefficient(q.GetData(), mu, sacs.GetData()); - flux_vec(dim+2) -= (mu + fn*q(dim+2))*SAflux/(sacs(2)*Re); + flux_vec(dim+2) -= (mu/q(0) + fn*q(dim+2))*SAflux/(sacs(2)*Re); // Step 3: evaluate the no-slip penalty calcNoSlipPenaltyFlux(dir.GetData(), jac, mu_Re_SA, Pr, qfs.GetData(), q.GetData(), work_vec.GetData()); work_vec(dim+2) = 0.0; flux_vec += work_vec; double dnu = q(dim+2); - double dnuflux = (mu + fn*q(dim+2))*dnu/(sacs(2)*Re); - double fac = 100*sqrt(dot(dir, dir))/jac; + double dnuflux = (mu/q(0) + fn*q(dim+2))*dnu/(sacs(2)*Re); + double fac = 10*sqrt(dot(dir, dir))/jac; flux_vec(dim+2) += dnuflux*fac; } @@ -623,7 +517,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacState( sacs_a.data()); if (q_a[dim+2]<0) fv1 = 0.0; - adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + adouble mu_Re_SA = (mu_Re + q_a[0]*q_a[dim+2]*fv1)/Re; mach::calcAdiabaticWallFlux(dir_a.data(), mu_Re_SA, Pr, q_a.data(), Dw_a.data(), work_vec_a.data()); work_vec_a[dim+2] = 0.0; @@ -638,7 +532,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacState( adouble SAflux = dot(dir_a.data(), grad); adouble fn = calcSANegativeCoefficient(q_a.data(), mu, sacs_a.data()); - flux_a[dim+2] -= (mu + fn*q_a[dim+2])*SAflux/(sacs_a[2]*Re); + flux_a[dim+2] -= (mu/q_a[0] + fn*q_a[dim+2])*SAflux/(sacs_a[2]*Re); // Step 3: evaluate the no-slip penalty mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re_SA, Pr, qfs_a.data(), q_a.data(), work_vec_a.data()); @@ -648,8 +542,8 @@ void SANoSlipAdiabaticWallBC::calcFluxJacState( flux_a[i] += work_vec_a[i]; } adouble dnu = q_a[dim+2]; - adouble dnuflux = (mu + fn*q_a[dim+2])*dnu/(sacs_a[2]*Re); - adouble fac = 100*sqrt(dot(dir_a.data(), dir_a.data()))/jac; + adouble dnuflux = (mu/q_a[0] + fn*q_a[dim+2])*dnu/(sacs_a[2]*Re); + adouble fac = 10*sqrt(dot(dir_a.data(), dir_a.data()))/jac; flux_a[dim+2] += dnuflux*fac; this->stack.independent(q_a.data(), q.Size()); @@ -696,7 +590,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf sacs_a.data()); if (q_a[dim+2]<0) fv1 = 0.0; - adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + adouble mu_Re_SA = (mu_Re + q_a[0]*q_a[dim+2]*fv1)/Re; mach::calcAdiabaticWallFlux(dir_a.data(), mu_Re_SA, Pr, q_a.data(), Dw_a.data(), work_vec_a.data()); work_vec_a[dim+2] = 0.0; @@ -711,7 +605,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf adouble SAflux = dot(dir_a.data(), grad); adouble fn = calcSANegativeCoefficient(q_a.data(), mu, sacs_a.data()); - flux_a[dim+2] -= (mu + fn*q_a[dim+2])*SAflux/(sacs_a[2]*Re); + flux_a[dim+2] -= (mu/q_a[0] + fn*q_a[dim+2])*SAflux/(sacs_a[2]*Re); // Step 3: evaluate the no-slip penalty mach::calcNoSlipPenaltyFlux(dir_a.data(), jac, mu_Re_SA, Pr, qfs_a.data(), q_a.data(), work_vec_a.data()); @@ -721,8 +615,8 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf flux_a[i] += work_vec_a[i]; } adouble dnu = q_a[dim+2]; - adouble dnuflux = (mu + fn*q_a[dim+2])*dnu/(sacs_a[2]*Re); - adouble fac = 100*sqrt(dot(dir_a.data(), dir_a.data()))/jac; + adouble dnuflux = (mu/q_a[0] + fn*q_a[dim+2])*dnu/(sacs_a[2]*Re); + adouble fac = 10*sqrt(dot(dir_a.data(), dir_a.data()))/jac; flux_a[dim+2] += dnuflux*fac; this->stack.independent(Dw_a.data(),Dw_size); @@ -750,10 +644,10 @@ void SANoSlipAdiabaticWallBC::calcFluxDv(const mfem::Vector &x, sacs.GetData()); if (q(dim+2)<0) fv1 = 0.0; - double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; + double mu_Re_SA = (mu_Re + q(0)*q(dim+2)*fv1)/Re; double fn = calcSANegativeCoefficient(q.GetData(), mu, sacs.GetData()); - double mu_2 = (mu_Re + q(dim+2)*fn)/Re; + double mu_2 = (mu_Re/q(0) + q(dim+2)*fn)/Re; calcNoSlipDualFluxSA(dir.GetData(), mu_Re_SA, mu_2, Pr, q.GetData(), flux_mat.GetData()); } @@ -785,10 +679,10 @@ void SANoSlipAdiabaticWallBC::calcFluxDvJacState( sacs_a.data()); if (q_a[dim+2]<0) fv1 = 0.0; - adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + adouble mu_Re_SA = (mu_Re + q_a[0]*q_a[dim+2]*fv1)/Re; adouble fn = calcSANegativeCoefficient(q_a.data(), mu, sacs_a.data()); - adouble mu_2 = (mu_Re + q_a[dim+2]*fn)/Re; + adouble mu_2 = (mu_Re/q_a[0] + q_a[dim+2]*fn)/Re; calcNoSlipDualFluxSA(dir_a.data(), mu_Re_SA, mu_2, Pr, q_a.data(), fluxes_a.data()); @@ -849,7 +743,7 @@ void SAViscousSlipWallBC::calcFlux(const mfem::Vector &x, sacs.GetData()); if (q(dim+2)<0) fv1 = 0.0; - double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; + double mu_Re_SA = (mu_Re + q(0)*q(dim+2)*fv1)/Re; for (int d = 0; d < dim; ++d) { work_vec = 0.0; @@ -859,7 +753,7 @@ void SAViscousSlipWallBC::calcFlux(const mfem::Vector &x, flux_vec -= work_vec; double fn = calcSANegativeCoefficient(q.GetData(), mu, sacs.GetData()); - flux_vec(dim+2) -= (mu + fn*q(dim+2))*Dw_work(dim+2 + d*(dim+3))/(sacs(2)*Re); + flux_vec(dim+2) -= (mu/q(0) + fn*q(dim+2))*Dw_work(dim+2 + d*(dim+3))/(sacs(2)*Re); } } @@ -919,7 +813,7 @@ void SAViscousSlipWallBC::calcFluxJacState( sacs_a.data()); if (q_a[dim+2]<0) fv1 = 0.0; - adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + adouble mu_Re_SA = (mu_Re + q_a[0]*q_a[dim+2]*fv1)/Re; for (int d = 0; d < dim; ++d) { work_vec_a[dim+2] = 0.0; @@ -932,7 +826,7 @@ void SAViscousSlipWallBC::calcFluxJacState( } adouble fn = calcSANegativeCoefficient(q_a.data(), mu, sacs_a.data()); - flux_a[dim+2] -= (mu + fn*q_a[dim+2])*Dw_work_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); + flux_a[dim+2] -= (mu/q_a[0] + fn*q_a[dim+2])*Dw_work_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); } this->stack.independent(q_a.data(), q.Size()); this->stack.dependent(flux_a.data(), q.Size()); @@ -993,7 +887,7 @@ void SAViscousSlipWallBC::calcFluxJacDw(const mfem::Vector &x, const mfem:: sacs_a.data()); if (q_a[dim+2]<0) fv1 = 0.0; - adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + adouble mu_Re_SA = (mu_Re + q_a[0]*q_a[dim+2]*fv1)/Re; for (int d = 0; d < dim; ++d) { work_vec_a[dim+2] = 0.0; @@ -1006,7 +900,7 @@ void SAViscousSlipWallBC::calcFluxJacDw(const mfem::Vector &x, const mfem:: } adouble fn = calcSANegativeCoefficient(q_a.data(), mu, sacs_a.data()); - flux_a[dim+2] -= (mu + fn*q_a[dim+2])*Dw_work_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); + flux_a[dim+2] -= (mu/q_a[0] + fn*q_a[dim+2])*Dw_work_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); } this->stack.independent(Dw_a.data(),Dw_size); @@ -1339,12 +1233,12 @@ void SAViscousIntegrator::applyScaling(int d, const mfem::Vector &x, sacs.GetData()); if (q(dim+2)<0) fv1 = 0.0; - double mu_Re_SA = (mu_Re + q(dim+2)*fv1)/Re; + double mu_Re_SA = (mu_Re + q(0)*q(dim+2)*fv1)/Re; applyViscousScalingSA(d, mu_Re_SA, Pr, q.GetData(), Dw.GetData(), CDw.GetData()); double fn = calcSANegativeCoefficient(q.GetData(), mu, sacs.GetData()); - CDw(dim+2) = (mu + fn*q(dim+2))*Dw(dim+2, d)/(sacs(2)*Re); + CDw(dim+2) = (mu/q(0) + fn*q(dim+2))*Dw(dim+2, d)/(sacs(2)*Re); // if (mu + fn*q(dim+2) < 0) // cout << "Negative diffusion!: " <::applyScalingJacState(int d, const mfem::Vector &x sacs_a.data()); if (q_a[dim+2]<0) fv1 = 0.0; - adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + adouble mu_Re_SA = (mu_Re + q_a[0]*q_a[dim+2]*fv1)/Re; applyViscousScalingSA(d, mu_Re_SA, Pr, q_a.data(), Dw_a.data(), CDw_a.data()); adouble fn = calcSANegativeCoefficient(q_a.data(), mu, sacs_a.data()); - CDw_a[dim+2] = (mu + fn*q_a[dim+2])*Dw_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); + CDw_a[dim+2] = (mu/q_a[0] + fn*q_a[dim+2])*Dw_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); // identify independent and dependent variables this->stack.independent(q_a.data(), q.Size()); this->stack.dependent(CDw_a.data(), q.Size()); @@ -1420,12 +1314,12 @@ void SAViscousIntegrator::applyScalingJacDw( sacs_a.data()); if (q_a[dim+2]<0) fv1 = 0.0; - adouble mu_Re_SA = (mu_Re + q_a[dim+2]*fv1)/Re; + adouble mu_Re_SA = (mu_Re + q_a[0]*q_a[dim+2]*fv1)/Re; applyViscousScalingSA(d, mu_Re_SA, Pr, q_a.data(), Dw_a.data(), CDw_a.data()); adouble fn = calcSANegativeCoefficient(q_a.data(), mu, sacs_a.data()); - CDw_a[dim+2] = (mu + fn*q_a[dim+2])*Dw_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); + CDw_a[dim+2] = (mu/q_a[0] + fn*q_a[dim+2])*Dw_a[dim+2 + d*(dim+3)]/(sacs_a[2]*Re); // identify independent and dependent variables this->stack.independent(Dw_a.data(), Dw_size); this->stack.dependent(CDw_a.data(), q.Size()); From 37df9d4bf3c43872dd7c82cd25c8de8ab422607f Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Tue, 22 Dec 2020 14:51:55 -0500 Subject: [PATCH 23/28] started adding cfd mesh sensitivities, rans mms problem --- sandbox/CMakeLists.txt | 31 +-- sandbox/rans_freestream.cpp | 42 +++- sandbox/rans_freestream_options.json | 1 + sandbox/rans_mms.cpp | 214 ++++++++++++++++++ sandbox/rans_mms_options.json | 52 +++++ sandbox/rans_walltest_options.json | 1 + src/common/default_options.cpp | 3 +- src/common/sbp_fe.cpp | 27 +++ src/common/sbp_fe.hpp | 7 + src/physics/fluidflow/CMakeLists.txt | 1 + src/physics/fluidflow/mesh_sens_integ.hpp | 74 ++++++ src/physics/fluidflow/mesh_sens_integ_def.hpp | 162 +++++++++++++ .../fluidflow/navier_stokes_sens_integ.hpp | 68 ++++++ .../navier_stokes_sens_integ_def.hpp | 0 src/physics/fluidflow/rans.cpp | 77 +++++-- src/physics/fluidflow/rans.hpp | 1 + src/physics/fluidflow/rans_fluxes.hpp | 55 ++++- src/physics/fluidflow/rans_integ.hpp | 30 +++ src/physics/fluidflow/rans_integ_def.hpp | 47 ---- src/physics/fluidflow/rans_sens_integ.hpp | 81 +++++++ src/physics/fluidflow/rans_sens_integ_def.hpp | 18 ++ test/unit/CMakeLists.txt | 33 +-- test/unit/test_navier_stokes_sens_integ.cpp | 90 ++++++++ test/unit/test_rans_fluxes.cpp | 43 ++-- 24 files changed, 1032 insertions(+), 126 deletions(-) create mode 100644 sandbox/rans_mms.cpp create mode 100644 sandbox/rans_mms_options.json create mode 100644 src/physics/fluidflow/navier_stokes_sens_integ.hpp create mode 100644 src/physics/fluidflow/navier_stokes_sens_integ_def.hpp create mode 100644 src/physics/fluidflow/rans_sens_integ.hpp create mode 100644 src/physics/fluidflow/rans_sens_integ_def.hpp create mode 100644 test/unit/test_navier_stokes_sens_integ.cpp diff --git a/sandbox/CMakeLists.txt b/sandbox/CMakeLists.txt index 14b6f31f..64a8d318 100644 --- a/sandbox/CMakeLists.txt +++ b/sandbox/CMakeLists.txt @@ -14,23 +14,24 @@ function(create_sandbox source_list) endfunction(create_sandbox) set(SANDBOX_SRCS - steady_vortex - steady_vortex_adjoint - unsteady_vortex - viscous_shock - airfoil_steady - airfoil_chaotic - magnetostatic_box - magnetostatic_motor - magnetostatic_wire - navier_stokes_mms - thermal_square - surface_distance - joule_wire - ns_walltest + #steady_vortex + #steady_vortex_adjoint + #unsteady_vortex + #viscous_shock + #airfoil_steady + #airfoil_chaotic + #magnetostatic_box + #magnetostatic_motor + #magnetostatic_wire + #navier_stokes_mms + #thermal_square + #surface_distance + #joule_wire + #ns_walltest rans_freestream rans_walltest - gf_error + rans_mms + #gf_error ) create_sandbox("${SANDBOX_SRCS}") diff --git a/sandbox/rans_freestream.cpp b/sandbox/rans_freestream.cpp index d3735848..522a2790 100644 --- a/sandbox/rans_freestream.cpp +++ b/sandbox/rans_freestream.cpp @@ -23,6 +23,7 @@ static double aoa_fs; static int iroll; static int ipitch; static double chi_fs; +static bool mms; /// \brief Defines the random function for the jacobian check /// \param[in] x - coordinate of the point at which the state is needed @@ -39,6 +40,9 @@ void uexact(const Vector &x, Vector& u); /// \param[out] u - conservative + SA variables stored as a 5-vector void uinit_pert(const Vector &x, Vector& u); +void uinit_pert_mms(const Vector &x, Vector& u); + + int main(int argc, char *argv[]) { const char *options_file = "rans_freestream_options.json"; @@ -53,8 +57,8 @@ int main(int argc, char *argv[]) // Parse command-line options OptionsParser args(argc, argv); int degree = 2.0; - int nx = 4; - int ny = 4; + int nx = 10; + int ny = 10; args.AddOption(&nx, "-nx", "--numx", "Number of elements in x direction"); args.AddOption(&ny, "-ny", "--numy", @@ -80,6 +84,8 @@ int main(int argc, char *argv[]) iroll = file_options["flow-param"]["roll-axis"].template get(); ipitch = file_options["flow-param"]["pitch-axis"].template get(); chi_fs = file_options["flow-param"]["chi"].template get(); + mms = file_options["flow-param"]["rans-mms"].template get(); + // generate a simple tri mesh and a distance function std::unique_ptr smesh(new Mesh(nx, ny, Element::TRIANGLE, true /* gen. edges */, 1.0, @@ -89,8 +95,13 @@ int main(int argc, char *argv[]) // construct the solver and set initial conditions auto solver = createSolver>(opt_file_name, move(smesh)); - solver->setInitialCondition(uinit_pert); + if (mms) + solver->setInitialCondition(uinit_pert_mms); + else + solver->setInitialCondition(uinit_pert); solver->printSolution("rans_init", 0); + solver->printResidual("rans_res_init", 0); + // get the initial density error double l2_error_init = (static_cast&>(*solver) @@ -176,3 +187,28 @@ void uinit_pert(const Vector &x, Vector& q) q = u; } + +// initial guess perturbed from exact +void uinit_pert_mms(const Vector &x, Vector& q) +{ + const double rho0 = 1.0; + const double rhop = 0.05; + const double U0 = 0.5; + const double Up = 0.05; + const double T0 = 1.0; + const double Tp = 0.05; + const double chif = 100.0; + const double mu = 1.0; + q.SetSize(5); + q(0) = rho0 + rhop*pow(sin(M_PI*x(0)),2)*sin(M_PI*x(1)); + q(1) = 4.0*U0*x(1)*(1.0 - x(1)) + Up*sin(2 * M_PI * x(1)) * pow(sin(M_PI * x(0)),2); + q(2) = -Up*pow(sin(2 * M_PI * x(0)),2) * sin(M_PI * x(1)); + double T = T0 + Tp*(pow(x(0), 4) - 2 * pow(x(0), 3) + pow(x(0), 2) + + pow(x(1), 4) - 2 * pow(x(1), 3) + pow(x(1), 2)); + double p = q(0)*T; // T is nondimensionalized by 1/(R*a_infty^2) + q(3) = p/euler::gami + 0.5*q(0)*(q(1)*q(1) + q(2)*q(2)); + q(1) *= q(0); + q(2) *= q(0); + + q(4) = q(0)*chif*mu*x(1); +} diff --git a/sandbox/rans_freestream_options.json b/sandbox/rans_freestream_options.json index 5c3c287a..7201eb7f 100644 --- a/sandbox/rans_freestream_options.json +++ b/sandbox/rans_freestream_options.json @@ -8,6 +8,7 @@ "Re": 1000.0, "Pr": 0.75, "mu": 1.0, + "rans-mms": false, "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9], "sa-srcs": [1.0, 1.0] }, diff --git a/sandbox/rans_mms.cpp b/sandbox/rans_mms.cpp new file mode 100644 index 00000000..c7e41a38 --- /dev/null +++ b/sandbox/rans_mms.cpp @@ -0,0 +1,214 @@ +// set this const expression to true in order to use entropy variables for state (doesn't work for rans) +constexpr bool entvar = false; + +#include +#include "adept.h" + +#include "mfem.hpp" +#include "rans.hpp" +#include +#include + +using namespace std; +using namespace mfem; +using namespace mach; + +std::default_random_engine gen(std::random_device{}()); +std::uniform_real_distribution uniform_rand(0.0,1.0); + +static double pert_fs; +static double mu; +static double mach_fs; +static double aoa_fs; +static int iroll; +static int ipitch; +static double chi_fs; +static bool mms; + +/// \brief Defines the random function for the jacobian check +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void pert(const Vector &x, Vector& p); + +/// \brief Defines the exact solution for the rans freestream problem +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void uexact(const Vector &x, Vector& u); + +/// \brief Defines a perturbed solution for the rans freestream problem +/// \param[in] x - coordinate of the point at which the state is needed +/// \param[out] u - conservative + SA variables stored as a 5-vector +void uinit_pert(const Vector &x, Vector& u); + +void uinit_pert_mms(const Vector &x, Vector& u); + + +int main(int argc, char *argv[]) +{ + const char *options_file = "rans_mms_options.json"; + + // Initialize MPI + int num_procs, rank; + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &num_procs); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + ostream *out = getOutStream(rank); + + // Parse command-line options + OptionsParser args(argc, argv); + int degree = 2.0; + int nx = 10; + int ny = 10; + args.AddOption(&nx, "-nx", "--numx", + "Number of elements in x direction"); + args.AddOption(&ny, "-ny", "--numy", + "Number of elements in y direction"); + args.Parse(); + if (!args.Good()) + { + args.PrintUsage(*out); + return 1; + } + + try + { + // construct the mesh + string opt_file_name(options_file); + nlohmann::json file_options; + std::ifstream opts(opt_file_name); + opts >> file_options; + pert_fs = 1.0 + file_options["init-pert"].template get(); + mu = file_options["flow-param"]["mu"].template get(); + mach_fs = file_options["flow-param"]["mach"].template get(); + aoa_fs = file_options["flow-param"]["aoa"].template get()*M_PI/180; + iroll = file_options["flow-param"]["roll-axis"].template get(); + ipitch = file_options["flow-param"]["pitch-axis"].template get(); + chi_fs = file_options["flow-param"]["chi"].template get(); + mms = file_options["flow-param"]["sa-mms"].template get(); + + // generate a simple tri mesh and a distance function + std::unique_ptr smesh(new Mesh(nx, ny, + Element::TRIANGLE, true /* gen. edges */, 1.0, + 1.0, true)); + *out << "Number of elements " << smesh->GetNE() <<'\n'; + + // construct the solver and set initial conditions + auto solver = createSolver>(opt_file_name, + move(smesh)); + if (mms) + solver->setInitialCondition(uinit_pert_mms); + else + solver->setInitialCondition(uinit_pert); + solver->printSolution("rans_mms_init", 0); + solver->printResidual("rans_res_mms_init", 0); + + + // get the initial density error + double l2_error_init = (static_cast&>(*solver) + .calcConservativeVarsL2Error(uexact, 0)); + *out << "\n|| rho_h - rho ||_{L^2} init = " << l2_error_init << endl; + + double res_error = solver->calcResidualNorm(); + *out << "\ninitial residual norm = " << res_error << endl; + solver->checkJacobian(pert); + solver->solveForState(); + solver->printSolution("rans_mms_final",0); + // get the final density error + double l2_error_final = (static_cast&>(*solver) + .calcConservativeVarsL2Error(uexact, 0)); + res_error = solver->calcResidualNorm(); + + *out << "\nfinal residual norm = " << res_error; + *out << "\n|| rho_h - rho ||_{L^2} final = " << l2_error_final << endl; + } + catch (MachException &exception) + { + exception.print_message(); + } + catch (std::exception &exception) + { + cerr << exception.what() << endl; + } + +#ifdef MFEM_USE_PETSC + MFEMFinalizePetsc(); +#endif + + MPI_Finalize(); +} + +// perturbation function used to check the jacobian in each iteration +void pert(const Vector &x, Vector& p) +{ + p.SetSize(5); + for (int i = 0; i < 5; i++) + { + p(i) = 2.0 * uniform_rand(gen) - 1.0; + } +} + +// Exact solution; same as freestream bc +void uexact(const Vector &x, Vector& q) +{ + // q.SetSize(4); + // Vector u(4); + q.SetSize(5); + Vector u(5); + + u = 0.0; + u(0) = 1.0; + u(1) = u(0)*mach_fs*cos(aoa_fs); + u(2) = u(0)*mach_fs*sin(aoa_fs); + u(3) = 1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; + u(4) = chi_fs*mu; + + if (entvar == false) + { + q = u; + } + else + { + throw MachException("No entvar for this"); + } +} + +// initial guess perturbed from exact +void uinit_pert(const Vector &x, Vector& q) +{ + q.SetSize(5); + Vector u(5); + + u = 0.0; + u(0) = pert_fs*1.0; + u(1) = u(0)*mach_fs*cos(aoa_fs); + u(2) = u(0)*mach_fs*sin(aoa_fs); + u(3) = pert_fs*1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; + u(4) = pert_fs*chi_fs*mu; + + q = u; +} + +// initial guess perturbed from exact +void uinit_pert_mms(const Vector &x, Vector& q) +{ + const double rho0 = 1.0; + const double rhop = 0.05; + const double U0 = 0.5; + const double Up = 0.05; + const double T0 = 1.0; + const double Tp = 0.05; + const double chif = 100.0; + const double mu = 1.0; + q.SetSize(5); + q(0) = rho0 + rhop*pow(sin(M_PI*x(0)),2)*sin(M_PI*x(1)); + q(1) = 4.0*U0*x(1)*(1.0 - x(1)) + Up*sin(2 * M_PI * x(1)) * pow(sin(M_PI * x(0)),2); + q(2) = -Up*pow(sin(2 * M_PI * x(0)),2) * sin(M_PI * x(1)); + double T = T0 + Tp*(pow(x(0), 4) - 2 * pow(x(0), 3) + pow(x(0), 2) + + pow(x(1), 4) - 2 * pow(x(1), 3) + pow(x(1), 2)); + double p = q(0)*T; // T is nondimensionalized by 1/(R*a_infty^2) + q(3) = p/euler::gami + 0.5*q(0)*(q(1)*q(1) + q(2)*q(2)); + q(1) *= q(0); + q(2) *= q(0); + + q(4) = q(0)*chif*mu*x(1); +} diff --git a/sandbox/rans_mms_options.json b/sandbox/rans_mms_options.json new file mode 100644 index 00000000..05c94606 --- /dev/null +++ b/sandbox/rans_mms_options.json @@ -0,0 +1,52 @@ +{ + "flow-param": { + "mach": 1.0, + "aoa": 0.3, + "roll-axis": 0, + "pitch-axis": 1, + "chi": 3.0, + "Re": 1.0, + "Pr": 0.75, + "mu": 1.0, + "sa-mms": true, + "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9], + "sa-srcs": [1.0, 1.0] + }, + "space-dis": { + "degree": 1, + "lps-coeff": 1.0, + "basis-type": "csbp" + }, + "time-dis": { + "steady": true, + "steady-abstol": 1e-12, + "steady-restol": 1e-10, + "const-cfl": true, + "ode-solver": "PTC", + "t-final": 100, + "dt": 1e6, + "cfl": 1.0 + }, + "nonlin-solver": { + "abstol": 1e-12, + "maxiter": 100, + "printlevel": 1, + "reltol": 1e-2, + "type": "newton" + }, + "lin-solver": { + "reltol": 1e-2, + "abstol": 1e-12, + "printlevel": 0, + "maxiter": 100 + }, + "bcs": { + "far-field": [1, 1, 1, 1] + }, + "wall-func": { + "type": "const", + "val": 1.0 + }, + "file-names": "rans_mms", + "init-pert": 0.0 +} diff --git a/sandbox/rans_walltest_options.json b/sandbox/rans_walltest_options.json index d34760a1..3e92ad3c 100644 --- a/sandbox/rans_walltest_options.json +++ b/sandbox/rans_walltest_options.json @@ -9,6 +9,7 @@ "Pr": 0.75, "mu": 1.0, "viscous-mms": false, + "rans-mms": false, "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9], "sa-srcs": [1.0, 1.0] }, diff --git a/src/common/default_options.cpp b/src/common/default_options.cpp index 04fce734..d3e11af5 100644 --- a/src/common/default_options.cpp +++ b/src/common/default_options.cpp @@ -21,7 +21,8 @@ const nlohmann::json default_options {"mu", -1.0}, // nondimensional viscosity (if negative, use Sutherland's) {"sa-consts", {0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9}}, //Spalart-Allmaras turbulence model constants, defined in rans_fluxes.hpp - {"sa-srcs", {1.0, 1.0}} + {"sa-srcs", {1.0, 1.0}}, + {"sa-mms", false} }}, {"space-dis", // options related to spatial discretization diff --git a/src/common/sbp_fe.cpp b/src/common/sbp_fe.cpp index 9d840e86..33a0f3c5 100644 --- a/src/common/sbp_fe.cpp +++ b/src/common/sbp_fe.cpp @@ -195,6 +195,16 @@ double SBPFiniteElement::getSymEntry(int di, int i, return Eij; } +void SBPFiniteElement::getSymEntryRevDiff(int di, int i, double Sij_bar, + mfem::DenseMatrix &adjJ_i_bar) const +{ + for (int k = 0; k < GetDim(); ++k) + { + //Eij += adjJ_i(k,di)*Q[k](i,i); + adjJ_i_bar(k,di) += Sij_bar*Q[k](i,i); + } +} + double SBPFiniteElement::getQEntry(int di, int i, int j, const mfem::DenseMatrix &adjJ_i, const mfem::DenseMatrix &adjJ_j) const @@ -209,6 +219,23 @@ double SBPFiniteElement::getQEntry(int di, int i, int j, } } +void SBPFiniteElement::getQEntryRevDiff(int di, int i, int j, double Sij_bar, + mfem::DenseMatrix &adjJ_i_bar, + mfem::DenseMatrix &adjJ_j_bar) const +{ + if (i == j) + { + getSymEntryRevDiff(di, i, Sij_bar, adjJ_i_bar); + //adjJ_j_bar *= 0.0; + } + else + { + getSkewEntryRevDiff(di, i, j, 0.5*Sij_bar, adjJ_i_bar, adjJ_j_bar); + // adjJ_i_bar *= 0.5; + // adjJ_j_bar *= 0.5; + } +} + void SBPFiniteElement::getProjOperator(DenseMatrix &P) const { MFEM_ASSERT( P.Size() == dof, ""); diff --git a/src/common/sbp_fe.hpp b/src/common/sbp_fe.hpp index a107cfbf..cb03bfe4 100644 --- a/src/common/sbp_fe.hpp +++ b/src/common/sbp_fe.hpp @@ -137,6 +137,9 @@ class SBPFiniteElement : public NodalFiniteElement /// \returns \f$ (E_{di})_{i,j} \f$ in physical space double getSymEntry(int di, int i, const mfem::DenseMatrix &adjJ_i) const; + void getSymEntryRevDiff(int di, int i, double Sij_bar, + mfem::DenseMatrix &adjJ_i_bar) const; + /// `(i,j)`th entry of skew-symmetric matrix \f$ S_{di} \f$ in physical space /// \param[in] di - desired physical space coordinate direction /// \param[in] i - row index for \f$ S_{di} \f$ @@ -163,6 +166,10 @@ class SBPFiniteElement : public NodalFiniteElement double getQEntry(int di, int i, int j, const mfem::DenseMatrix &adjJ_i, const mfem::DenseMatrix &adjJ_j) const; + void getQEntryRevDiff(int di, int i, int j, double Sij_bar, + mfem::DenseMatrix &adjJ_i_bar, + mfem::DenseMatrix &adjJ_j_bar) const; + /// Attempts to find the index corresponding to a given IntegrationPoint /// \param[in] ip - try to match the coordinates of this point /// \returns index - the index of the node corresponding to `ip` diff --git a/src/physics/fluidflow/CMakeLists.txt b/src/physics/fluidflow/CMakeLists.txt index c0103758..bd72ef0a 100644 --- a/src/physics/fluidflow/CMakeLists.txt +++ b/src/physics/fluidflow/CMakeLists.txt @@ -17,6 +17,7 @@ target_sources(mach navier_stokes_fluxes.hpp navier_stokes_integ_def.hpp navier_stokes_integ.hpp + navier_stokes_sens_integ.hpp navier_stokes.cpp navier_stokes.hpp viscous_integ_def.hpp diff --git a/src/physics/fluidflow/mesh_sens_integ.hpp b/src/physics/fluidflow/mesh_sens_integ.hpp index c6826871..b4dbf64c 100644 --- a/src/physics/fluidflow/mesh_sens_integ.hpp +++ b/src/physics/fluidflow/mesh_sens_integ.hpp @@ -67,6 +67,80 @@ class DyadicMeshSensIntegrator : public mfem::LinearFormIntegrator } }; +/// Integrator for mesh sensitivity of symmetrics viscous integrators +/// \tparam Derived - a class Derived from this one (needed for CRTP) +template +class SymmetricViscousMeshSensIntegrator : public mfem::LinearFormIntegrator +{ +public: + /// Constructs an integrator for dyadic-integrator mesh sensitivities + /// \param[in] state_vec - the state at which to evaluate the senstivity + /// \param[in] adjoint_vec - the adjoint that weights the residual + /// \param[in] num_state_vars - the number of state variables per node + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + SymmetricViscousMeshSensIntegrator(const mfem::GridFunction &state_vec, + const mfem::GridFunction &adjoint_vec, + int num_state_vars = 1, double a = 1.0) + : state(state_vec), adjoint(adjoint_vec), num_states(num_state_vars), + alpha(a) {} + + /// Construct the element local contribution to dF/dx + /// \param[in] el - the finite element whose dF/dx contribution we want + /// \param[in] trans - defines the reference to physical element mapping + /// \param[out] elvect - element local dF/dx + virtual void AssembleRHSElementVect(const mfem::FiniteElement &el, + mfem::ElementTransformation &trans, + mfem::Vector &elvect); + +protected: + /// The state vector used to evaluate fluxes + const mfem::GridFunction &state; + /// The adjoint vector that weights the residuals + const mfem::GridFunction &adjoint; + /// number of states (could extract from state or adjoint) + int num_states; + /// scales the terms; can be used to move to rhs/lhs + double alpha; +#ifndef MFEM_THREAD_SAFE + /// stores the result of calling the flux function + mfem::Vector fluxij; + /// used to store the adjugate of the mapping Jacobian at node i + mfem::DenseMatrix adjJ_i; + /// used to store the adjugate of the mapping Jacobian at node j + mfem::DenseMatrix adjJ_j; + /// used to store the adjugate of the mapping Jacobian at node j + mfem::DenseMatrix adjJ_k; + /// stores derivatives w.r.t. adjugate Jacobian at node i + mfem::DenseMatrix adjJ_i_bar; + /// stores derivatives w.r.t. adjugate Jacobian at node k + mfem::DenseMatrix adjJ_k_bar; + /// stores derivatives w.r.t. mesh nodes + mfem::DenseMatrix PointMat_bar; +#endif + + /// converts working variables to another set (e.g. conservative to entropy) + /// \param[in] u - working states that are to be converted + /// \param[out] w - transformed variables + /// \note This uses the CRTP, so it wraps a call to `convertVars` in Derived. + void convert(const mfem::Vector &u, mfem::Vector &w) + { + static_cast(this)->convertVars(u, w); + } + + /// applies symmetric matrices \f$ C_{d,:}(u) \f$ to input `Dw` + /// \param[in] d - index `d` in \f$ C_{d,:} \f$ matrices + /// \param[in] x - coordinate location at which scaling is evaluated + /// \param[in] u - state at which the symmetric matrices `C` are evaluated + /// \param[in] Dw - `Dw[:,d2]` stores derivative of `w` in direction `d2`. + /// \param[out] CDw - product of the multiplication between the `C` and `Dw`. + /// \note This uses the CRTP, so it wraps call to `applyScaling` in Derived. + void scale(int d, const mfem::Vector &x, const mfem::Vector &u, + const mfem::DenseMatrix &Dw, mfem::Vector &CDw) + { + static_cast(this)->applyScaling(d, x, u, Dw, CDw); + } +}; + /// Integrator for mesh sensitivity associated with boundary integrators /// \tparam Derived - a class Derived from this one (needed for CRTP) template diff --git a/src/physics/fluidflow/mesh_sens_integ_def.hpp b/src/physics/fluidflow/mesh_sens_integ_def.hpp index 78cbc7aa..4e0ce29b 100644 --- a/src/physics/fluidflow/mesh_sens_integ_def.hpp +++ b/src/physics/fluidflow/mesh_sens_integ_def.hpp @@ -78,6 +78,168 @@ void DyadicMeshSensIntegrator::AssembleRHSElementVect( } } + +template +void SymmetricViscousMeshSensIntegrator::AssembleRHSElementVect( + const mfem::FiniteElement &el, + mfem::ElementTransformation &trans, + mfem::Vector &elvect) +{ + using namespace mfem; + + // reverse-diff functions we need are only defined for IsoparametricTrans + IsoparametricTransformation &isotrans = + dynamic_cast(trans); + // extract the relevant sbp operator for this element + const FiniteElementSpace *fes = state.FESpace(); // Should check that fes match with adjoint + const FiniteElement *fe = fes->GetFE(trans.ElementNo); + const SBPFiniteElement &sbp = dynamic_cast(*fe); + // extract the state and adjoint values for this element + const IntegrationRule& ir = sbp.GetNodes(); + DenseMatrix u, psi, Dwi, Dpsi; + state.GetVectorValues(isotrans, ir, u); + adjoint.GetVectorValues(isotrans, ir, psi); + u.Transpose(); psi.Transpose(); + + int num_nodes = sbp.GetDof(); // number of state dofs + int ndof = el.GetDof(); // number of coord node dofs != num_nodes + int dim = el.GetDim(); + Vector xi, ui, uj, wi, wj, psii, psj, CDwi, CDpsi; +#ifdef MFEM_THREAD_SAFE + Vector fluxij; + DenseMatrix adjJ_i_bar, adjJ_k_bar, PointMat_bar; +#endif + fluxij.SetSize(num_states); + xi.SetSize(dim); + wj.SetSize(num_states); + ui.SetSize(num_states); + uj.SetSize(num_states); + psii.SetSize(num_states); + psj.SetSize(num_states); + Dwi.SetSize(num_states,dim); + Dpsi.SetSize(num_states,dim); + CDwi.SetSize(num_states); + CDpsi.SetSize(num_states); + adjJ_i.SetSize(dim); + adjJ_j.SetSize(dim); + adjJ_k.SetSize(dim); + adjJ_i_bar.SetSize(dim); + adjJ_k_bar.SetSize(dim); + elvect.SetSize(dim*ndof); + PointMat_bar.SetSize(dim, ndof); // PointMat_bar = dfdx + PointMat_bar = 0.0; + for (int i = 0; i < num_nodes; ++i) + { + // get the Jacobian (trans.Weight) and cubature weight (node.weight) + const IntegrationPoint &node = sbp.GetNodes().IntPoint(i); + isotrans.SetIntPoint(&node); + trans.SetIntPoint(&node); + double Hinv = 1.0 / (sbp.getDiagNormEntry(i) * trans.Weight()); + CalcAdjugate(trans.Jacobian(), adjJ_i); + u.GetRow(i, ui); + psi.GetRow(i, psii); + trans.Transform(node, xi); + + // compute Dwi and Dpsi derivatives at node i + Dwi = 0.0; Dpsi = 0.0; + for (int j = 0; j < num_nodes; ++j) + { + // Get mapping Jacobian adjugate and transform state to entropy vars + trans.SetIntPoint(&sbp.GetNodes().IntPoint(j)); + CalcAdjugate(trans.Jacobian(), adjJ_j); + u.GetRow(j, uj); psi.GetRow(j, psj); + convert(uj, wj); + for (int d = 0; d < dim; ++d) + { + double Qij = sbp.getQEntry(d, i, j, adjJ_i, adjJ_j); + for (int s = 0; s < num_states; ++s) + { + Dwi(s,d) += Qij * wj(s); + Dpsi(s,d) += Qij * psj(s); + } + } // loop over space dimensions d + } // loop over element nodes j + Dwi *= Hinv; + Dpsi *= Hinv; + double dHinv = -1.0 / (trans.Weight()); // multiply Dwi by this afterward + + // next, scale the derivatives (e.g. using \hat{C} matrices), and then + // apply adjugate jacobian sensitivities + adjJ_i_bar = 0.0; // prepare adjJ_i_bar for accumulation + for (int d = 0; d < dim; ++d) + { + scale(d, xi, ui, Dwi, CDwi); + scale(d, xi, ui, Dpsi, CDpsi); + + for (int k = 0; k < num_nodes; ++k) + { + adjJ_k_bar = 0.0; + trans.SetIntPoint(&sbp.GetNodes().IntPoint(k)); + CalcAdjugate(trans.Jacobian(), adjJ_k); + //double Qik = sbp.getQEntry(d, i, k, adjJ_i, adjJ_k); + // apply transposed derivative in `d` direction + // this evaluates Q_d'*(C_d,1 D_1 + C_d,2 D_2 + ...) w + // begin reverse sweep... + double Sij_u_bar = 0.0; + double Sij_psi_bar = 0.0; + for (int s = 0; s < num_states; ++s) + { + // res(k, s) += alpha * Qik * CDwi(s); + Sij_u_bar += alpha*CDwi(s)*psii(s); + Sij_psi_bar += alpha*CDpsi(s)*ui(s); + } + sbp.getQEntryRevDiff(d, i, k, Sij_u_bar, adjJ_i_bar, adjJ_k_bar); + sbp.getQEntryRevDiff(d, i, k, Sij_psi_bar, adjJ_i_bar, adjJ_k_bar); + isotrans.SetIntPoint(&ir.IntPoint(k)); + // adjJ_i = isotrans.AdjugateJacobian(); + isotrans.AdjugateJacobianRevDiff(adjJ_k_bar, PointMat_bar); + } + } // loop over space dimensions d + isotrans.SetIntPoint(&ir.IntPoint(i)); + // adjJ_i = isotrans.AdjugateJacobian(); + isotrans.AdjugateJacobianRevDiff(adjJ_i_bar, PointMat_bar); + + // Hinv sensitivity: compute dHinv, and accumulate energy in H_bar + Dwi *= dHinv; + double H_bar = 0.0; + for (int d = 0; d < dim; ++d) { + scale(d, xi, ui, Dwi, CDwi); + for (int k = 0; k < num_nodes; ++k) + { + psi.GetRow(k, psj); + // Get mapping Jacobian adjugate + trans.SetIntPoint(&sbp.GetNodes().IntPoint(k)); + CalcAdjugate(trans.Jacobian(), adjJ_k); + double Qik = sbp.getQEntry(d, i, k, adjJ_i, adjJ_k); + // apply transposed derivative in `d` direction + for (int s = 0; s < num_states; ++s) + { + H_bar += alpha * Qik * CDwi(s)*psj(s); + } + } // loop over element nodes k + } // loop over space dimensions d + DenseMatrix PointMat_bar_w(dim, ndof); + PointMat_bar_w = 0.0; + isotrans.WeightRevDiff(PointMat_bar_w); + + // add to total sensitivity + PointMat_bar.Add(H_bar, PointMat_bar_w); + } // i node loop + + + + // Insert PointMat_bar = dfdx into elvect + for (int i = 0; i < ndof; ++i) + { + for (int d = 0; d < dim; ++d) + { + elvect(d*ndof + i) = PointMat_bar(d, i); + } + } +} + + + template void BoundaryMeshSensIntegrator::AssembleRHSElementVect( const mfem::FiniteElement &el, mfem::ElementTransformation &trans, diff --git a/src/physics/fluidflow/navier_stokes_sens_integ.hpp b/src/physics/fluidflow/navier_stokes_sens_integ.hpp new file mode 100644 index 00000000..a6b8fcbc --- /dev/null +++ b/src/physics/fluidflow/navier_stokes_sens_integ.hpp @@ -0,0 +1,68 @@ +#ifndef MACH_NAVIER_STOKES_SENS_INTEG +#define MACH_NAVIER_STOKES_SENS_INTEG + +#include "adept.h" +#include "mfem.hpp" + +#include "mesh_sens_integ.hpp" +#include "navier_stokes_fluxes.hpp" + +using adept::adouble; + +namespace mach +{ + +/// Integrator for the mesh sensitivity of the Ismail-Roe domain integrator +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \tparam entvar - if true, the state variables are the entropy variables +/// \note This derived class uses the CRTP +template +class ESViscousMeshSensIntegrator : public SymmetricViscousMeshSensIntegrator< + ESViscousMeshSensIntegrator> +{ +public: + /// Construct an integrator for the Ismail-Roe flux over domains + /// \param[in] a - factor, usually used to move terms to rhs + ESViscousMeshSensIntegrator(const mfem::GridFunction &state_vec, + const mfem::GridFunction &adjoint_vec, + double Re_num, double Pr_num, + double vis = -1.0, double a = 1.0) + : SymmetricViscousMeshSensIntegrator>( + state_vec, adjoint_vec, dim+2, a), + Re(Re_num), Pr(Pr_num), mu(vis) {} + + void convertVars(const mfem::Vector &q, mfem::Vector &w) + { + calcEntropyVars(q.GetData(), w.GetData()); + } + + void applyScaling(int d, const mfem::Vector &x, const mfem::Vector &q, + const mfem::DenseMatrix &Dw, mfem::Vector &CDw) + { + double mu_Re = mu; + if (mu < 0.0) + { + mu_Re = calcSutherlandViscosity(q.GetData()); + } + mu_Re /= Re; + // applyViscousScaling(d, mu_Re, Pr, q.GetData(), Dw.GetData(), + // CDw.GetData()); + for(int i = 0; i < CDw.Size(); i++) + { + CDw(i) = mu_Re*Dw(i,d); + } + } +private: + /// Reynolds number + double Re; + /// Prandtl number + double Pr; + /// nondimensional dynamic viscosity + double mu; +}; + +#include "navier_stokes_sens_integ_def.hpp" + +} // namespace mach + +#endif \ No newline at end of file diff --git a/src/physics/fluidflow/navier_stokes_sens_integ_def.hpp b/src/physics/fluidflow/navier_stokes_sens_integ_def.hpp new file mode 100644 index 00000000..e69de29b diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index eab0742e..e0597730 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -32,22 +32,39 @@ RANavierStokesSolver::RANavierStokesSolver(const nlohmann::json &js template void RANavierStokesSolver::addResVolumeIntegrators(double alpha) { - // add Navier Stokes integrators vector srcs = this->options["flow-param"]["sa-srcs"].template get>(); Vector q_ref(dim+3); - this->getFreeStreamState(q_ref); - this->res->AddDomainIntegrator(new SAInviscidIntegrator( //Inviscid term - this->diff_stack, alpha)); - this->res->AddDomainIntegrator(new SAViscousIntegrator( //SAViscousIntegrator - this->diff_stack, this->re_fs, this->pr_fs, sacs, mu, alpha)); - // now add RANS integrators + this->getFreeStreamState(q_ref); double d0 = getZeroDistance(); - this->res->AddDomainIntegrator(new SASourceIntegrator( + bool mms = this->options["flow-param"]["sa-mms"].template get(); + + // MMS option + if (mms) + { + if (dim != 2) + { + throw MachException("SA MMS problem only available for 2D!"); + } + this->res->AddDomainIntegrator( + new SAMMSIntegrator(this->re_fs, this->pr_fs)); + this->res->AddDomainIntegrator(new SASourceIntegrator( this->diff_stack, *dist, this->re_fs, sacs, mu, -alpha, srcs[0], srcs[1], d0)); - // add LPS stabilization - double lps_coeff = this->options["space-dis"]["lps-coeff"].template get(); - this->res->AddDomainIntegrator(new SALPSIntegrator( - this->diff_stack, alpha, lps_coeff)); + } + else + { + + this->res->AddDomainIntegrator(new SAInviscidIntegrator( //Inviscid term + this->diff_stack, alpha)); + this->res->AddDomainIntegrator(new SAViscousIntegrator( //SAViscousIntegrator + this->diff_stack, this->re_fs, this->pr_fs, sacs, mu, alpha)); + // now add RANS integrators + this->res->AddDomainIntegrator(new SASourceIntegrator( + this->diff_stack, *dist, this->re_fs, sacs, mu, -alpha, srcs[0], srcs[1], d0)); + // add LPS stabilization + double lps_coeff = this->options["space-dis"]["lps-coeff"].template get(); + this->res->AddDomainIntegrator(new SALPSIntegrator( + this->diff_stack, alpha, lps_coeff)); + } } @@ -261,9 +278,7 @@ void RANavierStokesSolver::getDistanceFunction() } if (wall_type == "y-dist") // y distance from the origin { - double offset = - this->options["mesh"]["offset"].template get(); - auto walldist = [offset](const Vector &x) + auto walldist = [](const Vector &x) { // if(x(1) == 0.0) // return 0.25*offset; //// not going to do it this way @@ -272,8 +287,33 @@ void RANavierStokesSolver::getDistanceFunction() }; FunctionCoefficient wall_coeff(walldist); dist->ProjectCoefficient(wall_coeff); + } + if (wall_type == "true") // true computed distance function + { + // assemble wall attribute vector + auto &bcs = this->options["bcs"]; + vector tmp = bcs["no-slip-adiabatic"].template get>(); + vector tmp2 = bcs["slip-wall"].template get>(); + for(int i = 0; i < tmp.size(); i++) + { + tmp[i] = tmp[i] + tmp2[i]; + } + mfem::Array wall_marker; + wall_marker.SetSize(tmp.size(), 0); + wall_marker.Assign(tmp.data()); + + // create surface object + Surface surf(*this->mesh, wall_marker); + + // project surface function + auto walldist = [&surf](const Vector &x) + { + return surf.calcDistance(x); + }; + FunctionCoefficient wall_coeff(walldist); + dist->ProjectCoefficient(wall_coeff); } - ///TODO: Add option for proper wall distance function + ///TODO: Differentiate true wall function } template @@ -294,15 +334,14 @@ double RANavierStokesSolver::getZeroDistance() double val = this->options["wall-func"]["val"].template get(); d0 = val; } - if (wall_type == "y-dist") // y distance from the origin + else { - // this should probably work for any distance function double work = 1e15; // exhaustive search of the mesh for the smallest d value for(int i = 0; i < dist->Size(); i++) { - if(dist->Elem(i) < work && dist->Elem(i) > 0.0) + if(dist->Elem(i) < work && dist->Elem(i) > 1e-15) { work = dist->Elem(i); } diff --git a/src/physics/fluidflow/rans.hpp b/src/physics/fluidflow/rans.hpp index 02d29516..ede22977 100644 --- a/src/physics/fluidflow/rans.hpp +++ b/src/physics/fluidflow/rans.hpp @@ -4,6 +4,7 @@ #include "mfem.hpp" #include "navier_stokes.hpp" +#include "surface.hpp" namespace mach { diff --git a/src/physics/fluidflow/rans_fluxes.hpp b/src/physics/fluidflow/rans_fluxes.hpp index a00922af..a5e55477 100644 --- a/src/physics/fluidflow/rans_fluxes.hpp +++ b/src/physics/fluidflow/rans_fluxes.hpp @@ -331,10 +331,11 @@ xdouble calcSAFullSource(const xdouble *q, const xdouble *dir2, const xdouble *sacs, const xdouble prod, const xdouble dest) { - xdouble src = calcSASource( - q, dir, sacs)/Re; - src -= calcSASource2( - q, mu, dir, dir2, sacs)/Re; + xdouble src = 0.0; + // src += calcSASource( + // q, dir, sacs)/Re; + // src -= calcSASource2( + // q, mu, dir, dir2, sacs)/Re; if (fabs(d) > 1e-12) { if (q[dim+2] < 0) @@ -348,8 +349,8 @@ xdouble calcSAFullSource(const xdouble *q, { src += prod*calcSAProduction( q, mu, d, S, Re, sacs); - src += dest*calcSADestruction( - q, mu, d, S, Re, sacs); + // src += dest*calcSADestruction( + // q, mu, d, S, Re, sacs); } } else @@ -703,6 +704,48 @@ void applyViscousScalingSA(int d, xdouble mu, double Pr, const xdouble *q, } } + +/// MMS source term for a particular Navier-Stokes verification +/// \param[in] mu - nondimensionalized dynamic viscosity +/// \param[in] Pr - Prandtl number +/// \param[in] x - location at which to evaluate the source +/// \param[out] src - the source value +/// \tparam xdouble - typically `double` or `adept::adouble` +template +void calcSAMMS(double mu, double Pr, const xdouble *x, xdouble *src) +{ + double gamma = euler::gamma; + const double rho0 = 1.0; + const double rhop = 0.05; + const double U0 = 0.5; + const double Up = 0.05; + const double T0 = 1.0; + const double Tp = 0.05; + double kappa = mu * gamma / (Pr * euler::gami); + const double chif = 100.0; + // const double nut = chif*mu*x[1]; + // const double rho = rho0 + rhop*sin(M_PI*x[0])*sin(M_PI*x[0])*sin(M_PI*x[1]); + // const double chi = nut*rho/mu; + // const double fv1 = (chi*chi*chi)/(chi*chi*chi + 7.1*7.1*7.1); + // const double fv2 = 1 - chi/(1 + chi*fv1); + // const double mu_t = mu + rho*nut*fv1; + // const double dummy = sqrt(pow(-4*U0*x[1] + 4*U0*(1 - x[1]) + 2*M_PI*Up*pow(sin(M_PI*x[0]), 2)*cos(2*M_PI*x[1]) + 4*M_PI*Up*sin(2*M_PI*x[0])*sin(M_PI*x[1])*cos(2*M_PI*x[0]), 2)); + + src[0] = 0 ; + + src[1] = 0 ; + + src[2] = 0 ; + + src[3] = 0 ; + + //-P + //src[4] = chif*mu*x[1]*(0.13550000000000001 - 0.16259999999999999*exp(-0.5*pow(chif, 2)*pow(x[1], 2)*pow(rho0 + rhop*pow(sin(M_PI*x[0]), 2)*sin(M_PI*x[1]), 2)))*(-3.5693039857227848*chif*mu*(chif*x[1]*(rho0 + rhop*pow(sin(M_PI*x[0]), 2)*sin(M_PI*x[1]))/(pow(chif, 4)*pow(x[1], 4)*pow(rho0 + rhop*pow(sin(M_PI*x[0]), 2)*sin(M_PI*x[1]), 4)/(pow(chif, 3)*pow(x[1], 3)*pow(rho0 + rhop*pow(sin(M_PI*x[0]), 2)*sin(M_PI*x[1]), 3) + 357.91099999999994) + 1) - 1)/x[1] - 0.67333333333333334*sqrt(pow(-4*U0*x[1] + 4*U0*(1 - x[1]) + 2*M_PI*Up*pow(sin(M_PI*x[0]), 2)*cos(2*M_PI*x[1]) + 4*M_PI*Up*sin(2*M_PI*x[0])*sin(M_PI*x[1])*cos(2*M_PI*x[0]), 2))) ; + + //-P-src + src[4] = -1.0718113612004292*pow(chif, 2)*pow(mu, 2) - chif*mu*x[1]*(0.13550000000000001 - 0.16259999999999999*exp(-0.5*pow(chif, 2)*pow(x[1], 2)*pow(rho0 + rhop*pow(sin(M_PI*x[0]), 2)*sin(M_PI*x[1]), 2)))*(3.5693039857227848*chif*mu*(chif*x[1]*(rho0 + rhop*pow(sin(M_PI*x[0]), 2)*sin(M_PI*x[1]))/(pow(chif, 4)*pow(x[1], 4)*pow(rho0 + rhop*pow(sin(M_PI*x[0]), 2)*sin(M_PI*x[1]), 4)/(pow(chif, 3)*pow(x[1], 3)*pow(rho0 + rhop*pow(sin(M_PI*x[0]), 2)*sin(M_PI*x[1]), 3) + 357.91099999999994) + 1) - 1)/x[1] + 0.67333333333333334*sqrt(pow(-4*U0*x[1] + 4*U0*(1 - x[1]) + 2*M_PI*Up*pow(sin(M_PI*x[0]), 2)*cos(2*M_PI*x[1]) + 4*M_PI*Up*sin(2*M_PI*x[0])*sin(M_PI*x[1])*cos(2*M_PI*x[0]), 2))) ;} + + } // namespace mach #endif diff --git a/src/physics/fluidflow/rans_integ.hpp b/src/physics/fluidflow/rans_integ.hpp index 0611a137..deae219d 100644 --- a/src/physics/fluidflow/rans_integ.hpp +++ b/src/physics/fluidflow/rans_integ.hpp @@ -940,6 +940,36 @@ class SAViscousIntegrator : public SymmetricViscousIntegrator +{ +public: + /// Construct an integrator for a 2D RANS SA MMS source + /// \param[in] diff_stack - for algorithmic differentiation + /// \param[in] Re_num - Reynolds number + /// \param[in] Pr_num - Prandtl number + /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) + SAMMSIntegrator(double Re_num, double Pr_num, double a = -1.0) + : MMSIntegrator(5, a), + Re(Re_num), Pr(Pr_num) {} + + /// Computes the MMS source term at a give point + /// \param[in] x - spatial location at which to evaluate the source + /// \param[out] src - source term evaluated at `x` + void calcSource(const mfem::Vector &x, mfem::Vector &src) + { + double mu = 1.0/Re; + calcSAMMS(mu, Pr, x.GetData(), src.GetData()); + } + +private: + /// Reynolds number + double Re; + /// Prandtl number + double Pr; +}; + #include "rans_integ_def.hpp" } // namespace mach diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index 91621076..a9884626 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -213,41 +213,6 @@ void SASourceIntegrator::AssembleElementGrad( mu_a, d, S, Re_a, d0_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data(), prod, dest); - - // adouble src = calcSASource( - // ui_a.data(), grad_nu_i_a.data(), sacs_a.data())/Re_a; - // src -= calcSASource2( - // ui_a.data(), mu_a, grad_nu_i_a.data(), grad_rho_i_a.data(), sacs_a.data())/Re_a; - // if (fabs(d) > 1e-12) - // { - // if (ui_a[dim+2] < 0) - // { - // src += prod*calcSANegativeProduction( - // ui_a.data(), S, sacs_a.data()); - // src += dest*calcSANegativeDestruction( - // ui_a.data(), d, Re_a, sacs_a.data()); - // } - // else - // { - // src += prod*calcSAProduction( - // ui_a.data(), mu_a, d, S, Re_a, sacs_a.data()); - // src += dest*calcSADestruction( - // ui_a.data(), mu_a, d, S, Re_a, sacs_a.data()); - // } - // } - // else - // { - // if (ui_a[dim+2] < 0) - // { - // src += dest*calcSANegativeDestruction( - // ui_a.data(), d0_a, Re_a, sacs_a.data()); - // } - // else - // { - // src += dest*calcSADestruction( - // ui_a.data(), mu_a, d0_a, S, Re_a, sacs_a.data()); - // } - // } //src = prod*(uci_a[dim+2]-3)*d; ///!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! this->stack.independent(ui_a.data(), ui.Size()); this->stack.dependent(src); @@ -331,18 +296,6 @@ void SASourceIntegrator::AssembleElementGrad( //dnu(nn) -= dnu(nn + (dim+2)*num_nodes)*uc(nn, dim+2)/(uc(nn, 0)*uc(nn, 0)); //dnu(nn + (dim+2)*num_nodes) *= 1.0/uc(nn, 0); } - // for (int nn = 0; nn < num_nodes; nn++) - // { - // for (int di = 0; di < dim; di++) - // { - // dnu(nn + (di+1)*num_nodes) *= 1.0/uc(nn, 0); - // dnu(nn + (di+1)*num_nodes) += dnu(nn)/u(di+1, nn); - // } - // dnu(nn + (dim+2)*num_nodes) *= 1.0/uc(nn, 0); - // dnu(nn + (dim+2)*num_nodes) += dnu(nn)/u(dim+2, nn); - // } - - // Set elmat entry diff --git a/src/physics/fluidflow/rans_sens_integ.hpp b/src/physics/fluidflow/rans_sens_integ.hpp new file mode 100644 index 00000000..6a0b49e3 --- /dev/null +++ b/src/physics/fluidflow/rans_sens_integ.hpp @@ -0,0 +1,81 @@ +#ifndef MACH_RANS_SENS_INTEG +#define MACH_RANS_SENS_INTEG + +#include "adept.h" +#include "mfem.hpp" + +#include "mesh_sens_integ.hpp" +#include "rans_fluxes.hpp" + +using adept::adouble; + +namespace mach +{ + +/// Integrator for the mesh sensitivity of the SA inviscid integrators +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \tparam entvar - if true, the state variables are the entropy variables +/// \note This derived class uses the CRTP +template +class SAInviscidMeshSensIntegrator : public DyadicMeshSensIntegrator< + SAInviscidMeshSensIntegrator> +{ +public: + /// Construct an integrator for the Ismail-Roe flux over domains + /// \param[in] a - factor, usually used to move terms to rhs + SAInviscidMeshSensIntegrator(const mfem::GridFunction &state_vec, + const mfem::GridFunction &adjoint_vec, + int num_state_vars = 1, double a = 1.0) + : DyadicMeshSensIntegrator>( + state_vec, adjoint_vec, num_state_vars, a) {} + + /// SA Inviscid two-point (dyadic) entropy conservative flux function + /// \param[in] di - physical coordinate direction in which flux is wanted + /// \param[in] qL - state variables at "left" state + /// \param[in] qR - state variables at "right" state + /// \param[out] flux - fluxes in the direction `di` + /// \note This is simply a wrapper for the function in `euler_fluxes.hpp` + void calcFlux(int di, const mfem::Vector &qL, + const mfem::Vector &qR, mfem::Vector &flux); +}; + +/// Integrator for viscous SA slip-wall boundary condition +/// \tparam dim - number of spatial dimensions (1, 2, or 3) +/// \tparam entvar - if true, states = ent. vars; otherwise, states = conserv. +/// \note This derived class uses the CRTP +// template +// class SASlipWallBCMeshSens : public BoundaryMeshSensIntegrator< +// SASlipWallBCMeshSens> +// { +// public: +// /// Constructs an integrator for a SA slip-wall boundary flux +// /// \param[in] diff_stack - for algorithmic differentiation +// /// \param[in] fe_coll - used to determine the face elements +// /// \param[in] a - used to move residual to lhs (1.0) or rhs(-1.0) +// SASlipWallBCMeshSens(adept::Stack &diff_stack, +// const mfem::GridFunction &state_vec, +// const mfem::GridFunction &adjoint_vec, +// int num_state_vars = 1, double a = 1.0) +// : BoundaryMeshSensIntegrator>( +// state_vec, adjoint_vec, dim + 2, a), stack(diff_stack) {} + +// /// Compute ther derivative of flux_bar*flux w.r.t. `dir` +// /// \param[in] x - location at which the derivative is evaluated (not used) +// /// \param[in] dir - vector normal to the boundary at `x` +// /// \param[in] u - state at which to evaluate the flux +// /// \param[in] flux_bar - flux weighting (e.g. the adjoint) +// /// \param[out] dir_bar - derivative with respect to `dir` +// void calcFluxBar(const mfem::Vector &x, const mfem::Vector &dir, +// const mfem::Vector &u, const mfem::Vector &flux_bar, +// mfem::Vector &dir_bar); + +// protected: +// /// stack used for algorithmic differentiation +// adept::Stack &stack; +// }; + +#include "rans_sens_integ_def.hpp" + +} // namespace mach + +#endif \ No newline at end of file diff --git a/src/physics/fluidflow/rans_sens_integ_def.hpp b/src/physics/fluidflow/rans_sens_integ_def.hpp new file mode 100644 index 00000000..d5afa763 --- /dev/null +++ b/src/physics/fluidflow/rans_sens_integ_def.hpp @@ -0,0 +1,18 @@ +template +void SAInviscidMeshSensIntegrator::calcFlux( + int di, const mfem::Vector &qL, const mfem::Vector &qR, mfem::Vector &flux) +{ + if (entvar) + { + calcIsmailRoeFluxUsingEntVars(di, qL.GetData(), qR.GetData(), + flux.GetData()); + } + else + { + calcIsmailRoeFlux(di, qL.GetData(), qR.GetData(), + flux.GetData()); + } + //add flux term for SA + //flux(dim+2) = 0.5*(qfs(di+1)/qfs(0) + qfs(di+1)/qfs(0))*0.5*(qL(dim+2) + qR(dim+2)); + flux(dim+2) = 0.5*(qL(di+1)/qL(0) + qR(di+1)/qR(0))*0.5*(qL(dim+2) + qR(dim+2)); +} \ No newline at end of file diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index e8d60030..6dff435a 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -13,22 +13,23 @@ endfunction(create_tests) # group together all fluids related tests set(FLUID_TEST_SRCS - test_euler_fluxes - test_euler_integ - test_euler_assemble - test_euler_sens_integ - test_evolver - test_inexact_newton - test_navier_stokes_fluxes - test_navier_stokes_integ - test_navier_stokes_assemble - test_reconstruction - test_sbp_fe - test_viscous_integ - test_surface - test_utils - test_thermal_integ - test_res_integ + #test_euler_fluxes + #test_euler_integ + #test_euler_assemble + #test_euler_sens_integ + #test_evolver + #test_inexact_newton + #test_navier_stokes_fluxes + #test_navier_stokes_integ + test_navier_stokes_sens_integ + #test_navier_stokes_assemble + #test_reconstruction + #test_sbp_fe + #test_viscous_integ + #test_surface + #test_utils + #test_thermal_integ + #test_res_integ test_rans_fluxes test_rans_integ ) diff --git a/test/unit/test_navier_stokes_sens_integ.cpp b/test/unit/test_navier_stokes_sens_integ.cpp new file mode 100644 index 00000000..8aaf35fd --- /dev/null +++ b/test/unit/test_navier_stokes_sens_integ.cpp @@ -0,0 +1,90 @@ +#include + +#include "catch.hpp" +#include "mfem.hpp" +#include "euler_fluxes.hpp" +#include "euler_integ.hpp" +#include "euler_sens_integ.hpp" +#include "euler_test_data.hpp" +#include "navier_stokes_integ.hpp" +#include "navier_stokes_sens_integ.hpp" + +TEMPLATE_TEST_CASE_SIG("ESViscousMeshSensIntegrator::AssembleElementVector", + "[ESViscousMeshSens]", + ((bool entvar), entvar), false) +{ + using namespace mfem; + using namespace euler_data; + + const int dim = 2; // templating is hard here because mesh constructors + double Re_num = 1; + double Pr_num = 1; + double mu = -1; + int num_state = dim + 2; + adept::Stack diff_stack; // needed for forward problem + double delta = 1e-5; + + // generate a 8 element mesh + int num_edge = 2; + std::unique_ptr mesh(new Mesh(num_edge, num_edge, Element::TRIANGLE, + true /* gen. edges */, 1.0, 1.0, true)); + mesh->EnsureNodes(); + for (int p = 1; p <= 4; ++p) + { + DYNAMIC_SECTION("...for degree p = " << p) + { + // get the finite-element space for the state and adjoint + std::unique_ptr fec( + new SBPCollection(p, dim)); + std::unique_ptr fes(new FiniteElementSpace( + mesh.get(), fec.get(), num_state, Ordering::byVDIM)); + + // we use res for finite-difference approximation + NonlinearForm res(fes.get()); + res.AddDomainIntegrator( + new mach::ESViscousIntegrator<2>(diff_stack, Re_num, Pr_num, mu)); + + // initialize state and adjoint; here we randomly perturb a constant state + GridFunction state(fes.get()), adjoint(fes.get()); + VectorFunctionCoefficient pert(num_state, randBaselineVectorPert<2,entvar>); + state.ProjectCoefficient(pert); + adjoint.ProjectCoefficient(pert); + + // extract mesh nodes and get their finite-element space + GridFunction *x_nodes = mesh->GetNodes(); + FiniteElementSpace *mesh_fes = x_nodes->FESpace(); + + // build the linear form for d(psi^T R)/dx + LinearForm dfdx(mesh_fes); + dfdx.AddDomainIntegrator( + new mach::ESViscousMeshSensIntegrator<2,entvar>( + state, adjoint, Re_num, Pr_num, mu)); + dfdx.Assemble(); + + // initialize the vector that we use to perturb the mesh nodes + GridFunction v(mesh_fes); + VectorFunctionCoefficient v_rand(dim, randVectorState); + v.ProjectCoefficient(v_rand); + + // contract dfdx with v + double dfdx_v = dfdx * v; + + // now compute the finite-difference approximation... + GridFunction x_pert(*x_nodes); + GridFunction r(fes.get()); + x_pert.Add(delta, v); + mesh->SetNodes(x_pert); + res.Mult(state, r); + double dfdx_v_fd = adjoint * r; + x_pert.Add(-2 * delta, v); + mesh->SetNodes(x_pert); + res.Mult(state, r); + dfdx_v_fd -= adjoint * r; + dfdx_v_fd /= (2 * delta); + mesh->SetNodes(*x_nodes); // remember to reset the mesh nodes + + REQUIRE(dfdx_v == Approx(dfdx_v_fd).margin(1e-10)); + } + } +} + diff --git a/test/unit/test_rans_fluxes.cpp b/test/unit/test_rans_fluxes.cpp index 6a9a3bec..e982e714 100644 --- a/test/unit/test_rans_fluxes.cpp +++ b/test/unit/test_rans_fluxes.cpp @@ -27,7 +27,7 @@ TEMPLATE_TEST_CASE_SIG("SA calcVorticity Accuracy", "[SAVorticity]", std::unique_ptr mesh(new Mesh(num_edge, num_edge, Element::TRIANGLE, true /* gen. edges */, 1.0, 1.0, true)); - for (int p = 1; p <= 2; ++p) + for (int p = 1; p <= 1; ++p) { DYNAMIC_SECTION("Vorticity correct for degree p = " << p) { @@ -102,6 +102,11 @@ TEMPLATE_TEST_CASE_SIG("SA calcVorticity Accuracy", "[SAVorticity]", } + ofstream sol_ofs("why.vtk"); + sol_ofs.precision(14); + mesh->PrintVTK(sol_ofs, 1); + s_comp.SaveVTK(sol_ofs, "vorticity_error", 1); + // compare vorticity for (int j = 0; j < s_comp.Size(); j++) { @@ -430,14 +435,14 @@ TEST_CASE("SA grad derivatives", "[SAVorticity]") src += mach::calcSANegativeProduction( q_a.data(), S_a, sacs_a.data()); src += mach::calcSANegativeDestruction( - q_a.data(), d_a, sacs_a.data())/Re_a; + q_a.data(), d_a, Re_a, sacs_a.data()); } else { src += mach::calcSAProduction( - q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; + q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data()); src += mach::calcSADestruction( - q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; + q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data()); } stack.independent(grad_a.data(), dim); @@ -462,14 +467,14 @@ TEST_CASE("SA grad derivatives", "[SAVorticity]") srcp += mach::calcSANegativeProduction( q.GetData(), S, sacs.GetData()); srcp += mach::calcSANegativeDestruction( - q.GetData(), d, sacs.GetData())/Re; + q.GetData(), d, Re, sacs.GetData()); } else { srcp += mach::calcSAProduction( - q.GetData(), mu, d, S, Re, sacs.GetData())/Re; + q.GetData(), mu, d, S, Re, sacs.GetData()); srcp += mach::calcSADestruction( - q.GetData(), mu, d, S, Re, sacs.GetData())/Re; + q.GetData(), mu, d, S, Re, sacs.GetData()); } double srcm = mach::calcSASource( @@ -479,14 +484,14 @@ TEST_CASE("SA grad derivatives", "[SAVorticity]") srcm += mach::calcSANegativeProduction( q.GetData(), S, sacs.GetData()); srcm += mach::calcSANegativeDestruction( - q.GetData(), d, sacs.GetData())/Re; + q.GetData(), d, Re, sacs.GetData()); } else { srcm += mach::calcSAProduction( - q.GetData(), mu, d, S, Re, sacs.GetData())/Re; + q.GetData(), mu, d, S, Re, sacs.GetData()); srcm += mach::calcSADestruction( - q.GetData(), mu, d, S, Re, sacs.GetData())/Re; + q.GetData(), mu, d, S, Re, sacs.GetData()); } // finite difference jacobian @@ -560,14 +565,14 @@ TEST_CASE("SA q derivatives", "[SAVorticity]") src += mach::calcSANegativeProduction( q_a.data(), S_a, sacs_a.data()); src += mach::calcSANegativeDestruction( - q_a.data(), d_a, sacs_a.data())/Re_a; + q_a.data(), d_a, Re_a, sacs_a.data()); } else { src += mach::calcSAProduction( - q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; + q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data()); src += mach::calcSADestruction( - q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data())/Re_a; + q_a.data(), mu_a, d_a, S_a, Re_a, sacs_a.data()); } stack.independent(q_a.data(), dim+3); @@ -592,14 +597,14 @@ TEST_CASE("SA q derivatives", "[SAVorticity]") srcp += mach::calcSANegativeProduction( qp.GetData(), S, sacs.GetData()); srcp += mach::calcSANegativeDestruction( - qp.GetData(), d, sacs.GetData())/Re; + qp.GetData(), d, Re, sacs.GetData()); } else { srcp += mach::calcSAProduction( - qp.GetData(), mu, d, S, Re, sacs.GetData())/Re; + qp.GetData(), mu, d, S, Re, sacs.GetData()); srcp += mach::calcSADestruction( - qp.GetData(), mu, d, S, Re, sacs.GetData())/Re; + qp.GetData(), mu, d, S, Re, sacs.GetData()); } double srcm = mach::calcSASource( @@ -609,14 +614,14 @@ TEST_CASE("SA q derivatives", "[SAVorticity]") srcm += mach::calcSANegativeProduction( qm.GetData(), S, sacs.GetData()); srcm += mach::calcSANegativeDestruction( - qm.GetData(), d, sacs.GetData())/Re; + qm.GetData(), d, Re, sacs.GetData()); } else { srcm += mach::calcSAProduction( - qm.GetData(), mu, d, S, Re, sacs.GetData())/Re; + qm.GetData(), mu, d, S, Re, sacs.GetData()); srcm += mach::calcSADestruction( - qm.GetData(), mu, d, S, Re, sacs.GetData())/Re; + qm.GetData(), mu, d, S, Re, sacs.GetData()); } // finite difference jacobian From a9c1f8dcb987fdacedd6513d33728836aa62967e Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Thu, 14 Jan 2021 12:49:44 -0500 Subject: [PATCH 24/28] somewhat accurate solution, need to implement startup solver --- sandbox/rans_walltest.cpp | 17 ++++++++++- sandbox/rans_walltest_options.json | 4 +-- src/common/inexact_newton.cpp | 37 +++++++++++++++++------- src/common/inexact_newton.hpp | 4 +++ src/physics/fluidflow/rans.cpp | 6 ++-- src/physics/fluidflow/rans_fluxes.hpp | 20 ++++++------- src/physics/fluidflow/rans_integ_def.hpp | 8 ++--- 7 files changed, 66 insertions(+), 30 deletions(-) diff --git a/sandbox/rans_walltest.cpp b/sandbox/rans_walltest.cpp index b6a69e02..39912ae0 100644 --- a/sandbox/rans_walltest.cpp +++ b/sandbox/rans_walltest.cpp @@ -2,6 +2,8 @@ constexpr bool entvar = false; #include +#include +#include #include "adept.h" #include "mfem.hpp" @@ -194,7 +196,7 @@ void uexact(const Vector &x, Vector& q) u(1) = u(0)*mach_fs*cos(aoa_fs); u(2) = u(0)*mach_fs*sin(aoa_fs); u(3) = 1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; - u(4) = u(0)*chi_fs*mu; + u(4) = chi_fs*mu; if (entvar == false) { @@ -255,6 +257,19 @@ std::unique_ptr buildWalledMesh(int num_x, int num_y) auto mesh_ptr = unique_ptr(new Mesh(num_x, num_y, Element::TRIANGLE, true /* gen. edges */, 2.33333, 1.0, true)); + + // Randomly reorder elements + int numelem = mesh_ptr->GetNE(); + unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); + Array randelem(numelem); + for (int n = 0; n < numelem; n++) + randelem[n] = n; + //randelem[2*num_x - 1] = 0; + //randelem[0] = 2*num_x - 1; + //std::shuffle(randelem.begin(), randelem.end(), std::default_random_engine(seed)); + mesh_ptr->GetGeckoElementOrdering(randelem); + mesh_ptr->ReorderElements(randelem); + // strategy: // 1) generate a fes for Lagrange elements of desired degree // 2) create a Grid Function using a VectorFunctionCoefficient diff --git a/sandbox/rans_walltest_options.json b/sandbox/rans_walltest_options.json index 3e92ad3c..6a29446e 100644 --- a/sandbox/rans_walltest_options.json +++ b/sandbox/rans_walltest_options.json @@ -25,7 +25,7 @@ "const-cfl": true, "ode-solver": "PTC", "t-final": 100.0, - "dt": 1.0, + "dt": 10.0, "cfl": 0.5, "res-exp": 2.0 }, @@ -55,7 +55,7 @@ "no-slip-adiabatic": [1, 0, 0, 0, 0] }, "wall-func": { - "type": "y-dist" + "type": "true" }, "mesh": { "num-x": 21, diff --git a/src/common/inexact_newton.cpp b/src/common/inexact_newton.cpp index c8242939..60108369 100755 --- a/src/common/inexact_newton.cpp +++ b/src/common/inexact_newton.cpp @@ -100,6 +100,9 @@ void InexactNewton::Mult(const Vector &b, Vector &x) const int it; double norm0, norm, norm_goal; const bool have_b = (b.Size() == Height()); + + Vector scaled_r(r.Size()); + Vector scaling_v(r.Size()); if (!iterative_mode) { x = 0.0; @@ -147,22 +150,36 @@ void InexactNewton::Mult(const Vector &b, Vector &x) const jac = &oper->GetGradient(x); - // time to scale ? can't since newton expects the base Operator class - // double scale = 1e-4; - // for(int s = 0; s < r.Size()/5; s++) + /// scaling stuff + // for(int k = 0; k < r.Size(); k++) // { - // r(4 + s*5) *= scale; - - // jac->ScaleRow(4 + s*5, scale); + // scaling_v(k) = 1/r(k); // } + //scaling_m = new SparseMatrix(scaling_v); + //jac_scaled = new ProductOperator(scaling_m, jac, false, false); + //HypreParMatrix* jac_scaled(jac); + //jac_scaled->ScaleRows(scaling_v); - stringstream nssolname; - nssolname << "jac_" <PrintMatlab(matlab); + if (it == 12) + { + stringstream nssolname; stringstream nssolname2; + nssolname << "jac_" <PrintMatlab(matlab); + //jac_scaled->PrintMatlab(matlab2); + } + // stringstream nssolname; + // nssolname << "jac_" <PrintMatlab(matlab); // std::cout << "Get the jacobian matrix.\n"; + + //prec->SetOperator(*jac); prec->SetOperator(*jac); + //scaling_m->Mult(r, scaled_r); //std::cout << "jac is set as one operator.\n"; prec->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b] // std::cout << "Solve for the newton step.\n"; diff --git a/src/common/inexact_newton.hpp b/src/common/inexact_newton.hpp index 2cb0027e..46a2d95c 100755 --- a/src/common/inexact_newton.hpp +++ b/src/common/inexact_newton.hpp @@ -45,6 +45,10 @@ class InexactNewton : public mfem::NewtonSolver protected: /// Jacobian of the nonlinear operator; needed by ComputeStepSize(); mutable Operator *jac; + /// Diagonal row scaling matrix + mutable SparseMatrix *scaling_m; + /// Jacobian scaled by an automatic matrix to improve condition number + mutable ProductOperator *jac_scaled; /// member vector saves the new x position. mutable mfem::Vector x_new; /// Parameters for inexact newton method. diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index e0597730..28024193 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -48,7 +48,7 @@ void RANavierStokesSolver::addResVolumeIntegrators(double alpha) this->res->AddDomainIntegrator( new SAMMSIntegrator(this->re_fs, this->pr_fs)); this->res->AddDomainIntegrator(new SASourceIntegrator( - this->diff_stack, *dist, this->re_fs, sacs, mu, -alpha, srcs[0], srcs[1], d0)); + this->diff_stack, *dist, this->re_fs, sacs, mu, alpha, srcs[0], srcs[1], d0)); } else { @@ -59,7 +59,7 @@ void RANavierStokesSolver::addResVolumeIntegrators(double alpha) this->diff_stack, this->re_fs, this->pr_fs, sacs, mu, alpha)); // now add RANS integrators this->res->AddDomainIntegrator(new SASourceIntegrator( - this->diff_stack, *dist, this->re_fs, sacs, mu, -alpha, srcs[0], srcs[1], d0)); + this->diff_stack, *dist, this->re_fs, sacs, mu, alpha, srcs[0], srcs[1], d0)); // add LPS stabilization double lps_coeff = this->options["space-dis"]["lps-coeff"].template get(); this->res->AddDomainIntegrator(new SALPSIntegrator( @@ -211,7 +211,7 @@ void RANavierStokesSolver::getFreeStreamState(mfem::Vector &q_ref) q_ref(this->ipitch+1) = q_ref(0)*this->mach_fs*sin(this->aoa_fs); } q_ref(dim+1) = 1/(euler::gamma*euler::gami) + 0.5*this->mach_fs*this->mach_fs; - q_ref(dim+2) = q_ref(0)*this->chi_fs*mu; + q_ref(dim+2) = this->chi_fs*mu; } static void pert(const Vector &x, Vector& p); diff --git a/src/physics/fluidflow/rans_fluxes.hpp b/src/physics/fluidflow/rans_fluxes.hpp index a5e55477..cabed2dd 100644 --- a/src/physics/fluidflow/rans_fluxes.hpp +++ b/src/physics/fluidflow/rans_fluxes.hpp @@ -332,10 +332,10 @@ xdouble calcSAFullSource(const xdouble *q, const xdouble prod, const xdouble dest) { xdouble src = 0.0; - // src += calcSASource( - // q, dir, sacs)/Re; - // src -= calcSASource2( - // q, mu, dir, dir2, sacs)/Re; + src += calcSASource( + q, dir, sacs)/Re; + //src -= calcSASource2( + // q, mu, dir, dir2, sacs)/Re; if (fabs(d) > 1e-12) { if (q[dim+2] < 0) @@ -349,8 +349,8 @@ xdouble calcSAFullSource(const xdouble *q, { src += prod*calcSAProduction( q, mu, d, S, Re, sacs); - // src += dest*calcSADestruction( - // q, mu, d, S, Re, sacs); + src += dest*calcSADestruction( + q, mu, d, S, Re, sacs); } } else @@ -507,16 +507,16 @@ void calcVorticity(const xdouble *Dq, const xdouble *jac_inv, { curl[0] = 0; curl[1] = 0; - curl[2] = DqJ[2] - DqJ[1 + dim+3]; + curl[2] = (DqJ[2] - DqJ[1 + dim+3]); } ////!!!!!!! //curl[2] = 1.0;// min(abs(curl[2]), 0.0); ////!!!!!!! if(dim == 3) { - curl[0] = DqJ[3 + dim+3] - DqJ[2 + 2*(dim+3)]; - curl[1] = DqJ[1 + 2*(dim+3)] - DqJ[3]; - curl[2] = DqJ[2] - DqJ[1 + dim+3]; + curl[0] = (DqJ[3 + dim+3] - DqJ[2 + 2*(dim+3)]); + curl[1] = (DqJ[1 + 2*(dim+3)] - DqJ[3]); + curl[2] = (DqJ[2] - DqJ[1 + dim+3]); } } diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index a9884626..a746c951 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -56,7 +56,7 @@ void SASourceIntegrator::AssembleElementVector( // compute vorticity at node and take magnitude calcVorticity(Dui.GetData(), Trans.InverseJacobian().GetData(), curl_i.GetData()); - double S = sqrt(curl_i(0)*curl_i(0) + curl_i(1)*curl_i(1) +curl_i(2)*curl_i(2)); + double S = sqrt((curl_i(0)*curl_i(0) + curl_i(1)*curl_i(1) +curl_i(2)*curl_i(2))); //S = abs(curl_i(2)); // compute gradient of turbulent viscosity at node calcGrad(dim+2, Dui.GetData(), Trans.InverseJacobian().GetData(), grad_nu_i.GetData()); @@ -74,7 +74,7 @@ void SASourceIntegrator::AssembleElementVector( //byNODES - elvect(i + num_nodes*(num_states-1)) = alpha * Trans.Weight() * node.weight * src; + elvect(i + num_nodes*(num_states-1)) = -alpha * Trans.Weight() * node.weight * src; //elvect((dim+2) + i*(num_states)) = alpha * Trans.Weight() * node.weight * src; } // loop over element nodes i } @@ -199,7 +199,7 @@ void SASourceIntegrator::AssembleElementGrad( // vorticity magnitude deriv this->stack.new_recording(); - adouble S = sqrt(curl_i_a[0]*curl_i_a[0] + curl_i_a[1]*curl_i_a[1] +curl_i_a[2]*curl_i_a[2]); + adouble S = sqrt((curl_i_a[0]*curl_i_a[0] + curl_i_a[1]*curl_i_a[1] +curl_i_a[2]*curl_i_a[2])); //S = abs(curl_i_a[2]); this->stack.independent(curl_i_a.data(), curl_i.Size()); this->stack.dependent(S); @@ -303,7 +303,7 @@ void SASourceIntegrator::AssembleElementGrad( //elmat.SetRow((num_states-1)+i*num_states, dnu); elmat.SetRow(i+num_nodes*(num_states-1), dnu); } // loop over element nodes i - elmat *= alpha; + elmat *= -alpha; } //==================================================================================== From 0e40b76141e06340f746ae6d7321db9886beed61 Mon Sep 17 00:00:00 2001 From: Garo Bedonian Date: Thu, 4 Feb 2021 15:02:58 -0500 Subject: [PATCH 25/28] cleanup commit before dev merge --- sandbox/CMakeLists.txt | 28 ++- sandbox/gf_error.cpp | 261 ---------------------- sandbox/gf_error_options.json | 60 ----- sandbox/ns_walltest.cpp | 271 ----------------------- sandbox/ns_walltest_options.json | 59 ----- sandbox/rans_walltest_options.json | 3 +- src/common/default_options.cpp | 3 +- src/common/inexact_newton.cpp | 145 ++++++------ src/physics/fluidflow/euler.cpp | 13 +- src/physics/fluidflow/euler.hpp | 2 + src/physics/fluidflow/rans.cpp | 36 ++- src/physics/fluidflow/rans.hpp | 2 + src/physics/fluidflow/rans_integ_def.hpp | 6 +- src/physics/solver.cpp | 7 + test/unit/CMakeLists.txt | 34 +-- test/unit/euler_test_data.cpp | 5 +- test/unit/test_rans_fluxes.cpp | 4 +- test/unit/test_rans_integ.cpp | 14 +- 18 files changed, 176 insertions(+), 777 deletions(-) delete mode 100644 sandbox/gf_error.cpp delete mode 100644 sandbox/gf_error_options.json delete mode 100644 sandbox/ns_walltest.cpp delete mode 100644 sandbox/ns_walltest_options.json diff --git a/sandbox/CMakeLists.txt b/sandbox/CMakeLists.txt index 64a8d318..df3e5b59 100644 --- a/sandbox/CMakeLists.txt +++ b/sandbox/CMakeLists.txt @@ -14,24 +14,22 @@ function(create_sandbox source_list) endfunction(create_sandbox) set(SANDBOX_SRCS - #steady_vortex - #steady_vortex_adjoint - #unsteady_vortex - #viscous_shock - #airfoil_steady - #airfoil_chaotic - #magnetostatic_box - #magnetostatic_motor - #magnetostatic_wire - #navier_stokes_mms - #thermal_square - #surface_distance - #joule_wire - #ns_walltest + steady_vortex + steady_vortex_adjoint + unsteady_vortex + viscous_shock + airfoil_steady + airfoil_chaotic + magnetostatic_box + magnetostatic_motor + magnetostatic_wire + navier_stokes_mms + thermal_square + surface_distance + joule_wire rans_freestream rans_walltest rans_mms - #gf_error ) create_sandbox("${SANDBOX_SRCS}") diff --git a/sandbox/gf_error.cpp b/sandbox/gf_error.cpp deleted file mode 100644 index 8381b5d7..00000000 --- a/sandbox/gf_error.cpp +++ /dev/null @@ -1,261 +0,0 @@ -// set this const expression to true in order to use entropy variables for state (doesn't work for rans) -constexpr bool entvar = false; - -#include -#include "adept.h" - -#include "mfem.hpp" -#include "rans.hpp" -#include "mach.hpp" -#include -#include - -using namespace std; -using namespace mfem; -using namespace mach; - -std::default_random_engine gen(std::random_device{}()); -std::uniform_real_distribution uniform_rand(0.0,1.0); - -static double pert_fs; -static double mu; -static double mach_fs; -static double aoa_fs; -static int iroll; -static int ipitch; -static double chi_fs; - -static double m_offset; -static double m_coeff; -static int m_x; -static int m_y; - -/// \brief Defines the random function for the jacobian check -/// \param[in] x - coordinate of the point at which the state is needed -/// \param[out] u - conservative + SA variables stored as a 5-vector -void pert(const Vector &x, Vector& p); - -/// \brief Defines the exact solution for the rans freestream problem -/// \param[in] x - coordinate of the point at which the state is needed -/// \param[out] u - conservative + SA variables stored as a 5-vector -void uexact(const Vector &x, Vector& u); - -/// \brief Defines a perturbed solution for the rans freestream problem -/// \param[in] x - coordinate of the point at which the state is needed -/// \param[out] u - conservative + SA variables stored as a 5-vector -void uinit_pert(const Vector &x, Vector& u); - -/// \brief Defines a pseudo wall-distance function -/// \param[in] x - coordinate of the point at which the distance is needed -double walldist(const Vector &x); - -/// Generate a quadrilateral wall mesh, more dense near the wall -/// \param[in] num_x - number of nodes in x -/// \param[in] num_y - number of nodes in y -std::unique_ptr buildWalledMesh(int num_x, - int num_y); - -int main(int argc, char *argv[]) -{ - const char *options_file = "gf_error_options.json"; - - // Initialize MPI - int num_procs, rank; - MPI_Init(&argc, &argv); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - ostream *out = getOutStream(rank); - - // Parse command-line options - OptionsParser args(argc, argv); - int degree = 2.0; - args.Parse(); - if (!args.Good()) - { - args.PrintUsage(*out); - return 1; - } - - // construct the mesh - string opt_file_name(options_file); - nlohmann::json file_options; - std::ifstream opts(opt_file_name); - opts >> file_options; - pert_fs = 1.0 + file_options["init-pert"].template get(); - mu = file_options["flow-param"]["mu"].template get(); - mach_fs = file_options["flow-param"]["mach"].template get(); - aoa_fs = file_options["flow-param"]["aoa"].template get()*M_PI/180; - iroll = file_options["flow-param"]["roll-axis"].template get(); - ipitch = file_options["flow-param"]["pitch-axis"].template get(); - chi_fs = file_options["flow-param"]["chi"].template get(); - - m_offset = file_options["mesh"]["offset"].template get(); - m_coeff = file_options["mesh"]["coeff"].template get(); - m_x = file_options["mesh"]["num-x"].template get(); - m_y = file_options["mesh"]["num-y"].template get(); - - string file = file_options["file-names-1"].template get(); - string file2 = file_options["file-names-2"].template get(); - - // generate the walled mesh, and its distance function - std::unique_ptr mesh = buildWalledMesh(m_x, m_y); - - stringstream nssolname; stringstream ranssolname; - nssolname << file <<".gf"; ranssolname << file2 <<".gf"; - std::ifstream sol_ns(nssolname.str()); std::ifstream sol_rans(ranssolname.str()); - // std::unique_ptr u_ns; - // std::unique_ptr u_rans; - // std::unique_ptr u_diff; - // u_ns.reset(new GridFunction(mesh.get(), sol_ns)); - // u_rans.reset(new GridFunction(mesh.get(), sol_rans)); - // u_diff.reset(new GridFunction(*u_ns)); - Vector u_ns; Vector u_rans; Vector u_diff; - u_ns.Load(sol_ns, 4); - u_rans.Load(sol_rans, 4); - u_diff = u_ns - u_rans; - //*u_diff -= *u_rans; - - cout << "Error Norm: "<< u_diff.Norml2()< buildWalledMesh(int num_x, int num_y) -{ - auto mesh_ptr = unique_ptr(new Mesh(num_x, num_y, - Element::TRIANGLE, true /* gen. edges */, - 2.33333, 1.0, true)); - // strategy: - // 1) generate a fes for Lagrange elements of desired degree - // 2) create a Grid Function using a VectorFunctionCoefficient - // 4) use mesh_ptr->NewNodes(nodes, true) to set the mesh nodes - - // Problem: fes does not own fec, which is generated in this function's scope - // Solution: the grid function can own both the fec and fes - H1_FECollection *fec = new H1_FECollection(1, 2 /* = dim */); - FiniteElementSpace *fes = new FiniteElementSpace(mesh_ptr.get(), fec, 2, - Ordering::byVDIM); - - // Lambda function increases element density towards wall - double offset = m_offset; - double coeff = m_coeff; - auto xy_fun = [coeff, offset, num_y](const Vector& rt, Vector &xy) - { - xy(0) = rt(0) - 0.33333; - xy(1) = rt(1); - - double c = 1.0/num_y; - double b = log(offset)/(c - 1.0); - double a = 1.0/exp(1.0*b); - - if(rt(1) > 0.0 && rt(1) < 1.0) - { - xy(1) = coeff*a*exp(b*rt(1)); - //std::cout << xy(1) << std::endl; - } - }; - VectorFunctionCoefficient xy_coeff(2, xy_fun); - GridFunction *xy = new GridFunction(fes); - xy->MakeOwner(fec); - xy->ProjectCoefficient(xy_coeff); - - mesh_ptr->NewNodes(*xy, true); - - // Assign extra boundary attribute - for (int i = 0; i < mesh_ptr->GetNBE(); ++i) - { - Element *elem = mesh_ptr->GetBdrElement(i); - - Array verts; - elem->GetVertices(verts); - - bool before = true; //before wall - for (int j = 0; j < 2; ++j) - { - auto vtx = mesh_ptr->GetVertex(verts[j]); - //cout<< "B El: "<SetAttribute(5); - } - } - mesh_ptr->bdr_attributes.Append(5); - // mesh_ptr->SetAttributes(); - // mesh_ptr->Finalize(); - - cout << "Number of bdr attr " << mesh_ptr->bdr_attributes.Size() <<'\n'; - cout << "Number of elements " << mesh_ptr->GetNE() <<'\n'; - - return mesh_ptr; -} diff --git a/sandbox/gf_error_options.json b/sandbox/gf_error_options.json deleted file mode 100644 index 62607166..00000000 --- a/sandbox/gf_error_options.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "flow-param": { - "mach": 0.2, - "aoa": 0.0, - "roll-axis": 0, - "pitch-axis": 1, - "chi": 0.0, - "Re": 5000000.0, - "Pr": 0.75, - "mu": 1.0, - "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9] - }, - "space-dis": { - "degree": 1, - "lps-coeff": 1.0, - "basis-type": "csbp" - }, - "time-dis": { - "steady": true, - "steady-abstol": 1e-12, - "steady-restol": 1e-10, - "const-cfl": true, - "ode-solver": "PTC", - "t-final": 100, - "dt": 100.0, - "cfl": 1.0, - "res-exp": 2.0 - }, - "nonlin-solver": { - "abstol": 1e-12, - "maxiter": 100, - "printlevel": 1, - "reltol": 1e-2, - "type": "newton" - }, - "lin-solver": { - "reltol": 1e-2, - "abstol": 1e-12, - "printlevel": 0, - "maxiter": 100 - }, - "bcs": { - "far-field": [0, 1, 1, 1, 0], - "slip-wall": [0, 0, 0, 0, 1], - "no-slip-adiabatic": [1, 0, 0, 0, 0] - }, - "wall-func": { - "type": "y-dist" - }, - "mesh": { - "num-x": 26, - "num-y": 18, - "offset": 1e-2, - "coeff": 1.0 - }, - "init-pert": 0.0, - "file-names-1": "ns_1e-2_final", - "file-names-2": "rans_noturb_1e-2_final" -} - diff --git a/sandbox/ns_walltest.cpp b/sandbox/ns_walltest.cpp deleted file mode 100644 index 718cf3ee..00000000 --- a/sandbox/ns_walltest.cpp +++ /dev/null @@ -1,271 +0,0 @@ -// set this const expression to true in order to use entropy variables for state (doesn't work for rans) -constexpr bool entvar = false; - -#include -#include "adept.h" - -#include "mfem.hpp" -#include "rans.hpp" -#include "navier_stokes.hpp" -#include -#include - -using namespace std; -using namespace mfem; -using namespace mach; - -std::default_random_engine gen(std::random_device{}()); -std::uniform_real_distribution uniform_rand(0.0,1.0); - -static double pert_fs; -static double mu; -static double mach_fs; -static double aoa_fs; -static int iroll; -static int ipitch; -static double chi_fs; - -static double m_offset; -static double m_coeff; -static int m_x; -static int m_y; - -/// \brief Defines the random function for the jacobian check -/// \param[in] x - coordinate of the point at which the state is needed -/// \param[out] u - conservative + SA variables stored as a 5-vector -void pert(const Vector &x, Vector& p); - -/// \brief Defines the exact solution for the rans freestream problem -/// \param[in] x - coordinate of the point at which the state is needed -/// \param[out] u - conservative + SA variables stored as a 5-vector -void uexact(const Vector &x, Vector& u); - -/// \brief Defines a perturbed solution for the rans freestream problem -/// \param[in] x - coordinate of the point at which the state is needed -/// \param[out] u - conservative + SA variables stored as a 5-vector -void uinit_pert(const Vector &x, Vector& u); - -/// \brief Defines a pseudo wall-distance function -/// \param[in] x - coordinate of the point at which the distance is needed -double walldist(const Vector &x); - -/// Generate a quadrilateral wall mesh, more dense near the wall -/// \param[in] num_x - number of nodes in x -/// \param[in] num_y - number of nodes in y -std::unique_ptr buildWalledMesh(int num_x, - int num_y); - -int main(int argc, char *argv[]) -{ - const char *options_file = "ns_walltest_options.json"; - - // Initialize MPI - int num_procs, rank; - MPI_Init(&argc, &argv); - MPI_Comm_size(MPI_COMM_WORLD, &num_procs); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); - ostream *out = getOutStream(rank); - - // Parse command-line options - OptionsParser args(argc, argv); - int degree = 2.0; - int nx = 14; - int ny = 10; - args.AddOption(&nx, "-nx", "--numx", - "Number of elements in x direction"); - args.AddOption(&ny, "-ny", "--numy", - "Number of elements in y direction"); - args.Parse(); - if (!args.Good()) - { - args.PrintUsage(*out); - return 1; - } - - try - { - // construct the mesh - string opt_file_name(options_file); - nlohmann::json file_options; - std::ifstream opts(opt_file_name); - opts >> file_options; - pert_fs = 1.0 + file_options["init-pert"].template get(); - mu = file_options["flow-param"]["mu"].template get(); - mach_fs = file_options["flow-param"]["mach"].template get(); - aoa_fs = file_options["flow-param"]["aoa"].template get()*M_PI/180; - iroll = file_options["flow-param"]["roll-axis"].template get(); - ipitch = file_options["flow-param"]["pitch-axis"].template get(); - chi_fs = file_options["flow-param"]["chi"].template get(); - - m_offset = file_options["mesh"]["offset"].template get(); - m_coeff = file_options["mesh"]["coeff"].template get(); - m_x = file_options["mesh"]["num-x"].template get(); - m_y = file_options["mesh"]["num-y"].template get(); - - // generate the walled mesh, and its distance function - std::unique_ptr smesh = buildWalledMesh(m_x, m_y); - - // construct the solver and set initial conditions - auto solver = createSolver>(opt_file_name, - move(smesh)); - solver->setInitialCondition(uinit_pert); - solver->printSolution("ns_wall_init", 0); - - // get the initial density error - double l2_error_init = (static_cast&>(*solver) - .calcConservativeVarsL2Error(uexact, 0)); - *out << "\n|| rho_h - rho ||_{L^2} init = " << l2_error_init << endl; - - double res_error = solver->calcResidualNorm(); - *out << "\ninitial residual norm = " << res_error << endl; - solver->checkJacobian(pert); - solver->solveForState(); - solver->printSolution("ns_wall_final",0); - // get the final density error - double l2_error_final = (static_cast&>(*solver) - .calcConservativeVarsL2Error(uexact, 0)); - res_error = solver->calcResidualNorm(); - - *out << "\nfinal residual norm = " << res_error; - *out << "\n|| rho_h - rho ||_{L^2} final = " << l2_error_final << endl; - } - catch (MachException &exception) - { - exception.print_message(); - } - catch (std::exception &exception) - { - cerr << exception.what() << endl; - } - -#ifdef MFEM_USE_PETSC - MFEMFinalizePetsc(); -#endif - - MPI_Finalize(); -} - -// perturbation function used to check the jacobian in each iteration -void pert(const Vector &x, Vector& p) -{ - p.SetSize(4); - for (int i = 0; i < 4; i++) - { - p(i) = 2.0 * uniform_rand(gen) - 1.0; - } -} - -// Exact solution; same as freestream bc -void uexact(const Vector &x, Vector& q) -{ - q.SetSize(4); - Vector u(4); - - u = 0.0; - u(0) = 1.0; - u(1) = u(0)*mach_fs*cos(aoa_fs); - u(2) = u(0)*mach_fs*sin(aoa_fs); - u(3) = 1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; - - if (entvar == false) - { - q = u; - } - else - { - throw MachException("No entvar for this"); - } -} - -// initial guess perturbed from exact -void uinit_pert(const Vector &x, Vector& q) -{ - q.SetSize(4); - Vector u(4); - - u = 0.0; - u(0) = pert_fs*1.0; - u(1) = u(0)*mach_fs*cos(aoa_fs); - u(2) = u(0)*mach_fs*sin(aoa_fs); - u(3) = pert_fs*1/(euler::gamma*euler::gami) + 0.5*mach_fs*mach_fs; - - q = u; -} - -std::unique_ptr buildWalledMesh(int num_x, int num_y) -{ - auto mesh_ptr = unique_ptr(new Mesh(num_x, num_y, - Element::TRIANGLE, true /* gen. edges */, - 2.33333, 1.0, true)); - // strategy: - // 1) generate a fes for Lagrange elements of desired degree - // 2) create a Grid Function using a VectorFunctionCoefficient - // 4) use mesh_ptr->NewNodes(nodes, true) to set the mesh nodes - - // Problem: fes does not own fec, which is generated in this function's scope - // Solution: the grid function can own both the fec and fes - H1_FECollection *fec = new H1_FECollection(1, 2 /* = dim */); - FiniteElementSpace *fes = new FiniteElementSpace(mesh_ptr.get(), fec, 2, - Ordering::byVDIM); - - // Lambda function increases element density towards wall - double offset = m_offset; - double coeff = m_coeff; - auto xy_fun = [coeff, offset, num_y](const Vector& rt, Vector &xy) - { - xy(0) = rt(0) - 0.33333; - xy(1) = rt(1); - - double c = 1.0/num_y; - double b = log(offset)/(c - 1.0); - double a = 1.0/exp(1.0*b); - - if(rt(1) > 0.0 && rt(1) < 1.0) - { - xy(1) = coeff*a*exp(b*rt(1)); - //std::cout << xy(1) << std::endl; - } - }; - VectorFunctionCoefficient xy_coeff(2, xy_fun); - GridFunction *xy = new GridFunction(fes); - xy->MakeOwner(fec); - xy->ProjectCoefficient(xy_coeff); - - mesh_ptr->NewNodes(*xy, true); - - // Assign extra boundary attribute - for (int i = 0; i < mesh_ptr->GetNBE(); ++i) - { - Element *elem = mesh_ptr->GetBdrElement(i); - - Array verts; - elem->GetVertices(verts); - - bool before = true; //before wall - for (int j = 0; j < 2; ++j) - { - auto vtx = mesh_ptr->GetVertex(verts[j]); - cout<< "B El: "<SetAttribute(5); - } - } - mesh_ptr->bdr_attributes.Append(5); - // mesh_ptr->SetAttributes(); - // mesh_ptr->Finalize(); - - cout << "Number of bdr attr " << mesh_ptr->bdr_attributes.Size() <<'\n'; - cout << "Number of elements " << mesh_ptr->GetNE() <<'\n'; - - return mesh_ptr; -} diff --git a/sandbox/ns_walltest_options.json b/sandbox/ns_walltest_options.json deleted file mode 100644 index 05116837..00000000 --- a/sandbox/ns_walltest_options.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "flow-param": { - "mach": 0.2, - "aoa": 0.0, - "roll-axis": 0, - "pitch-axis": 1, - "chi": 3.0, - "Re": 5000000.0, - "Pr": 0.75, - "mu": 1.0, - "viscous-mms": false, - "sa-consts": [0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9] - }, - "space-dis": { - "degree": 1, - "lps-coeff": 1.0, - "basis-type": "csbp" - }, - "time-dis": { - "steady": true, - "steady-abstol": 1e-12, - "steady-restol": 1e-10, - "const-cfl": true, - "ode-solver": "PTC", - "t-final": 100, - "dt": 1.0, - "cfl": 1.0, - "res-exp": 2.0 - }, - "nonlin-solver": { - "abstol": 1e-12, - "maxiter": 100, - "printlevel": 1, - "reltol": 1e-2, - "type": "newton" - }, - "lin-solver": { - "reltol": 1e-2, - "abstol": 1e-12, - "printlevel": 0, - "maxiter": 100 - }, - "bcs": { - "far-field": [0, 1, 1, 1, 0], - "slip-wall": [0, 0, 0, 0, 1], - "no-slip-adiabatic": [1, 0, 0, 0, 0] - }, - "wall-func": { - "type": "y-dist" - }, - "mesh": { - "num-x": 25, - "num-y": 15, - "offset": 1e-3, - "coeff": 1.0 - }, - "init-pert": 0.0, - "file-names": "ns_1e-2" -} diff --git a/sandbox/rans_walltest_options.json b/sandbox/rans_walltest_options.json index 6a29446e..8b1cbc8f 100644 --- a/sandbox/rans_walltest_options.json +++ b/sandbox/rans_walltest_options.json @@ -27,7 +27,8 @@ "t-final": 100.0, "dt": 10.0, "cfl": 0.5, - "res-exp": 2.0 + "res-exp": 2.0, + "start-up": true }, "nonlin-solver": { "abstol": 1e-12, diff --git a/src/common/default_options.cpp b/src/common/default_options.cpp index d3e11af5..a23c6f89 100644 --- a/src/common/default_options.cpp +++ b/src/common/default_options.cpp @@ -45,7 +45,8 @@ const nlohmann::json default_options {"t-final", 1.0}, // final time to simulate to {"dt", 0.01}, // time-step size when `const-cfl` is false {"cfl", 1.0}, // target CFL number - {"max-iter", 10000} // safe-guard upper bound on number of iterations + {"max-iter", 10000}, // safe-guard upper bound on number of iterations + {"start-up", false} // in steady case, iterate PTC with constant step at first }}, {"nonlin-solver", // options related to root-finding algorithms diff --git a/src/common/inexact_newton.cpp b/src/common/inexact_newton.cpp index 60108369..e5d4c0cc 100755 --- a/src/common/inexact_newton.cpp +++ b/src/common/inexact_newton.cpp @@ -13,68 +13,68 @@ namespace mfem double InexactNewton::ComputeStepSize (const Vector &x, const Vector &b, const double norm) const { - // double theta = 0.0; - // double s = 1.0; - // // p0, p1, and p0p are used for quadratic interpolation p(s) in [0,1]. - // // p0 is the value of p(0), p0p is the derivative p'(0), and - // // p1 is the value of p(1). */ - // double p0, p1, p0p; - // // A temporary vector for calculating p0p. - // Vector temp(r.Size()); - - // p0 = 0.5 * norm * norm; - // // temp=F'(x_i)*r(x_i) - // jac->Mult(r,temp); - // // c is the negative inexact newton step size. - // p0p = -Dot(c,temp); - // //Calculate the new norm. - - // add(x,-1.0,c,x_new); - // oper->Mult(x_new,r); - // const bool have_b = (b.Size()==Height()); - // if (have_b) - // { - // r -= b; - // } - // double err_new = Norm(r); - - // // Globalization start from here. - // int itt=0; - // while (err_new > (1 - t * (1 - theta) ) * norm) - // { - // p1 = 0.5*err_new*err_new; - // // Quadratic interpolation between [0,1] - // theta = quadInterp(0.0, p0, p0p, 1.0, p1); - // theta = (theta > theta_min) ? theta : theta_min; - // theta = (theta < theta_max) ? theta : theta_max; - // // set the new trial step size. - // s *= theta; - // // update eta - // eta = 1 - theta * (1- eta); - // eta = (eta < eta_max) ? eta : eta_max; - // // re-evaluate the error norm at new x. - // add(x,-s,c,x_new); - // oper->Mult(x_new, r); - // if (have_b) - // { - // r-=b; - // } - // err_new = Norm(r); - - // // Check the iteration counts. - // itt ++; - // if (itt > max_iter) - // { - // mfem::mfem_error("Fail to globalize: Exceed maximum iterations.\n"); - // break; - // } - // } - // if (print_level>=0) - // { - // mfem::out << " Globalization factors: theta= "<< s - // << ", eta= " << eta <<'\n'; - // } - return 1.0;//s; + double theta = 0.0; + double s = 1.0; + // p0, p1, and p0p are used for quadratic interpolation p(s) in [0,1]. + // p0 is the value of p(0), p0p is the derivative p'(0), and + // p1 is the value of p(1). */ + double p0, p1, p0p; + // A temporary vector for calculating p0p. + Vector temp(r.Size()); + + p0 = 0.5 * norm * norm; + // temp=F'(x_i)*r(x_i) + jac->Mult(r,temp); + // c is the negative inexact newton step size. + p0p = -Dot(c,temp); + //Calculate the new norm. + + add(x,-1.0,c,x_new); + oper->Mult(x_new,r); + const bool have_b = (b.Size()==Height()); + if (have_b) + { + r -= b; + } + double err_new = Norm(r); + + // Globalization start from here. + int itt=0; + while (err_new > (1 - t * (1 - theta) ) * norm) + { + p1 = 0.5*err_new*err_new; + // Quadratic interpolation between [0,1] + theta = quadInterp(0.0, p0, p0p, 1.0, p1); + theta = (theta > theta_min) ? theta : theta_min; + theta = (theta < theta_max) ? theta : theta_max; + // set the new trial step size. + s *= theta; + // update eta + eta = 1 - theta * (1- eta); + eta = (eta < eta_max) ? eta : eta_max; + // re-evaluate the error norm at new x. + add(x,-s,c,x_new); + oper->Mult(x_new, r); + if (have_b) + { + r-=b; + } + err_new = Norm(r); + + // Check the iteration counts. + itt ++; + if (itt > max_iter) + { + mfem::mfem_error("Fail to globalize: Exceed maximum iterations.\n"); + break; + } + } + if (print_level>=0) + { + mfem::out << " Globalization factors: theta= "<< s + << ", eta= " << eta <<'\n'; + } + return s; } void InexactNewton::SetOperator(const Operator &op) @@ -161,23 +161,22 @@ void InexactNewton::Mult(const Vector &b, Vector &x) const //HypreParMatrix* jac_scaled(jac); //jac_scaled->ScaleRows(scaling_v); - if (it == 12) - { - stringstream nssolname; stringstream nssolname2; - nssolname << "jac_" <PrintMatlab(matlab); - //jac_scaled->PrintMatlab(matlab2); - } + // if (it == 12) + // { + // stringstream nssolname; stringstream nssolname2; + // nssolname << "jac_" <PrintMatlab(matlab); + // //jac_scaled->PrintMatlab(matlab2); + // } // stringstream nssolname; // nssolname << "jac_" <PrintMatlab(matlab); // std::cout << "Get the jacobian matrix.\n"; - //prec->SetOperator(*jac); prec->SetOperator(*jac); //scaling_m->Mult(r, scaled_r); //std::cout << "jac is set as one operator.\n"; diff --git a/src/physics/fluidflow/euler.cpp b/src/physics/fluidflow/euler.cpp index e822e707..37775ac5 100644 --- a/src/physics/fluidflow/euler.cpp +++ b/src/physics/fluidflow/euler.cpp @@ -279,12 +279,21 @@ double EulerSolver::calcStepSize(int iter, double t, { if (options["time-dis"]["steady"].template get()) { + double dt = 0.0; + double res_norm = calcResidualNorm(); + // run a start-up period at constant dt until a threshold is reached + // if (start_up) + // { + // //dt = options["time-dis"]["dt"].template get(); + + // return dt_old; + // } + // ramp up time step for pseudo-transient continuation // TODO: the l2 norm of the weak residual is probably not ideal here // A better choice might be the l1 norm - double res_norm = calcResidualNorm(); double exponent = options["time-dis"]["res-exp"]; - double dt = options["time-dis"]["dt"].template get() * + dt = options["time-dis"]["dt"].template get() * pow(res_norm0 / res_norm, exponent); return max(dt, dt_old); } diff --git a/src/physics/fluidflow/euler.hpp b/src/physics/fluidflow/euler.hpp index 7d606c77..2da309ae 100644 --- a/src/physics/fluidflow/euler.hpp +++ b/src/physics/fluidflow/euler.hpp @@ -73,6 +73,8 @@ class EulerSolver : public AbstractSolver /// used to store the initial residual norm for PTC and convergence checks double res_norm0 = -1.0; + bool start_up = false; + /// Class constructor (protected to prevent misuse) /// \param[in] json_options - json object containing the options /// \param[in] smesh - if provided, defines the mesh for the problem diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index 28024193..60f5ca21 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -23,6 +23,7 @@ RANavierStokesSolver::RANavierStokesSolver(const nlohmann::json &js // define free-stream parameters; may or may not be used, depending on case chi_fs = this->options["flow-param"]["chi"].template get(); mu = this->options["flow-param"]["mu"].template get(); + this->start_up = this->options["time-dis"]["start-up"].template get(); vector sa = this->options["flow-param"]["sa-consts"].template get>(); sacs.SetSize(13); sacs = sa.data(); @@ -223,6 +224,10 @@ void RANavierStokesSolver::iterationHook(int iter, string file = this->options["file-names"].template get(); stringstream filename; filename << file <<"_last"; + int num_rans = this->res->Height(); + Array disable_ns(num_rans); + //disable_ns = NULL; + //this->res->SetEssentialTrueDofs(disable_ns); this->checkJacobian(pert); this->printSolution(filename.str(), 0); @@ -238,8 +243,35 @@ void RANavierStokesSolver::iterationHook(int iter, GridFunction r(this->u->FESpace()); this->res->Mult(*this->u, r); r.Save(rsol); - - cout << "Iter "<calcResidualNorm()<calcResidualNorm(); + + cout << "Iter "<start_up) + // cout << "Start-Up Phase"< 1 && res_norm/this->res_norm0 > 1e-9 && sa_conv == false) + // { + // cout << "Navier Stokes Disabled"<start_up = true; + // for(int s = 0; s < num_rans; s++) + // { + // if((s+1)%(dim+3) == 0) + // disable_ns[s] = s-1; + // else + // disable_ns[s] = s; + // } + // } + // else if (iter <= 1) + // disable_ns = NULL; + // else + // { + // disable_ns = NULL; + // sa_conv = true; + // this->start_up = false; + // } + // this->res->SetEssentialTrueDofs(disable_ns); } diff --git a/src/physics/fluidflow/rans.hpp b/src/physics/fluidflow/rans.hpp index ede22977..4943a4cd 100644 --- a/src/physics/fluidflow/rans.hpp +++ b/src/physics/fluidflow/rans.hpp @@ -76,6 +76,8 @@ class RANavierStokesSolver : public NavierStokesSolver double chi_fs; /// material dynamic viscosity double mu; + + bool sa_conv = false; }; } //namespace mach diff --git a/src/physics/fluidflow/rans_integ_def.hpp b/src/physics/fluidflow/rans_integ_def.hpp index a746c951..01cc5b70 100644 --- a/src/physics/fluidflow/rans_integ_def.hpp +++ b/src/physics/fluidflow/rans_integ_def.hpp @@ -425,7 +425,7 @@ void SANoSlipAdiabaticWallBC::calcFlux(const mfem::Vector &x, flux_vec += work_vec; double dnu = q(dim+2); double dnuflux = (mu/q(0) + fn*q(dim+2))*dnu/(sacs(2)*Re); - double fac = 10*sqrt(dot(dir, dir))/jac; + double fac = 1*sqrt(dot(dir, dir))/jac; flux_vec(dim+2) += dnuflux*fac; } @@ -496,7 +496,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacState( } adouble dnu = q_a[dim+2]; adouble dnuflux = (mu/q_a[0] + fn*q_a[dim+2])*dnu/(sacs_a[2]*Re); - adouble fac = 10*sqrt(dot(dir_a.data(), dir_a.data()))/jac; + adouble fac = 1*sqrt(dot(dir_a.data(), dir_a.data()))/jac; flux_a[dim+2] += dnuflux*fac; this->stack.independent(q_a.data(), q.Size()); @@ -569,7 +569,7 @@ void SANoSlipAdiabaticWallBC::calcFluxJacDw(const mfem::Vector &x, const mf } adouble dnu = q_a[dim+2]; adouble dnuflux = (mu/q_a[0] + fn*q_a[dim+2])*dnu/(sacs_a[2]*Re); - adouble fac = 10*sqrt(dot(dir_a.data(), dir_a.data()))/jac; + adouble fac = 1*sqrt(dot(dir_a.data(), dir_a.data()))/jac; flux_a[dim+2] += dnuflux*fac; this->stack.independent(Dw_a.data(),Dw_size); diff --git a/src/physics/solver.cpp b/src/physics/solver.cpp index 9e735b53..c9496f84 100644 --- a/src/physics/solver.cpp +++ b/src/physics/solver.cpp @@ -1119,6 +1119,13 @@ unique_ptr AbstractSolver::constructLinearSolver( cg->SetPrintLevel(ptl); cg->SetPreconditioner(dynamic_cast(_prec)); } +#ifdef MFEM_USE_SUPERLU + else if (solver_type == "superlu") + { + lin_solver.reset(new SuperLUSolver(comm)); + SuperLUSolver *slu = dynamic_cast(lin_solver.get()); + } +#endif else { throw MachException("Unsupported iterative solver type!\n" diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 6dff435a..2045911b 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -13,23 +13,23 @@ endfunction(create_tests) # group together all fluids related tests set(FLUID_TEST_SRCS - #test_euler_fluxes - #test_euler_integ - #test_euler_assemble - #test_euler_sens_integ - #test_evolver - #test_inexact_newton - #test_navier_stokes_fluxes - #test_navier_stokes_integ - test_navier_stokes_sens_integ - #test_navier_stokes_assemble - #test_reconstruction - #test_sbp_fe - #test_viscous_integ - #test_surface - #test_utils - #test_thermal_integ - #test_res_integ + test_euler_fluxes + test_euler_integ + test_euler_assemble + test_euler_sens_integ + test_evolver + test_inexact_newton + test_navier_stokes_fluxes + test_navier_stokes_integ + #test_navier_stokes_sens_integ + test_navier_stokes_assemble + test_reconstruction + test_sbp_fe + test_viscous_integ + test_surface + test_utils + test_thermal_integ + test_res_integ test_rans_fluxes test_rans_integ ) diff --git a/test/unit/euler_test_data.cpp b/test/unit/euler_test_data.cpp index ce83cbc3..29ffef88 100644 --- a/test/unit/euler_test_data.cpp +++ b/test/unit/euler_test_data.cpp @@ -127,10 +127,7 @@ void randVectorState(const Vector &x, Vector &u) { for (int i = 0; i < u.Size(); ++i) { - // if(u.Size() > 3 && (i == 1 || i == 2)) - // u(i) = 0.01*(2.0 * uniform_rand(gen) - 1.0); - // else - u(i) = 2.0 * uniform_rand(gen) - 1.0; + u(i) = 2.0 * uniform_rand(gen) - 1.0; } } diff --git a/test/unit/test_rans_fluxes.cpp b/test/unit/test_rans_fluxes.cpp index e982e714..a339da06 100644 --- a/test/unit/test_rans_fluxes.cpp +++ b/test/unit/test_rans_fluxes.cpp @@ -299,7 +299,7 @@ TEST_CASE("SA S derivatives", "[SASource]") S_a.set_value(sval); adouble d_a = 1.001; adouble mu_a = 1.1; - adouble Re_a = 5000000; + adouble Re_a = 5000; adouble src;// = mach::calcSADestruction(q_a.data(), S_a, mu_a, d_a, Re_a, sacs_a.data()); src = mach::calcSASource( q_a.data(), grad_a.data(), sacs_a.data())/Re_a; @@ -326,7 +326,7 @@ TEST_CASE("SA S derivatives", "[SASource]") double Sp = sval + delta; double Sm = sval - delta; double d = 1.001; double mu = 1.1; - double Re = 5000000; + double Re = 5000; // get perturbed states conv vector //double srcp = mach::calcSADestruction(q.GetData(), Sp, mu, d, Re, sacs.GetData()); diff --git a/test/unit/test_rans_integ.cpp b/test/unit/test_rans_integ.cpp index 7fb4c6a3..d9e7822c 100644 --- a/test/unit/test_rans_integ.cpp +++ b/test/unit/test_rans_integ.cpp @@ -348,7 +348,7 @@ TEMPLATE_TEST_CASE_SIG("SANoSlipAdiabaticWallBC Dw Jacobian", "[SANoSlipAdiabati } TEMPLATE_TEST_CASE_SIG("SAViscousSlipWallBC Jacobian", "[SASlipWallBC]", - ((int dim), dim), 1, 2, 3) + ((int dim), dim), 1, 2) { using namespace euler_data; // copy the data into mfem vectors for convenience @@ -1117,6 +1117,8 @@ TEST_CASE("SAFarFieldBC::AssembleElementGrad", "[SAFarFieldBC]") } } + +///NOTE: Not currently active TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") { using namespace mfem; @@ -1219,11 +1221,11 @@ TEST_CASE("SASourceIntegrator::AssembleElementGrad", "[SASourceIntegrator]") for (int i = 0; i < jac_v.Size(); ++i) { int n = i/5; - std::cout << "Node Coord: "<< x_nodes(0+2*n) <<", "<< x_nodes(1+2*n) < Date: Thu, 4 Feb 2021 17:29:46 -0500 Subject: [PATCH 26/28] last rans commit for a while, to submit for pr. no added regression test --- sandbox/CMakeLists.txt | 35 ++++++++++++++++----------------- sandbox/rans_walltest.cpp | 1 + src/common/default_options.cpp | 6 ++---- src/physics/fluidflow/euler.cpp | 1 - src/physics/fluidflow/rans.cpp | 21 +++++++++++--------- src/physics/fluidflow/rans.hpp | 10 ++++++---- 6 files changed, 38 insertions(+), 36 deletions(-) diff --git a/sandbox/CMakeLists.txt b/sandbox/CMakeLists.txt index 6f7d5b58..cfeddb25 100644 --- a/sandbox/CMakeLists.txt +++ b/sandbox/CMakeLists.txt @@ -14,27 +14,26 @@ function(create_sandbox source_list) endfunction(create_sandbox) set(SANDBOX_SRCS - advection2d - steady_vortex - steady_vortex_adjoint - unsteady_vortex - viscous_shock - airfoil_steady - airfoil_chaotic - magnetostatic_box + #advection2d + #steady_vortex + #steady_vortex_adjoint + #unsteady_vortex + #viscous_shock + #airfoil_steady + #airfoil_chaotic + #magnetostatic_box magnetostatic_motor - magnetostatic_wire - navier_stokes_mms + #magnetostatic_wire + #navier_stokes_mms thermal_square - # joule_wire - joule_box - mesh_move - mesh_move_2 - surface_distance - joule_wire - rans_freestream + #joule_wire + #joule_box + #mesh_move + #mesh_move_2 + #surface_distance + #rans_freestream rans_walltest - rans_mms + #rans_mms ) create_sandbox("${SANDBOX_SRCS}") diff --git a/sandbox/rans_walltest.cpp b/sandbox/rans_walltest.cpp index 39912ae0..6f0d0401 100644 --- a/sandbox/rans_walltest.cpp +++ b/sandbox/rans_walltest.cpp @@ -7,6 +7,7 @@ constexpr bool entvar = false; #include "adept.h" #include "mfem.hpp" +#include "euler.hpp" #include "rans.hpp" #include #include diff --git a/src/common/default_options.cpp b/src/common/default_options.cpp index a23c6f89..e1f730de 100644 --- a/src/common/default_options.cpp +++ b/src/common/default_options.cpp @@ -21,8 +21,7 @@ const nlohmann::json default_options {"mu", -1.0}, // nondimensional viscosity (if negative, use Sutherland's) {"sa-consts", {0.1355, 0.622, 0.666666666666667, 0.41, 0.3, 2, 7.1, 1.2, 0.5, 10, 16, 0.7, 0.9}}, //Spalart-Allmaras turbulence model constants, defined in rans_fluxes.hpp - {"sa-srcs", {1.0, 1.0}}, - {"sa-mms", false} + {"sa-srcs", {1.0, 1.0}} //for debug purposes, disable SA source terms }}, {"space-dis", // options related to spatial discretization @@ -45,8 +44,7 @@ const nlohmann::json default_options {"t-final", 1.0}, // final time to simulate to {"dt", 0.01}, // time-step size when `const-cfl` is false {"cfl", 1.0}, // target CFL number - {"max-iter", 10000}, // safe-guard upper bound on number of iterations - {"start-up", false} // in steady case, iterate PTC with constant step at first + {"max-iter", 10000} // safe-guard upper bound on number of iterations }}, {"nonlin-solver", // options related to root-finding algorithms diff --git a/src/physics/fluidflow/euler.cpp b/src/physics/fluidflow/euler.cpp index 8856e6a9..b90496de 100644 --- a/src/physics/fluidflow/euler.cpp +++ b/src/physics/fluidflow/euler.cpp @@ -286,7 +286,6 @@ double EulerSolver::calcStepSize(int iter, double t, if (options["time-dis"]["steady"].template get()) { double dt = 0.0; - double res_norm = calcResidualNorm(); // run a start-up period at constant dt until a threshold is reached // if (start_up) // { diff --git a/src/physics/fluidflow/rans.cpp b/src/physics/fluidflow/rans.cpp index 60f5ca21..2a31d90a 100644 --- a/src/physics/fluidflow/rans.cpp +++ b/src/physics/fluidflow/rans.cpp @@ -12,8 +12,9 @@ namespace mach template RANavierStokesSolver::RANavierStokesSolver(const nlohmann::json &json_options, - unique_ptr smesh) - : NavierStokesSolver(json_options, move(smesh)) + unique_ptr smesh, + MPI_Comm comm) + : NavierStokesSolver(json_options, move(smesh), comm) { if (entvar) { @@ -37,9 +38,9 @@ void RANavierStokesSolver::addResVolumeIntegrators(double alpha) Vector q_ref(dim+3); this->getFreeStreamState(q_ref); double d0 = getZeroDistance(); - bool mms = this->options["flow-param"]["sa-mms"].template get(); - // MMS option + // MMS option ///NOTE: Doesn't work + bool mms = false;//this->options["flow-param"]["sa-mms"].template get(); if (mms) { if (dim != 2) @@ -219,7 +220,8 @@ static void pert(const Vector &x, Vector& p); template void RANavierStokesSolver::iterationHook(int iter, - double t, double dt) + double t, double dt, + const ParGridFunction &state) { string file = this->options["file-names"].template get(); stringstream filename; @@ -276,7 +278,8 @@ void RANavierStokesSolver::iterationHook(int iter, template -void RANavierStokesSolver::terminalHook(int iter, double t_final) +void RANavierStokesSolver::terminalHook(int iter, double t_final, + const ParGridFunction &state) { // double entropy = ent->GetEnergy(*u); // entropylog << t_final << ' ' << entropy << endl; @@ -402,9 +405,9 @@ void pert(const Vector &x, Vector& p) } // explicit instantiation -template class RANavierStokesSolver<1>; -template class RANavierStokesSolver<2>; -template class RANavierStokesSolver<3>; +template class RANavierStokesSolver<1, false>; +template class RANavierStokesSolver<2, false>; +template class RANavierStokesSolver<3, false>; }//namespace mach diff --git a/src/physics/fluidflow/rans.hpp b/src/physics/fluidflow/rans.hpp index 4943a4cd..4dbe55f7 100644 --- a/src/physics/fluidflow/rans.hpp +++ b/src/physics/fluidflow/rans.hpp @@ -22,7 +22,8 @@ class RANavierStokesSolver : public NavierStokesSolver /// \param[in] smesh - if provided, defines the mesh for the problem /// \param[in] dim - number of dimensions RANavierStokesSolver(const nlohmann::json &json_options, - std::unique_ptr smesh = nullptr); + std::unique_ptr smesh, + MPI_Comm comm); /// Sets `q_in` to the inflow conservative + SA variables void getRANSInflowState(mfem::Vector &q_in); @@ -56,13 +57,14 @@ class RANavierStokesSolver : public NavierStokesSolver /// Return the number of state variables virtual int getNumState() override {return dim+3; } - virtual void iterationHook(int iter, double t, double dt) override; + virtual void iterationHook(int iter, double t, double dt, const mfem::ParGridFunction &state) override; - virtual void terminalHook(int iter, double t_final) override; + virtual void terminalHook(int iter, double t_final, const mfem::ParGridFunction &state) override; friend SolverPtr createSolver>( const nlohmann::json &json_options, - std::unique_ptr smesh); + std::unique_ptr smesh, + MPI_Comm comm); /// create mesh distance function from options void getDistanceFunction(); From a8eadcdd0741639411dd266fc55c1d6600752ba4 Mon Sep 17 00:00:00 2001 From: Tucker Babcock Date: Mon, 5 Apr 2021 12:07:10 -0700 Subject: [PATCH 27/28] fix documentation for added linear solver and nonlinear solver --- src/physics/solver.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/physics/solver.cpp b/src/physics/solver.cpp index 5b5220a9..8084affd 100644 --- a/src/physics/solver.cpp +++ b/src/physics/solver.cpp @@ -1252,7 +1252,11 @@ unique_ptr AbstractSolver::constructLinearSolver( { throw MachException("Unsupported iterative solver type!\n" "\tavilable options are: HypreGMRES, HypreFGMRES, GMRESSolver,\n" - "\tHyprePCG, CGSolver"); + "\tHyprePCG, CGSolver" +#ifdef MFEM_USE_SUPERLU + ", SuperLU" +#endif + "\n"); } return lin_solver; } @@ -1332,7 +1336,7 @@ unique_ptr AbstractSolver::constructNonlinearSolver( else { throw MachException("Unsupported nonlinear solver type!\n" - "\tavilable options are: newton\n"); + "\tavilable options are: Newton, InexactNewton\n"); } //double eta = 1e-1; //newton_solver.reset(new InexactNewton(comm, eta)); From ab601d5e6cc649eed75fa3dd8d5aa43005b98505 Mon Sep 17 00:00:00 2001 From: Tucker Babcock Date: Wed, 7 Apr 2021 14:02:25 -0700 Subject: [PATCH 28/28] change ctest run to only be verbose on failure --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7ea76f7..f23a6a5f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -206,4 +206,4 @@ jobs: shell: bash # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest -C $BUILD_TYPE -V \ No newline at end of file + run: ctest -C $BUILD_TYPE --output-on-failure