diff --git a/documentation/source/development/standards.md b/documentation/source/development/standards.md index 7aea9d99e1..46c6c96901 100644 --- a/documentation/source/development/standards.md +++ b/documentation/source/development/standards.md @@ -259,6 +259,14 @@ This should be used for units of $\text{kg} \cdot \text{m}^{-2}\text{s}^{-1}$ --------------------- +##### Specific Heat Capacities + +- Specific heat capacities for materials $[\text{J/kg/K}]$ should start with the `heatcap_` prefix +- Specific heat capacities at constant volume should start with the `heatcap_vol_` pefix +- Specific heat capacities at constant pressure should start with the `heatcap_pres_` pefix + +--------------------- + ##### Pressures - Pressures should start with the `pres_` prefix diff --git a/documentation/source/eng-models/blanket_overview.md b/documentation/source/eng-models/blanket_overview.md index 66a84b15f7..97bd9dc6dc 100644 --- a/documentation/source/eng-models/blanket_overview.md +++ b/documentation/source/eng-models/blanket_overview.md @@ -5,100 +5,3 @@ -------------------- -### Coolant mechanical pumping power | `coolant_pumping_power()` - -To calculate the coolant pumping power we use the change in enthalpies of the coolant as it goes through the pump. -**We assume the pump is isentropic so the entropy change of the coolant is 0**. - -The mechanical pumping power is defined as: - -$$ -P = \frac{\frac{\dot{m} \times \left(H_{\text{out}}-H_{\text{in}}\right)}{\eta}}{\left(1-fp\right)} -$$ - -where $\dot{m}$ is the coolant mass flow rate, $H$ is the coolant enthalpy, $\eta$ is the isentropic efficiency of the pump and $\gamma$ is the adiabatic index of the coolant. - -$$ -fp = \frac{T_{\text{pump,out}}\left(\frac{P_{\text{pump,out}}}{P_{\text{pump,in}}}\right)^{-\frac{\gamma -1}{\gamma}}}{\eta \left(T_{\text{pump,in}}-T_{\text{pump,out}}\right)} -$$ - ------------------- - -### Coolant pressure drop | `coolant_friction_pressure_drop()` - -The pressure drop in the coolant is given by the [Darcy-Weisbach Equation](https://en.wikipedia.org/wiki/Darcy%E2%80%93Weisbach_equation) - -For a cylindrical pipe of uniform diameter the pressure loss due to viscous effects can be characterized by: - -$$ -\Delta P = L\left[f_{\text{D}}\frac{\rho}{2}\frac{\langle v \rangle^2}{D_{\text{H}}}\right] -$$ - -where $L$ is the pipe length, $f_{\text{D}}$ is the [Darcy friction factor](https://en.wikipedia.org/wiki/Darcy_friction_factor_formulae), $\rho$ is the coolant density, $\langle v \rangle$ is the mean flow coolant velocity and $D_{\text{H}}$ is the hydraulic diameter or the pipe diameter in this case. - -To find the Darcy friction factor we need to know the Reynolds number given by: - -$$ -\text{Re} = \frac{\rho v L}{\mu} -$$ - -here $L$ is the characteristic length which we set to be the pipe diameter and $\mu$ is the coolant dynamic viscosity. - -Using the Reynolds number we calculate the Darcy friction factor using the Haaland approximation calculated by [`darcy_friction_haaland()`](../eng-models/generic_methods/pumping.md#pumping-coolant-friction--darcy_friction_haaland). - -For the radius of the pipe bend we assume it to be 3 times the radius of the coolant channel. - -The elbow coefficients for the 90 and 180 degree bends $\left(f_{\text{90,elbow}}, f_{\text{180,elbow}}\right)$ are calculated via [`elbow_coeff()`](#pipe-bend-elbow-coefficient--elbow_coeff). - -The pressure drop for the straights along the entire pipe length is the same as above: - -$$ -\Delta P = L\left[f_{\text{D}}\frac{\rho}{2}\frac{\langle v \rangle^2}{D_{\text{H}}}\right] -$$ - -where we define $\frac{f_{\text{D}}L}{D_{\text{H}}}$ as our straight section coefficient. - -The pressure drop for the 90 and 180 degree bends are: - -$$ -\Delta P = N_{\text{90}} \left[f_{\text{90,elbow}} \frac{\rho \langle v \rangle^2}{2}\right] -$$ - -$$ -\Delta P = N_{\text{180}} \left[f_{\text{180,elbow}} \frac{\rho \langle v \rangle^2}{2}\right] -$$ - -where $N_{\text{90}}$ and $N_{\text{180}}$ are the number of 90 and 180 degree bends in the system. - -The total returned pressure drop is simply: - -$$ -\Delta P = L\left[f_{\text{D}}\frac{\rho}{2}\frac{\langle v \rangle^2}{D_{\text{H}}}\right] + N_{\text{90}} \left[f_{\text{90,elbow}} \frac{\rho \langle v \rangle^2}{2}\right] + N_{\text{180}} \left[f_{\text{180,elbow}} \frac{\rho \langle v \rangle^2}{2}\right] -$$ - -------------------- - -### Pipe bend elbow coefficient | `elbow_coeff()` - -This function calculates the elbow bend coefficients for pressure drop calculations. - -$$ -a = 1.0 \quad \text{if} \ \theta = 90^{\circ} \\ -a = 0.9 \times \sin{\left(\frac{\theta \pi}{180^{\circ}}\right)} \quad \text{if} \ \theta < 70^{\circ} \\ -a = 0.7 + 0.35 \times \sin{\left(\frac{\theta}{90^{\circ}} \times \frac{\pi}{180^{\circ}}\right)} \quad \text{if} \ \theta > 90^{\circ} \\ -$$ - -where $\theta$ is the angle of the pipe bend. - -$$ -b = \frac{0.21}{\sqrt{\frac{R_{\text{elbow}}}{D_{\text{pipe}}}}}\quad \text{if} \ \frac{R_{\text{elbow}}}{D_{\text{pipe}}} \ge 1 \\ -b = \frac{0.21}{\left(\frac{R_{\text{elbow}}}{D_{\text{pipe}}}\right)^{2.5}}\quad \text{if} \ \frac{R_{\text{elbow}}}{D_{\text{pipe}}} \le 1 \\ -\text{else} \quad b =0.21 -$$ - -The elbow coefficient is given by: - -$$ -ab + \left( f_{\text{D}} \times \frac{R_{\text{elbow}}}{D_{\text{pipe}}}\right) \times \theta \times \left(\frac{\pi}{180^{\circ}}\right) -$$ - diff --git a/documentation/source/eng-models/generic_methods/pumping.md b/documentation/source/eng-models/generic_methods/pumping.md index a4822ba00b..543756144a 100644 --- a/documentation/source/eng-models/generic_methods/pumping.md +++ b/documentation/source/eng-models/generic_methods/pumping.md @@ -1,5 +1,80 @@ # Pumping Methods + +## Coolant mechanical pumping power | `coolant_pumping_power()` + +To calculate the coolant pumping power we use the change in enthalpies of the coolant as it goes through the pump. +**We assume the pump is isentropic so the entropy change of the coolant is 0**. + +The mechanical pumping power is defined as: + +$$ +P = \frac{\frac{\dot{m} \times \left(H_{\text{out}}-H_{\text{in}}\right)}{\eta}}{\left(1-fp\right)} +$$ + +where $\dot{m}$ is the coolant mass flow rate, $H$ is the coolant enthalpy, $\eta$ is the isentropic efficiency of the pump and $\gamma$ is the adiabatic index of the coolant. + +$$ +fp = \frac{T_{\text{pump,out}}\left(\frac{P_{\text{pump,out}}}{P_{\text{pump,in}}}\right)^{-\frac{\gamma -1}{\gamma}}}{\eta \left(T_{\text{pump,in}}-T_{\text{pump,out}}\right)} +$$ + +------------------ + +## Coolant pressure drop | `coolant_friction_pressure_drop()` + +The pressure drop in the coolant is given by the [Darcy-Weisbach Equation](https://en.wikipedia.org/wiki/Darcy%E2%80%93Weisbach_equation) + +For a cylindrical pipe of uniform diameter the pressure loss due to viscous effects can be characterized by: + +$$ +\Delta P = L\left[f_{\text{D}}\frac{\rho}{2}\frac{\langle v \rangle^2}{D_{\text{H}}}\right] +$$ + +where $L$ is the pipe length, $f_{\text{D}}$ is the [Darcy friction factor](https://en.wikipedia.org/wiki/Darcy_friction_factor_formulae), $\rho$ is the coolant density, $\langle v \rangle$ is the mean flow coolant velocity and $D_{\text{H}}$ is the hydraulic diameter or the pipe diameter in this case. + +To find the Darcy friction factor we need to know the Reynolds number given by: + +$$ +\text{Re} = \frac{\rho v L}{\mu} +$$ + +here $L$ is the characteristic length which we set to be the pipe diameter and $\mu$ is the coolant dynamic viscosity. + +Using the Reynolds number we calculate the Darcy friction factor using the Haaland approximation calculated by [`darcy_friction_haaland()`](../eng-models/generic_methods/pumping.md#pumping-coolant-friction--darcy_friction_haaland). + +For the radius of the pipe bend we assume it to be 3 times the radius of the coolant channel. + +The elbow coefficients for the 90 and 180 degree bends $\left(f_{\text{90,elbow}}, f_{\text{180,elbow}}\right)$ are calculated via [`elbow_coeff()`](#pipe-bend-elbow-coefficient--elbow_coeff). + +The pressure drop for the straights along the entire pipe length is the same as above: + +$$ +\Delta P = L\left[f_{\text{D}}\frac{\rho}{2}\frac{\langle v \rangle^2}{D_{\text{H}}}\right] +$$ + +where we define $\frac{f_{\text{D}}L}{D_{\text{H}}}$ as our straight section coefficient. + +The pressure drop for the 90 and 180 degree bends are: + +$$ +\Delta P = N_{\text{90}} \left[f_{\text{90,elbow}} \frac{\rho \langle v \rangle^2}{2}\right] +$$ + +$$ +\Delta P = N_{\text{180}} \left[f_{\text{180,elbow}} \frac{\rho \langle v \rangle^2}{2}\right] +$$ + +where $N_{\text{90}}$ and $N_{\text{180}}$ are the number of 90 and 180 degree bends in the system. + +The total returned pressure drop is simply: + +$$ +\Delta P = L\left[f_{\text{D}}\frac{\rho}{2}\frac{\langle v \rangle^2}{D_{\text{H}}}\right] + N_{\text{90}} \left[f_{\text{90,elbow}} \frac{\rho \langle v \rangle^2}{2}\right] + N_{\text{180}} \left[f_{\text{180,elbow}} \frac{\rho \langle v \rangle^2}{2}\right] +$$ + +------------------- + + ## Pumping coolant friction | `darcy_friction_haaland()` The pressure drop is based on the Darcy friction factor, using the [Haaland equation](https://en.wikipedia.org/wiki/Darcy_friction_factor_formulae#Haaland_equation), an approximation to the implicit Colebrook–White equation. @@ -61,4 +136,58 @@ where $\rho$ is the coolant density and $\mu$ is the coolant viscosity. $$ h = \frac{\mathrm{Nu_D}k}{2r_{\text{channel}}} - $$ \ No newline at end of file + $$ + +------------------------- + +## Pipe bend elbow coefficient | `elbow_coeff()` + +This function calculates the elbow bend coefficients for pressure drop calculations. + +$$ +a = 1.0 \quad \text{if} \ \theta = 90^{\circ} \\ +a = 0.9 \times \sin{\left(\frac{\theta \pi}{180^{\circ}}\right)} \quad \text{if} \ \theta < 70^{\circ} \\ +a = 0.7 + 0.35 \times \sin{\left(\frac{\theta}{90^{\circ}} \times \frac{\pi}{180^{\circ}}\right)} \quad \text{if} \ \theta > 90^{\circ} \\ +$$ + +where $\theta$ is the angle of the pipe bend. + +$$ +b = \frac{0.21}{\sqrt{\frac{R_{\text{elbow}}}{D_{\text{pipe}}}}}\quad \text{if} \ \frac{R_{\text{elbow}}}{D_{\text{pipe}}} \ge 1 \\ +b = \frac{0.21}{\left(\frac{R_{\text{elbow}}}{D_{\text{pipe}}}\right)^{2.5}}\quad \text{if} \ \frac{R_{\text{elbow}}}{D_{\text{pipe}}} \le 1 \\ +\text{else} \quad b =0.21 +$$ + +The elbow coefficient is given by: + +$$ +ab + \left( f_{\text{D}} \times \frac{R_{\text{elbow}}}{D_{\text{pipe}}}\right) \times \theta \times \left(\frac{\pi}{180^{\circ}}\right) +$$ + +-------------- + +## Required mass flow rate | `calculate_required_mass_flow_rate()` + +The required mass flow rate of a coolant is given simply by the fundamental heat transfer equation: + +$$ +\dot{m} = \frac{P}{c_{\text{p}}(T)\times \Delta T} +$$ + +where $\dot{m}$ is the required mass flow rate in, $P$ is the heating power to be removed, $c_{\text{p}}$ is the coolant specific heat capacity for constant pressure and $\Delta T$ is the temperature change in the coolant. + +!!! note "Variation specific heat capacity" + + The heat capacity itself is a function of temperature. Therefore it is common to use the heat capacity value at the simple average between the initial and final temperature. + This however assumes a linear relationship. Ideally the equation should be solves as: + + $$ + \dot{m} = \frac{P}{\int_{T_{\text{in}}}^{T_{\text{in}}}c_{\text{p}}(T) dT} + $$ + + +!!! info "Choice of specific heat capacity" + + For pumping, the specific heat capacity for constant pressure $(c_{\text{p}})$ is used as cooling loops are open-flow systems where the fluid moves continuously through pipes, heat exchangers, and pumps. As the coolant heats up, it expands freely along the loop. Because it is free to expand, the local pressure remains relatively constant while the volume changes. + + You would only use the specific heat capacity for constant volume $(c_{\text{v}})$ if the coolant was completely sealed inside a rigid, unyielding container with zero flow, where heating it would cause the pressure to spike but the volume to stay exactly the same. \ No newline at end of file diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 241adb8c5f..9eb697e2e1 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -15298,6 +15298,89 @@ def plot_cs_radial_stress_profile( axis.legend(loc="best") +def plot_blanket_coolant_channel_structure_and_properties( + fig: plt.Figure, m_file: MFile, scan: int +): + """Combined plot of blanket coolant channel structure and properties.""" + # Add info about the Winding Pack + textstr_outboard_blkt = ( + f"$\\mathbf{{Outboard \\ blanket:}}$\n \n" + f"Radius of blanket channel: {m_file.get('radius_blkt_channel', scan=scan):.4f} m\n" + f"Channel roughness ($\\epsilon$): {m_file.get('roughness_fw_channel', scan=scan):.4e} m\n\n" + f"Radial coolant channel length: {m_file.get('len_blkt_outboard_coolant_channel_radial', scan=scan):.4f} m\n" + f"Poloidal coolant channel length: {m_file.get('len_blkt_outboard_segment_poloidal', scan=scan):.4f} m\n" + f"Number of radial channels: {m_file.get('n_blkt_outboard_module_coolant_sections_radial', scan=scan)}\n" + f"Number of poloidal channels: {m_file.get('n_blkt_outboard_module_coolant_sections_poloidal', scan=scan)}\n" + f"Total length of coolant channel straight sections: {m_file.get('len_blkt_outboard_channel_total', scan=scan):.4f} m\n\n" + f"Pressure drop for straight sections: {m_file.get('dpres_blkt_outboard_coolant_channel_straight_total', scan=scan):.2e} Pa\n" + f"Pressure drop for 90° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_90_bend', scan=scan):.2e} Pa\n" + f"Total pressure drop for 90° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_90_bends_total', scan=scan):.2e} Pa\n" + f"Pressure drop for 180° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_180_bend', scan=scan):.2e} Pa\n" + f"Total pressure drop for 180° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_180_bends_total', scan=scan):.2e} Pa\n" + f"Total pressure drop for all bends: {m_file.get('dpres_blkt_outboard_bends_total', scan=scan):.2e} Pa\n\n" + f"Reynolds number ($Re$): {m_file.get('reynolds_blkt_outboard_coolant', scan=scan):.4f}\n" + f"Darcy Friction factor ($f$): {m_file.get('darcy_frict_blkt_outboard_coolant', scan=scan):.4f}\n\n" + f"Friction drop coefficient for straight sections: {m_file.get('f_straight_blkt_outboard_coolant', scan=scan):.4f}\n" + f"Friction drop coefficient for 90° bends: {m_file.get('f_elbow_blkt_outboard_90_bend', scan=scan):.4f}\n" + f"Friction drop coefficient for 180° bends: {m_file.get('f_elbow_blkt_outboard_180_bend', scan=scan):.4f}\n" + ) + + fig.text( + 0.5, + 0.5, + textstr_outboard_blkt, + fontsize=9, + verticalalignment="top", + horizontalalignment="left", + transform=fig.transFigure, + bbox={ + "boxstyle": "round", + "facecolor": "wheat", + "alpha": 1.0, + "linewidth": 2, + }, + ) + + # Add info about the Winding Pack + textstr_inboard_blkt = ( + f"$\\mathbf{{Inboard \\ blanket:}}$\n \n" + f"Radius of blanket channel: {m_file.get('radius_blkt_channel', scan=scan):.4f} m\n" + f"Channel roughness ($\\epsilon$): {m_file.get('roughness_fw_channel', scan=scan):.4e} m\n\n" + f"Radial coolant channel length: {m_file.get('len_blkt_inboard_coolant_channel_radial', scan=scan):.4f} m\n" + f"Poloidal coolant channel length: {m_file.get('len_blkt_inboard_segment_poloidal', scan=scan):.4f} m\n" + f"Number of radial channels: {m_file.get('n_blkt_inboard_module_coolant_sections_radial', scan=scan)}\n" + f"Number of poloidal channels: {m_file.get('n_blkt_inboard_module_coolant_sections_poloidal', scan=scan)}\n" + f"Total length of coolant channel straight sections: {m_file.get('len_blkt_inboard_channel_total', scan=scan):.4f} m\n\n" + f"Pressure drop for straight sections: {m_file.get('dpres_blkt_inboard_coolant_channel_straight_total', scan=scan):.2e} Pa\n" + f"Pressure drop for 90° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_90_bend', scan=scan):.2e} Pa\n" + f"Total pressure drop for 90° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_90_bends_total', scan=scan):.2e} Pa\n" + f"Pressure drop for 180° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_180_bend', scan=scan):.2e} Pa\n" + f"Total pressure drop for 180° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_180_bends_total', scan=scan):.2e} Pa\n" + f"Total pressure drop for all bends: {m_file.get('dpres_blkt_inboard_bends_total', scan=scan):.2e} Pa\n\n" + f"Reynolds number ($Re$): {m_file.get('reynolds_blkt_inboard_coolant', scan=scan):.4f}\n" + f"Darcy Friction factor ($f$): {m_file.get('darcy_frict_blkt_inboard_coolant', scan=scan):.4f}\n\n" + f"Friction drop coefficient for straight sections: {m_file.get('f_straight_blkt_inboard_coolant', scan=scan):.4f}\n" + f"Friction drop coefficient for 90° bends: {m_file.get('f_elbow_blkt_inboard_90_bend', scan=scan):.4f}\n" + f"Friction drop coefficient for 180° bends: {m_file.get('f_elbow_blkt_inboard_180_bend', scan=scan):.4f}\n" + ) + + fig.text( + 0.1, + 0.5, + textstr_inboard_blkt, + fontsize=9, + verticalalignment="top", + horizontalalignment="left", + transform=fig.transFigure, + bbox={ + "boxstyle": "round", + "facecolor": "wheat", + "alpha": 1.0, + "linewidth": 2, + }, + ) + + def main_plot( m_file: MFile, scan: int, @@ -15759,15 +15842,14 @@ def _add_page(name: str | None = None): ) plot_fw_90_deg_pipe_bend(pages["fw_td_cross_section"].add_subplot(337), m_file, scan) - plot_blkt_pipe_bends(_add_page("blkt_pipe_bends"), m_file, scan) - ax_blanket = pages["blkt_pipe_bends"].add_subplot(122, aspect="equal") + ax_blanket = _add_page("blkt_structure").add_subplot(122, aspect="equal") plot_blkt_structure( - ax_blanket, - pages["blkt_pipe_bends"], - m_file, - scan, - radial_build, - colour_scheme, + ax_blanket, pages["blkt_structure"], m_file, scan, radial_build, colour_scheme + ) + + plot_blkt_pipe_bends(_add_page("blkt_cooling"), m_file, scan) + plot_blanket_coolant_channel_structure_and_properties( + pages["blkt_cooling"], m_file, scan ) plot_main_power_flow( diff --git a/process/core/output.py b/process/core/output.py index 1368eaae7f..8104b4c80c 100644 --- a/process/core/output.py +++ b/process/core/output.py @@ -1,5 +1,6 @@ from process.core.log import logging_model_handler from process.data_structure.blanket_variables import BlktModelTypes +from process.data_structure.build_variables import InboardBlanketConfiguration from process.models.tfcoil.base import TFConductorModel from process.models.tfcoil.superconducting import ( SuperconductingTFTurnType, @@ -127,6 +128,11 @@ def write(models, data, _outfile): # DCLL model models.dcll.output() + if data.build.i_blkt_inboard == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT: + models.blanket_library.output_inboard_blkt_pumping_variables() + + models.blanket_library.output_outboard_blkt_pumping_variables() + # FISPACT and LOCA model (not used)- removed # Power model diff --git a/process/data_structure/blanket_variables.py b/process/data_structure/blanket_variables.py index 2849473727..c7ea5d74b8 100644 --- a/process/data_structure/blanket_variables.py +++ b/process/data_structure/blanket_variables.py @@ -206,5 +206,77 @@ class BlanketData: f_deg_blkt_inboard_poloidal_plasma: float = 0.0 """Fraction of inboard blanket poloidal angle subtended by plasma (degrees)""" + reynolds_blkt_inboard_coolant: float = 0.0 + """Inboard blanket coolant Reynolds number""" + + reynolds_blkt_outboard_coolant: float = 0.0 + """Outboard blanket coolant Reynolds number""" + + darcy_frict_blkt_inboard_coolant: float = 0.0 + """Inboard blanket coolant Darcy friction factor""" + + darcy_frict_blkt_outboard_coolant: float = 0.0 + """Outboard blanket coolant Darcy friction factor""" + + f_elbow_blkt_inboard_90_bend: float = 0.0 + """Inboard blanket coolant 90 degree bend loss coefficient""" + + f_elbow_blkt_outboard_90_bend: float = 0.0 + """Outboard blanket coolant 90 degree bend loss coefficient""" + + f_elbow_blkt_inboard_180_bend: float = 0.0 + """Inboard blanket coolant 180 degree bend loss coefficient""" + + f_elbow_blkt_outboard_180_bend: float = 0.0 + """Outboard blanket coolant 180 degree bend loss coefficient""" + + f_straight_blkt_inboard_coolant: float = 0.0 + """Inboard blanket coolant straight length loss coefficient""" + + f_straight_blkt_outboard_coolant: float = 0.0 + """Outboard blanket coolant straight length loss coefficient""" + + dpres_blkt_inboard_coolant_channel_straight_total: float = 0.0 + """Total pressure drop in inboard blanket coolant channel straight sections (Pa)""" + + dpres_blkt_outboard_coolant_channel_straight_total: float = 0.0 + """Total pressure drop in outboard blanket coolant channel straight sections (Pa)""" + + dpres_blkt_inboard_coolant_channel_90_bends_total: float = 0.0 + """Total pressure drop in inboard blanket coolant channel 90 degree bends (Pa)""" + + dpres_blkt_outboard_coolant_channel_90_bends_total: float = 0.0 + """Total pressure drop in outboard blanket coolant channel 90 degree bends (Pa)""" + + dpres_blkt_inboard_coolant_channel_180_bends_total: float = 0.0 + """Total pressure drop in inboard blanket coolant channel 180 degree bends (Pa)""" + + dpres_blkt_outboard_coolant_channel_180_bends_total: float = 0.0 + """Total pressure drop in outboard blanket coolant channel 180 degree bends (Pa)""" + + dpres_blkt_inboard_coolant_channel_90_bend: float = 0.0 + """Pressure drop in inboard blanket coolant channel 90 degree bend (Pa)""" + + dpres_blkt_outboard_coolant_channel_90_bend: float = 0.0 + """Pressure drop in outboard blanket coolant channel 90 degree bend (Pa)""" + + dpres_blkt_inboard_coolant_channel_180_bend: float = 0.0 + """Pressure drop in inboard blanket coolant channel 180 degree bend (Pa)""" + + dpres_blkt_outboard_coolant_channel_180_bend: float = 0.0 + """Pressure drop in outboard blanket coolant channel 180 degree bend (Pa)""" + + dpres_blkt_inboard_bends_total: float = 0.0 + """Total pressure drop in inboard blanket coolant channel bends (Pa)""" + + dpres_blkt_outboard_bends_total: float = 0.0 + """Total pressure drop in outboard blanket coolant channel bends (Pa)""" + + len_blkt_inboard_coolant_channel_straight_total: float = 0.0 + """Total length of inboard blanket coolant channel straight sections (m)""" + + len_blkt_outboard_coolant_channel_straight_total: float = 0.0 + """Total length of outboard blanket coolant channel straight sections (m)""" + CREATE_DICTS_FROM_DATACLASS = BlanketData diff --git a/process/data_structure/fwbs_variables.py b/process/data_structure/fwbs_variables.py index 0936ff3a6e..b3dea9a4b6 100644 --- a/process/data_structure/fwbs_variables.py +++ b/process/data_structure/fwbs_variables.py @@ -313,7 +313,7 @@ class FWBSData: """peak first wall temperature [K]""" roughness_fw_channel: float = 1.0e-6 - """first wall channel roughness epsilon [m]""" + """First wall channel roughness (ε) [m]""" len_fw_channel: float = 4.0 """Length of a single first wall channel (all in parallel) [m] @@ -638,17 +638,17 @@ class FWBSData: visc_blkt_coolant: float = 0.0 """Viscosity of the blanket primary coolant""" - cp_fw: float = 0.0 - """Spesific heat for FW and blanket primary coolant(s)""" + heatcap_pres_fw_coolant_average: float = 0.0 + """FW coolant average specific heat capacity at constant pressure [J/kg/K]""" - cv_fw: float = 0.0 - """Spesific heat for FW and blanket primary coolant(s)""" + heatcap_vol_fw_coolant_average: float = 0.0 + """FW coolant average specific heat capacity at constant volume [J/kg/K]""" - cp_bl: float = 0.0 - """Spesific heat for FW and blanket primary coolant(s)""" + heatcap_pres_blkt_coolant_average: float = 0.0 + """Blanket coolant average specific heat capacity at constant pressure [J/kg/K]""" - cv_bl: float = 0.0 - """Spesific heat for FW and blanket primary coolant(s)""" + heatcap_vol_blkt_coolant_average: float = 0.0 + """Blanket coolant average specific heat capacity at constant volume [J/kg/K]""" f_nuc_pow_bz_struct: float = 0.34 """For a dual-coolant blanket, fraction of BZ power cooled by primary coolant""" diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index db1c458f0c..828a86c17c 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -21,10 +21,13 @@ eshellvol, ) from process.models.engineering.pumping import ( + CoolantFrictionLossParameters, CoolantType, - calculate_reynolds_number, - darcy_friction_haaland, + calculate_required_mass_flow_rate, + coolant_friction_pressure_drop, + coolant_pumping_power, ) +from process.models.fw import N_FW_PIPE_90_DEG_BENDS, N_FW_PIPE_180_DEG_BENDS from process.models.power import PumpingPowerModelTypes logger = logging.getLogger(__name__) @@ -719,14 +722,22 @@ def primary_coolant_properties(self, output: bool): pressure=self.data.fwbs.pres_fw_coolant, ) self.data.fwbs.den_fw_coolant = fw_bb_fluid_properties.density - self.data.fwbs.cp_fw = fw_bb_fluid_properties.specific_heat_const_p - self.data.fwbs.cv_fw = fw_bb_fluid_properties.specific_heat_const_v + self.data.fwbs.heatcap_pres_fw_coolant_average = ( + fw_bb_fluid_properties.specific_heat_const_p + ) + self.data.fwbs.heatcap_vol_fw_coolant_average = ( + fw_bb_fluid_properties.specific_heat_const_v + ) self.data.fwbs.visc_fw_coolant = fw_bb_fluid_properties.viscosity self.data.fwbs.den_blkt_coolant = self.data.fwbs.den_fw_coolant self.data.fwbs.visc_blkt_coolant = self.data.fwbs.visc_fw_coolant - self.data.fwbs.cp_bl = self.data.fwbs.cp_fw - self.data.fwbs.cv_bl = self.data.fwbs.cv_fw + self.data.fwbs.heatcap_pres_blkt_coolant_average = ( + self.data.fwbs.heatcap_pres_fw_coolant_average + ) + self.data.fwbs.heatcap_vol_blkt_coolant_average = ( + self.data.fwbs.heatcap_vol_fw_coolant_average + ) # If FW and BB have different coolants... else: @@ -740,8 +751,12 @@ def primary_coolant_properties(self, output: bool): pressure=self.data.fwbs.pres_fw_coolant, ) self.data.fwbs.den_fw_coolant = fw_fluid_properties.density - self.data.fwbs.cp_fw = fw_fluid_properties.specific_heat_const_p - self.data.fwbs.cv_fw = fw_fluid_properties.specific_heat_const_v + self.data.fwbs.heatcap_pres_fw_coolant_average = ( + fw_fluid_properties.specific_heat_const_p + ) + self.data.fwbs.heatcap_vol_fw_coolant_average = ( + fw_fluid_properties.specific_heat_const_v + ) self.data.fwbs.visc_fw_coolant = fw_fluid_properties.viscosity # BB @@ -755,8 +770,12 @@ def primary_coolant_properties(self, output: bool): pressure=self.data.fwbs.pres_blkt_coolant, ) self.data.fwbs.den_blkt_coolant = bb_fluid_properties.density - self.data.fwbs.cp_bl = bb_fluid_properties.specific_heat_const_p - self.data.fwbs.cv_bl = bb_fluid_properties.specific_heat_const_v + self.data.fwbs.heatcap_pres_blkt_coolant_average = ( + bb_fluid_properties.specific_heat_const_p + ) + self.data.fwbs.heatcap_vol_blkt_coolant_average = ( + bb_fluid_properties.specific_heat_const_v + ) self.data.fwbs.visc_blkt_coolant = bb_fluid_properties.viscosity if ( @@ -1097,12 +1116,6 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): # Coolant channel bends - # Number of angle turns in FW and blanket flow channels, n.b. these are the - # same for CCFE HCPB and KIT DCLL. FW is also be the same for DCLL MMS ans SMS. - - N_FW_PIPE_90_DEG_BENDS = 2 - N_FW_PIPE_180_DEG_BENDS = 0 - # N.B. This is for BZ only, does not include MF/BSS. if self.data.fwbs.i_blkt_dual_coolant in {1, 2}: N_BLKT_PIPE_90_DEG_BENDS = 4 @@ -1392,7 +1405,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): b_bz_liq=self.data.fwbs.b_bz_liq, ) - dpres_fw_inboard_coolant = self.total_pressure_drop( + dpres_fw_inboard_coolant, _ = self.total_pressure_drop( output, icoolpump=1, vel_coolant=vel_fw_inboard_coolant, @@ -1407,7 +1420,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): label="Inboard first wall", ) - dpres_fw_outboard_coolant = self.total_pressure_drop( + dpres_fw_outboard_coolant, _ = self.total_pressure_drop( output, icoolpump=1, vel_coolant=vel_fw_outboard_coolant, @@ -1440,43 +1453,123 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): npoltoti = self.data.fwbs.nopol * npblkti_liq npoltoto = self.data.fwbs.nopol * npblkto_liq - dpres_blkt_outboard_coolant = self.total_pressure_drop( - output, - icoolpump=1, - vel_coolant=self.data.blanket.vel_blkt_outboard_coolant, - len_pipe=self.data.blanket.len_blkt_outboard_channel_total, - n_pipe_90_deg_bends=N_BLKT_PIPE_90_DEG_BENDS, - n_pipe_180_deg_bends=N_BLKT_PIPE_180_DEG_BENDS, - den_coolant=self.data.fwbs.den_blkt_coolant, - visc_coolant_dynamic=self.data.fwbs.visc_blkt_coolant, - coolant_electrical_conductivity=0.0e0, - pol_channel_length=pollengo, - nopolchan=npoltoto, - label="Outboard blanket", - ) - - if ( - self.data.build.i_blkt_inboard - == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT - ): - dpres_blkt_inboard_coolant = self.total_pressure_drop( + dpres_blkt_outboard_coolant, blkt_outboard_friction_params = ( + self.total_pressure_drop( output, icoolpump=1, - vel_coolant=self.data.blanket.vel_blkt_inboard_coolant, - len_pipe=self.data.blanket.len_blkt_inboard_channel_total, + vel_coolant=self.data.blanket.vel_blkt_outboard_coolant, + len_pipe=self.data.blanket.len_blkt_outboard_channel_total, n_pipe_90_deg_bends=N_BLKT_PIPE_90_DEG_BENDS, n_pipe_180_deg_bends=N_BLKT_PIPE_180_DEG_BENDS, den_coolant=self.data.fwbs.den_blkt_coolant, visc_coolant_dynamic=self.data.fwbs.visc_blkt_coolant, coolant_electrical_conductivity=0.0e0, - pol_channel_length=pollengi, - nopolchan=npoltoti, - label="Inboard blanket", + pol_channel_length=pollengo, + nopolchan=npoltoto, + label="Outboard blanket", + ) + ) + + self.data.blanket.dpres_blkt_outboard_coolant_channel_straight_total = ( + blkt_outboard_friction_params.dpres_straight + ) + self.data.blanket.dpres_blkt_outboard_coolant_channel_90_bend = ( + blkt_outboard_friction_params.dpres_90 + ) + self.data.blanket.dpres_blkt_outboard_coolant_channel_90_bends_total = ( + blkt_outboard_friction_params.dpres_90_total + ) + self.data.blanket.dpres_blkt_outboard_coolant_channel_180_bend = ( + blkt_outboard_friction_params.dpres_180 + ) + self.data.blanket.dpres_blkt_outboard_coolant_channel_180_bends_total = ( + blkt_outboard_friction_params.dpres_180_total + ) + self.data.blanket.dpres_blkt_outboard_bends_total = ( + blkt_outboard_friction_params.dpres_bends_total + ) + + self.data.blanket.reynolds_blkt_outboard_coolant = ( + blkt_outboard_friction_params.reynolds_number + ) + self.data.blanket.darcy_frict_blkt_outboard_coolant = ( + blkt_outboard_friction_params.darcy_friction_factor + ) + self.data.blanket.f_straight_blkt_outboard_coolant = ( + blkt_outboard_friction_params.f_straight + ) + self.data.blanket.len_blkt_outboard_coolant_channel_straight_total = ( + blkt_outboard_friction_params.len_straight + ) + self.data.blanket.f_elbow_blkt_outboard_90_bend = ( + blkt_outboard_friction_params.f_elbow_90 + ) + self.data.blanket.f_elbow_blkt_outboard_180_bend = ( + blkt_outboard_friction_params.f_elbow_180 + ) + + if ( + self.data.build.i_blkt_inboard + == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT + ): + dpres_blkt_inboard_coolant, blkt_inboard_friction_params = ( + self.total_pressure_drop( + output, + icoolpump=1, + vel_coolant=self.data.blanket.vel_blkt_inboard_coolant, + len_pipe=self.data.blanket.len_blkt_inboard_channel_total, + n_pipe_90_deg_bends=N_BLKT_PIPE_90_DEG_BENDS, + n_pipe_180_deg_bends=N_BLKT_PIPE_180_DEG_BENDS, + den_coolant=self.data.fwbs.den_blkt_coolant, + visc_coolant_dynamic=self.data.fwbs.visc_blkt_coolant, + coolant_electrical_conductivity=0.0e0, + pol_channel_length=pollengi, + nopolchan=npoltoti, + label="Inboard blanket", + ) ) + self.data.blanket.dpres_blkt_inboard_coolant_channel_straight_total = ( + blkt_inboard_friction_params.dpres_straight + ) + self.data.blanket.dpres_blkt_inboard_coolant_channel_90_bend = ( + blkt_inboard_friction_params.dpres_90 + ) + self.data.blanket.dpres_blkt_inboard_coolant_channel_90_bends_total = ( + blkt_inboard_friction_params.dpres_90_total + ) + self.data.blanket.dpres_blkt_inboard_coolant_channel_180_bend = ( + blkt_inboard_friction_params.dpres_180 + ) + self.data.blanket.dpres_blkt_inboard_coolant_channel_180_bends_total = ( + blkt_inboard_friction_params.dpres_180_total + ) + self.data.blanket.dpres_blkt_inboard_bends_total = ( + blkt_inboard_friction_params.dpres_bends_total + ) + + self.data.blanket.reynolds_blkt_inboard_coolant = ( + blkt_inboard_friction_params.reynolds_number + ) + self.data.blanket.darcy_frict_blkt_inboard_coolant = ( + blkt_inboard_friction_params.darcy_friction_factor + ) + self.data.blanket.f_straight_blkt_inboard_coolant = ( + blkt_inboard_friction_params.f_straight + ) + self.data.blanket.len_blkt_inboard_coolant_channel_straight_total = ( + blkt_inboard_friction_params.len_straight + ) + self.data.blanket.f_elbow_blkt_inboard_90_bend = ( + blkt_inboard_friction_params.f_elbow_90 + ) + self.data.blanket.f_elbow_blkt_inboard_180_bend = ( + blkt_inboard_friction_params.f_elbow_180 + ) + # If the blanket has a liquid metal breeder... if self.data.fwbs.i_blkt_dual_coolant > 0: - deltap_blo_liq = self.total_pressure_drop( + deltap_blo_liq, _ = self.total_pressure_drop( output, icoolpump=2, vel_coolant=velblkto_liq, @@ -1494,7 +1587,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): self.data.build.i_blkt_inboard == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT ): - deltap_bli_liq = self.total_pressure_drop( + deltap_bli_liq, _ = self.total_pressure_drop( output, icoolpump=2, vel_coolant=velblkti_liq, @@ -2217,26 +2310,26 @@ def thermo_hydraulic_model(self, output: bool): self.data.build.i_blkt_inboard == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT ): - fwoutleti = (f_nuc_fwi * self.data.fwbs.temp_blkt_coolant_out) + ( - 1 - f_nuc_fwi - ) * self.data.fwbs.temp_fw_coolant_in - inlet_tempi = fwoutleti + temp_fw_coolant_out = ( + f_nuc_fwi * self.data.fwbs.temp_blkt_coolant_out + ) + (1 - f_nuc_fwi) * self.data.fwbs.temp_fw_coolant_in + temp_blkt_coolant_in = temp_fw_coolant_out else: - fwoutleti = self.data.fwbs.temp_fw_coolant_out + temp_fw_coolant_out = self.data.fwbs.temp_fw_coolant_out - fwoutleto = (f_nuc_fwo * self.data.fwbs.temp_blkt_coolant_out) + ( + temp_fw_coolant_out = (f_nuc_fwo * self.data.fwbs.temp_blkt_coolant_out) + ( 1 - f_nuc_fwo ) * self.data.fwbs.temp_fw_coolant_in - inlet_tempo = fwoutleto + temp_blkt_coolant_in = temp_fw_coolant_out elif ( self.data.fwbs.i_fw_blkt_shared_coolant == FWBlktCoolantLoopTypes.SEPARATE_LOOPS ): - fwoutleti = self.data.fwbs.temp_fw_coolant_out - inlet_tempi = self.data.fwbs.temp_blkt_coolant_in - fwoutleto = self.data.fwbs.temp_fw_coolant_out - inlet_tempo = self.data.fwbs.temp_blkt_coolant_in + temp_fw_coolant_out = self.data.fwbs.temp_fw_coolant_out + temp_blkt_coolant_in = self.data.fwbs.temp_blkt_coolant_in + temp_fw_coolant_out = self.data.fwbs.temp_fw_coolant_out + temp_blkt_coolant_in = self.data.fwbs.temp_blkt_coolant_in # Maximum FW temperature. (27/11/2015) Issue #348 # First wall flow is just along the first wall, with no allowance for radial @@ -2279,35 +2372,49 @@ def thermo_hydraulic_model(self, output: bool): # Total mass flow rate to remove inboard FW power (kg/s) self.data.blanket.mflow_fw_inboard_coolant_total = ( - 1.0e6 - * (self.data.blanket.p_fw_inboard_nuclear_heat_mw + self.data.fwbs.psurffwi) - / (self.data.fwbs.cp_fw * (fwoutleti - self.data.fwbs.temp_fw_coolant_in)) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 + * ( + self.data.blanket.p_fw_inboard_nuclear_heat_mw + + self.data.fwbs.psurffwi + ), + heatcap_coolant=self.data.fwbs.heatcap_pres_fw_coolant_average, + temp_in_coolant=self.data.fwbs.temp_fw_coolant_in, + temp_out_coolant=temp_fw_coolant_out, + ) ) + # Total mass flow rate to remove outboard FW power (kg/s) self.data.blanket.mflow_fw_outboard_coolant_total = ( - 1.0e6 - * (self.data.blanket.p_fw_outboard_nuclear_heat_mw + self.data.fwbs.psurffwo) - / (self.data.fwbs.cp_fw * (fwoutleto - self.data.fwbs.temp_fw_coolant_in)) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 + * ( + self.data.blanket.p_fw_outboard_nuclear_heat_mw + + self.data.fwbs.psurffwo + ), + heatcap_coolant=self.data.fwbs.heatcap_pres_fw_coolant_average, + temp_in_coolant=self.data.fwbs.temp_fw_coolant_in, + temp_out_coolant=temp_fw_coolant_out, + ) ) # If the blanket is dual-coolant... if self.data.fwbs.i_blkt_dual_coolant == 2: # Mass flow rates for outboard blanket coolants (kg/s) self.data.blanket.mflow_blkt_outboard_coolant = ( - 1.0e6 - * (pnucblkto_struct) - / ( - self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempo) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 * pnucblkto_struct, + heatcap_coolant=self.data.fwbs.heatcap_pres_blkt_coolant_average, + temp_in_coolant=temp_blkt_coolant_in, + temp_out_coolant=self.data.fwbs.temp_blkt_coolant_out, ) ) - self.data.blanket.mfblkto_liq = ( - 1.0e6 - * (pnucblkto_liq) - / ( - self.data.fwbs.specific_heat_liq - * (self.data.fwbs.outlet_temp_liq - self.data.fwbs.inlet_temp_liq) - ) + + self.data.blanket.mfblkto_liq = calculate_required_mass_flow_rate( + p_heat_total=1.0e6 * pnucblkto_liq, + heatcap_coolant=self.data.fwbs.specific_heat_liq, + temp_in_coolant=self.data.fwbs.inlet_temp_liq, + temp_out_coolant=self.data.fwbs.outlet_temp_liq, ) # If there is an IB blanket... @@ -2317,34 +2424,31 @@ def thermo_hydraulic_model(self, output: bool): ): # Mass flow rates for inboard blanket coolants (kg/s) self.data.blanket.mflow_blkt_inboard_coolant = ( - 1.0e6 - * (pnucblkti_struct) - / ( - self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempi) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 * pnucblkti_struct, + heatcap_coolant=self.data.fwbs.heatcap_pres_blkt_coolant_average, + temp_in_coolant=temp_blkt_coolant_in, + temp_out_coolant=self.data.fwbs.temp_blkt_coolant_out, ) ) - self.data.blanket.mfblkti_liq = ( - 1.0e6 - * (pnucblkti_liq) - / ( - self.data.fwbs.specific_heat_liq - * ( - self.data.fwbs.outlet_temp_liq - - self.data.fwbs.inlet_temp_liq - ) - ) + + self.data.blanket.mfblkti_liq = calculate_required_mass_flow_rate( + p_heat_total=1.0e6 * pnucblkti_liq, + heatcap_coolant=self.data.fwbs.specific_heat_liq, + temp_in_coolant=self.data.fwbs.inlet_temp_liq, + temp_out_coolant=self.data.fwbs.outlet_temp_liq, ) # If the blanket is single-coolant with liquid metal breeder... elif self.data.fwbs.i_blkt_dual_coolant == 1: # Mass flow rate for outboard blanket coolant (kg/s) self.data.blanket.mflow_blkt_outboard_coolant = ( - 1.0e6 - * (self.data.blanket.p_blkt_nuclear_heat_outboard_mw) - / ( - self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempo) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 + * self.data.blanket.p_blkt_nuclear_heat_outboard_mw, + heatcap_coolant=self.data.fwbs.heatcap_pres_blkt_coolant_average, + temp_in_coolant=temp_blkt_coolant_in, + temp_out_coolant=self.data.fwbs.temp_blkt_coolant_out, ) ) @@ -2362,13 +2466,15 @@ def thermo_hydraulic_model(self, output: bool): ): # Mass flow rate for inboard blanket coolant (kg/s) self.data.blanket.mflow_blkt_inboard_coolant = ( - 1.0e6 - * (self.data.blanket.p_blkt_nuclear_heat_inboard_mw) - / ( - self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempi) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 + * self.data.blanket.p_blkt_nuclear_heat_inboard_mw, + heatcap_coolant=self.data.fwbs.heatcap_pres_blkt_coolant_average, + temp_in_coolant=temp_blkt_coolant_in, + temp_out_coolant=self.data.fwbs.temp_blkt_coolant_out, ) ) + # Mass flow rate for inboard breeder flow (kg/s) self.data.fwbs.mfblkti_liq = ( self.data.fwbs.n_liq_recirc * self.data.fwbs.wht_liq_ib @@ -2378,11 +2484,12 @@ def thermo_hydraulic_model(self, output: bool): else: # Mass flow rate for inboard blanket coolant (kg/s) self.data.blanket.mflow_blkt_outboard_coolant = ( - 1.0e6 - * (self.data.blanket.p_blkt_nuclear_heat_outboard_mw) - / ( - self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempo) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 + * self.data.blanket.p_blkt_nuclear_heat_outboard_mw, + heatcap_coolant=self.data.fwbs.heatcap_pres_blkt_coolant_average, + temp_in_coolant=temp_blkt_coolant_in, + temp_out_coolant=self.data.fwbs.temp_blkt_coolant_out, ) ) @@ -2393,11 +2500,12 @@ def thermo_hydraulic_model(self, output: bool): == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT ): self.data.blanket.mflow_blkt_inboard_coolant = ( - 1.0e6 - * (self.data.blanket.p_blkt_nuclear_heat_inboard_mw) - / ( - self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempi) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 + * self.data.blanket.p_blkt_nuclear_heat_inboard_mw, + heatcap_coolant=self.data.fwbs.heatcap_pres_blkt_coolant_average, + temp_in_coolant=temp_blkt_coolant_in, + temp_out_coolant=self.data.fwbs.temp_blkt_coolant_out, ) ) @@ -2411,15 +2519,15 @@ def thermo_hydraulic_model(self, output: bool): deltap = self.thermo_hydraulic_model_pressure_drop_calculations( output=output ) - deltap_fwi = deltap[0] - deltap_fwo = deltap[1] - deltap_blo = deltap[2] + dpres_fw_inboard_coolant = deltap[0] + dpres_fw_outboard_coolant = deltap[1] + dpres_blkt_outboard_coolant = deltap[2] if self.data.fwbs.i_blkt_dual_coolant > 0: if ( self.data.build.i_blkt_inboard == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT ): - deltap_bli = deltap[3] + dpres_blkt_inboard_coolant = deltap[3] deltap_blo_liq = deltap[4] deltap_bli_liq = deltap[5] else: @@ -2428,7 +2536,7 @@ def thermo_hydraulic_model(self, output: bool): self.data.build.i_blkt_inboard == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT ): - deltap_bli = deltap[3] + dpres_blkt_inboard_coolant = deltap[3] # Pumping Power # If FW and BB have the same coolant... @@ -2439,12 +2547,21 @@ def thermo_hydraulic_model(self, output: bool): self.data.build.i_blkt_inboard == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT ): - deltap_fw_blkt = deltap_fwi + deltap_bli + deltap_fwo + deltap_blo + deltap_fw_blkt = ( + dpres_fw_inboard_coolant + + dpres_blkt_inboard_coolant + + dpres_fw_outboard_coolant + + dpres_blkt_outboard_coolant + ) if ( self.data.build.i_blkt_inboard == InboardBlanketConfiguration.NO_INBOARD_BLANKET ): - deltap_fw_blkt = deltap_fwi + deltap_fwo + deltap_blo + deltap_fw_blkt = ( + dpres_fw_inboard_coolant + + dpres_fw_outboard_coolant + + dpres_blkt_outboard_coolant + ) elif ( i_p_coolant_pumping == PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP @@ -2457,19 +2574,17 @@ def thermo_hydraulic_model(self, output: bool): ) # Total mechanical pumping power (MW) - self.data.primary_pumping.p_fw_blkt_coolant_pump_mw = ( - self.coolant_pumping_power( - output=output, - i_liquid_breeder=1, - temp_coolant_pump_outlet=self.data.fwbs.temp_fw_coolant_in, - temp_coolant_pump_inlet=self.data.fwbs.temp_blkt_coolant_out, - pres_coolant_pump_inlet=self.data.fwbs.pres_fw_coolant, - dpres_coolant=deltap_fw_blkt, - mflow_coolant_total=self.data.blanket.mftotal, - primary_coolant_switch=self.data.fwbs.i_fw_coolant_type, - den_coolant=self.data.fwbs.den_fw_coolant, - label="First Wall and Blanket", - ) + self.data.primary_pumping.p_fw_blkt_coolant_pump_mw = coolant_pumping_power( + i_liquid_breeder=1, + temp_coolant_pump_outlet=self.data.fwbs.temp_fw_coolant_in, + temp_coolant_pump_inlet=self.data.fwbs.temp_blkt_coolant_out, + pres_coolant_pump_inlet=self.data.fwbs.pres_fw_coolant, + dpres_coolant=deltap_fw_blkt, + mflow_coolant_total=self.data.blanket.mftotal, + i_coolant_type=self.data.fwbs.i_fw_coolant_type, + den_coolant=self.data.fwbs.den_fw_coolant, + etaiso=self.data.fwbs.etaiso, + etaiso_liq=self.data.fwbs.etaiso_liq, ) # If FW and BB have different coolants... @@ -2479,25 +2594,29 @@ def thermo_hydraulic_model(self, output: bool): ): if i_p_coolant_pumping == PumpingPowerModelTypes.MECHANICAL: # Total pressure drop in the first wall (Pa) - deltap_fw = deltap_fwi + deltap_fwo + dpres_fw_coolant_total = ( + dpres_fw_inboard_coolant + dpres_fw_outboard_coolant + ) # Total pressure drop in the blanket (Pa) if ( self.data.build.i_blkt_inboard == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT ): - deltap_blkt = deltap_bli + deltap_blo + dpres_blkt_coolant_total = ( + dpres_blkt_inboard_coolant + dpres_blkt_outboard_coolant + ) if ( self.data.build.i_blkt_inboard == InboardBlanketConfiguration.NO_INBOARD_BLANKET ): - deltap_blkt = deltap_blo + dpres_blkt_coolant_total = dpres_blkt_outboard_coolant elif ( i_p_coolant_pumping == PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP ): - deltap_fw = self.data.primary_pumping.dp_fw - deltap_blkt = self.data.primary_pumping.dp_blkt + dpres_fw_coolant_total = self.data.primary_pumping.dp_fw + dpres_blkt_coolant_total = self.data.primary_pumping.dp_blkt # Total coolant mass flow rate in the first wall (kg/s) self.data.blanket.mflow_fw_coolant_total = ( @@ -2511,31 +2630,31 @@ def thermo_hydraulic_model(self, output: bool): ) # Mechanical pumping power for the first wall (MW) - self.data.heat_transport.p_fw_coolant_pump_mw = self.coolant_pumping_power( - output=output, + self.data.heat_transport.p_fw_coolant_pump_mw = coolant_pumping_power( i_liquid_breeder=1, temp_coolant_pump_outlet=self.data.fwbs.temp_fw_coolant_in, temp_coolant_pump_inlet=self.data.fwbs.temp_fw_coolant_out, pres_coolant_pump_inlet=self.data.fwbs.pres_fw_coolant, - dpres_coolant=deltap_fw, + dpres_coolant=dpres_fw_coolant_total, mflow_coolant_total=self.data.blanket.mflow_fw_coolant_total, i_coolant_type=self.data.fwbs.i_fw_coolant_type, den_coolant=self.data.fwbs.den_fw_coolant, - label="First Wall", + etaiso=self.data.fwbs.etaiso, + etaiso_liq=self.data.fwbs.etaiso_liq, ) # Mechanical pumping power for the blanket (MW) - self.data.heat_transport.p_blkt_coolant_pump_mw = self.coolant_pumping_power( - output=output, + self.data.heat_transport.p_blkt_coolant_pump_mw = coolant_pumping_power( i_liquid_breeder=1, temp_coolant_pump_outlet=self.data.fwbs.temp_blkt_coolant_in, temp_coolant_pump_inlet=self.data.fwbs.temp_blkt_coolant_out, pres_coolant_pump_inlet=self.data.fwbs.pres_blkt_coolant, - dpres_coolant=deltap_blkt, + dpres_coolant=dpres_blkt_coolant_total, mflow_coolant_total=self.data.blanket.mflow_blkt_coolant_total, i_coolant_type=(self.data.fwbs.i_blkt_coolant_type), den_coolant=self.data.fwbs.den_blkt_coolant, - label="Blanket", + etaiso=self.data.fwbs.etaiso, + etaiso_liq=self.data.fwbs.etaiso_liq, ) # Total mechanical pumping power (MW) @@ -2569,8 +2688,7 @@ def thermo_hydraulic_model(self, output: bool): ) # Mechanical pumping power for the blanket (MW) - self.data.heat_transport.p_blkt_breeder_pump_mw = self.coolant_pumping_power( - output=output, + self.data.heat_transport.p_blkt_breeder_pump_mw = coolant_pumping_power( i_liquid_breeder=2, temp_coolant_pump_outlet=self.data.fwbs.inlet_temp_liq, temp_coolant_pump_inlet=self.data.fwbs.outlet_temp_liq, @@ -2579,7 +2697,8 @@ def thermo_hydraulic_model(self, output: bool): mflow_coolant_total=self.data.blanket.mfblkt_liq, i_coolant_type=(self.data.fwbs.i_blkt_coolant_type), den_coolant=self.data.fwbs.den_liq, - label="Liquid Metal Breeder/Coolant", + etaiso=self.data.fwbs.etaiso, + etaiso_liq=self.data.fwbs.etaiso_liq, ) self.data.heat_transport.htpmw_blkt_tot = ( @@ -2840,7 +2959,7 @@ def total_pressure_drop( pol_channel_length: float, nopolchan: int, label: str, - ) -> float: + ) -> tuple[float, CoolantFrictionLossParameters]: """Calculate the total pressure drop (Pa) for coolant flow in the first wall (FW) and breeding blanket (BZ). This includes frictional losses and, for liquid breeder coolants, magnetohydrodynamic (MHD) losses. @@ -2874,8 +2993,8 @@ def total_pressure_drop( Returns ------- - float - Total pressure drop (Pa). + tuple + Total pressure drop (Pa) and friction loss parameters. """ radius_pipe_90_deg_bend, radius_pipe_180_deg_bend = calculate_pipe_bend_radius( i_ps=icoolpump, @@ -2884,7 +3003,7 @@ def total_pressure_drop( ) # Friction - for all coolants - dpres_friction = self.coolant_friction_pressure_drop( + friction_params: CoolantFrictionLossParameters = coolant_friction_pressure_drop( i_ps=icoolpump, radius_pipe_90_deg_bend=radius_pipe_90_deg_bend, radius_pipe_180_deg_bend=radius_pipe_180_deg_bend, @@ -2894,9 +3013,12 @@ def total_pressure_drop( den_coolant=den_coolant, visc_coolant=visc_coolant_dynamic, vel_coolant=vel_coolant, - label=label, - output=output, + roughness_channel=self.data.fwbs.roughness_fw_channel, + radius_channel=self.data.fwbs.radius_fw_channel, + a_bz_liq=self.data.fwbs.a_bz_liq, + b_bz_liq=self.data.fwbs.b_bz_liq, ) + dpres_friction = friction_params.dpres_total if icoolpump == 2: dpres_mhd = self.liquid_breeder_mhd_pressure_drop( @@ -2914,22 +3036,7 @@ def total_pressure_drop( # Total pressure drop (Pa) dpres_total = dpres_friction + dpres_mhd - if output: - po.osubhd(self.outfile, f"Total pressure drop for {label}") - - po.ocmmnt(self.outfile, "Friction drops plus MHD drops if applicaple") - po.ovarre( - self.outfile, "Total pressure drop (Pa)", "(deltap)", dpres_total, "OP " - ) - po.ovarre( - self.outfile, - "Coolant flow velocity (m/s)", - "(flow_velocity, formerly vv)", - vel_coolant, - "OP ", - ) - - return dpres_total + return dpres_total, friction_params def liquid_breeder_mhd_pressure_drop( self, @@ -3085,452 +3192,322 @@ def liquid_breeder_mhd_pressure_drop( return liquid_breeder_pressure_drop_mhd - def coolant_friction_pressure_drop( - self, - i_ps: int, - radius_pipe_90_deg_bend: float, - radius_pipe_180_deg_bend: float, - n_pipe_90_deg_bends: float, - n_pipe_180_deg_bends: float, - len_pipe: float, - den_coolant: float, - visc_coolant: float, - vel_coolant: float, - label: str, - output: bool = False, - ): - """Pressure drops are calculated for a pipe with a number of 90 - and 180 degree bends. The pressure drop due to frictional forces along - the total straight length of the pipe is calculated, then the pressure - drop due to the bends is calculated. The total pressure drop is the sum - of all contributions. + def output_outboard_blkt_pumping_variables(self): - Parameters - ---------- - i_ps : - switch for primary or secondary coolant - radius_pipe_90_deg_bend : - radius of 90 degree bend in pipe (m) - radius_pipe_180_deg_bend : - radius of 180 degree bend in pipe (m) - n_pipe_90_deg_bends : - number of 90 degree bends in the pipe - n_pipe_180_deg_bends : - number of 180 degree bends in the pipe - len_pipe : - total flow length along pipe (m) - den_coolant : - coolant density (kg/m³) - visc_coolant : - coolant viscosity (Pa s) - vel_coolant : - coolant flow velocity (m/s) - label : - component name - output : - boolean of whether to write data to output file + po.oheadr(self.outfile, "Outboard Blanket pumping variables") - :Notes: - Darcy-Weisbach Equation (straight pipe): - - ΔP = λ * L/D * (p 〈v〉²) / 2 - - λ - Darcy friction factor, L - pipe length, D - hydraulic diameter, - p - fluid density, 〈v〉 - fluid flow average velocity - - This function also calculates pressure drop equations for elbow bends, - with modified coefficients. - - N.B. Darcy friction factor is estimated from the Haaland approximation. - """ - # Calculate hydraulic dimater for round or retancular pipe (m) - dia_pipe = self.pipe_hydraulic_diameter(i_ps) - - # Reynolds number - reynolds_number = calculate_reynolds_number( - den_coolant=den_coolant, - vel_coolant=vel_coolant, - radius_channel=dia_pipe / 2, - visc_coolant=visc_coolant, + po.ovarre( + self.outfile, + "Outboard blanket coolant channel length (radial direction) (m)", + "(len_blkt_outboard_coolant_channel_radial)", + self.data.blanket.len_blkt_outboard_coolant_channel_radial, + "OP ", ) - - # Calculate Darcy friction factor - # N.B. friction function Uses Haaland approx. which assumes a filled circular pipe. - # Use dh which allows us to do fluid calculations for non-cicular tubes - # (dh is estimate appropriate for fully developed flow). - - darcy_friction_factor = darcy_friction_haaland( - reynolds=reynolds_number, - roughness_channel=self.data.fwbs.roughness_fw_channel, - radius_channel=self.data.fwbs.radius_fw_channel, + po.ovarre( + self.outfile, + "Outboard blanket coolant channel length (poloidal direction) (m)", + "(len_blkt_outboard_segment_poloidal)", + self.data.blanket.len_blkt_outboard_segment_poloidal, + "OP ", ) - - # Pressure drop coefficient - - # Straight section - f_straight = darcy_friction_factor * len_pipe / dia_pipe - - # 90 degree elbow pressure drop coefficient - f_elbow_90 = self.elbow_coeff( - radius_pipe_elbow=radius_pipe_90_deg_bend, - deg_pipe_elbow=90.0, - darcy_friction=darcy_friction_factor, - dia_pipe=dia_pipe, + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Number of outboard blanket coolant sections in the radial direction", + "(n_blkt_outboard_module_coolant_sections_radial)", + self.data.fwbs.n_blkt_outboard_module_coolant_sections_radial, + "OP ", ) - - # 180 degree elbow pressure drop coefficient - f_elbow_180 = self.elbow_coeff( - radius_pipe_elbow=radius_pipe_180_deg_bend, - deg_pipe_elbow=180.0, - darcy_friction=darcy_friction_factor, - dia_pipe=dia_pipe, + po.ovarre( + self.outfile, + "Number of outboard blanket coolant sections in the poloidal direction", + "(n_blkt_outboard_module_coolant_sections_poloidal)", + self.data.fwbs.n_blkt_outboard_module_coolant_sections_poloidal, + "OP ", ) - - # Pressure drop due to friction in straight sections - dpres_straight = f_straight * 0.5 * den_coolant * vel_coolant**2 - - # Pressure drop due to 90 and 180 degree bends - dpres_90 = n_pipe_90_deg_bends * f_elbow_90 * 0.5 * den_coolant * vel_coolant**2 - dpres_180 = ( - n_pipe_180_deg_bends * f_elbow_180 * 0.5 * den_coolant * vel_coolant**2 + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Total length of outboard blanket coolant channel straight sections (m)", + "(len_blkt_outboard_channel_total)", + self.data.blanket.len_blkt_outboard_channel_total, + "OP ", ) - - # Total pressure drop (Pa) - dpres_total = dpres_straight + dpres_90 + dpres_180 - - if output: - po.osubhd(self.outfile, f"Pressure drop (friction) for {label}") - po.ovarre(self.outfile, "Reynolds number", "(reyn)", reynolds_number, "OP ") - po.ovarre( - self.outfile, - "Darcy friction factor", - "(lambda)", - darcy_friction_factor, - "OP ", - ) - po.ovarre( - self.outfile, - "Pressure drop (Pa)", - "(pressure_drop)", - dpres_total, - "OP ", - ) - po.ocmmnt(self.outfile, "This is the sum of the following:") - po.ovarre( - self.outfile, - " Straight sections (Pa)", - "(pdropstraight)", - dpres_straight, - "OP ", - ) - po.ovarre( - self.outfile, - " 90 degree bends (Pa)", - "(pdrop90)", - dpres_90, - "OP ", - ) - po.ovarre( - self.outfile, - " 180 degree bends (Pa)", - "(pdrop180)", - dpres_180, - "OP ", - ) - - # TN: always write verbose stuff, it has no harm - po.ovarre( - self.outfile, - "Straight section pressure drop coefficient", - "(kstrght)", - f_straight, - "OP ", - ) - po.ovarre( - self.outfile, - "90 degree elbow coefficient", - "(kelbwn)", - f_elbow_90, - "OP ", - ) - po.ovarre( - self.outfile, - "180 degree elbow coefficient coefficient", - "(kelbwt)", - f_elbow_180, - "OP ", - ) - - return dpres_total - - def pipe_hydraulic_diameter(self, i_channel_shape): - """Caculate the hydraulic diameter (m) for a given coolant pipe size/shape. - - - Parameters - ---------- - i_channel_shape : - switch for circular or rectangular channel crossection. - Shape depends on whether primary or secondary coolant - """ - # If primary coolant then circular channels assumed - if i_channel_shape == 1: - return 2.0 * self.data.fwbs.radius_fw_channel - - # If secondary coolant then rectangular channels assumed - if i_channel_shape == 2: - return ( - 2 - * self.data.fwbs.a_bz_liq - * self.data.fwbs.b_bz_liq - / (self.data.fwbs.a_bz_liq + self.data.fwbs.b_bz_liq) - ) - - raise ProcessValueError( - f"i_channel_shape ={i_channel_shape} is an invalid option." + po.oblnkl(self.outfile) + po.ocmmnt(self.outfile, "----------------------------") + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Pressure drop for straight sections of outboard blanket (Pa)", + "(dpres_blkt_outboard_coolant_channel_straight_total)", + self.data.blanket.dpres_blkt_outboard_coolant_channel_straight_total, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Pressure drop for 90° bends of outboard blanket (Pa)", + "(dpres_blkt_outboard_coolant_channel_90_bend)", + self.data.blanket.dpres_blkt_outboard_coolant_channel_90_bend, + "OP ", + ) + po.ovarre( + self.outfile, + "Total pressure drop for 90° bends of outboard blanket (Pa)", + "(dpres_blkt_outboard_coolant_channel_90_bends_total)", + self.data.blanket.dpres_blkt_outboard_coolant_channel_90_bends_total, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Pressure drop for 180° bends of outboard blanket (Pa)", + "(dpres_blkt_outboard_coolant_channel_180_bend)", + self.data.blanket.dpres_blkt_outboard_coolant_channel_180_bend, + "OP ", + ) + po.ovarre( + self.outfile, + "Total pressure drop for 180° bends of outboard blanket (Pa)", + "(dpres_blkt_outboard_coolant_channel_180_bends_total)", + self.data.blanket.dpres_blkt_outboard_coolant_channel_180_bends_total, + "OP ", ) - @staticmethod - def elbow_coeff( - radius_pipe_elbow: float, - deg_pipe_elbow: float, - darcy_friction: float, - dia_pipe: float, - ) -> float: - """Calculates elbow bend coefficients for pressure drop calculations. - - Parameters - ---------- - radius_pipe_elbow : float - Pipe elbow radius (m) - deg_pipe_elbow : float - Pipe elbow angle (degrees) - darcy_friction : float - Darcy friction factor - dia_pipe : float - Pipe diameter (m) - - Returns - ------- - float - Elbow coefficient for pressure drop calculation - - References - ---------- - - [Ide1969] Idel'Cik, I. E. (1969), Memento des pertes de charge, - Collection de la Direction des Etudes et Recherches d'Electricité de France. - """ - if deg_pipe_elbow == 90: - a = 1.0 - elif deg_pipe_elbow < 70: - a = 0.9 * np.sin(deg_pipe_elbow * np.pi / 180.0) - elif deg_pipe_elbow > 100: - a = 0.7 + (0.35 * np.sin((deg_pipe_elbow / 90.0) * (np.pi / 180.0))) - else: - raise ProcessValueError( - "No formula for 70 <= elbow angle(deg) <= 100, only 90 deg option available in this range." - ) - - r_ratio = radius_pipe_elbow / dia_pipe - - if r_ratio > 1: - b = 0.21 / r_ratio**0.5 - elif r_ratio < 1: - b = 0.21 / r_ratio**2.5 - else: - b = 0.21 + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Total pressure drop for all bends (Pa)", + "(dpres_blkt_outboard_bends_total)", + self.data.blanket.dpres_blkt_outboard_bends_total, + "OP ", + ) - # Singularity - ximt = a * b + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Reynolds number of outboard blanket coolant", + "(reynolds_blkt_outboard_coolant)", + self.data.blanket.reynolds_blkt_outboard_coolant, + "OP ", + ) - # Friction - xift = ( - (np.pi / 180.0) - * darcy_friction - * (radius_pipe_elbow / dia_pipe) - * deg_pipe_elbow + po.ovarre( + self.outfile, + "Darcy friction factor of outboard blanket coolant", + "(darcy_frict_blkt_outboard_coolant)", + self.data.blanket.darcy_frict_blkt_outboard_coolant, + "OP ", ) + po.oblnkl(self.outfile) - # Elbow Coefficient - return ximt + xift + po.ovarre( + self.outfile, + "Pressure drop coefficient for straight sections of outboard blanket", + "(f_straight_blkt_outboard_coolant)", + self.data.blanket.f_straight_blkt_outboard_coolant, + "OP ", + ) - def coolant_pumping_power( - self, - output: bool, - i_liquid_breeder: int, - temp_coolant_pump_outlet: float, - temp_coolant_pump_inlet: float, - pres_coolant_pump_inlet: float, - dpres_coolant: float, - mflow_coolant_total: float, - i_coolant_type: int, - den_coolant: float, - label: str, - ) -> float: - """Calculate the coolant pumping power in MW for the first wall (FW) or breeding blanket (BZ) coolant. + po.ovarre( + self.outfile, + "Total length of straight sections of outboard blanket coolant channels (m)", + "(len_blkt_outboard_coolant_channel_straight_total)", + self.data.blanket.len_blkt_outboard_coolant_channel_straight_total, + "OP ", + ) - Parameters - ---------- - output : bool - Whether to write data to output file. - i_liquid_breeder : int - Switch for primary coolant or secondary coolant/breeder (1=primary He/H2O, 2=secondary PbLi/Li). - temp_coolant_pump_outlet : float - Pump outlet temperature (K). - temp_coolant_pump_inlet : float - Pump inlet temperature (K). - pressure : float - Outlet (pump inlet) coolant pressure (Pa). - dpres_coolant : float - Coolant pressure drop (Pa). - mflow_coolant_total : float - Total coolant mass flow rate in (kg/s). - i_coolant_type : int - Type of FW/blanket coolant (e.g., 1=Helium, 2=Water) - den_coolant : float - Density of coolant or liquid breeder (kg/m³). - label : str - Description label for output. + po.ovarre( + self.outfile, + "Pressure drop coefficient for 90° bends in outboard blanket", + "(f_elbow_blkt_outboard_90_bend)", + self.data.blanket.f_elbow_blkt_outboard_90_bend, + "OP ", + ) - Returns - ------- - float - Pumping power in MW. + po.ovarre( + self.outfile, + "Pressure drop coefficient for 180° bends in outboard blanket", + "(f_elbow_blkt_outboard_180_bend)", + self.data.blanket.f_elbow_blkt_outboard_180_bend, + "OP ", + ) + po.oblnkl(self.outfile) + po.ocmmnt(self.outfile, "----------------------------") + po.oblnkl(self.outfile) - References - ---------- - - Idel'Cik, I. E. (1969), Memento des pertes de charge - - S.P. Sukhatme (2005), A Textbook on Heat Transfer - """ - # Pump outlet pressure (Pa) - # The pump adds the pressure lost going through the coolant channels back - pres_coolant_pump_outlet = pres_coolant_pump_inlet + dpres_coolant + po.ovarre( + self.outfile, + "Mass flow rate of outboard blanket coolant (kg/s)", + "(mflow_blkt_outboard_coolant)", + self.data.blanket.mflow_blkt_outboard_coolant, + "OP ", + ) - # Adiabatic index for helium or water - gamma = (5 / 3) if i_coolant_type == CoolantType.HELIUM else (4 / 3) + def output_inboard_blkt_pumping_variables(self): - # If calculating for primary coolant - if i_liquid_breeder == 1: - # The pumping power is be calculated in the most general way, - # using enthalpies before and after the pump. + po.oheadr(self.outfile, "Inboard Blanket pumping variables") - pump_outlet_fluid_properties = FluidProperties.of( - fluid_name=CoolantType(i_coolant_type).full_name, - temperature=temp_coolant_pump_outlet, - pressure=pres_coolant_pump_outlet, - ) + po.ovarre( + self.outfile, + "Inboard blanket coolant channel length (radial direction) (m)", + "(len_blkt_inboard_coolant_channel_radial)", + self.data.blanket.len_blkt_inboard_coolant_channel_radial, + "OP ", + ) + po.ovarre( + self.outfile, + "Inboard blanket coolant channel length (poloidal direction) (m)", + "(len_blkt_inboard_segment_poloidal)", + self.data.blanket.len_blkt_inboard_segment_poloidal, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Number of inboard blanket coolant sections in the radial direction", + "(n_blkt_inboard_module_coolant_sections_radial)", + self.data.fwbs.n_blkt_inboard_module_coolant_sections_radial, + "OP ", + ) + po.ovarre( + self.outfile, + "Number of inboard blanket coolant sections in the poloidal direction", + "(n_blkt_inboard_module_coolant_sections_poloidal)", + self.data.fwbs.n_blkt_inboard_module_coolant_sections_poloidal, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Total length of inboard blanket coolant channel straight sections (m)", + "(len_blkt_inboard_channel_total)", + self.data.blanket.len_blkt_inboard_channel_total, + "OP ", + ) + po.oblnkl(self.outfile) + po.ocmmnt(self.outfile, "----------------------------") + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Pressure drop for straight sections of inboard blanket (Pa)", + "(dpres_blkt_inboard_coolant_channel_straight_total)", + self.data.blanket.dpres_blkt_inboard_coolant_channel_straight_total, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Pressure drop for 90° bends of inboard blanket (Pa)", + "(dpres_blkt_inboard_coolant_channel_90_bend)", + self.data.blanket.dpres_blkt_inboard_coolant_channel_90_bend, + "OP ", + ) + po.ovarre( + self.outfile, + "Total pressure drop for 90° bends of inboard blanket (Pa)", + "(dpres_blkt_inboard_coolant_channel_90_bends_total)", + self.data.blanket.dpres_blkt_inboard_coolant_channel_90_bends_total, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Pressure drop for 180° bends of inboard blanket (Pa)", + "(dpres_blkt_inboard_coolant_channel_180_bend)", + self.data.blanket.dpres_blkt_inboard_coolant_channel_180_bend, + "OP ", + ) + po.ovarre( + self.outfile, + "Total pressure drop for 180° bends of inboard blanket (Pa)", + "(dpres_blkt_inboard_coolant_channel_180_bends_total)", + self.data.blanket.dpres_blkt_inboard_coolant_channel_180_bends_total, + "OP ", + ) - # Assume isentropic pump so that s1 = s2 - s1 = pump_outlet_fluid_properties.entropy + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Total pressure drop for all bends (Pa)", + "(dpres_blkt_inboard_bends_total)", + self.data.blanket.dpres_blkt_inboard_bends_total, + "OP ", + ) - # Get specific enthalpy at the outlet (J/kg) before pump using pressure and entropy s1 - pump_inlet_fluid_properties = FluidProperties.of( - fluid_name=CoolantType(i_coolant_type).full_name, - pressure=pres_coolant_pump_inlet, - entropy=s1, - ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Reynolds number of inboard blanket coolant", + "(reynolds_blkt_inboard_coolant)", + self.data.blanket.reynolds_blkt_inboard_coolant, + "OP ", + ) - # Pumping power (MW) is given by enthalpy change, with a correction for - # the isentropic efficiency of the pump. - fp = ( - temp_coolant_pump_outlet - * ( - 1 - - (pres_coolant_pump_outlet / pres_coolant_pump_inlet) - ** -((gamma - 1) / gamma) - ) - / ( - self.data.fwbs.etaiso - * (temp_coolant_pump_inlet - temp_coolant_pump_outlet) - ) - ) - pumppower = ( - 1e-6 - * mflow_coolant_total - * ( - pump_outlet_fluid_properties.enthalpy - - pump_inlet_fluid_properties.enthalpy - ) - / self.data.fwbs.etaiso - ) / (1 - fp) + po.ovarre( + self.outfile, + "Darcy friction factor of inboard blanket coolant", + "(darcy_frict_blkt_inboard_coolant)", + self.data.blanket.darcy_frict_blkt_inboard_coolant, + "OP ", + ) + po.oblnkl(self.outfile) - # If calculating for secondary coolant/breeder... - else: - # Calculate specific volume - spec_vol = 1 / den_coolant + po.ovarre( + self.outfile, + "Pressure drop coefficient for straight sections of inboard blanket", + "(f_straight_blkt_inboard_coolant)", + self.data.blanket.f_straight_blkt_inboard_coolant, + "OP ", + ) - # Pumping power (MW) is given by pressure change, with a correction for - # the isentropic efficiency of the pump. - fp = ( - temp_coolant_pump_outlet - * ( - 1 - - (pres_coolant_pump_outlet / pres_coolant_pump_inlet) - ** -((gamma - 1) / gamma) - ) - / ( - self.data.fwbs.etaiso_liq - * (temp_coolant_pump_inlet - temp_coolant_pump_outlet) - ) - ) - pumppower = ( - 1e-6 - * mflow_coolant_total - * spec_vol - * dpres_coolant - / self.data.fwbs.etaiso_liq - ) / (1 - fp) + po.ovarre( + self.outfile, + "Total length of straight sections of inboard blanket coolant channels (m)", + "(len_blkt_inboard_coolant_channel_straight_total)", + self.data.blanket.len_blkt_inboard_coolant_channel_straight_total, + "OP ", + ) - # Error for dpres_coolant too large - if fp >= 1: - raise ProcessValueError( - "Pressure drops in coolant are too large to be feasible" - ) + po.ovarre( + self.outfile, + "Pressure drop coefficient for 90° bends in inboard blanket", + "(f_elbow_blkt_inboard_90_bend)", + self.data.blanket.f_elbow_blkt_inboard_90_bend, + "OP ", + ) - if output: - po.oheadr(self.outfile, "Mechanical Pumping Power for " + label) - po.osubhd(self.outfile, "Pumping power for " + label) + po.ovarre( + self.outfile, + "Pressure drop coefficient for 180° bends in inboard blanket", + "(f_elbow_blkt_inboard_180_bend)", + self.data.blanket.f_elbow_blkt_inboard_180_bend, + "OP ", + ) - po.ovarre( - self.outfile, "Pumping power (MW)", "(pumppower)", pumppower, "OP " - ) - po.ovarre( - self.outfile, - "FW or Blanket inlet (pump oulet) pressure (Pa)", - "(coolpin)", - pres_coolant_pump_outlet, - "OP ", - ) - po.ovarre( - self.outfile, - "FW or Blanket oulet (pump inlet) pressure (Pa)", - "(pres_coolant_pump_inlet)", - pres_coolant_pump_inlet, - "OP ", - ) - po.ovarre( - self.outfile, - "FW or Blanket total pressure drop (Pa)", - "(dpres_coolant)", - dpres_coolant, - "OP ", - ) - po.ovarre( - self.outfile, - "Mass flow rate in (kg/s) = ", - "(mf)", - mflow_coolant_total, - "OP ", - ) + po.oblnkl(self.outfile) + po.ocmmnt(self.outfile, "----------------------------") + po.oblnkl(self.outfile) - return pumppower + po.ovarre( + self.outfile, + "Mass flow rate of inboard blanket coolant (kg/s)", + "(mflow_blkt_inboard_coolant)", + self.data.blanket.mflow_blkt_inboard_coolant, + "OP ", + ) class OutboardBlanket(BlanketLibrary): def calculate_basic_geometry(self): self.component_volumes() - dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) + dia_blkt_channel = self.pipe_hydraulic_diameter( + i_channel_shape=1, + radius_fw_channel=self.data.fwbs.radius_fw_channel, + a_bz_liq=self.data.fwbs.a_bz_liq, + b_bz_liq=self.data.fwbs.b_bz_liq, + ) self.data.fwbs.radius_blkt_channel = dia_blkt_channel / 2 ( self.data.fwbs.radius_blkt_channel_90_bend, @@ -3658,7 +3635,12 @@ class InboardBlanket(BlanketLibrary): def calculate_basic_geometry(self): self.component_volumes() - dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) + dia_blkt_channel = self.pipe_hydraulic_diameter( + i_channel_shape=1, + radius_fw_channel=self.data.fwbs.radius_fw_channel, + a_bz_liq=self.data.fwbs.a_bz_liq, + b_bz_liq=self.data.fwbs.b_bz_liq, + ) self.data.fwbs.radius_blkt_channel = dia_blkt_channel / 2 ( self.data.fwbs.radius_blkt_channel_90_bend, diff --git a/process/models/blankets/dcll.py b/process/models/blankets/dcll.py index 25b49073eb..4f6835dde7 100644 --- a/process/models/blankets/dcll.py +++ b/process/models/blankets/dcll.py @@ -112,7 +112,12 @@ def run(self, output: bool = False): self.data.blanket.deg_blkt_inboard_poloidal_plasma / 360.0 ) - dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) + dia_blkt_channel = self.pipe_hydraulic_diameter( + i_channel_shape=1, + radius_fw_channel=self.data.fwbs.radius_fw_channel, + a_bz_liq=self.data.fwbs.a_bz_liq, + b_bz_liq=self.data.fwbs.b_bz_liq, + ) self.data.fwbs.radius_blkt_channel = dia_blkt_channel / 2 ( self.data.fwbs.radius_blkt_channel_90_bend, diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 4187fe05cb..e78d07c1dd 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -13,7 +13,10 @@ calculate_pipe_bend_radius, pumping_powers_as_fractions, ) -from process.models.engineering.pumping import CoolantType +from process.models.engineering.pumping import ( + CoolantType, + pipe_hydraulic_diameter, +) from process.models.power import PumpingPowerModelTypes from process.models.tfcoil.base import TFConductorModel @@ -66,7 +69,12 @@ def run(self, output: bool = False): self.data.blanket.deg_blkt_inboard_poloidal_plasma / 360.0 ) - dia_blkt_channel = self.pipe_hydraulic_diameter(i_channel_shape=1) + dia_blkt_channel = pipe_hydraulic_diameter( + i_channel_shape=1, + radius_fw_channel=self.data.fwbs.radius_fw_channel, + a_bz_liq=self.data.fwbs.a_bz_liq, + b_bz_liq=self.data.fwbs.b_bz_liq, + ) self.data.fwbs.radius_blkt_channel = dia_blkt_channel / 2 ( self.data.fwbs.radius_blkt_channel_90_bend, @@ -847,6 +855,53 @@ def powerflow_calc(self, output: bool): + self.data.fwbs.p_div_rad_total_mw ) ) + if output: + po.ovarre( + self.outfile, + "Mechanical pumping power for FW and blanket cooling loop including heat exchanger (MW)", + "(p_fw_blkt_coolant_pump_mw)", + self.data.primary_pumping.p_fw_blkt_coolant_pump_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Pumping power for FW and Blanket multiplier factor", + "(f_p_fw_blkt_pump)", + self.data.primary_pumping.f_p_fw_blkt_pump, + "IP ", + ) + po.ovarre( + self.outfile, + "Mechanical pumping power for divertor (MW)", + "(p_div_coolant_pump_mw)", + self.data.heat_transport.p_div_coolant_pump_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Mechanical pumping power for shield and vacuum vessel (MW)", + "(p_shld_coolant_pump_mw)", + self.data.heat_transport.p_shld_coolant_pump_mw, + "OP ", + ) + po.ovarre( + self.outfile, + "Radius of blanket cooling channels (m)", + "(radius_blkt_channel)", + self.data.fwbs.radius_blkt_channel, + ) + po.ovarre( + self.outfile, + "Radius of 90 degree coolant channel bend (m)", + "(radius_blkt_channel_90_bend)", + self.data.fwbs.radius_blkt_channel_90_bend, + ) + po.ovarre( + self.outfile, + "Radius of 180 degree coolant channel bend (m)", + "(radius_blkt_channel_180_bend)", + self.data.fwbs.radius_blkt_channel_180_bend, + ) elif i_p_coolant_pumping == PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP: # Issue #503 diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index ae7f2f6131..72a1d044eb 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -1,11 +1,17 @@ """Engineering models for pumping system analysis.""" import logging +from dataclasses import dataclass from enum import IntEnum from types import DynamicClassAttribute import numpy as np +from process.core.coolprop_interface import FluidProperties +from process.core.exceptions import ProcessValueError + +logger = logging.getLogger(__name__) + class CoolantType(IntEnum): """Enum for coolant types.""" @@ -40,9 +46,221 @@ def full_name(self): return self._full_name_ -logger = logging.getLogger(__name__) +@dataclass +class CoolantFrictionLossParameters: + """Parameters for calculating coolant friction losses.""" + + dpres_total: float + """Total pressure drop across the coolant channel (Pa)""" + dpres_straight: float + """Pressure drop due to straight length of the coolant channel (Pa)""" + dpres_90: float + """Pressure drop due to 90 degree bends in the coolant channel (Pa)""" + dpres_90_total: float + """Total pressure drop due to 90 degree bends in the coolant channel (Pa)""" + dpres_180: float + """Pressure drop due to 180 degree bends in the coolant channel (Pa)""" + dpres_180_total: float + """Total pressure drop due to 180 degree bends in the coolant channel (Pa)""" + dpres_bends_total: float + """Total pressure drop due to bends in the coolant channel (Pa)""" + reynolds_number: float + """Reynolds number of the coolant flow in the channel""" + darcy_friction_factor: float + """Darcy friction factor for the coolant flow in the channel""" + f_straight: float + """Friction factor for straight length of the coolant channel""" + len_straight: float + """Length of straight sections of the coolant channel (m)""" + f_elbow_90: float + """Friction factor for 90 degree bends in the coolant channel""" + f_elbow_180: float + """Friction factor for 180 degree bends in the coolant channel""" + + +def coolant_friction_pressure_drop( + i_ps: int, + radius_pipe_90_deg_bend: float, + radius_pipe_180_deg_bend: float, + n_pipe_90_deg_bends: float, + n_pipe_180_deg_bends: float, + len_pipe: float, + den_coolant: float, + visc_coolant: float, + vel_coolant: float, + roughness_channel: float, + radius_channel: float, + a_bz_liq: float, + b_bz_liq: float, +) -> CoolantFrictionLossParameters: + """Pressure drops are calculated for a pipe with a number of 90 + and 180 degree bends. The pressure drop due to frictional forces along + the total straight length of the pipe is calculated, then the pressure + drop due to the bends is calculated. The total pressure drop is the sum + of all contributions. + + Parameters + ---------- + i_ps : + switch for primary or secondary coolant + radius_pipe_90_deg_bend : + radius of 90 degree bend in pipe [m] + radius_pipe_180_deg_bend : + radius of 180 degree bend in pipe [m] + n_pipe_90_deg_bends : + number of 90 degree bends in the pipe + n_pipe_180_deg_bends : + number of 180 degree bends in the pipe + len_pipe : + total flow length along pipe [m] + den_coolant : + coolant density [kg/m³] + visc_coolant : + coolant viscosity [Pa s] + vel_coolant : + coolant flow velocity [m/s] + roughness_channel : + roughness of the channel wall (ε) [m] + radius_channel : + radius of the channel [m] + a_bz_liq : + width of the breeding blanket coolant channel [m] + b_bz_liq : + height of the breeding blanket coolant channel [m] + + + Returns + ------- + : + `CoolantFrictionLossParameters` dataclass containing: + - Total pressure drop due to friction (Pa) + - Pressure drop due to straight sections (Pa) + - Pressure drop due to 90 degree bends (Pa) + - Pressure drop due to 180 degree bends (Pa) + - Reynolds number + - Darcy friction factor + - Pressure drop coefficient for straight sections + - Pressure drop coefficient for 90 degree bends + - Pressure drop coefficient for 180 degree bends + + Notes + ----- + Darcy-Weisbach Equation (straight pipe): + + ΔP = λ * L/D * (p 〈v〉²) / 2 + + λ - Darcy friction factor, L - pipe length, D - hydraulic diameter, + p - fluid density, 〈v〉 - fluid flow average velocity + + This function also calculates pressure drop equations for elbow bends, + with modified coefficients. + + N.B. Darcy friction factor is estimated from the Haaland approximation. + """ + # Calculate hydraulic dimater for round or retancular pipe (m) + dia_pipe = pipe_hydraulic_diameter( + i_channel_shape=i_ps, + radius_fw_channel=radius_channel, + a_bz_liq=a_bz_liq, + b_bz_liq=b_bz_liq, + ) + + # Reynolds number + reynolds_number = calculate_reynolds_number( + den_coolant=den_coolant, + vel_coolant=vel_coolant, + radius_channel=dia_pipe / 2, + visc_coolant=visc_coolant, + ) + + # Calculate Darcy friction factor + # N.B. friction function Uses Haaland approx. which assumes a filled + # circular pipe. + # Use dh which allows us to do fluid calculations for non-circular tubes + # (dh is estimate appropriate for fully developed flow). + + darcy_friction_factor = darcy_friction_haaland( + reynolds=reynolds_number, + roughness_channel=roughness_channel, + radius_channel=radius_channel, + ) + + # Pressure drop coefficient + + # Straight section + f_straight = darcy_friction_factor * len_pipe / dia_pipe + + # 90 degree elbow pressure drop coefficient + f_elbow_90 = elbow_coeff( + radius_pipe_elbow=radius_pipe_90_deg_bend, + deg_pipe_elbow=90.0, + darcy_friction=darcy_friction_factor, + dia_pipe=dia_pipe, + ) + + # 180 degree elbow pressure drop coefficient + f_elbow_180 = elbow_coeff( + radius_pipe_elbow=radius_pipe_180_deg_bend, + deg_pipe_elbow=180.0, + darcy_friction=darcy_friction_factor, + dia_pipe=dia_pipe, + ) + + # Pressure drop due to friction in straight sections + dpres_straight = f_straight * 0.5 * den_coolant * vel_coolant**2 + + # Pressure drop due to 90 and 180 degree bends + dpres_90 = f_elbow_90 * 0.5 * den_coolant * vel_coolant**2 + dpres_90_total = n_pipe_90_deg_bends * dpres_90 + dpres_180 = f_elbow_180 * 0.5 * den_coolant * vel_coolant**2 + dpres_180_total = n_pipe_180_deg_bends * dpres_180 + + dpres_bends_total = dpres_90_total + dpres_180_total + + # Total pressure drop (Pa) + dpres_total = dpres_straight + dpres_bends_total + + return CoolantFrictionLossParameters( + dpres_total=dpres_total, + dpres_straight=dpres_straight, + dpres_90=dpres_90, + dpres_90_total=dpres_90_total, + dpres_180=dpres_180, + dpres_180_total=dpres_180_total, + dpres_bends_total=dpres_bends_total, + reynolds_number=reynolds_number, + darcy_friction_factor=darcy_friction_factor, + f_straight=f_straight, + len_straight=len_pipe, + f_elbow_90=f_elbow_90, + f_elbow_180=f_elbow_180, + ) + + +def pipe_hydraulic_diameter( + i_channel_shape, radius_fw_channel: float, a_bz_liq: float, b_bz_liq: float +) -> float: + """Caculate the hydraulic diameter (m) for a given coolant pipe size/shape. + + + Parameters + ---------- + i_channel_shape : + switch for circular or rectangular channel crossection. + Shape depends on whether primary or secondary coolant + """ + # If primary coolant then circular channels assumed + if i_channel_shape == 1: + return 2.0 * radius_fw_channel + + # If secondary coolant then rectangular channels assumed + if i_channel_shape == 2: + return 2 * a_bz_liq * b_bz_liq / (a_bz_liq + b_bz_liq) + raise ProcessValueError(f"i_channel_shape ={i_channel_shape} is an invalid option.") + +@staticmethod def darcy_friction_haaland( reynolds: float, roughness_channel: float, radius_channel: float ) -> float: @@ -64,8 +282,8 @@ def darcy_friction_haaland( Notes ----- - The Haaland equation is an approximation to the implicit Colebrook-White equation. - It is used to calculate the Darcy friction factor for turbulent flow in pipes. + The Haaland equation is an approximation to the implicit Colebrook-White equation. + It is used to calculate the Darcy friction factor for turbulent flow in pipes. References ---------- @@ -78,6 +296,131 @@ def darcy_friction_haaland( return (1.8 * np.log10(bracket)) ** (-2) +def coolant_pumping_power( + i_liquid_breeder: int, + temp_coolant_pump_outlet: float, + temp_coolant_pump_inlet: float, + pres_coolant_pump_inlet: float, + dpres_coolant: float, + mflow_coolant_total: float, + i_coolant_type: int, + den_coolant: float, + etaiso: float, + etaiso_liq: float, +) -> float: + """Calculate the coolant pumping power in MW for the first wall (FW) or breeding blanket (BZ) coolant. + + Parameters + ---------- + output : bool + Whether to write data to output file. + i_liquid_breeder : int + Switch for primary coolant or secondary coolant/breeder (1=primary He/H2O, 2=secondary PbLi/Li). + temp_coolant_pump_outlet : float + Pump outlet temperature (K). + temp_coolant_pump_inlet : float + Pump inlet temperature (K). + pressure : float + Outlet (pump inlet) coolant pressure (Pa). + dpres_coolant : float + Coolant pressure drop (Pa). + mflow_coolant_total : float + Total coolant mass flow rate in (kg/s). + i_coolant_type : int + Type of FW/blanket coolant (e.g., 1=Helium, 2=Water) + den_coolant : float + Density of coolant or liquid breeder (kg/m³). + etaiso : float + Isentropic efficiency of the pump for primary coolant. + etaiso_liq : float + Isentropic efficiency of the pump for secondary coolant/breeder. + + Returns + ------- + float + Pumping power in MW. + + References + ---------- + - Idel'Cik, I. E. (1969), Memento des pertes de charge + - S.P. Sukhatme (2005), A Textbook on Heat Transfer + """ + # Pump outlet pressure (Pa) + # The pump adds the pressure lost going through the coolant channels back + pres_coolant_pump_outlet = pres_coolant_pump_inlet + dpres_coolant + + # Adiabatic index for helium or water + gamma = (5 / 3) if i_coolant_type == CoolantType.HELIUM else (4 / 3) + + # If calculating for primary coolant + if i_liquid_breeder == 1: + # The pumping power is be calculated in the most general way, + # using enthalpies before and after the pump. + + pump_outlet_fluid_properties = FluidProperties.of( + fluid_name=CoolantType(i_coolant_type).full_name, + temperature=temp_coolant_pump_outlet, + pressure=pres_coolant_pump_outlet, + ) + + # Assume isentropic pump so that s1 = s2 + s1 = pump_outlet_fluid_properties.entropy + + # Get specific enthalpy at the outlet (J/kg) before pump using pressure and entropy s1 + pump_inlet_fluid_properties = FluidProperties.of( + fluid_name=CoolantType(i_coolant_type).full_name, + pressure=pres_coolant_pump_inlet, + entropy=s1, + ) + + # Pumping power (MW) is given by enthalpy change, with a correction for + # the isentropic efficiency of the pump. + fp = ( + temp_coolant_pump_outlet + * ( + 1 + - (pres_coolant_pump_outlet / pres_coolant_pump_inlet) + ** -((gamma - 1) / gamma) + ) + / (etaiso * (temp_coolant_pump_inlet - temp_coolant_pump_outlet)) + ) + pumppower = ( + 1e-6 + * mflow_coolant_total + * ( + pump_outlet_fluid_properties.enthalpy + - pump_inlet_fluid_properties.enthalpy + ) + / etaiso + ) / (1 - fp) + + # If calculating for secondary coolant/breeder... + else: + # Calculate specific volume + spec_vol = 1 / den_coolant + + # Pumping power (MW) is given by pressure change, with a correction for + # the isentropic efficiency of the pump. + fp = ( + temp_coolant_pump_outlet + * ( + 1 + - (pres_coolant_pump_outlet / pres_coolant_pump_inlet) + ** -((gamma - 1) / gamma) + ) + / (etaiso_liq * (temp_coolant_pump_inlet - temp_coolant_pump_outlet)) + ) + pumppower = ( + 1e-6 * mflow_coolant_total * spec_vol * dpres_coolant / etaiso_liq + ) / (1 - fp) + + # Error for dpres_coolant too large + if fp >= 1: + raise ProcessValueError("Pressure drops in coolant are too large to be feasible") + + return pumppower + + def gnielinski_heat_transfer_coefficient( mflux_coolant: float, den_coolant: float, @@ -172,8 +515,12 @@ def gnielinski_heat_transfer_coefficient( return heat_transfer_coefficient +@staticmethod def calculate_reynolds_number( - den_coolant: float, vel_coolant: float, radius_channel: float, visc_coolant: float + den_coolant: float, + vel_coolant: float, + radius_channel: float, + visc_coolant: float, ) -> float: """Calculate Reynolds number for flow in a pipe. @@ -199,3 +546,103 @@ def calculate_reynolds_number( # Calculate Reynolds number return den_coolant * vel_coolant * diameter / visc_coolant + + +@staticmethod +def elbow_coeff( + radius_pipe_elbow: float, + deg_pipe_elbow: float, + darcy_friction: float, + dia_pipe: float, +) -> float: + """Calculates elbow bend coefficients for pressure drop calculations. + + Parameters + ---------- + radius_pipe_elbow : float + Pipe elbow radius (m) + deg_pipe_elbow : float + Pipe elbow angle (degrees) + darcy_friction : float + Darcy friction factor + dia_pipe : float + Pipe diameter (m) + + Returns + ------- + float + Elbow coefficient for pressure drop calculation + + References + ---------- + [1] Idel'Cik, I. E. (1969), Memento des pertes de charge, + Collection de la Direction des Etudes et Recherches d'Electricité de France. + """ + if deg_pipe_elbow == 90: + a = 1.0 + elif deg_pipe_elbow < 70: + a = 0.9 * np.sin(deg_pipe_elbow * np.pi / 180.0) + elif deg_pipe_elbow > 100: + a = 0.7 + (0.35 * np.sin((deg_pipe_elbow / 90.0) * (np.pi / 180.0))) + else: + raise ProcessValueError( + "No formula for 70 <= elbow angle(deg) <= 100, only 90 deg option available in this range." + ) + + r_ratio = radius_pipe_elbow / dia_pipe + + if r_ratio > 1: + b = 0.21 / r_ratio**0.5 + elif r_ratio < 1: + b = 0.21 / r_ratio**2.5 + else: + b = 0.21 + + # Singularity + ximt = a * b + + # Friction + xift = ( + (np.pi / 180.0) + * darcy_friction + * (radius_pipe_elbow / dia_pipe) + * deg_pipe_elbow + ) + + # Elbow Coefficient + return ximt + xift + + +@staticmethod +def calculate_required_mass_flow_rate( + p_heat_total: float, + heatcap_coolant: float, + temp_in_coolant: float, + temp_out_coolant: float, +) -> float: + """Calculate the required mass flow rate of coolant to remove the specified heat + load, due to the fundamental energy balance formula. + + Parameters + ---------- + p_heat_total: + Total heat load to be removed (W). + heatcap_coolant: + Specific heat capacity of the coolant (J/kg/K). + temp_in_coolant: + Inlet temperature of the coolant (K). + temp_out_coolant: + Outlet temperature of the coolant (K). + + Returns + ------- + float + Required mass flow rate of the coolant (kg/s). + + Notes + ----- + The heat capacity is assumed to be constant over the temperature range of the + coolant. + + """ + return p_heat_total / (heatcap_coolant * (temp_out_coolant - temp_in_coolant)) diff --git a/process/models/fw.py b/process/models/fw.py index 9f847fcf5f..6134f58e2a 100644 --- a/process/models/fw.py +++ b/process/models/fw.py @@ -22,6 +22,12 @@ logger = logging.getLogger(__name__) +N_FW_PIPE_90_DEG_BENDS = 2 +"Number of 90 degree bends in first wall coolant channels." +N_FW_PIPE_180_DEG_BENDS = 0 +"Number of 180 degree bends in first wall coolant channels." + + class FirstWall(Model): def __init__(self): self.outfile = constants.NOUT diff --git a/tests/unit/models/blankets/test_blanket_library.py b/tests/unit/models/blankets/test_blanket_library.py index acfdf502f0..44f3d1269a 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -18,6 +18,16 @@ def blanket_library(process_models): return process_models.blanket_library +@pytest.fixture +def pumping(process_models): + """Fixture to get the Physics instance from process_models. + + :returns: initialised Physics object + :rtype: process.physics.Physics + """ + return process_models.pumping + + class PrimaryCoolantPropertiesParam(NamedTuple): i_fw_coolant_type: Any = None @@ -29,9 +39,9 @@ class PrimaryCoolantPropertiesParam(NamedTuple): den_fw_coolant: Any = None - cp_fw: Any = None + heatcap_pres_fw_coolant_average: Any = None - cv_fw: Any = None + heatcap_vol_fw_coolant_average: Any = None i_blkt_coolant_type: Any = None @@ -47,9 +57,9 @@ class PrimaryCoolantPropertiesParam(NamedTuple): visc_blkt_coolant: Any = None - cp_bl: Any = None + heatcap_pres_blkt_coolant_average: Any = None - cv_bl: Any = None + heatcap_vol_blkt_coolant_average: Any = None visc_fw_coolant: Any = None @@ -81,8 +91,8 @@ class PrimaryCoolantPropertiesParam(NamedTuple): temp_fw_coolant_out=773, pres_fw_coolant=8000000, den_fw_coolant=0, - cp_fw=0, - cv_fw=0, + heatcap_pres_fw_coolant_average=0, + heatcap_vol_fw_coolant_average=0, i_blkt_coolant_type=CoolantType.HELIUM, temp_blkt_coolant_in=573, temp_blkt_coolant_out=773, @@ -90,8 +100,8 @@ class PrimaryCoolantPropertiesParam(NamedTuple): den_blkt_coolant=0, i_blkt_dual_coolant=2, visc_blkt_coolant=0, - cp_bl=0, - cv_bl=0, + heatcap_pres_blkt_coolant_average=0, + heatcap_vol_blkt_coolant_average=0, visc_fw_coolant=0, i_fw_blkt_shared_coolant=0, expected_den_fw_coolant=5.6389735407435868, @@ -109,8 +119,8 @@ class PrimaryCoolantPropertiesParam(NamedTuple): temp_fw_coolant_out=773, pres_fw_coolant=8000000, den_fw_coolant=5.6389735407435868, - cp_fw=5188.5588430173211, - cv_fw=3123.5687263525392, + heatcap_pres_fw_coolant_average=5188.5588430173211, + heatcap_vol_fw_coolant_average=3123.5687263525392, i_blkt_coolant_type=CoolantType.HELIUM, temp_blkt_coolant_in=573, temp_blkt_coolant_out=773, @@ -118,8 +128,8 @@ class PrimaryCoolantPropertiesParam(NamedTuple): den_blkt_coolant=5.6389735407435868, i_blkt_dual_coolant=2, visc_blkt_coolant=3.5036293160410249e-05, - cp_bl=5188.5588430173211, - cv_bl=3123.5687263525392, + heatcap_pres_blkt_coolant_average=5188.5588430173211, + heatcap_vol_blkt_coolant_average=3123.5687263525392, visc_fw_coolant=3.5036293160410249e-05, i_fw_blkt_shared_coolant=0, expected_den_fw_coolant=5.6389735407435868, @@ -179,11 +189,15 @@ def test_primary_coolant_properties( ) monkeypatch.setattr( - blanket_library.data.fwbs, "cp_fw", primarycoolantpropertiesparam.cp_fw + blanket_library.data.fwbs, + "heatcap_pres_fw_coolant_average", + primarycoolantpropertiesparam.heatcap_pres_fw_coolant_average, ) monkeypatch.setattr( - blanket_library.data.fwbs, "cv_fw", primarycoolantpropertiesparam.cv_fw + blanket_library.data.fwbs, + "heatcap_vol_fw_coolant_average", + primarycoolantpropertiesparam.heatcap_vol_fw_coolant_average, ) monkeypatch.setattr( @@ -229,11 +243,15 @@ def test_primary_coolant_properties( ) monkeypatch.setattr( - blanket_library.data.fwbs, "cp_bl", primarycoolantpropertiesparam.cp_bl + blanket_library.data.fwbs, + "heatcap_pres_blkt_coolant_average", + primarycoolantpropertiesparam.heatcap_pres_blkt_coolant_average, ) monkeypatch.setattr( - blanket_library.data.fwbs, "cv_bl", primarycoolantpropertiesparam.cv_bl + blanket_library.data.fwbs, + "heatcap_vol_blkt_coolant_average", + primarycoolantpropertiesparam.heatcap_vol_blkt_coolant_average, ) monkeypatch.setattr( @@ -254,11 +272,11 @@ def test_primary_coolant_properties( primarycoolantpropertiesparam.expected_den_fw_coolant, rel=1e-4 ) - assert blanket_library.data.fwbs.cp_fw == pytest.approx( + assert blanket_library.data.fwbs.heatcap_pres_fw_coolant_average == pytest.approx( primarycoolantpropertiesparam.expected_cp_fw, rel=1e-4 ) - assert blanket_library.data.fwbs.cv_fw == pytest.approx( + assert blanket_library.data.fwbs.heatcap_vol_fw_coolant_average == pytest.approx( primarycoolantpropertiesparam.expected_cv_fw, rel=1e-4 ) @@ -270,11 +288,11 @@ def test_primary_coolant_properties( primarycoolantpropertiesparam.expected_visc_blkt_coolant, rel=1e-4 ) - assert blanket_library.data.fwbs.cp_bl == pytest.approx( + assert blanket_library.data.fwbs.heatcap_pres_blkt_coolant_average == pytest.approx( primarycoolantpropertiesparam.expected_cp_bl, rel=1e-4 ) - assert blanket_library.data.fwbs.cv_bl == pytest.approx( + assert blanket_library.data.fwbs.heatcap_vol_blkt_coolant_average == pytest.approx( primarycoolantpropertiesparam.expected_cv_bl, rel=1e-4 ) @@ -301,10 +319,8 @@ def test_deltap_tot_inboard_first_wall(monkeypatch, blanket_library): "label": "Inboard first wall", } - assert ( - pytest.approx(blanket_library.total_pressure_drop(False, **data)) - == 5884.982168510442 - ) + dpres_total, _ = blanket_library.total_pressure_drop(False, **data) + assert dpres_total == pytest.approx(5884.982168510442) def test_deltap_tot_outboard_blanket_breeder_liquid(monkeypatch, blanket_library): @@ -332,54 +348,8 @@ def test_deltap_tot_outboard_blanket_breeder_liquid(monkeypatch, blanket_library "label": "Outboard blanket breeder liquid", } - assert ( - pytest.approx(blanket_library.total_pressure_drop(False, **data)) - == 56.95922064419226 - ) - - -def test_pumppower_primary_helium(monkeypatch, blanket_library): - monkeypatch.setattr(blanket_library.data.fwbs, "etaiso", 0.9) - monkeypatch.setattr(blanket_library.data.fwbs, "etaiso_liq", 0.85) - - data = { - "i_liquid_breeder": 2, - "temp_coolant_pump_outlet": 570, - "temp_coolant_pump_inlet": 720, - "pres_coolant_pump_inlet": 1700000, - "dpres_coolant": 303517.3, - "mflow_coolant_total": 35677.7, - "i_coolant_type": 1, - "den_coolant": 9753.25, - "label": "Liquid Metal Breeder/Coolant", - } - - assert ( - pytest.approx(blanket_library.coolant_pumping_power(False, **data)) - == 1.8251284651310427 - ) - - -def test_pumppower_secondary_pb_li(monkeypatch, blanket_library): - monkeypatch.setattr(blanket_library.data.fwbs, "etaiso", 0.9) - monkeypatch.setattr(blanket_library.data.fwbs, "etaiso_liq", 0.85) - - data = { - "i_liquid_breeder": 1, - "temp_coolant_pump_outlet": 573, - "temp_coolant_pump_inlet": 773, - "pres_coolant_pump_inlet": 8000000, - "dpres_coolant": 20088.23, - "mflow_coolant_total": 956.3, - "i_coolant_type": CoolantType.HELIUM, - "den_coolant": 5.64, - "label": "First Wall and Blanket", - } - - assert ( - pytest.approx(blanket_library.coolant_pumping_power(False, **data), rel=1e-4) - == 3.2374845432302464 - ) + dpres_total, _ = blanket_library.total_pressure_drop(False, **data) + assert dpres_total == pytest.approx(56.95922064419226) class ComponentHalfHeightParam(NamedTuple): @@ -1018,114 +988,6 @@ def test_liquid_breeder_properties( ) -class PressureDropParam(NamedTuple): - radius_fw_channel: Any = None - radius_pipe_90_deg_bend: Any = None - radius_pipe_180_deg_bend: Any = None - a_bz_liq: Any = None - b_bz_liq: Any = None - roughness_fw_channel: Any = None - ip: Any = None - i_ps: Any = None - num_90: Any = None - num_180: Any = None - l_pipe: Any = None - den: Any = None - vsc: Any = None - vv: Any = None - label: Any = None - expected_pressure_drop_out: Any = None - - -@pytest.mark.parametrize( - "pressuredropparam", - [ - PressureDropParam( - radius_fw_channel=0.0060000000000000001, - radius_pipe_90_deg_bend=0.018, - radius_pipe_180_deg_bend=0.09, - a_bz_liq=0.20000000000000001, - b_bz_liq=0.20000000000000001, - roughness_fw_channel=9.9999999999999995e-07, - ip=0, - i_ps=1, - num_90=2, - num_180=0, - l_pipe=4, - den=10.405276820718059, - vsc=3.604452999475736e-05, - vv=32.753134225223164, - label="Inboard first wall", - expected_pressure_drop_out=36213.58989742931, - ), - PressureDropParam( - radius_fw_channel=1.0, - radius_pipe_90_deg_bend=1.0, - radius_pipe_180_deg_bend=1.0, - a_bz_liq=1.0, - b_bz_liq=1.0, - roughness_fw_channel=1e-6, - ip=0, - i_ps=2, - num_90=1.0, - num_180=1.0, - l_pipe=1.0, - den=1.0, - vsc=1.0, - vv=1.0, - label="label", - expected_pressure_drop_out=1.4325633520224854, - ), - ], -) -def test_pressure_drop(pressuredropparam, monkeypatch, blanket_library): - """ - Automatically generated Regression Unit Test for pressure_drop. - - This test was generated using data from - blanket_files/large_tokamak_primary_pumping2.IN.DAT. - - :param pressuredropparam: the data used to mock and assert in this test. - :type pressuredropparam: pressuredropparam - - :param monkeypatch: pytest fixture used to mock module/class variables - :type monkeypatch: _pytest.monkeypatch.monkeypatch - """ - monkeypatch.setattr( - blanket_library.data.fwbs, - "radius_fw_channel", - pressuredropparam.radius_fw_channel, - ) - monkeypatch.setattr( - blanket_library.data.fwbs, "a_bz_liq", pressuredropparam.a_bz_liq - ) - monkeypatch.setattr( - blanket_library.data.fwbs, "b_bz_liq", pressuredropparam.b_bz_liq - ) - monkeypatch.setattr( - blanket_library.data.fwbs, - "roughness_fw_channel", - pressuredropparam.roughness_fw_channel, - ) - - pressure_drop_out = blanket_library.coolant_friction_pressure_drop( - i_ps=pressuredropparam.i_ps, - radius_pipe_90_deg_bend=pressuredropparam.radius_pipe_90_deg_bend, - radius_pipe_180_deg_bend=pressuredropparam.radius_pipe_180_deg_bend, - n_pipe_90_deg_bends=pressuredropparam.num_90, - n_pipe_180_deg_bends=pressuredropparam.num_180, - len_pipe=pressuredropparam.l_pipe, - den_coolant=pressuredropparam.den, - visc_coolant=pressuredropparam.vsc, - vel_coolant=pressuredropparam.vv, - label=pressuredropparam.label, - ) - - assert pressure_drop_out == pytest.approx( - pressuredropparam.expected_pressure_drop_out - ) - - class LiquidBreederPressureDropMhdParam(NamedTuple): i_blkt_liquid_breeder_channel_type: Any = None a_bz_liq: Any = None @@ -1590,43 +1452,6 @@ def test_calculate_elliptical_blkt_volumes( ) -def test_hydraulic_diameter(monkeypatch, blanket_library): - """ - Test for hydraulic_diameter function. - """ - # Set var values - monkeypatch.setattr(blanket_library.data.fwbs, "radius_fw_channel", 1.0) - monkeypatch.setattr(blanket_library.data.fwbs, "a_bz_liq", 1.0) - monkeypatch.setattr(blanket_library.data.fwbs, "b_bz_liq", 1.0) - - # hydraulic_diameter input = i_channel_shape: 1 = circle, 2 = rectangle - # 2.0D0*radius_fw_channel - assert blanket_library.pipe_hydraulic_diameter(1) == pytest.approx(2.0) - # 2*a_bz_liq*b_bz_liq/(a_bz_liq+b_bz_liq) - assert blanket_library.pipe_hydraulic_diameter(2) == pytest.approx(1.0) - - -def test_elbow_coeff(blanket_library): - """ - Test for elbow_coeff function. - """ - # input = r_elbow, ang_elbow, lambda, dh - assert blanket_library.elbow_coeff(1, 0, 1, 1) == pytest.approx(0.0, rel=1e-3) - assert blanket_library.elbow_coeff(1, 90, 1, 1) == pytest.approx( - 1.7807963267948965, rel=1e-3 - ) - assert blanket_library.elbow_coeff(1, 180, 1, 1) == pytest.approx( - 3.291157766597427, rel=1e-3 - ) - assert blanket_library.elbow_coeff(1, 90, 1, 0.1) == pytest.approx( - 15.774371098812502, rel=1e-3 - ) - assert blanket_library.elbow_coeff(0.1, 90, 1, 1) == pytest.approx(66.57, rel=1e-3) - assert blanket_library.elbow_coeff(1, 90, 0.1, 1) == pytest.approx( - 0.3670796326794896, rel=1e-3 - ) - - def test_flow_velocity(monkeypatch, blanket_library): """ Test for flow_velocity function. diff --git a/tests/unit/models/engineering/test_pumping.py b/tests/unit/models/engineering/test_pumping.py index 6b1898bb1a..ac5a6f9fbd 100644 --- a/tests/unit/models/engineering/test_pumping.py +++ b/tests/unit/models/engineering/test_pumping.py @@ -1,12 +1,31 @@ +from typing import Any, NamedTuple + import pytest from process.models.engineering.pumping import ( + CoolantFrictionLossParameters, + CoolantType, + calculate_required_mass_flow_rate, calculate_reynolds_number, + coolant_friction_pressure_drop, + coolant_pumping_power, darcy_friction_haaland, + elbow_coeff, gnielinski_heat_transfer_coefficient, + pipe_hydraulic_diameter, ) +@pytest.fixture +def blanket_library(process_models): + """Provides BlanketLibrary object for testing. + + :returns: initialised BlanketLibrary object + :rtype: process.blanket_library.BlanketLibrary + """ + return process_models.blanket_library + + def test_darcy_friction_haaland(): assert darcy_friction_haaland( reynolds=5500, roughness_channel=1e-6, radius_channel=0.1 @@ -33,3 +52,198 @@ def test_calculate_reynolds_number(): radius_channel=0.0060000000000000001, visc_coolant=4.0416219836935569e-05, ) == pytest.approx(33302.602975971815) + + +def test_elbow_coeff(): + """ + Test for elbow_coeff function. + """ + # input = r_elbow, ang_elbow, lambda, dh + assert elbow_coeff(1, 0, 1, 1) == pytest.approx(0.0, rel=1e-3) + assert elbow_coeff(1, 90, 1, 1) == pytest.approx(1.7807963267948965, rel=1e-3) + assert elbow_coeff(1, 180, 1, 1) == pytest.approx(3.291157766597427, rel=1e-3) + assert elbow_coeff(1, 90, 1, 0.1) == pytest.approx(15.774371098812502, rel=1e-3) + assert elbow_coeff(0.1, 90, 1, 1) == pytest.approx(66.57, rel=1e-3) + assert elbow_coeff(1, 90, 0.1, 1) == pytest.approx(0.3670796326794896, rel=1e-3) + + +def test_calculate_required_mass_flow_rate(): + assert calculate_required_mass_flow_rate( + p_heat_total=1000.0, + heatcap_coolant=100.0, + temp_in_coolant=300.0, + temp_out_coolant=310.0, + ) == pytest.approx(1.0) + + +def test_calculate_required_mass_flow_rate_with_realistic_values(): + assert calculate_required_mass_flow_rate( + p_heat_total=50000.0, + heatcap_coolant=4180.0, + temp_in_coolant=293.15, + temp_out_coolant=313.15, + ) == pytest.approx(0.5980861244019139) + + +def test_calculate_required_mass_flow_rate_zero_temperature_rise(): + with pytest.raises(ZeroDivisionError): + calculate_required_mass_flow_rate( + p_heat_total=1000.0, + heatcap_coolant=4180.0, + temp_in_coolant=300.0, + temp_out_coolant=300.0, + ) + + +def test_hydraulic_diameter(): + """ + Test for hydraulic_diameter function. + """ + + # hydraulic_diameter input = i_channel_shape: 1 = circle, 2 = rectangle + # 2.0D0*radius_fw_channel + assert pipe_hydraulic_diameter( + 1, radius_fw_channel=1.0, a_bz_liq=1.0, b_bz_liq=1.0 + ) == pytest.approx(2.0) + # 2*a_bz_liq*b_bz_liq/(a_bz_liq+b_bz_liq) + assert pipe_hydraulic_diameter( + 2, radius_fw_channel=1.0, a_bz_liq=1.0, b_bz_liq=1.0 + ) == pytest.approx(1.0) + + +class CoolantFrictionLossParam(NamedTuple): + radius_channel: Any = None + radius_pipe_90_deg_bend: Any = None + radius_pipe_180_deg_bend: Any = None + a_bz_liq: Any = None + b_bz_liq: Any = None + roughness_channel: Any = None + i_ps: Any = None + n_pipe_90_deg_bends: Any = None + n_pipe_180_deg_bends: Any = None + len_pipe: Any = None + den_coolant: Any = None + visc_coolant: Any = None + vel_coolant: Any = None + label: Any = None + expected_pressure_drop_out: Any = None + + +@pytest.mark.parametrize( + "coolantfrictionlossparam", + [ + CoolantFrictionLossParam( + radius_channel=0.0060000000000000001, + radius_pipe_90_deg_bend=0.018, + radius_pipe_180_deg_bend=0.09, + a_bz_liq=0.20000000000000001, + b_bz_liq=0.20000000000000001, + roughness_channel=9.9999999999999995e-07, + i_ps=1, + n_pipe_90_deg_bends=2, + n_pipe_180_deg_bends=0, + len_pipe=4, + den_coolant=10.405276820718059, + visc_coolant=3.604452999475736e-05, + vel_coolant=32.753134225223164, + label="Inboard first wall", + expected_pressure_drop_out=36213.58989742931, + ), + CoolantFrictionLossParam( + radius_channel=1.0, + radius_pipe_90_deg_bend=1.0, + radius_pipe_180_deg_bend=1.0, + a_bz_liq=1.0, + b_bz_liq=1.0, + roughness_channel=1e-6, + i_ps=2, + n_pipe_90_deg_bends=1.0, + n_pipe_180_deg_bends=1.0, + len_pipe=1.0, + den_coolant=1.0, + visc_coolant=1.0, + vel_coolant=1.0, + label="label", + expected_pressure_drop_out=1.4325633520224854, + ), + ], +) +def test_coolant_friction_loss(coolantfrictionlossparam, monkeypatch, blanket_library): + """ + Automatically generated Regression Unit Test for coolant_friction_loss. + + This test was generated using data from + blanket_files/large_tokamak_primary_pumping2.IN.DAT. + + Parameters + ---------- + coolantfrictionlossparam : CoolantFrictionLossParam + the data used to mock and assert in this test. + monkeypatch : _pytest.monkeypatch.monkeypatch + pytest fixture used to mock module/class variables + blanket_library : BlanketLibrary + the blanket library instance used in this test. + + """ + monkeypatch.setattr( + blanket_library.data.fwbs, "a_bz_liq", coolantfrictionlossparam.a_bz_liq + ) + monkeypatch.setattr( + blanket_library.data.fwbs, "b_bz_liq", coolantfrictionlossparam.b_bz_liq + ) + + pressure_params: CoolantFrictionLossParameters = coolant_friction_pressure_drop( + i_ps=coolantfrictionlossparam.i_ps, + radius_pipe_90_deg_bend=(coolantfrictionlossparam.radius_pipe_90_deg_bend), + radius_pipe_180_deg_bend=(coolantfrictionlossparam.radius_pipe_180_deg_bend), + n_pipe_90_deg_bends=coolantfrictionlossparam.n_pipe_90_deg_bends, + n_pipe_180_deg_bends=coolantfrictionlossparam.n_pipe_180_deg_bends, + len_pipe=coolantfrictionlossparam.len_pipe, + den_coolant=coolantfrictionlossparam.den_coolant, + visc_coolant=coolantfrictionlossparam.visc_coolant, + vel_coolant=coolantfrictionlossparam.vel_coolant, + roughness_channel=coolantfrictionlossparam.roughness_channel, + radius_channel=coolantfrictionlossparam.radius_channel, + a_bz_liq=coolantfrictionlossparam.a_bz_liq, + b_bz_liq=coolantfrictionlossparam.b_bz_liq, + ) + + assert pressure_params.dpres_total == pytest.approx( + coolantfrictionlossparam.expected_pressure_drop_out + ) + + +def test_pumppower_primary_helium(): + + data = { + "i_liquid_breeder": 2, + "temp_coolant_pump_outlet": 570, + "temp_coolant_pump_inlet": 720, + "pres_coolant_pump_inlet": 1700000, + "dpres_coolant": 303517.3, + "mflow_coolant_total": 35677.7, + "i_coolant_type": CoolantType.HELIUM, + "den_coolant": 9753.25, + "etaiso": 0.9, + "etaiso_liq": 0.85, + } + + assert pytest.approx(coolant_pumping_power(**data)) == 1.8251284651310427 + + +def test_pumppower_secondary_pb_li(): + + data = { + "i_liquid_breeder": 1, + "temp_coolant_pump_outlet": 573, + "temp_coolant_pump_inlet": 773, + "pres_coolant_pump_inlet": 8000000, + "dpres_coolant": 20088.23, + "mflow_coolant_total": 956.3, + "i_coolant_type": CoolantType.HELIUM, + "den_coolant": 5.64, + "etaiso": 0.9, + "etaiso_liq": 0.85, + } + + assert pytest.approx(coolant_pumping_power(**data), rel=1e-4) == 3.2374845432302464