From c8e1fa8efdec92948d7ffb114a862e7bc6f24565 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Tue, 6 Jan 2026 15:07:05 +0000 Subject: [PATCH 1/8] Add initial implementation of the coaxial juction component --- include/components/CoaxialJunction1Phase.h | 22 +++++ include/components/CoaxialPipe1Phase.h | 4 +- src/components/CoaxialJunction1Phase.C | 110 +++++++++++++++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 include/components/CoaxialJunction1Phase.h create mode 100644 src/components/CoaxialJunction1Phase.C diff --git a/include/components/CoaxialJunction1Phase.h b/include/components/CoaxialJunction1Phase.h new file mode 100644 index 0000000..8c8e8be --- /dev/null +++ b/include/components/CoaxialJunction1Phase.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Component.h" +#include "InputParameters.h" + +class CoaxialJunction1Phase : public Component { +public: + static InputParameters validParams(); + + CoaxialJunction1Phase(const InputParameters ¶ms); + +protected: + /// Connects the same solid region of two coaxial pipes + void ConnectSolidRegion(const std::string ®ion_name, + const ComponentName &component1, + const ComponentName &component2); + + /// Connects the same flow region of two coaxial pipes + void ConnectFlowRegion(const std::string ®ion_name, + const ComponentName &component1, + const ComponentName &component2); +}; \ No newline at end of file diff --git a/include/components/CoaxialPipe1Phase.h b/include/components/CoaxialPipe1Phase.h index 97785b8..4ceb4b2 100644 --- a/include/components/CoaxialPipe1Phase.h +++ b/include/components/CoaxialPipe1Phase.h @@ -1,4 +1,6 @@ -#include +#pragma once + +#include #include #include diff --git a/src/components/CoaxialJunction1Phase.C b/src/components/CoaxialJunction1Phase.C new file mode 100644 index 0000000..7577310 --- /dev/null +++ b/src/components/CoaxialJunction1Phase.C @@ -0,0 +1,110 @@ +#include "CoaxialJunction1Phase.h" +#include "Component.h" +#include "InputParameters.h" +#include "MooseError.h" +#include "MooseTypes.h" + +namespace { +inline std::pair +getComponentAndBoundary(const ComponentName &component) { + auto it = component.rfind(":"); + if (it == component.size()) + mooseError("No boundary specified. 'coaxial_connections' must be specified " + "as :."); + + auto comp = component.substr(0, it); + auto boundary = component.substr(it); + + if (!(boundary == "in" || boundary == "out")) + mooseError("Boundary must be 'in' or 'out'."); + + return {comp, boundary}; +} +} // namespace + +InputParameters CoaxialJunction1Phase::validParams() { + auto params = Component::validParams(); + params.addRequiredParam>( + "coaxial_connections", "Coaxial pipes boundaries to connect. " + ":in indicates start of the pipe, " + ":out indicates the end of the pipe."); + + params.addRequiredParam("tube_htc", + "HTC used for coupling tube regions."); + params.addRequiredParam("shell_htc", + "HTC used for coupling shell regions."); + + params.addParam("connect_inner", true, + "Whether to connect inner pipe."); + params.addParam("connect_outer", true, + "Whether to connect the outer annulus."); + + return params; +} + +CoaxialJunction1Phase::CoaxialJunction1Phase(const InputParameters ¶ms) + : Component(params) { + + auto coaxials = params.get>("coaxial_connections"); + + if (coaxials.size() != 2) + mooseError("'coaxial_connections' must have size 2."); + + ConnectSolidRegion("tube", coaxials[0], coaxials[1]); + ConnectSolidRegion("shell", coaxials[0], coaxials[1]); + + if (params.get("connect_inner")) { + ConnectFlowRegion("inner", coaxials[0], coaxials[1]); + } + if (params.get("connect_outer")) { + ConnectFlowRegion("outer", coaxials[0], coaxials[1]); + } +} + +void CoaxialJunction1Phase::ConnectSolidRegion( + const std::string ®ion_name, const ComponentName &component1, + const ComponentName &component2) { + auto comp_boundary1 = getComponentAndBoundary(component1); + auto comp_boundary2 = getComponentAndBoundary(component2); + + const std::string class_name = "HeatStructure2DCoupler"; + auto params = _factory.getValidParams(class_name); + + auto boundary1 = (comp_boundary1.second == "in") ? "start" : "end"; + auto boundary2 = (comp_boundary2.second == "in") ? "start" : "end"; + + params.set("primary_heat_structure") = + comp_boundary1.first + "/" + region_name; + params.set("primary_boundary") = + comp_boundary1.first + "/" + region_name + ":" + boundary1; + + params.set("secondary_heat_structure") = + comp_boundary1.first + "/" + region_name; + params.set("secondary_boundary") = + comp_boundary2.first + "/" + region_name + ":" + boundary2; + + params.set("heat_transfer_coefficient") = + parameters().get(region_name + "_htc"); + + getTHMProblem().addComponent(class_name, + name() + "/" + region_name + "_coupler", params); +} + +void CoaxialJunction1Phase::ConnectFlowRegion(const std::string ®ion_name, + const ComponentName &component1, + const ComponentName &component2) { + auto comp_boundary1 = getComponentAndBoundary(component1); + auto comp_boundary2 = getComponentAndBoundary(component2); + + const std::string class_name = "JunctionOneToOne1Phase"; + auto params = _factory.getValidParams(class_name); + + std::vector connections = { + comp_boundary1.first + "/" + region_name + ":" + comp_boundary1.second, + comp_boundary2.first + "/" + region_name + ":" + comp_boundary2.second, + }; + params.set>("connections") = connections; + + getTHMProblem().addComponent( + class_name, name() + "/" + region_name + "_junction", params); +} \ No newline at end of file From 8f2d8c036335235fbb360c69c0cb4bcf4448d82e Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Wed, 7 Jan 2026 13:16:53 +0000 Subject: [PATCH 2/8] Add passing initial tests for coaxial junctions --- src/components/CoaxialJunction1Phase.C | 15 +- src/components/CoaxialPipe1Phase.C | 17 +- .../coaxial_junction/junction_fluid.i | 210 +++++++++++++++++ .../coaxial_junction/junction_solid.i | 222 ++++++++++++++++++ .../tests/components/coaxial_junction/test.py | 65 +++++ test/tests/components/coaxial_junction/tests | 26 ++ 6 files changed, 548 insertions(+), 7 deletions(-) create mode 100644 test/tests/components/coaxial_junction/junction_fluid.i create mode 100644 test/tests/components/coaxial_junction/junction_solid.i create mode 100644 test/tests/components/coaxial_junction/test.py create mode 100644 test/tests/components/coaxial_junction/tests diff --git a/src/components/CoaxialJunction1Phase.C b/src/components/CoaxialJunction1Phase.C index 7577310..582b2e8 100644 --- a/src/components/CoaxialJunction1Phase.C +++ b/src/components/CoaxialJunction1Phase.C @@ -3,6 +3,9 @@ #include "InputParameters.h" #include "MooseError.h" #include "MooseTypes.h" +#include "Registry.h" + +registerMooseObject("ProteusApp", CoaxialJunction1Phase); namespace { inline std::pair @@ -13,10 +16,10 @@ getComponentAndBoundary(const ComponentName &component) { "as :."); auto comp = component.substr(0, it); - auto boundary = component.substr(it); + auto boundary = component.substr(it + 1); if (!(boundary == "in" || boundary == "out")) - mooseError("Boundary must be 'in' or 'out'."); + mooseError("Boundary must be 'in' or 'out', not '", boundary, "'."); return {comp, boundary}; } @@ -69,18 +72,19 @@ void CoaxialJunction1Phase::ConnectSolidRegion( const std::string class_name = "HeatStructure2DCoupler"; auto params = _factory.getValidParams(class_name); + params.set("_thm_problem") = &getTHMProblem(); auto boundary1 = (comp_boundary1.second == "in") ? "start" : "end"; auto boundary2 = (comp_boundary2.second == "in") ? "start" : "end"; params.set("primary_heat_structure") = comp_boundary1.first + "/" + region_name; - params.set("primary_boundary") = + params.set("primary_boundary") = comp_boundary1.first + "/" + region_name + ":" + boundary1; params.set("secondary_heat_structure") = - comp_boundary1.first + "/" + region_name; - params.set("secondary_boundary") = + comp_boundary2.first + "/" + region_name; + params.set("secondary_boundary") = comp_boundary2.first + "/" + region_name + ":" + boundary2; params.set("heat_transfer_coefficient") = @@ -98,6 +102,7 @@ void CoaxialJunction1Phase::ConnectFlowRegion(const std::string ®ion_name, const std::string class_name = "JunctionOneToOne1Phase"; auto params = _factory.getValidParams(class_name); + params.set("_thm_problem") = &getTHMProblem(); std::vector connections = { comp_boundary1.first + "/" + region_name + ":" + comp_boundary1.second, diff --git a/src/components/CoaxialPipe1Phase.C b/src/components/CoaxialPipe1Phase.C index bf22a5b..d49a176 100644 --- a/src/components/CoaxialPipe1Phase.C +++ b/src/components/CoaxialPipe1Phase.C @@ -130,8 +130,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", - "global"); + params.addRequiredParam("gravity_vector", "Gravity vector"); + params.addParamNamesToGroup( + "fp closures initial_T initial_p initial_vel gravity_vector", "global"); return params; } @@ -175,6 +176,10 @@ void CoaxialPipe1Phase::AddInnerPipe(const InputParameters ¶ms) { params.get("orientation"); pipe_params.set>("length") = params.get>("length"); + pipe_params.set>("axial_region_names") = + params.get>("axial_region_names"); + pipe_params.set("gravity_vector") = + params.get("gravity_vector"); Real radius = params.get("tube_inner_radius"); @@ -214,6 +219,10 @@ void CoaxialPipe1Phase::AddOuterAnnulus(const InputParameters ¶ms) { params.get("orientation"); pipe_params.set>("length") = params.get>("length"); + pipe_params.set>("axial_region_names") = + params.get>("axial_region_names"); + pipe_params.set("gravity_vector") = + params.get("gravity_vector"); Real tube_radius = params.get("tube_inner_radius"); auto tube_widths = params.get>("tube_widths"); @@ -264,6 +273,8 @@ void CoaxialPipe1Phase::AddSolidTube(const InputParameters ¶ms) { params.get("orientation"); tube_params.set>("length") = params.get>("length"); + tube_params.set>("axial_region_names") = + params.get>("axial_region_names"); copyParamFromParamWithGlobal("initial_T", "tube_initial_T", "initial_T", tube_params, params); @@ -297,6 +308,8 @@ void CoaxialPipe1Phase::AddSolidShell(const InputParameters ¶ms) { params.get("orientation"); tube_params.set>("length") = params.get>("length"); + tube_params.set>("axial_region_names") = + params.get>("axial_region_names"); copyParamFromParamWithGlobal("initial_T", "shell_initial_T", "initial_T", tube_params, params); diff --git a/test/tests/components/coaxial_junction/junction_fluid.i b/test/tests/components/coaxial_junction/junction_fluid.i new file mode 100644 index 0000000..13a7154 --- /dev/null +++ b/test/tests/components/coaxial_junction/junction_fluid.i @@ -0,0 +1,210 @@ +# 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 = 300 +press = 1e5 # operating pressure + +L = 1 # length of pipe + +[GlobalParams] + initial_p = ${press} + closures = thm_closures + fp=fluid + gravity_vector = '0 0 0' +[] + +[FluidProperties] + [fluid] # mimic of water + type = SimpleFluidProperties + cv = 4000 + [] + [fluid2] # mimic of water + type = SimpleFluidProperties + cv = 2000 + [] +[] + +[SolidProperties] + [adamantium] # fake solid material that ensures solid heats quickly + type = ThermalFunctionSolidProperties + cp = 1 + k = 1 + rho = 1 + [] + [ebony] # fake solid material that ensures solid heats quickly + type = ThermalFunctionSolidProperties + cp = 1 + k = 4 + rho = 16 + [] +[] + +[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} + m_dot = 1. + input = coaxial1/inner:in + [] + [inlet_outer] + type = InletMassFlowRateTemperature1Phase + T = ${T} + m_dot = 1. + input = coaxial2/outer:out + [] + [coaxial1] + type = CoaxialPipe1Phase + length = '${L}' + n_elems = '10' + orientation = '1 0 0' + position = '${fparse -L} 0 0' + shell_inner_radius = 0.075 + shell_materials = 'adamantium' + shell_n_elems = '1' + shell_names = 'shell' + shell_widths = '0.025' + shell_T_ref = '${T}' + tube_T_ref = ${T} + tube_inner_radius = 0.025 + tube_materials = 'adamantium' + tube_n_elems = '1' + tube_names = 'tube' + tube_widths = '0.025' + inner_initial_vel = 0. + outer_initial_vel = 0. + initial_T = ${T} + inner_tube_Hw = 0. + outer_tube_Hw = 0. + outer_shell_Hw = 0. + [] + [jct] + type = CoaxialJunction1Phase + coaxial_connections = 'coaxial1:out coaxial2:in' + shell_htc = 1e10 + tube_htc = 1e10 + [] + [coaxial2] + type = CoaxialPipe1Phase + length = '${L}' + n_elems = '10' + orientation = '1 0 0' + position = '0 0 0' + shell_inner_radius = 0.075 + shell_materials = 'ebony' + shell_n_elems = '1' + shell_names = 'shell' + shell_widths = '0.025' + shell_T_ref = '${T}' + tube_T_ref = ${T} + tube_inner_radius = 0.025 + tube_materials = 'ebony' + tube_n_elems = '1' + tube_names = 'tube' + tube_widths = '0.025' + inner_initial_vel = 0. + outer_initial_vel = 0. + initial_T = ${T} + inner_tube_Hw = 0. + outer_tube_Hw = 0. + outer_shell_Hw = 0. + [] + [outlet_outer] + type =Outlet1Phase + input = coaxial1/outer:in + p = ${press} + [] + [outlet_inner] + type =Outlet1Phase + input = coaxial2/inner:out + p = ${press} + [] + [tube_start1] + type = HSBoundarySpecifiedTemperature + T = ${T} + boundary = 'coaxial1/tube:start' + hs = coaxial1/tube + [] + [shell_start1] + type = HSBoundarySpecifiedTemperature + T = ${T} + boundary = 'coaxial1/shell:start' + hs = coaxial1/tube + [] + [tube_end2] + type = HSBoundarySpecifiedTemperature + T = ${T} + boundary = 'coaxial2/tube:end' + hs = coaxial1/tube + [] + [shell_end2] + type = HSBoundarySpecifiedTemperature + T = ${T} + boundary = 'coaxial2/shell:end' + hs = coaxial2/tube + [] + +[] + + +[Postprocessors] + [mdot_inner] + type = ADFlowBoundaryFlux1Phase + boundary = outlet_inner + equation = mass + [] + [mdot_outer] + type = ADFlowBoundaryFlux1Phase + boundary = outlet_outer + equation = mass + [] + +[] + +[Preconditioning] + [pc] + type = SMP + full = true + [] +[] + +[Executioner] + type = Transient + start_time = 0 + + dt = 1. + end_time = 10 + + line_search = basic + solve_type = NEWTON + + petsc_options_iname = '-pc_type' + petsc_options_value = 'lu' + + nl_rel_tol = 1e-4 + nl_abs_tol = 1e-6 + nl_max_its = 25 + automatic_scaling = true + steady_state_detection = true + # steady_state_tolerance = 2e-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_junction/junction_solid.i b/test/tests/components/coaxial_junction/junction_solid.i new file mode 100644 index 0000000..6bc34f0 --- /dev/null +++ b/test/tests/components/coaxial_junction/junction_solid.i @@ -0,0 +1,222 @@ +# 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_1 = 301 +T_2 = 300 +press = 1e5 # operating pressure + +L = 1 # length of pipe + +[GlobalParams] + initial_p = ${press} + closures = thm_closures + fp=fluid + gravity_vector = '0 0 0' +[] + +[FluidProperties] + [fluid] # mimic of water + type = SimpleFluidProperties + cv = 4000 + [] + [fluid2] # mimic of water + type = SimpleFluidProperties + cv = 2000 + [] +[] + +[SolidProperties] + [adamantium] # fake solid material that ensures solid heats quickly + type = ThermalFunctionSolidProperties + cp = 1 + k = 1 + rho = 1 + [] + [ebony] # fake solid material that ensures solid heats quickly + type = ThermalFunctionSolidProperties + cp = 1 + k = 4 + rho = 16 + [] +[] + +[Closures] # defines friction factors and heat transfer coefficients + [thm_closures] + type = Closures1PhaseTHM # default Churchill friction factor, DB HTC + [] +[] + +[Components] + [inlet_inner] + type = InletMassFlowRateTemperature1Phase + T = ${fparse 0.5*(T_1 + T_2)} + m_dot = 0. + input = coaxial1/inner:in + [] + [inlet_outer] + type = InletMassFlowRateTemperature1Phase + T = ${fparse 0.5*(T_1 + T_2)} + m_dot = 0. + input = coaxial2/outer:out + [] + [coaxial1] + type = CoaxialPipe1Phase + length = '${fparse L/3} ${fparse L/3} ${fparse L/3}' + axial_region_names = 'part1 part2 part3' + n_elems = '8 16 32' + orientation = '1 0 0' + position = '${fparse -L} 0 0' + shell_inner_radius = 0.075 + shell_materials = 'adamantium' + shell_n_elems = '1' + shell_names = 'shell' + shell_widths = '0.025' + shell_T_ref = '${T_1}' + tube_T_ref = ${T_1} + tube_inner_radius = 0.025 + tube_materials = 'adamantium' + tube_n_elems = '1' + tube_names = 'tube' + tube_widths = '0.025' + inner_initial_vel = 0. + outer_initial_vel = 0. + initial_T = ${T_1} + inner_tube_Hw = 0. + outer_tube_Hw = 0. + outer_shell_Hw = 0. + [] + [jct] + type = CoaxialJunction1Phase + coaxial_connections = 'coaxial1:out coaxial2:in' + shell_htc = 1e10 + tube_htc = 1e10 + [] + [coaxial2] + type = CoaxialPipe1Phase + length = '${fparse L/3} ${fparse L/3} ${fparse L/3}' + axial_region_names = 'part1 part2 part3' + n_elems = '32 16 8' + orientation = '1 0 0' + position = '0 0 0' + shell_inner_radius = 0.075 + shell_materials = 'ebony' + shell_n_elems = '1' + shell_names = 'shell' + shell_widths = '0.025' + shell_T_ref = '${T_2}' + tube_T_ref = ${T_2} + tube_inner_radius = 0.025 + tube_materials = 'ebony' + tube_n_elems = '1' + tube_names = 'tube' + tube_widths = '0.025' + inner_initial_vel = 0. + outer_initial_vel = 0. + initial_T = ${T_2} + inner_tube_Hw = 0. + outer_tube_Hw = 0. + outer_shell_Hw = 0. + [] + [outlet_outer] + type =Outlet1Phase + input = coaxial1/outer:in + p = ${press} + [] + [outlet_inner] + type =Outlet1Phase + input = coaxial2/inner:out + p = ${press} + [] + [tube_start1] + type = HSBoundarySpecifiedTemperature + T = ${T_1} + boundary = 'coaxial1/tube:start' + hs = coaxial1/tube + [] + [shell_start1] + type = HSBoundarySpecifiedTemperature + T = ${T_1} + boundary = 'coaxial1/shell:start' + hs = coaxial1/tube + [] + [tube_end2] + type = HSBoundarySpecifiedTemperature + T = ${T_2} + boundary = 'coaxial2/tube:end' + hs = coaxial1/tube + [] + [shell_end2] + type = HSBoundarySpecifiedTemperature + T = ${T_2} + boundary = 'coaxial2/shell:end' + hs = coaxial2/tube + [] + +[] + + +[Postprocessors] + [T_shell_out1] + type = SideAverageValue + boundary = coaxial1/shell:end + variable = T_solid + [] + [T_shell_in2] + type = SideAverageValue + boundary = coaxial2/shell:start + variable = T_solid + [] + [T_tube_out1] + type = SideAverageValue + boundary = coaxial1/tube:end + variable = T_solid + [] + [T_tube_in2] + type = SideAverageValue + boundary = coaxial2/tube:start + variable = T_solid + [] +[] + +[Preconditioning] + [pc] + type = SMP + full = true + [] +[] + +[Executioner] + type = Transient + start_time = 0 + + dt = 0.0001 + end_time = 0.01 + + line_search = basic + solve_type = NEWTON + + petsc_options_iname = '-pc_type' + petsc_options_value = 'lu' + + nl_rel_tol = 1e-4 + nl_abs_tol = 1e-6 + nl_max_its = 25 + automatic_scaling = true + steady_state_detection = true + # steady_state_tolerance = 2e-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_junction/test.py b/test/tests/components/coaxial_junction/test.py new file mode 100644 index 0000000..5281641 --- /dev/null +++ b/test/tests/components/coaxial_junction/test.py @@ -0,0 +1,65 @@ +"""Python test module for coaxial junction.""" + +import unittest +import numpy as np + +def heat_flux(time: float, + temp_cold: float, + temp_hot: float, + k1: float, + k2: float, + rho_cp1: float, + rho_cp2: float) -> np.ndarray: + """Computes boundary heat flux at junction.""" + + numerator = (temp_hot - temp_cold) * np.sqrt(k2 * rho_cp2) + denominator = np.sqrt(k1 * rho_cp1) + np.sqrt(k2 * rho_cp2) + arg = 1 / (2 * np.sqrt(k1 / rho_cp1 * time)) + return k1*(numerator / denominator) * 2*arg/np.sqrt(np.pi) + + + +class TestCoaxialJunction(unittest.TestCase): + """Test class for the coaxial junction component.""" + def test_solid_continuity(self): + """Checks the solid temperature is similar on both sides on the solid coupler.""" + + time, t_shell_in2, t_shell_out1, t_tube_in2, t_tube_out1 = np.loadtxt( + "junction_solid_out.csv", + skiprows=2, + delimiter=',', + unpack=True + ) + + # The 2D coupling uses a HTC approach. Here we set it very high: 1e10. + # This means that we should expect a small but finite difference between each + # side of the coupler + # The minimum difference should be delta_t = qw/HTC + # qw can be calculated from the analytical solution + qw = heat_flux(time, 300, 301, 1, 4, 1, 16) + eps = 5e-9 # account numerical errors, set small but arbitrary + expected_err = qw/1e10 + eps + + diff = abs(t_shell_out1 - t_shell_in2) + + idx = np.argmax(diff-expected_err) + assert all(diff < expected_err), \ + f"shell temperature difference ({time[idx]}) " \ + f"greater than {expected_err[idx]}: {diff[idx]}" + + diff = abs(t_tube_out1 - t_tube_in2) + + idx = np.argmax(diff-expected_err) + assert all(diff < expected_err), \ + f"tube temperature difference ({time[idx]}) " \ + f"greater than {expected_err[idx]}: {diff[idx]}" + + def test_fluid_mdot(self): + """Checks the outlet mass flow rate of second coaxial pipe matches inlet.""" + _, mdot_inner, mdot_outer = np.loadtxt("junction_solid_out.csv", + skiprows=1, + delimiter=',', + unpack=True)[:,-1] + + assert abs(mdot_inner -1) < 1e-4, "Inner mass flow rate incorrect." + assert abs(mdot_outer -1) < 1e-4, "Outer mass flow rate incorrect." diff --git a/test/tests/components/coaxial_junction/tests b/test/tests/components/coaxial_junction/tests new file mode 100644 index 0000000..9a9b3d8 --- /dev/null +++ b/test/tests/components/coaxial_junction/tests @@ -0,0 +1,26 @@ +[coaxial_junction] + [test_solid_connection] + [run] + type = RunApp + input = junction_solid.i + [] + [verify] + type = PythonUnitTest + input = test.py + test_case = TestCoaxialJunction.test_solid_continuity + prereq = test_solid_connection/run + [] + [] + [test_fluid_connection] + [run] + type = RunApp + input = junction_fluid.i + [] + [verify] + type = PythonUnitTest + input = test.py + test_case = TestCoaxialJunction.test_solid_continuity + prereq = test_fluid_connection/run + [] + [] +[] \ No newline at end of file From b1b335b7515ed4add9f0c64eba5e028655028198 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Wed, 7 Jan 2026 13:27:37 +0000 Subject: [PATCH 3/8] Add moose error tests to coaxial junction --- src/components/CoaxialJunction1Phase.C | 1 - test/tests/components/coaxial_junction/tests | 24 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/components/CoaxialJunction1Phase.C b/src/components/CoaxialJunction1Phase.C index 582b2e8..f4d03c0 100644 --- a/src/components/CoaxialJunction1Phase.C +++ b/src/components/CoaxialJunction1Phase.C @@ -1,7 +1,6 @@ #include "CoaxialJunction1Phase.h" #include "Component.h" #include "InputParameters.h" -#include "MooseError.h" #include "MooseTypes.h" #include "Registry.h" diff --git a/test/tests/components/coaxial_junction/tests b/test/tests/components/coaxial_junction/tests index 9a9b3d8..56ff92b 100644 --- a/test/tests/components/coaxial_junction/tests +++ b/test/tests/components/coaxial_junction/tests @@ -10,6 +10,30 @@ test_case = TestCoaxialJunction.test_solid_continuity prereq = test_solid_connection/run [] + [check_insufficient_connections] + type = RunException + input = junction_solid.i + cli_args = "Components/jct/coaxial_connections='coaxial1:out'" + expect_err = 'must have size 2' + [] + [check_boundary] + type = RunException + input = junction_solid.i + cli_args = "Components/jct/coaxial_connections='coaxial1:out coaxial2:in1'" + expect_err = 'Boundary must be' + [] + [check_no_inner] + type = RunException + input = junction_solid.i + cli_args = "Components/jct/connect_inner=false" + expect_err = "inner' does not have connected inlet." + [] + [check_no_outer] + type = RunException + input = junction_solid.i + cli_args = "Components/jct/connect_outer=false" + expect_err = "outer' does not have connected inlet." + [] [] [test_fluid_connection] [run] From f54028e20a849f6dadafccda5c870147ffecafac Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Wed, 7 Jan 2026 13:32:17 +0000 Subject: [PATCH 4/8] Improve comments for coaxial junction --- src/components/CoaxialJunction1Phase.C | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/CoaxialJunction1Phase.C b/src/components/CoaxialJunction1Phase.C index f4d03c0..df68b2b 100644 --- a/src/components/CoaxialJunction1Phase.C +++ b/src/components/CoaxialJunction1Phase.C @@ -7,6 +7,7 @@ registerMooseObject("ProteusApp", CoaxialJunction1Phase); namespace { +// Splits component name into component and boundary pair and checks validity inline std::pair getComponentAndBoundary(const ComponentName &component) { auto it = component.rfind(":"); @@ -31,11 +32,13 @@ InputParameters CoaxialJunction1Phase::validParams() { ":in indicates start of the pipe, " ":out indicates the end of the pipe."); + // Passed to 2D coupler params.addRequiredParam("tube_htc", "HTC used for coupling tube regions."); params.addRequiredParam("shell_htc", "HTC used for coupling shell regions."); + // Allows other componenets such as pumps to be inserted instead params.addParam("connect_inner", true, "Whether to connect inner pipe."); params.addParam("connect_outer", true, @@ -73,6 +76,8 @@ void CoaxialJunction1Phase::ConnectSolidRegion( auto params = _factory.getValidParams(class_name); params.set("_thm_problem") = &getTHMProblem(); + // The ends of the solid regions are called start and end rather than in and + // out auto boundary1 = (comp_boundary1.second == "in") ? "start" : "end"; auto boundary2 = (comp_boundary2.second == "in") ? "start" : "end"; @@ -99,6 +104,8 @@ void CoaxialJunction1Phase::ConnectFlowRegion(const std::string ®ion_name, auto comp_boundary1 = getComponentAndBoundary(component1); auto comp_boundary2 = getComponentAndBoundary(component2); + // In the future, we could create a component to account for form + // loss due to geometry changes const std::string class_name = "JunctionOneToOne1Phase"; auto params = _factory.getValidParams(class_name); params.set("_thm_problem") = &getTHMProblem(); @@ -111,4 +118,4 @@ void CoaxialJunction1Phase::ConnectFlowRegion(const std::string ®ion_name, getTHMProblem().addComponent( class_name, name() + "/" + region_name + "_junction", params); -} \ No newline at end of file +} From 83f22f089ff43f5bce24b55a9a08cb575e56ccd1 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Wed, 7 Jan 2026 15:12:46 +0000 Subject: [PATCH 5/8] Make gravity an optional parameter --- src/components/CoaxialPipe1Phase.C | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/CoaxialPipe1Phase.C b/src/components/CoaxialPipe1Phase.C index d49a176..0afc3fe 100644 --- a/src/components/CoaxialPipe1Phase.C +++ b/src/components/CoaxialPipe1Phase.C @@ -130,7 +130,8 @@ InputParameters CoaxialPipe1Phase::validParams() { params.addParam("initial_p", "Global pressure initialisation"); params.addParam("initial_vel", "Global velocity initialisation"); - params.addRequiredParam("gravity_vector", "Gravity vector"); + params.addParam( + "gravity_vector", RealVectorValue{0, 0, -9.81}, "Gravity vector"); params.addParamNamesToGroup( "fp closures initial_T initial_p initial_vel gravity_vector", "global"); From beb1ab56d7c02b11fc0830c2228cad0e18dcd838 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Wed, 7 Jan 2026 15:54:26 +0000 Subject: [PATCH 6/8] Comment out solid connections for now in coaxial junction --- src/components/CoaxialJunction1Phase.C | 4 ++-- test/tests/components/coaxial_junction/tests | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/CoaxialJunction1Phase.C b/src/components/CoaxialJunction1Phase.C index df68b2b..60f4c5c 100644 --- a/src/components/CoaxialJunction1Phase.C +++ b/src/components/CoaxialJunction1Phase.C @@ -55,8 +55,8 @@ CoaxialJunction1Phase::CoaxialJunction1Phase(const InputParameters ¶ms) if (coaxials.size() != 2) mooseError("'coaxial_connections' must have size 2."); - ConnectSolidRegion("tube", coaxials[0], coaxials[1]); - ConnectSolidRegion("shell", coaxials[0], coaxials[1]); + // ConnectSolidRegion("tube", coaxials[0], coaxials[1]); + // ConnectSolidRegion("shell", coaxials[0], coaxials[1]); if (params.get("connect_inner")) { ConnectFlowRegion("inner", coaxials[0], coaxials[1]); diff --git a/test/tests/components/coaxial_junction/tests b/test/tests/components/coaxial_junction/tests index 56ff92b..018832c 100644 --- a/test/tests/components/coaxial_junction/tests +++ b/test/tests/components/coaxial_junction/tests @@ -1,15 +1,15 @@ [coaxial_junction] [test_solid_connection] - [run] - type = RunApp - input = junction_solid.i - [] - [verify] - type = PythonUnitTest - input = test.py - test_case = TestCoaxialJunction.test_solid_continuity - prereq = test_solid_connection/run - [] + # [run] + # type = RunApp + # input = junction_solid.i + # [] + # [verify] + # type = PythonUnitTest + # input = test.py + # test_case = TestCoaxialJunction.test_solid_continuity + # prereq = test_solid_connection/run + # [] [check_insufficient_connections] type = RunException input = junction_solid.i From b79de886d31db459d2fb7c0516dadec4afa66513 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Wed, 7 Jan 2026 16:21:39 +0000 Subject: [PATCH 7/8] Re-add solid component with test to check the junctions works with small geometry changes --- src/components/CoaxialJunction1Phase.C | 4 +- .../coaxial_junction/junction_solid_expand.i | 222 ++++++++++++++++++ test/tests/components/coaxial_junction/tests | 25 +- 3 files changed, 239 insertions(+), 12 deletions(-) create mode 100644 test/tests/components/coaxial_junction/junction_solid_expand.i diff --git a/src/components/CoaxialJunction1Phase.C b/src/components/CoaxialJunction1Phase.C index 60f4c5c..df68b2b 100644 --- a/src/components/CoaxialJunction1Phase.C +++ b/src/components/CoaxialJunction1Phase.C @@ -55,8 +55,8 @@ CoaxialJunction1Phase::CoaxialJunction1Phase(const InputParameters ¶ms) if (coaxials.size() != 2) mooseError("'coaxial_connections' must have size 2."); - // ConnectSolidRegion("tube", coaxials[0], coaxials[1]); - // ConnectSolidRegion("shell", coaxials[0], coaxials[1]); + ConnectSolidRegion("tube", coaxials[0], coaxials[1]); + ConnectSolidRegion("shell", coaxials[0], coaxials[1]); if (params.get("connect_inner")) { ConnectFlowRegion("inner", coaxials[0], coaxials[1]); diff --git a/test/tests/components/coaxial_junction/junction_solid_expand.i b/test/tests/components/coaxial_junction/junction_solid_expand.i new file mode 100644 index 0000000..9b4aee7 --- /dev/null +++ b/test/tests/components/coaxial_junction/junction_solid_expand.i @@ -0,0 +1,222 @@ +# 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_1 = 301 +T_2 = 300 +press = 1e5 # operating pressure + +L = 1 # length of pipe + +[GlobalParams] + initial_p = ${press} + closures = thm_closures + fp=fluid + gravity_vector = '0 0 0' +[] + +[FluidProperties] + [fluid] # mimic of water + type = SimpleFluidProperties + cv = 4000 + [] + [fluid2] # mimic of water + type = SimpleFluidProperties + cv = 2000 + [] +[] + +[SolidProperties] + [adamantium] # fake solid material that ensures solid heats quickly + type = ThermalFunctionSolidProperties + cp = 1 + k = 1 + rho = 1 + [] + [ebony] # fake solid material that ensures solid heats quickly + type = ThermalFunctionSolidProperties + cp = 1 + k = 4 + rho = 16 + [] +[] + +[Closures] # defines friction factors and heat transfer coefficients + [thm_closures] + type = Closures1PhaseTHM # default Churchill friction factor, DB HTC + [] +[] + +[Components] + [inlet_inner] + type = InletMassFlowRateTemperature1Phase + T = ${fparse 0.5*(T_1 + T_2)} + m_dot = 0. + input = coaxial1/inner:in + [] + [inlet_outer] + type = InletMassFlowRateTemperature1Phase + T = ${fparse 0.5*(T_1 + T_2)} + m_dot = 0. + input = coaxial2/outer:out + [] + [coaxial1] + type = CoaxialPipe1Phase + length = '${fparse L/3} ${fparse L/3} ${fparse L/3}' + axial_region_names = 'part1 part2 part3' + n_elems = '8 16 32' + orientation = '1 0 0' + position = '${fparse -L} 0 0' + shell_inner_radius = 0.075 + shell_materials = 'adamantium' + shell_n_elems = '2' + shell_names = 'shell' + shell_widths = '0.025' + shell_T_ref = '${T_1}' + tube_T_ref = ${T_1} + tube_inner_radius = 0.025 + tube_materials = 'adamantium' + tube_n_elems = '1' + tube_names = 'tube' + tube_widths = '0.025' + inner_initial_vel = 0. + outer_initial_vel = 0. + initial_T = ${T_1} + inner_tube_Hw = 0. + outer_tube_Hw = 0. + outer_shell_Hw = 0. + [] + [jct] + type = CoaxialJunction1Phase + coaxial_connections = 'coaxial1:out coaxial2:in' + shell_htc = 1e10 + tube_htc = 1e10 + [] + [coaxial2] + type = CoaxialPipe1Phase + length = '${fparse L/3} ${fparse L/3} ${fparse L/3}' + axial_region_names = 'part1 part2 part3' + n_elems = '32 16 8' + orientation = '1 0 0' + position = '0 0 0' + shell_inner_radius = 0.08 + shell_materials = 'ebony' + shell_n_elems = '2' + shell_names = 'shell' + shell_widths = '0.025' + shell_T_ref = '${T_2}' + tube_T_ref = ${T_2} + tube_inner_radius = 0.025 + tube_materials = 'ebony' + tube_n_elems = '1' + tube_names = 'tube' + tube_widths = '0.025' + inner_initial_vel = 0. + outer_initial_vel = 0. + initial_T = ${T_2} + inner_tube_Hw = 0. + outer_tube_Hw = 0. + outer_shell_Hw = 0. + [] + [outlet_outer] + type =Outlet1Phase + input = coaxial1/outer:in + p = ${press} + [] + [outlet_inner] + type =Outlet1Phase + input = coaxial2/inner:out + p = ${press} + [] + [tube_start1] + type = HSBoundarySpecifiedTemperature + T = ${T_1} + boundary = 'coaxial1/tube:start' + hs = coaxial1/tube + [] + [shell_start1] + type = HSBoundarySpecifiedTemperature + T = ${T_1} + boundary = 'coaxial1/shell:start' + hs = coaxial1/tube + [] + [tube_end2] + type = HSBoundarySpecifiedTemperature + T = ${T_2} + boundary = 'coaxial2/tube:end' + hs = coaxial1/tube + [] + [shell_end2] + type = HSBoundarySpecifiedTemperature + T = ${T_2} + boundary = 'coaxial2/shell:end' + hs = coaxial2/tube + [] + +[] + + +[Postprocessors] + [T_shell_out1] + type = SideAverageValue + boundary = coaxial1/shell:end + variable = T_solid + [] + [T_shell_in2] + type = SideAverageValue + boundary = coaxial2/shell:start + variable = T_solid + [] + [T_tube_out1] + type = SideAverageValue + boundary = coaxial1/tube:end + variable = T_solid + [] + [T_tube_in2] + type = SideAverageValue + boundary = coaxial2/tube:start + variable = T_solid + [] +[] + +[Preconditioning] + [pc] + type = SMP + full = true + [] +[] + +[Executioner] + type = Transient + start_time = 0 + + dt = 0.0001 + end_time = 0.01 + + line_search = basic + solve_type = NEWTON + + petsc_options_iname = '-pc_type' + petsc_options_value = 'lu' + + nl_rel_tol = 1e-4 + nl_abs_tol = 1e-6 + nl_max_its = 25 + automatic_scaling = true + steady_state_detection = true + # steady_state_tolerance = 2e-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_junction/tests b/test/tests/components/coaxial_junction/tests index 018832c..365efdd 100644 --- a/test/tests/components/coaxial_junction/tests +++ b/test/tests/components/coaxial_junction/tests @@ -1,15 +1,20 @@ [coaxial_junction] [test_solid_connection] - # [run] - # type = RunApp - # input = junction_solid.i - # [] - # [verify] - # type = PythonUnitTest - # input = test.py - # test_case = TestCoaxialJunction.test_solid_continuity - # prereq = test_solid_connection/run - # [] + [run] + type = RunApp + input = junction_solid.i + [] + [verify] + type = PythonUnitTest + input = test.py + test_case = TestCoaxialJunction.test_solid_continuity + prereq = test_solid_connection/run + [] + [run_expansion] + type = RunApp + input = junction_solid_expand.i + requirement = "This system shall allow the coaxial junction to have small changes in geometry" + [] [check_insufficient_connections] type = RunException input = junction_solid.i From 37248285cf649a0568d7bb2fd1de8868fb801b95 Mon Sep 17 00:00:00 2001 From: Matthew Falcone Date: Thu, 15 Jan 2026 16:58:38 +0000 Subject: [PATCH 8/8] Make connecting the solid regions optional --- src/components/CoaxialJunction1Phase.C | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/CoaxialJunction1Phase.C b/src/components/CoaxialJunction1Phase.C index df68b2b..8a02f1a 100644 --- a/src/components/CoaxialJunction1Phase.C +++ b/src/components/CoaxialJunction1Phase.C @@ -44,6 +44,11 @@ InputParameters CoaxialJunction1Phase::validParams() { params.addParam("connect_outer", true, "Whether to connect the outer annulus."); + params.addParam("connect_tube", true, + "Whether to connect the solid tube regions."); + params.addParam("connect_shell", true, + "Whether to connect the solid shell regions."); + return params; } @@ -55,8 +60,13 @@ CoaxialJunction1Phase::CoaxialJunction1Phase(const InputParameters ¶ms) if (coaxials.size() != 2) mooseError("'coaxial_connections' must have size 2."); - ConnectSolidRegion("tube", coaxials[0], coaxials[1]); - ConnectSolidRegion("shell", coaxials[0], coaxials[1]); + if (getParam("connect_tube")) { + ConnectSolidRegion("tube", coaxials[0], coaxials[1]); + } + + if (getParam("connect_shell")) { + ConnectSolidRegion("shell", coaxials[0], coaxials[1]); + } if (params.get("connect_inner")) { ConnectFlowRegion("inner", coaxials[0], coaxials[1]);