From d14a72dfb324d62e481c858c06604ea8dc50dfc9 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Wed, 7 Jan 2026 17:28:44 +0000 Subject: [PATCH 1/9] Add files to create 0D coaxial elbow --- include/components/CoaxialElbow1Phase.h | 17 +++++++++++++++++ .../userobjects/ADElbow0D1PhaseUserObject.h | 18 ++++++++++++++++++ src/components/CoaxialElbow1Phase.C | 13 +++++++++++++ src/userobjects/Elbow0D1PhaseUserObject.C | 1 + 4 files changed, 49 insertions(+) create mode 100644 include/components/CoaxialElbow1Phase.h create mode 100644 include/userobjects/ADElbow0D1PhaseUserObject.h create mode 100644 src/components/CoaxialElbow1Phase.C create mode 100644 src/userobjects/Elbow0D1PhaseUserObject.C diff --git a/include/components/CoaxialElbow1Phase.h b/include/components/CoaxialElbow1Phase.h new file mode 100644 index 0000000..bd11bd9 --- /dev/null +++ b/include/components/CoaxialElbow1Phase.h @@ -0,0 +1,17 @@ +#pragma once + +#include "VolumeJunction1Phase.h" + +class CoaxialElbow1Phase : public VolumeJunction1Phase { +public: + CoaxialElbow1Phase(const InputParameters ¶ms); + +protected: + void buildVolumeJunctionUserObject() override; + + // radius of curvature + const Real _r_curv; + +public: + static InputParameters validParams(); +}; diff --git a/include/userobjects/ADElbow0D1PhaseUserObject.h b/include/userobjects/ADElbow0D1PhaseUserObject.h new file mode 100644 index 0000000..2340eaf --- /dev/null +++ b/include/userobjects/ADElbow0D1PhaseUserObject.h @@ -0,0 +1,18 @@ +#pragma once + +#include "ADVolumeJunction1PhaseUserObject.h" +#include "InputParameters.h" + +class ADElbow0D1PhaseUserObject : public ADVolumeJunction1PhaseUserObject { +public: + static InputParameters validParams(); + + ADElbow0D1PhaseUserObject(const InputParameters ¶ms); + +protected: + void computeFluxesAndResiduals(const unsigned int &c) override; + + const Real &_r_curv; + + const Real &_d_h; +}; diff --git a/src/components/CoaxialElbow1Phase.C b/src/components/CoaxialElbow1Phase.C new file mode 100644 index 0000000..321db6d --- /dev/null +++ b/src/components/CoaxialElbow1Phase.C @@ -0,0 +1,13 @@ +#include "CoaxialElbow1Phase.h" +#include "InputParameters.h" +#include "VolumeJunction1Phase.h" + +InputParameters CoaxialElbow1Phase::validParams() { + auto params = VolumeJunction1Phase::validParams(); + + params.addRequiredParam("r_curv", "Radius of curvature [m]."); + return params; +} + +CoaxialElbow1Phase::CoaxialElbow1Phase(const InputParameters ¶ms) + : VolumeJunction1Phase(params), _r_curv(getParam("r_curv")) {} \ No newline at end of file diff --git a/src/userobjects/Elbow0D1PhaseUserObject.C b/src/userobjects/Elbow0D1PhaseUserObject.C new file mode 100644 index 0000000..e542fff --- /dev/null +++ b/src/userobjects/Elbow0D1PhaseUserObject.C @@ -0,0 +1 @@ +#include "ADElbow0D1PhaseUserObject.h" From 9ab6afb7095087145f4a111af01787839e015551 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Mon, 12 Jan 2026 11:08:25 +0000 Subject: [PATCH 2/9] Refactor coaxial classes with common base class --- include/components/Coaxial1PhaseBase.h | 22 +++++ include/components/CoaxialElbow1Phase.h | 15 ++-- include/components/CoaxialPipe1Phase.h | 10 +-- src/components/Coaxial1PhaseBase.C | 25 ++++++ src/components/CoaxialElbow1Phase.C | 106 +++++++++++++++++++++++- src/components/CoaxialPipe1Phase.C | 80 ++++++------------ 6 files changed, 185 insertions(+), 73 deletions(-) create mode 100644 include/components/Coaxial1PhaseBase.h create mode 100644 src/components/Coaxial1PhaseBase.C diff --git a/include/components/Coaxial1PhaseBase.h b/include/components/Coaxial1PhaseBase.h new file mode 100644 index 0000000..0c0db24 --- /dev/null +++ b/include/components/Coaxial1PhaseBase.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Component.h" +#include "InputParameters.h" + +class Coaxial1PhaseBase : public Component { +public: + Coaxial1PhaseBase(const InputParameters ¶ms) : Component(params) {} + +protected: + // Create constant function based on scalar value + FunctionName CreateFunctionFromValue(const std::string &suffix, + const Real value); + + // Function that copies parameters depending on whether the + // local parameter or global parameter is specified + template + inline void CopyParamFromParamWithGlobal(const std::string dst_name, + const std::string src_name, + const std::string global_src_name, + InputParameters &dst_params); +}; \ No newline at end of file diff --git a/include/components/CoaxialElbow1Phase.h b/include/components/CoaxialElbow1Phase.h index bd11bd9..c4fac3d 100644 --- a/include/components/CoaxialElbow1Phase.h +++ b/include/components/CoaxialElbow1Phase.h @@ -1,17 +1,16 @@ #pragma once -#include "VolumeJunction1Phase.h" +#include "Coaxial1PhaseBase.h" -class CoaxialElbow1Phase : public VolumeJunction1Phase { +class CoaxialElbow1Phase : public Coaxial1PhaseBase { public: CoaxialElbow1Phase(const InputParameters ¶ms); -protected: - void buildVolumeJunctionUserObject() override; - - // radius of curvature - const Real _r_curv; - public: static InputParameters validParams(); + +protected: + void AddElbowInner(); + + void AddElbowOuter(); }; diff --git a/include/components/CoaxialPipe1Phase.h b/include/components/CoaxialPipe1Phase.h index 97785b8..bd1b644 100644 --- a/include/components/CoaxialPipe1Phase.h +++ b/include/components/CoaxialPipe1Phase.h @@ -1,8 +1,10 @@ -#include +#pragma once + +#include #include #include -class CoaxialPipe1Phase : public Component { +class CoaxialPipe1Phase : public Coaxial1PhaseBase { public: static InputParameters validParams(); @@ -25,8 +27,4 @@ class CoaxialPipe1Phase : public Component { void AddHeatTransferConnection(const std::string &flow_channel, const std::string &hs, const std::string &hs_side, const Real radius); - - // Create constant function based on scalar value - FunctionName CreateFunctionFromValue(const std::string &suffix, - const Real value); }; \ No newline at end of file diff --git a/src/components/Coaxial1PhaseBase.C b/src/components/Coaxial1PhaseBase.C new file mode 100644 index 0000000..c34cf64 --- /dev/null +++ b/src/components/Coaxial1PhaseBase.C @@ -0,0 +1,25 @@ +#include "Coaxial1PhaseBase.h" + +FunctionName +Coaxial1PhaseBase::CreateFunctionFromValue(const std::string &suffix, + const Real value) { + auto func_params = _factory.getValidParams("ConstantFunction"); + func_params.set("value") = value; + + auto func_name = name() + "_" + suffix; + getTHMProblem().addFunction("ConstantFunction", func_name, func_params); + return func_name; +} + +template +inline void Coaxial1PhaseBase::CopyParamFromParamWithGlobal( + const std::string dst_name, const std::string src_name, + const std::string global_src_name, InputParameters &dst_params) { + + if (!isParamSetByUser(src_name) && !isParamSetByUser(global_src_name)) + mooseError("Either ", src_name, " or ", global_src_name, " must be set."); + + dst_params.set(dst_name) = (isParamSetByUser(src_name)) + ? getParam(src_name) + : getParam(global_src_name); +} \ No newline at end of file diff --git a/src/components/CoaxialElbow1Phase.C b/src/components/CoaxialElbow1Phase.C index 321db6d..fb152e2 100644 --- a/src/components/CoaxialElbow1Phase.C +++ b/src/components/CoaxialElbow1Phase.C @@ -1,13 +1,111 @@ #include "CoaxialElbow1Phase.h" +#include "CoaxialPipe1Phase.h" #include "InputParameters.h" -#include "VolumeJunction1Phase.h" InputParameters CoaxialElbow1Phase::validParams() { - auto params = VolumeJunction1Phase::validParams(); - params.addRequiredParam("r_curv", "Radius of curvature [m]."); + InputParameters params = CoaxialPipe1Phase::validParams(); + params.addRequiredParam("radius", "Radius of the pipe [m]"); + params.addRequiredParam("start_angle", + "Angle at which the pipe starts [degrees]"); + params.addRequiredParam("end_angle", + "Angle at which the pipe ends [degrees]"); + + // Suppress length. Also need to set it to something, because it is required + // in the parent class + params.set>("length") = {0.0}; + params.suppressParameter>("length"); + + params.addClassDescription("Bent pipe for 1-phase coaxial flow"); + return params; } CoaxialElbow1Phase::CoaxialElbow1Phase(const InputParameters ¶ms) - : VolumeJunction1Phase(params), _r_curv(getParam("r_curv")) {} \ No newline at end of file + : Coaxial1PhaseBase(params) { + AddElbowInner(); + AddElbowOuter(); +} + +void CoaxialElbow1Phase::AddElbowInner() { + const std::string class_name = "ElbowPipe1Phase"; + + auto pipe_params = _factory.getValidParams(class_name); + pipe_params.set("_thm_problem") = &getTHMProblem(); + + CopyParamFromParamWithGlobal("fp", "inner_fp", "fp", + pipe_params); + + CopyParamFromParamWithGlobal>( + "closures", "inner_closures", "closures", pipe_params); + + passParameter>("n_elems", pipe_params); + passParameter("position", pipe_params); + passParameter("orientation", pipe_params); + + Real radius = getParam("tube_inner_radius"); + + auto area = pi * radius * radius; + pipe_params.set("A") = + CreateFunctionFromValue("inner_area", area); + + auto d_h = 2 * radius; + pipe_params.set("D_h") = + CreateFunctionFromValue("inner_dh", d_h); + + CopyParamFromParamWithGlobal("initial_T", "inner_initial_T", + "initial_T", pipe_params); + CopyParamFromParamWithGlobal("initial_p", "inner_initial_p", + "initial_p", pipe_params); + CopyParamFromParamWithGlobal("initial_vel", "inner_initial_vel", + "initial_vel", pipe_params); + + passParameter("radius", pipe_params); + passParameter("start_angle", pipe_params); + passParameter("end_angle", pipe_params); + + getTHMProblem().addComponent(class_name, name() + "/inner", pipe_params); +} + +void CoaxialElbow1Phase::AddElbowOuter() { + const std::string class_name = "ElbowPipe1Phase"; + auto pipe_params = _factory.getValidParams(class_name); + pipe_params.set("_thm_problem") = &getTHMProblem(); + + CopyParamFromParamWithGlobal("fp", "outer_fp", "fp", + pipe_params); + + CopyParamFromParamWithGlobal>( + "closures", "outer_closures", "closures", pipe_params); + + passParameter>("n_elems", pipe_params); + passParameter("position", pipe_params); + passParameter("orientation", pipe_params); + + Real tube_radius = getParam("tube_inner_radius"); + auto tube_widths = getParam>("tube_widths"); + Real inner_radius = + tube_radius + std::accumulate(tube_widths.begin(), tube_widths.end(), 0.); + Real outer_radius = getParam("shell_inner_radius"); + + auto area = pi * (outer_radius * outer_radius - inner_radius * inner_radius); + pipe_params.set("A") = + CreateFunctionFromValue("outer_area", area); + + auto d_h = 2 * (outer_radius - inner_radius); + pipe_params.set("D_h") = + CreateFunctionFromValue("outer_dh", d_h); + + CopyParamFromParamWithGlobal("initial_T", "outer_initial_T", + "initial_T", pipe_params); + CopyParamFromParamWithGlobal("initial_p", "outer_initial_p", + "initial_p", pipe_params); + CopyParamFromParamWithGlobal("initial_vel", "outer_initial_vel", + "initial_vel", pipe_params); + + passParameter("radius", pipe_params); + passParameter("start_angle", pipe_params); + passParameter("end_angle", pipe_params); + + getTHMProblem().addComponent(class_name, name() + "/outer", pipe_params); +} diff --git a/src/components/CoaxialPipe1Phase.C b/src/components/CoaxialPipe1Phase.C index bf22a5b..8a7c5a2 100644 --- a/src/components/CoaxialPipe1Phase.C +++ b/src/components/CoaxialPipe1Phase.C @@ -11,25 +11,6 @@ registerMooseObject("ProteusApp", CoaxialPipe1Phase); -namespace { -// Function that copies parameters depending on whether the -// local parameter or global parameter is specified -template -inline void copyParamFromParamWithGlobal(const std::string dst_name, - const std::string src_name, - const std::string global_src_name, - InputParameters &dst_params, - const InputParameters &src_params) { - if (!src_params.isParamSetByUser(src_name) && - !src_params.isParamSetByUser(global_src_name)) - mooseError("Either ", src_name, " or ", global_src_name, " must be set."); - - dst_params.set(dst_name) = (src_params.isParamSetByUser(src_name)) - ? src_params.get(src_name) - : src_params.get(global_src_name); -} -} // namespace - InputParameters CoaxialPipe1Phase::validParams() { // add basic parameters such n_elems, position, etc. InputParameters params = Component1D::validParams(); @@ -137,7 +118,7 @@ InputParameters CoaxialPipe1Phase::validParams() { } CoaxialPipe1Phase::CoaxialPipe1Phase(const InputParameters ¶ms) - : Component(params) { + : Coaxial1PhaseBase(params) { // Add components AddInnerPipe(params); AddOuterAnnulus(params); @@ -162,11 +143,11 @@ void CoaxialPipe1Phase::AddInnerPipe(const InputParameters ¶ms) { auto pipe_params = _factory.getValidParams(class_name); pipe_params.set("_thm_problem") = &getTHMProblem(); - copyParamFromParamWithGlobal("fp", "inner_fp", "fp", - pipe_params, params); + CopyParamFromParamWithGlobal("fp", "inner_fp", "fp", + pipe_params); - copyParamFromParamWithGlobal>( - "closures", "inner_closures", "closures", pipe_params, params); + CopyParamFromParamWithGlobal>( + "closures", "inner_closures", "closures", pipe_params); pipe_params.set>("n_elems") = params.get>("n_elems"); @@ -186,12 +167,12 @@ void CoaxialPipe1Phase::AddInnerPipe(const InputParameters ¶ms) { pipe_params.set("D_h") = CreateFunctionFromValue("inner_dh", d_h); - copyParamFromParamWithGlobal("initial_T", "inner_initial_T", - "initial_T", pipe_params, params); - copyParamFromParamWithGlobal("initial_p", "inner_initial_p", - "initial_p", pipe_params, params); - copyParamFromParamWithGlobal( - "initial_vel", "inner_initial_vel", "initial_vel", pipe_params, params); + CopyParamFromParamWithGlobal("initial_T", "inner_initial_T", + "initial_T", pipe_params); + CopyParamFromParamWithGlobal("initial_p", "inner_initial_p", + "initial_p", pipe_params); + CopyParamFromParamWithGlobal("initial_vel", "inner_initial_vel", + "initial_vel", pipe_params); getTHMProblem().addComponent(class_name, name() + "/inner", pipe_params); } @@ -201,11 +182,11 @@ void CoaxialPipe1Phase::AddOuterAnnulus(const InputParameters ¶ms) { auto pipe_params = _factory.getValidParams(class_name); pipe_params.set("_thm_problem") = &getTHMProblem(); - copyParamFromParamWithGlobal("fp", "outer_fp", "fp", - pipe_params, params); + CopyParamFromParamWithGlobal("fp", "outer_fp", "fp", + pipe_params); - copyParamFromParamWithGlobal>( - "closures", "outer_closures", "closures", pipe_params, params); + CopyParamFromParamWithGlobal>( + "closures", "outer_closures", "closures", pipe_params); pipe_params.set>("n_elems") = params.get>("n_elems"); @@ -229,12 +210,12 @@ void CoaxialPipe1Phase::AddOuterAnnulus(const InputParameters ¶ms) { pipe_params.set("D_h") = CreateFunctionFromValue("outer_dh", d_h); - copyParamFromParamWithGlobal("initial_T", "outer_initial_T", - "initial_T", pipe_params, params); - copyParamFromParamWithGlobal("initial_p", "outer_initial_p", - "initial_p", pipe_params, params); - copyParamFromParamWithGlobal( - "initial_vel", "outer_initial_vel", "initial_vel", pipe_params, params); + CopyParamFromParamWithGlobal("initial_T", "outer_initial_T", + "initial_T", pipe_params); + CopyParamFromParamWithGlobal("initial_p", "outer_initial_p", + "initial_p", pipe_params); + CopyParamFromParamWithGlobal("initial_vel", "outer_initial_vel", + "initial_vel", pipe_params); getTHMProblem().addComponent(class_name, name() + "/outer", pipe_params); } @@ -265,8 +246,8 @@ void CoaxialPipe1Phase::AddSolidTube(const InputParameters ¶ms) { tube_params.set>("length") = params.get>("length"); - copyParamFromParamWithGlobal("initial_T", "tube_initial_T", - "initial_T", tube_params, params); + CopyParamFromParamWithGlobal("initial_T", "tube_initial_T", + "initial_T", tube_params); getTHMProblem().addComponent(class_name, name() + "/tube", tube_params); } @@ -298,8 +279,8 @@ void CoaxialPipe1Phase::AddSolidShell(const InputParameters ¶ms) { tube_params.set>("length") = params.get>("length"); - copyParamFromParamWithGlobal("initial_T", "shell_initial_T", - "initial_T", tube_params, params); + CopyParamFromParamWithGlobal("initial_T", "shell_initial_T", + "initial_T", tube_params); getTHMProblem().addComponent(class_name, name() + "/shell", tube_params); } @@ -326,14 +307,3 @@ void CoaxialPipe1Phase::AddHeatTransferConnection( getTHMProblem().addComponent( class_name, name() + "_" + flow_channel + "_" + hs, ht_params); } - -FunctionName -CoaxialPipe1Phase::CreateFunctionFromValue(const std::string &suffix, - const Real value) { - auto func_params = _factory.getValidParams("ConstantFunction"); - func_params.set("value") = value; - - auto func_name = name() + "_" + suffix; - getTHMProblem().addFunction("ConstantFunction", func_name, func_params); - return func_name; -} \ No newline at end of file From 1a74e525538d5f57c6658da86b47aab1b19919d0 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Mon, 12 Jan 2026 11:32:12 +0000 Subject: [PATCH 3/9] Add form loss correlation for elbow --- src/components/CoaxialElbow1Phase.C | 169 +++++++++++++++++----------- 1 file changed, 103 insertions(+), 66 deletions(-) diff --git a/src/components/CoaxialElbow1Phase.C b/src/components/CoaxialElbow1Phase.C index fb152e2..2c2f2b3 100644 --- a/src/components/CoaxialElbow1Phase.C +++ b/src/components/CoaxialElbow1Phase.C @@ -1,6 +1,6 @@ #include "CoaxialElbow1Phase.h" #include "CoaxialPipe1Phase.h" -#include "InputParameters.h" +#include InputParameters CoaxialElbow1Phase::validParams() { @@ -23,89 +23,126 @@ InputParameters CoaxialElbow1Phase::validParams() { CoaxialElbow1Phase::CoaxialElbow1Phase(const InputParameters ¶ms) : Coaxial1PhaseBase(params) { + + auto start_angle = getParam("start_angle"); + auto end_angle = getParam("end_angle"); + + if (!MooseUtils::relativeFuzzyEqual(abs(start_angle - end_angle), 90.)) + mooseError("Difference between start and end angles should be 90 degrees."); + AddElbowInner(); AddElbowOuter(); } void CoaxialElbow1Phase::AddElbowInner() { - const std::string class_name = "ElbowPipe1Phase"; - - auto pipe_params = _factory.getValidParams(class_name); - pipe_params.set("_thm_problem") = &getTHMProblem(); - - CopyParamFromParamWithGlobal("fp", "inner_fp", "fp", - pipe_params); - - CopyParamFromParamWithGlobal>( - "closures", "inner_closures", "closures", pipe_params); - - passParameter>("n_elems", pipe_params); - passParameter("position", pipe_params); - passParameter("orientation", pipe_params); + // Create elbow geometry + const std::string component_name = name() + "/inner"; Real radius = getParam("tube_inner_radius"); - - auto area = pi * radius * radius; - pipe_params.set("A") = - CreateFunctionFromValue("inner_area", area); - auto d_h = 2 * radius; - pipe_params.set("D_h") = - CreateFunctionFromValue("inner_dh", d_h); - CopyParamFromParamWithGlobal("initial_T", "inner_initial_T", - "initial_T", pipe_params); - CopyParamFromParamWithGlobal("initial_p", "inner_initial_p", - "initial_p", pipe_params); - CopyParamFromParamWithGlobal("initial_vel", "inner_initial_vel", - "initial_vel", pipe_params); - - passParameter("radius", pipe_params); - passParameter("start_angle", pipe_params); - passParameter("end_angle", pipe_params); - - getTHMProblem().addComponent(class_name, name() + "/inner", pipe_params); + { + const std::string class_name = "ElbowPipe1Phase"; + auto pipe_params = _factory.getValidParams(class_name); + pipe_params.set("_thm_problem") = &getTHMProblem(); + + CopyParamFromParamWithGlobal("fp", "inner_fp", "fp", + pipe_params); + + CopyParamFromParamWithGlobal>( + "closures", "inner_closures", "closures", pipe_params); + + passParameter>("n_elems", pipe_params); + passParameter("position", pipe_params); + passParameter("orientation", pipe_params); + + auto area = pi * radius * radius; + pipe_params.set("A") = + CreateFunctionFromValue("inner_area", area); + + pipe_params.set("D_h") = + CreateFunctionFromValue("inner_dh", d_h); + + CopyParamFromParamWithGlobal("initial_T", "inner_initial_T", + "initial_T", pipe_params); + CopyParamFromParamWithGlobal("initial_p", "inner_initial_p", + "initial_p", pipe_params); + CopyParamFromParamWithGlobal( + "initial_vel", "inner_initial_vel", "initial_vel", pipe_params); + + passParameter("radius", pipe_params); + passParameter("start_angle", pipe_params); + passParameter("end_angle", pipe_params); + + getTHMProblem().addComponent(class_name, component_name, pipe_params); + } + + // Add form loss + { + const std::string class_name = "FormLossFromFunction1Phase"; + auto loss_params = _factory.getValidParams(class_name); + loss_params.set("flow_channel") = component_name; + Real rad_curv = getParam("radius"); + Real k_prime = (0.21 / (sqrt(rad_curv / d_h))) / (rad_curv * pi / 2); + loss_params.set("K_prime") = + CreateFunctionFromValue("inner_loss", k_prime); + } } void CoaxialElbow1Phase::AddElbowOuter() { - const std::string class_name = "ElbowPipe1Phase"; - auto pipe_params = _factory.getValidParams(class_name); - pipe_params.set("_thm_problem") = &getTHMProblem(); - - CopyParamFromParamWithGlobal("fp", "outer_fp", "fp", - pipe_params); - - CopyParamFromParamWithGlobal>( - "closures", "outer_closures", "closures", pipe_params); - - passParameter>("n_elems", pipe_params); - passParameter("position", pipe_params); - passParameter("orientation", pipe_params); Real tube_radius = getParam("tube_inner_radius"); auto tube_widths = getParam>("tube_widths"); Real inner_radius = tube_radius + std::accumulate(tube_widths.begin(), tube_widths.end(), 0.); Real outer_radius = getParam("shell_inner_radius"); - - auto area = pi * (outer_radius * outer_radius - inner_radius * inner_radius); - pipe_params.set("A") = - CreateFunctionFromValue("outer_area", area); - auto d_h = 2 * (outer_radius - inner_radius); - pipe_params.set("D_h") = - CreateFunctionFromValue("outer_dh", d_h); - - CopyParamFromParamWithGlobal("initial_T", "outer_initial_T", - "initial_T", pipe_params); - CopyParamFromParamWithGlobal("initial_p", "outer_initial_p", - "initial_p", pipe_params); - CopyParamFromParamWithGlobal("initial_vel", "outer_initial_vel", - "initial_vel", pipe_params); - - passParameter("radius", pipe_params); - passParameter("start_angle", pipe_params); - passParameter("end_angle", pipe_params); - getTHMProblem().addComponent(class_name, name() + "/outer", pipe_params); + const std::string component_name = name() + "/outer"; + { + const std::string class_name = "ElbowPipe1Phase"; + auto pipe_params = _factory.getValidParams(class_name); + pipe_params.set("_thm_problem") = &getTHMProblem(); + + CopyParamFromParamWithGlobal("fp", "outer_fp", "fp", + pipe_params); + + CopyParamFromParamWithGlobal>( + "closures", "outer_closures", "closures", pipe_params); + + passParameter>("n_elems", pipe_params); + passParameter("position", pipe_params); + passParameter("orientation", pipe_params); + + auto area = + pi * (outer_radius * outer_radius - inner_radius * inner_radius); + pipe_params.set("A") = + CreateFunctionFromValue("outer_area", area); + + pipe_params.set("D_h") = + CreateFunctionFromValue("outer_dh", d_h); + + CopyParamFromParamWithGlobal("initial_T", "outer_initial_T", + "initial_T", pipe_params); + CopyParamFromParamWithGlobal("initial_p", "outer_initial_p", + "initial_p", pipe_params); + CopyParamFromParamWithGlobal( + "initial_vel", "outer_initial_vel", "initial_vel", pipe_params); + + passParameter("radius", pipe_params); + passParameter("start_angle", pipe_params); + passParameter("end_angle", pipe_params); + + getTHMProblem().addComponent(class_name, component_name, pipe_params); + } + + { + const std::string class_name = "FormLossFromFunction1Phase"; + auto loss_params = _factory.getValidParams(class_name); + loss_params.set("flow_channel") = component_name; + Real rad_curv = getParam("radius"); + Real k_prime = (0.21 / (sqrt(rad_curv / d_h))) / (rad_curv * pi / 2); + loss_params.set("K_prime") = + CreateFunctionFromValue("inner_loss", k_prime); + } } From ebca83827951d27eb51813b13224b70f5d49cc93 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Tue, 13 Jan 2026 14:45:10 +0000 Subject: [PATCH 4/9] Improve form loss correlation for elbow --- src/components/CoaxialElbow1Phase.C | 45 +++++++++++++++++----- src/components/CoaxialPipe1Phase.C | 5 ++- test/tests/components/coaxial_pipe/test.py | 6 +-- 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/components/CoaxialElbow1Phase.C b/src/components/CoaxialElbow1Phase.C index 2c2f2b3..39c70c0 100644 --- a/src/components/CoaxialElbow1Phase.C +++ b/src/components/CoaxialElbow1Phase.C @@ -1,7 +1,25 @@ #include "CoaxialElbow1Phase.h" #include "CoaxialPipe1Phase.h" +#include "InputParameters.h" +#include "MooseTypes.h" +#include "Registry.h" #include +#include +registerMooseObject("ProteusApp", CoaxialElbow1Phase); + +namespace { +Real getElbowKPrime(const Real &R_c, const Real &D_h) { + const Real length = 0.5 * pi * R_c; + Real k; + if (R_c / D_h > 1.) + k = 0.21 / sqrt(R_c / D_h); + else + k = 0.21 / pow(R_c / D_h, 2.5); + + return k / length; +} +} // namespace InputParameters CoaxialElbow1Phase::validParams() { InputParameters params = CoaxialPipe1Phase::validParams(); @@ -76,16 +94,19 @@ void CoaxialElbow1Phase::AddElbowInner() { getTHMProblem().addComponent(class_name, component_name, pipe_params); } - - // Add form loss + // Add form loss from Handbook of Hydraulic Resistance p. 195 { const std::string class_name = "FormLossFromFunction1Phase"; auto loss_params = _factory.getValidParams(class_name); - loss_params.set("flow_channel") = component_name; - Real rad_curv = getParam("radius"); - Real k_prime = (0.21 / (sqrt(rad_curv / d_h))) / (rad_curv * pi / 2); + loss_params.set("flow_channel") = component_name; + + auto r_c = getParam("radius"); + loss_params.set("K_prime") = - CreateFunctionFromValue("inner_loss", k_prime); + CreateFunctionFromValue("inner_k_prime", getElbowKPrime(r_c, d_h)); + + getTHMProblem().addComponent(class_name, component_name + "_loss", + loss_params); } } @@ -139,10 +160,14 @@ void CoaxialElbow1Phase::AddElbowOuter() { { const std::string class_name = "FormLossFromFunction1Phase"; auto loss_params = _factory.getValidParams(class_name); - loss_params.set("flow_channel") = component_name; - Real rad_curv = getParam("radius"); - Real k_prime = (0.21 / (sqrt(rad_curv / d_h))) / (rad_curv * pi / 2); + loss_params.set("flow_channel") = component_name; + + auto r_c = getParam("radius"); + loss_params.set("K_prime") = - CreateFunctionFromValue("inner_loss", k_prime); + CreateFunctionFromValue("outer_k_prime", getElbowKPrime(r_c, d_h)); + + getTHMProblem().addComponent(class_name, component_name + "_loss", + loss_params); } } diff --git a/src/components/CoaxialPipe1Phase.C b/src/components/CoaxialPipe1Phase.C index 8a7c5a2..1275abd 100644 --- a/src/components/CoaxialPipe1Phase.C +++ b/src/components/CoaxialPipe1Phase.C @@ -3,7 +3,6 @@ #include "FEProblemBase.h" #include "Factory.h" #include "InputParameters.h" -#include "MooseError.h" #include "MooseTypes.h" #include "Registry.h" #include "THMProblem.h" @@ -111,7 +110,9 @@ InputParameters CoaxialPipe1Phase::validParams() { params.addParam("initial_p", "Global pressure initialisation"); params.addParam("initial_vel", "Global velocity initialisation"); - params.addParamNamesToGroup("fp closures initial_T initial_p initial_vel", + params.addParam("gravity_vector", RealVectorValue{0, 0, 9.81}, + "gravity vector."); + params.addParamNamesToGroup("fp closures initial_T initial_p initial_vel gravity_vector", "global"); return params; diff --git a/test/tests/components/coaxial_pipe/test.py b/test/tests/components/coaxial_pipe/test.py index 65ba671..c21470a 100644 --- a/test/tests/components/coaxial_pipe/test.py +++ b/test/tests/components/coaxial_pipe/test.py @@ -9,9 +9,9 @@ def test_energy_balance(self): """Compares energy increase in pipes to heat flux input on shell exterior.""" _, t_inner, t_outer, q = np.loadtxt("energy_balance_out.csv", - skiprows=2, - delimiter=',', - unpack=True)[:,-1] + skiprows=2, + delimiter=',', + unpack=True)[:,-1] # mass flow rate in pipe and annulus 0.1 kg/s m_dot = 0.1 From 582695e8f8232786eec9234195663e9a0b3703cf Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Tue, 13 Jan 2026 17:18:46 +0000 Subject: [PATCH 5/9] Remove unnecessary parameters for elbow --- include/components/Coaxial1PhaseBase.h | 2 +- src/components/Coaxial1PhaseBase.C | 4 +-- src/components/CoaxialElbow1Phase.C | 39 ++++++++++++++++++++------ src/components/CoaxialPipe1Phase.C | 23 ++++++++------- 4 files changed, 46 insertions(+), 22 deletions(-) diff --git a/include/components/Coaxial1PhaseBase.h b/include/components/Coaxial1PhaseBase.h index 0c0db24..7f5e02f 100644 --- a/include/components/Coaxial1PhaseBase.h +++ b/include/components/Coaxial1PhaseBase.h @@ -19,4 +19,4 @@ class Coaxial1PhaseBase : public Component { const std::string src_name, const std::string global_src_name, InputParameters &dst_params); -}; \ No newline at end of file +}; diff --git a/src/components/Coaxial1PhaseBase.C b/src/components/Coaxial1PhaseBase.C index c34cf64..5420f04 100644 --- a/src/components/Coaxial1PhaseBase.C +++ b/src/components/Coaxial1PhaseBase.C @@ -12,7 +12,7 @@ Coaxial1PhaseBase::CreateFunctionFromValue(const std::string &suffix, } template -inline void Coaxial1PhaseBase::CopyParamFromParamWithGlobal( +void Coaxial1PhaseBase::CopyParamFromParamWithGlobal( const std::string dst_name, const std::string src_name, const std::string global_src_name, InputParameters &dst_params) { @@ -22,4 +22,4 @@ inline void Coaxial1PhaseBase::CopyParamFromParamWithGlobal( dst_params.set(dst_name) = (isParamSetByUser(src_name)) ? getParam(src_name) : getParam(global_src_name); -} \ No newline at end of file +} diff --git a/src/components/CoaxialElbow1Phase.C b/src/components/CoaxialElbow1Phase.C index 39c70c0..c2c95f0 100644 --- a/src/components/CoaxialElbow1Phase.C +++ b/src/components/CoaxialElbow1Phase.C @@ -1,8 +1,10 @@ +#include "Closures1PhaseNone.h" #include "CoaxialElbow1Phase.h" #include "CoaxialPipe1Phase.h" #include "InputParameters.h" #include "MooseTypes.h" #include "Registry.h" +#include "THMProblem.h" #include #include @@ -34,6 +36,15 @@ InputParameters CoaxialElbow1Phase::validParams() { params.set>("length") = {0.0}; params.suppressParameter>("length"); + // Momentum losses provided by minor loss formula + params.suppressParameter>("inner_closures"); + params.suppressParameter>("outer_closures"); + params.suppressParameter>("closures"); + // f set to zero + params.suppressParameter("inner_f"); + params.suppressParameter("outer_f"); + params.suppressParameter("f"); + params.addClassDescription("Bent pipe for 1-phase coaxial flow"); return params; @@ -48,6 +59,12 @@ CoaxialElbow1Phase::CoaxialElbow1Phase(const InputParameters ¶ms) if (!MooseUtils::relativeFuzzyEqual(abs(start_angle - end_angle), 90.)) mooseError("Difference between start and end angles should be 90 degrees."); + auto closure_params = _factory.getValidParams("Closures1PhaseSimple"); + closure_params.set("_thm_problem") = &getTHMProblem(); + closure_params.set("_logger") = &(getTHMProblem().log()); + getTHMProblem().addClosures("Closures1PhaseSimple", name() + "_closure", + closure_params); + AddElbowInner(); AddElbowOuter(); } @@ -57,7 +74,7 @@ void CoaxialElbow1Phase::AddElbowInner() { // Create elbow geometry const std::string component_name = name() + "/inner"; Real radius = getParam("tube_inner_radius"); - auto d_h = 2 * radius; + const Real d_h = 2 * radius; { const std::string class_name = "ElbowPipe1Phase"; @@ -67,8 +84,9 @@ void CoaxialElbow1Phase::AddElbowInner() { CopyParamFromParamWithGlobal("fp", "inner_fp", "fp", pipe_params); - CopyParamFromParamWithGlobal>( - "closures", "inner_closures", "closures", pipe_params); + pipe_params.set>("closures") = {name() + + "_closure"}; + pipe_params.set("f") = CreateFunctionFromValue("inner_f", 0.); passParameter>("n_elems", pipe_params); passParameter("position", pipe_params); @@ -98,9 +116,10 @@ void CoaxialElbow1Phase::AddElbowInner() { { const std::string class_name = "FormLossFromFunction1Phase"; auto loss_params = _factory.getValidParams(class_name); - loss_params.set("flow_channel") = component_name; + loss_params.set("_thm_problem") = &getTHMProblem(); + loss_params.set("flow_channel") = component_name; - auto r_c = getParam("radius"); + const Real r_c = getParam("radius"); loss_params.set("K_prime") = CreateFunctionFromValue("inner_k_prime", getElbowKPrime(r_c, d_h)); @@ -117,7 +136,7 @@ void CoaxialElbow1Phase::AddElbowOuter() { Real inner_radius = tube_radius + std::accumulate(tube_widths.begin(), tube_widths.end(), 0.); Real outer_radius = getParam("shell_inner_radius"); - auto d_h = 2 * (outer_radius - inner_radius); + const Real d_h = 2 * (outer_radius - inner_radius); const std::string component_name = name() + "/outer"; { @@ -128,8 +147,9 @@ void CoaxialElbow1Phase::AddElbowOuter() { CopyParamFromParamWithGlobal("fp", "outer_fp", "fp", pipe_params); - CopyParamFromParamWithGlobal>( - "closures", "outer_closures", "closures", pipe_params); + pipe_params.set>("closures") = {name() + + "_closure"}; + pipe_params.set("f") = CreateFunctionFromValue("outer_f", 0.); passParameter>("n_elems", pipe_params); passParameter("position", pipe_params); @@ -160,7 +180,8 @@ void CoaxialElbow1Phase::AddElbowOuter() { { const std::string class_name = "FormLossFromFunction1Phase"; auto loss_params = _factory.getValidParams(class_name); - loss_params.set("flow_channel") = component_name; + loss_params.set("_thm_problem") = &getTHMProblem(); + loss_params.set("flow_channel") = component_name; auto r_c = getParam("radius"); diff --git a/src/components/CoaxialPipe1Phase.C b/src/components/CoaxialPipe1Phase.C index 1275abd..789d884 100644 --- a/src/components/CoaxialPipe1Phase.C +++ b/src/components/CoaxialPipe1Phase.C @@ -32,10 +32,10 @@ InputParameters CoaxialPipe1Phase::validParams() { "Initial inner pipe pressure."); params.addParam("inner_initial_vel", "Initial inner pipe velocity."); - + params.addParam("inner_f", "Friction factor for inner tube."); params.addParamNamesToGroup( "inner_fp inner_closures inner_initial_T inner_initial_p " - "inner_initial_vel", + "inner_initial_vel inner_f", "inner"); // add parameters for the outer annulus @@ -44,14 +44,16 @@ InputParameters CoaxialPipe1Phase::validParams() { params.addParam>( "outer_closures", "Fluid property for outer annulus."); params.addParam("outer_initial_T", - "Initial inner pipe temperature."); + "Initial outer annulus temperature."); params.addParam("outer_initial_p", - "Initial inner pipe pressure."); + "Initial outer annulus pressure."); params.addParam("outer_initial_vel", - "Initial inner pipe velocity."); + "Initial outer annulus velocity."); + params.addParam("outer_f", + "Friction factor for outer annulus."); params.addParamNamesToGroup( "outer_fp outer_closures outer_initial_T outer_initial_p " - "outer_initial_vel", + "outer_initial_vel outer_f", "outer"); // Add parameters for the solid tube @@ -110,10 +112,11 @@ InputParameters CoaxialPipe1Phase::validParams() { params.addParam("initial_p", "Global pressure initialisation"); params.addParam("initial_vel", "Global velocity initialisation"); - params.addParam("gravity_vector", RealVectorValue{0, 0, 9.81}, - "gravity vector."); - params.addParamNamesToGroup("fp closures initial_T initial_p initial_vel gravity_vector", - "global"); + params.addParam( + "gravity_vector", RealVectorValue{0, 0, 9.81}, "gravity vector."); + params.addParam("f", "Global friction factor"); + params.addParamNamesToGroup( + "fp closures initial_T initial_p initial_vel gravity_vector f", "global"); return params; } From ee7616d71512539ca6c2a4441eaa494e0a23f276 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Tue, 13 Jan 2026 17:19:04 +0000 Subject: [PATCH 6/9] Add initial passing tests --- .../components/coaxial_elbow/elbow_test.i | 184 ++++++++++++++++++ test/tests/components/coaxial_elbow/test.py | 44 +++++ test/tests/components/coaxial_elbow/tests | 10 + 3 files changed, 238 insertions(+) create mode 100644 test/tests/components/coaxial_elbow/elbow_test.i create mode 100644 test/tests/components/coaxial_elbow/test.py create mode 100644 test/tests/components/coaxial_elbow/tests diff --git a/test/tests/components/coaxial_elbow/elbow_test.i b/test/tests/components/coaxial_elbow/elbow_test.i new file mode 100644 index 0000000..8e04448 --- /dev/null +++ b/test/tests/components/coaxial_elbow/elbow_test.i @@ -0,0 +1,184 @@ +# Energy balance test +# =================== +# +# Apply 10 kW/m^2 to outer surface of the +# shell and check the increase in temperature at the fluid outlets + +T_in = ${fparse 50 + 273.15} # Cold inlet temperature in annulus +mdot = 0.5 # nominal mass flow rate for primary +press = 1e5 # operating pressure + + +[GlobalParams] + initial_p = ${press} + closures = thm_closures + initial_T = ${T_in} + fp=fluid + gravity_vector = '0 0 0' + f = 0. +[] + +[FluidProperties] + [fluid] # mimic of water + type = SimpleFluidProperties + [] +[] + +[SolidProperties] + [adamantium] # fake solid material that ensures solid heats quickly + type = ThermalFunctionSolidProperties + cp = 40 + k = 50 + rho = 100 + [] +[] + +[Closures] # defines friction factors and heat transfer coefficients + [thm_closures] + type = Closures1PhaseTHM # default Churchill friction factor, DB HTC + [] +[] + +[Components] + [inlet_inner] + type = InletMassFlowRateTemperature1Phase + T = ${T_in} + m_dot = ${mdot} + input = coaxial/inner:in + [] + [inlet_outer] + type = InletMassFlowRateTemperature1Phase + T = ${T_in} + m_dot = ${mdot} + input = coaxial/outer:in + [] + [coaxial] + type = CoaxialElbow1Phase + n_elems = 200 + orientation = '1 0 0' + start_angle = 0. + radius = 0.1 + end_angle = 90 + position = '0 0 0' + shell_inner_radius = 0.075 + shell_materials = 'adamantium' + shell_n_elems = '10' + shell_names = 'shell' + shell_widths = '0.025' + shell_T_ref = '${T_in}' + tube_T_ref = ${T_in} + tube_inner_radius = 0.025 + tube_materials = 'adamantium' + tube_n_elems = '10' + tube_names = 'tube' + tube_widths = '0.025' + inner_initial_vel = 0.02 + outer_initial_vel = 0.05 + [] + [outlet_outer] + type =Outlet1Phase + input = coaxial/outer:out + p = ${press} + [] + [outlet_inner] + type =Outlet1Phase + input = coaxial/inner:out + p = ${press} + [] +[] + + +[Postprocessors] + [p_inlet_inner] + type = SideAverageValue + boundary = coaxial/inner:in + variable = p + [] + [p_outlet_inner] + type = SideAverageValue + boundary = coaxial/inner:out + variable = p + [] + [p_inlet_outer] + type = SideAverageValue + boundary = coaxial/outer:in + variable = p + [] + [p_outlet_outer] + type = SideAverageValue + boundary = coaxial/outer:out + variable = p + [] + [vel_inlet_inner] + type = SideAverageValue + boundary = coaxial/inner:in + variable = vel_y + [] + [rho_outer] + type = ADSideAverageMaterialProperty + boundary = coaxial/outer:out + property = rho + [] + [rho_inner] + type = ADSideAverageMaterialProperty + boundary = coaxial/inner:out + property = rho + [] + [vel_inlet_outer] + type = SideAverageValue + boundary = coaxial/outer:in + variable = vel_y + [] + [delta_p_inner] + type = ParsedPostprocessor + expression = 'p_out - p_in' + pp_names = 'p_outlet_inner p_inlet_inner' + pp_symbols = 'p_in p_out' + [] + [delta_p_outer] + type = ParsedPostprocessor + expression = 'p_out - p_in' + pp_names = 'p_outlet_outer p_inlet_outer' + pp_symbols = 'p_in p_out' + [] +[] + +[Preconditioning] + [pc] + type = SMP + full = true + [] +[] + +[Executioner] + type = Transient + start_time = 0 + + dt = 0.025 + end_time = 2000 + + line_search = basic + solve_type = NEWTON + + petsc_options_iname = '-pc_type' + petsc_options_value = 'lu' + + nl_rel_tol = 1e-3 + nl_abs_tol = 1e-6 + nl_max_its = 25 + automatic_scaling = true + steady_state_detection = true + steady_state_tolerance = 5e-7 +[] + +[Outputs] + exodus = true + csv = true + [console] + type = Console + max_rows = 1 + execute_postprocessors_on = final + outlier_variable_norms = false + [] + print_linear_residuals = false +[] diff --git a/test/tests/components/coaxial_elbow/test.py b/test/tests/components/coaxial_elbow/test.py new file mode 100644 index 0000000..3716ac4 --- /dev/null +++ b/test/tests/components/coaxial_elbow/test.py @@ -0,0 +1,44 @@ +"""Python test module for coaxial elbows.""" + +import unittest + +import numpy as np + + +class TestCoaxialElbow(unittest.TestCase): + """Test class for the coaxial elbow component.""" + + def test_inner_pressure_loss(self): + """Compares pressure drop with expected value for inner pipe.""" + r_c = 0.1 + + delta_p_inner, rho, vel_inlet = np.loadtxt("elbow_test_out.csv", + delimiter=',', + skiprows=1, + usecols=(1, 7, 9), + unpack=True)[:,-1] + k = 0.21/np.sqrt(r_c/0.05) + expected = k* 0.5*rho*vel_inlet*vel_inlet + + diff = abs((expected - delta_p_inner)/expected) + assert diff < 5e-3, ( + f"Inner pressure loss wrong {expected} vs {delta_p_inner}. Diff {diff}" + ) + + def test_outer_pressure_loss(self): + """Compares pressure drop with expected value for outer pipe.""" + + r_c = 0.1 + + delta_p_inner, rho, vel_inlet = np.loadtxt("elbow_test_out.csv", + delimiter=',', + skiprows=1, + usecols=(2, 8, 10), + unpack=True)[:,-1] + k = 0.21/np.sqrt(r_c/0.05) + expected = k* 0.5*rho*vel_inlet*vel_inlet + + diff = abs((expected - delta_p_inner)/expected) + assert diff < 5e-3, ( + f"Inner pressure loss wrong {expected} vs {delta_p_inner}. Diff {diff}" + ) diff --git a/test/tests/components/coaxial_elbow/tests b/test/tests/components/coaxial_elbow/tests new file mode 100644 index 0000000..f47be99 --- /dev/null +++ b/test/tests/components/coaxial_elbow/tests @@ -0,0 +1,10 @@ +[coaxial_elbow] + [run] + type = RunApp + input = elbow_test.i + [] + [verify] + type = PythonUnitTest + input = test.py + [] +[] From fa65a81a6c4dec0c29dd6fb236ef9a08b6df1816 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Tue, 13 Jan 2026 17:26:19 +0000 Subject: [PATCH 7/9] Add test for checking that invalid elbow angles raise an error --- src/components/CoaxialElbow1Phase.C | 9 ++++++--- test/tests/components/coaxial_elbow/tests | 10 ++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/components/CoaxialElbow1Phase.C b/src/components/CoaxialElbow1Phase.C index c2c95f0..e8f4317 100644 --- a/src/components/CoaxialElbow1Phase.C +++ b/src/components/CoaxialElbow1Phase.C @@ -1,10 +1,7 @@ -#include "Closures1PhaseNone.h" #include "CoaxialElbow1Phase.h" #include "CoaxialPipe1Phase.h" #include "InputParameters.h" -#include "MooseTypes.h" #include "Registry.h" -#include "THMProblem.h" #include #include @@ -56,9 +53,12 @@ CoaxialElbow1Phase::CoaxialElbow1Phase(const InputParameters ¶ms) auto start_angle = getParam("start_angle"); auto end_angle = getParam("end_angle"); + // This component is for 90 degree bends if (!MooseUtils::relativeFuzzyEqual(abs(start_angle - end_angle), 90.)) mooseError("Difference between start and end angles should be 90 degrees."); + // Closure has been removed as a parameter to be specified but we must specify + // it auto closure_params = _factory.getValidParams("Closures1PhaseSimple"); closure_params.set("_thm_problem") = &getTHMProblem(); closure_params.set("_logger") = &(getTHMProblem().log()); @@ -76,6 +76,7 @@ void CoaxialElbow1Phase::AddElbowInner() { Real radius = getParam("tube_inner_radius"); const Real d_h = 2 * radius; + // Add elbow geometry for inner pipe { const std::string class_name = "ElbowPipe1Phase"; auto pipe_params = _factory.getValidParams(class_name); @@ -112,6 +113,7 @@ void CoaxialElbow1Phase::AddElbowInner() { getTHMProblem().addComponent(class_name, component_name, pipe_params); } + // Add form loss from Handbook of Hydraulic Resistance p. 195 { const std::string class_name = "FormLossFromFunction1Phase"; @@ -138,6 +140,7 @@ void CoaxialElbow1Phase::AddElbowOuter() { Real outer_radius = getParam("shell_inner_radius"); const Real d_h = 2 * (outer_radius - inner_radius); + // Add elbow geometry for outer annulus const std::string component_name = name() + "/outer"; { const std::string class_name = "ElbowPipe1Phase"; diff --git a/test/tests/components/coaxial_elbow/tests b/test/tests/components/coaxial_elbow/tests index f47be99..cf4e5fb 100644 --- a/test/tests/components/coaxial_elbow/tests +++ b/test/tests/components/coaxial_elbow/tests @@ -2,9 +2,19 @@ [run] type = RunApp input = elbow_test.i + requirement = 'This system shall permit a valid elbow component to run.' [] [verify] type = PythonUnitTest input = test.py + prereq = coaxial_elbow/run + requirement = 'This system shall calculate pressure drops corresponding with empirical correlations' + [] + [angle_check] + type = RunException + input = elbow_test.i + cli_args = 'Components/coaxial/end_angle=100' + expect_err = 'Difference between start and end angles should be 90 degrees' + requirement = 'This system shall only permit elbow angles of 90 degrees' [] [] From b200c6fec0da802acb70363333cfb5535bcc1d45 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Tue, 13 Jan 2026 17:36:19 +0000 Subject: [PATCH 8/9] Improve comments and clean-up elbow code --- include/components/CoaxialElbow1Phase.h | 7 +++-- src/components/CoaxialElbow1Phase.C | 6 ++-- .../components/coaxial_elbow/elbow_test.i | 30 ++++++++----------- test/tests/components/coaxial_elbow/test.py | 4 +-- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/include/components/CoaxialElbow1Phase.h b/include/components/CoaxialElbow1Phase.h index c4fac3d..df1aa25 100644 --- a/include/components/CoaxialElbow1Phase.h +++ b/include/components/CoaxialElbow1Phase.h @@ -3,14 +3,15 @@ #include "Coaxial1PhaseBase.h" class CoaxialElbow1Phase : public Coaxial1PhaseBase { -public: - CoaxialElbow1Phase(const InputParameters ¶ms); - public: static InputParameters validParams(); + CoaxialElbow1Phase(const InputParameters ¶ms); + protected: + // Add elbow geometry and form loss for inner pipe void AddElbowInner(); + // Add elbow geometry and form loss for outer annulus void AddElbowOuter(); }; diff --git a/src/components/CoaxialElbow1Phase.C b/src/components/CoaxialElbow1Phase.C index e8f4317..d96906d 100644 --- a/src/components/CoaxialElbow1Phase.C +++ b/src/components/CoaxialElbow1Phase.C @@ -8,6 +8,8 @@ registerMooseObject("ProteusApp", CoaxialElbow1Phase); namespace { +// Compute momentum loss per unit length using Handbook of Hydraulic Resistance +// p. 195 Real getElbowKPrime(const Real &R_c, const Real &D_h) { const Real length = 0.5 * pi * R_c; Real k; @@ -19,6 +21,7 @@ Real getElbowKPrime(const Real &R_c, const Real &D_h) { return k / length; } } // namespace + InputParameters CoaxialElbow1Phase::validParams() { InputParameters params = CoaxialPipe1Phase::validParams(); @@ -71,12 +74,11 @@ CoaxialElbow1Phase::CoaxialElbow1Phase(const InputParameters ¶ms) void CoaxialElbow1Phase::AddElbowInner() { - // Create elbow geometry const std::string component_name = name() + "/inner"; Real radius = getParam("tube_inner_radius"); const Real d_h = 2 * radius; - // Add elbow geometry for inner pipe + // Add elbow geometry for inner pipe and pipe information { const std::string class_name = "ElbowPipe1Phase"; auto pipe_params = _factory.getValidParams(class_name); diff --git a/test/tests/components/coaxial_elbow/elbow_test.i b/test/tests/components/coaxial_elbow/elbow_test.i index 8e04448..7aac19b 100644 --- a/test/tests/components/coaxial_elbow/elbow_test.i +++ b/test/tests/components/coaxial_elbow/elbow_test.i @@ -1,21 +1,18 @@ -# Energy balance test +# Elbow pressure drop tests # =================== # -# Apply 10 kW/m^2 to outer surface of the -# shell and check the increase in temperature at the fluid outlets +# Check pressure drop matches correlations -T_in = ${fparse 50 + 273.15} # Cold inlet temperature in annulus +T_in = ${fparse 50 + 273.15} # Arbitrary reference temperature mdot = 0.5 # nominal mass flow rate for primary press = 1e5 # operating pressure [GlobalParams] initial_p = ${press} - closures = thm_closures initial_T = ${T_in} fp=fluid gravity_vector = '0 0 0' - f = 0. [] [FluidProperties] @@ -24,12 +21,9 @@ press = 1e5 # operating pressure [] [] -[SolidProperties] +[SolidProperties] // Not currently needed as solid heat transfer not considered yet [adamantium] # fake solid material that ensures solid heats quickly type = ThermalFunctionSolidProperties - cp = 40 - k = 50 - rho = 100 [] [] @@ -114,20 +108,20 @@ press = 1e5 # operating pressure boundary = coaxial/inner:in variable = vel_y [] - [rho_outer] - type = ADSideAverageMaterialProperty - boundary = coaxial/outer:out - property = rho + [vel_inlet_outer] + type = SideAverageValue + boundary = coaxial/outer:in + variable = vel_y [] [rho_inner] type = ADSideAverageMaterialProperty boundary = coaxial/inner:out property = rho [] - [vel_inlet_outer] - type = SideAverageValue - boundary = coaxial/outer:in - variable = vel_y + [rho_outer] + type = ADSideAverageMaterialProperty + boundary = coaxial/outer:out + property = rho [] [delta_p_inner] type = ParsedPostprocessor diff --git a/test/tests/components/coaxial_elbow/test.py b/test/tests/components/coaxial_elbow/test.py index 3716ac4..87be41d 100644 --- a/test/tests/components/coaxial_elbow/test.py +++ b/test/tests/components/coaxial_elbow/test.py @@ -26,7 +26,7 @@ def test_inner_pressure_loss(self): ) def test_outer_pressure_loss(self): - """Compares pressure drop with expected value for outer pipe.""" + """Compares pressure drop with expected value for outer annulus.""" r_c = 0.1 @@ -40,5 +40,5 @@ def test_outer_pressure_loss(self): diff = abs((expected - delta_p_inner)/expected) assert diff < 5e-3, ( - f"Inner pressure loss wrong {expected} vs {delta_p_inner}. Diff {diff}" + f"Outer pressure loss wrong {expected} vs {delta_p_inner}. Diff {diff}" ) From 5963801f4d5678a6d5e55950a46d60b5cbd6beb4 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Thu, 15 Jan 2026 13:01:38 +0000 Subject: [PATCH 9/9] Fix test failures introduced by clean-up --- .../components/coaxial_elbow/elbow_test.i | 5 ++- test/tests/components/coaxial_elbow/tests | 38 ++++++++++--------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/test/tests/components/coaxial_elbow/elbow_test.i b/test/tests/components/coaxial_elbow/elbow_test.i index 7aac19b..70713be 100644 --- a/test/tests/components/coaxial_elbow/elbow_test.i +++ b/test/tests/components/coaxial_elbow/elbow_test.i @@ -21,9 +21,12 @@ press = 1e5 # operating pressure [] [] -[SolidProperties] // Not currently needed as solid heat transfer not considered yet +[SolidProperties] # Not currently needed as solid heat transfer not considered yet [adamantium] # fake solid material that ensures solid heats quickly type = ThermalFunctionSolidProperties + cp = 40 + k = 50 + rho = 100 [] [] diff --git a/test/tests/components/coaxial_elbow/tests b/test/tests/components/coaxial_elbow/tests index cf4e5fb..b983bd8 100644 --- a/test/tests/components/coaxial_elbow/tests +++ b/test/tests/components/coaxial_elbow/tests @@ -1,20 +1,22 @@ -[coaxial_elbow] - [run] - type = RunApp - input = elbow_test.i - requirement = 'This system shall permit a valid elbow component to run.' - [] - [verify] - type = PythonUnitTest - input = test.py - prereq = coaxial_elbow/run - requirement = 'This system shall calculate pressure drops corresponding with empirical correlations' - [] - [angle_check] - type = RunException - input = elbow_test.i - cli_args = 'Components/coaxial/end_angle=100' - expect_err = 'Difference between start and end angles should be 90 degrees' - requirement = 'This system shall only permit elbow angles of 90 degrees' +[Tests] + [coaxial_elbow] + [run] + type = RunApp + input = elbow_test.i + requirement = 'This system shall permit a valid elbow component to run.' + [] + [verify] + type = PythonUnitTest + input = test.py + prereq = coaxial_elbow/run + requirement = 'This system shall calculate pressure drops corresponding with empirical correlations' + [] + [angle_check] + type = RunException + input = elbow_test.i + cli_args = 'Components/coaxial/end_angle=100' + expect_err = 'Difference between start and end angles should be 90 degrees' + requirement = 'This system shall only permit elbow angles of 90 degrees' + [] [] []