LCOV - code coverage report
Current view: top level - src/postprocessors - SpecificImpulse1Phase.C (source / functions) Hit Total Coverage
Test: idaholab/moose thermal_hydraulics: #30301 (3b550b) with base 2ad78d Lines: 0 80 0.0 %
Date: 2025-07-30 13:02:48 Functions: 0 7 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "SpecificImpulse1Phase.h"
      11             : #include "SinglePhaseFluidProperties.h"
      12             : #include "Numerics.h"
      13             : #include "THMIndicesVACE.h"
      14             : #include "BoundaryFluxBase.h"
      15             : 
      16             : registerMooseObject("ThermalHydraulicsApp", SpecificImpulse1Phase);
      17             : 
      18             : InputParameters
      19           0 : SpecificImpulse1Phase::validParams()
      20             : {
      21           0 :   InputParameters params = SidePostprocessor::validParams();
      22           0 :   params.addRequiredParam<Real>("p_exit", "Outlet pressure at nozzle exit");
      23           0 :   params.addRequiredParam<UserObjectName>("fp", "Single-phase fluid properties");
      24           0 :   params.addParam<Real>(
      25           0 :       "bisection_tolerance", 1e-4, "Tolerance for bisection to find h(entropy_in, p_out)");
      26           0 :   params.addParam<unsigned int>(
      27             :       "bisection_max_it",
      28           0 :       100,
      29             :       "Maximum number of iterations for bisection to find h(entropy_in, p_out)");
      30           0 :   params.addParam<bool>("cumulative",
      31           0 :                         true,
      32             :                         "If specific impulse is accumulated over timesteps. If false, then "
      33             :                         "instantaneous value is computed");
      34           0 :   params.addCoupledVar("variables", "Single-phase flow variables");
      35           0 :   params.set<std::vector<VariableName>>("variables") = {"rhoA", "rhouA", "rhoEA", "A"};
      36           0 :   params.addClassDescription("Estimates specific impulse from fluid state at a boundary");
      37           0 :   return params;
      38           0 : }
      39             : 
      40           0 : SpecificImpulse1Phase::SpecificImpulse1Phase(const InputParameters & parameters)
      41             :   : SidePostprocessor(parameters),
      42           0 :     _n_components(THMVACE1D::N_FLUX_INPUTS),
      43           0 :     _boundary_name(getParam<std::vector<BoundaryName>>("boundary")[0]),
      44           0 :     _boundary_uo_name(_boundary_name + ":boundary_uo"),
      45           0 :     _boundary_uo(getUserObjectByName<BoundaryFluxBase>(_boundary_uo_name)),
      46           0 :     _p_exit(getParam<Real>("p_exit")),
      47           0 :     _H(getMaterialPropertyByName<Real>("H")),
      48           0 :     _v(getMaterialPropertyByName<Real>("v")),
      49           0 :     _e(getMaterialPropertyByName<Real>("e")),
      50           0 :     _T(getMaterialPropertyByName<Real>("T")),
      51           0 :     _fp(getUserObject<SinglePhaseFluidProperties>("fp")),
      52           0 :     _tol(getParam<Real>("bisection_tolerance")),
      53           0 :     _max_nit(getParam<unsigned int>("bisection_max_it")),
      54           0 :     _cumulative(getParam<bool>("cumulative")),
      55           0 :     _accumulated_mass_flow_rate(declareRestartableData<Real>("accumulated_mass_flow_rate", 0)),
      56           0 :     _accumulated_thrust(declareRestartableData<Real>("accumulated_thrust", 0)),
      57           0 :     _value(0.0)
      58             : {
      59           0 :   if (_cumulative && !_fe_problem.isTransient())
      60           0 :     paramError("cumulative", "Must be false unless problem is transient");
      61             : 
      62           0 :   for (unsigned int i = 0; i < _n_components; i++)
      63           0 :     _U.push_back(&coupledValue("variables", i));
      64           0 : }
      65             : 
      66             : void
      67           0 : SpecificImpulse1Phase::threadJoin(const UserObject & y)
      68             : {
      69             :   const auto & pps = static_cast<const SpecificImpulse1Phase &>(y);
      70           0 :   _thrust += pps._thrust;
      71           0 :   _mass_flow_rate += pps._mass_flow_rate;
      72           0 : }
      73             : 
      74             : void
      75           0 : SpecificImpulse1Phase::initialize()
      76             : {
      77           0 :   _mass_flow_rate = 0;
      78           0 :   _thrust = 0;
      79           0 : }
      80             : 
      81             : Real
      82           0 : SpecificImpulse1Phase::getValue() const
      83             : {
      84           0 :   return _value;
      85             : }
      86             : 
      87             : void
      88           0 : SpecificImpulse1Phase::finalize()
      89             : {
      90           0 :   gatherSum(_thrust);
      91           0 :   gatherSum(_mass_flow_rate);
      92             : 
      93           0 :   if (_cumulative)
      94             :   {
      95           0 :     _accumulated_thrust += _dt * _thrust;
      96           0 :     _accumulated_mass_flow_rate += _dt * _mass_flow_rate;
      97           0 :     _value = _accumulated_thrust / _accumulated_mass_flow_rate / THM::gravity_const;
      98             :   }
      99             :   else
     100           0 :     _value = _thrust / _mass_flow_rate / THM::gravity_const;
     101           0 : }
     102             : 
     103             : void
     104           0 : SpecificImpulse1Phase::execute()
     105             : {
     106           0 :   for (unsigned int qp = 0; qp < _qrule->n_points(); qp++)
     107             :   {
     108           0 :     std::vector<Real> U(_n_components);
     109           0 :     for (unsigned int i = 0; i < _n_components; i++)
     110           0 :       U[i] = (*_U[i])[qp];
     111             : 
     112           0 :     const auto & flux = _boundary_uo.getFlux(_current_side, _current_elem->id(), U, _normals[qp]);
     113             : 
     114           0 :     _mass_flow_rate += std::abs(flux[0]);
     115             : 
     116             :     // get entropy at inlet (= entropy at outlet because process is isentropic)
     117           0 :     Real entropy_in = _fp.s_from_v_e(_v[qp], _e[qp]);
     118             : 
     119             :     // compute outlet enthalpy from entropy at inlet (isentropic flow)
     120             :     // and pressure at outlet, need to do bisection because h_from_s_p does not
     121             :     // exist, it is better to work with temperature because s(p, T) is available
     122             : 
     123             :     // upper bound of temperature is the inlet temperature
     124           0 :     Real T_up = _T[qp];
     125             : 
     126             :     // compute entropy associated with T_up
     127           0 :     Real entropy_up = _fp.s_from_p_T(_p_exit, T_up);
     128             : 
     129             :     // lower bound for temperature is 0.1 K
     130             :     Real T_low = 0.1;
     131             : 
     132             :     // compute entropy associated with T_low
     133           0 :     Real entropy_low = _fp.s_from_p_T(_p_exit, T_low);
     134             : 
     135             :     // compute the midpoint temperature
     136           0 :     Real T_mid = 0.5 * (T_up + T_low);
     137             : 
     138             :     // the current guess for entropy
     139           0 :     Real entropy_mid = _fp.s_from_p_T(_p_exit, T_mid);
     140             : 
     141             :     // main bisection loop
     142             :     unsigned int nit = 0;
     143           0 :     while (std::abs(1 - entropy_mid / entropy_in) > _tol)
     144             :     {
     145             :       // check if we are over/underestimating to select either up/down
     146           0 :       if ((entropy_mid - entropy_in > 0) == (entropy_up - entropy_low > 0))
     147             :       {
     148             :         T_up = T_mid;
     149             :         entropy_up = entropy_mid;
     150             :       }
     151             :       else
     152             :       {
     153             :         T_low = T_mid;
     154             :         entropy_low = entropy_mid;
     155             :       }
     156             : 
     157             :       // update guess for T_mid
     158           0 :       T_mid = 0.5 * (T_up + T_low);
     159             : 
     160             :       // update the guess for entropy
     161           0 :       entropy_mid = _fp.s_from_p_T(_p_exit, T_mid);
     162             : 
     163           0 :       ++nit;
     164             : 
     165           0 :       if (nit == _max_nit)
     166             :       {
     167           0 :         mooseDoOnce(mooseWarning("Bisection in SpecificImpulse1Phase did not converge"));
     168             :         break;
     169             :       }
     170             :     }
     171             : 
     172             :     // the enthalpy evaluated at _p_exit and T_mid
     173           0 :     Real h_exit = _fp.h_from_p_T(_p_exit, T_mid);
     174             : 
     175             :     // compute outlet speed
     176           0 :     Real vel_exit = std::sqrt(2.0 * (_H[qp] - h_exit));
     177           0 :     _thrust += std::abs(vel_exit * flux[0]);
     178             :   }
     179           0 : }

Generated by: LCOV version 1.14