From 095a1f4ee79dfa79688ec7252b88712686e88844 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 4 Jun 2026 10:27:09 +0100 Subject: [PATCH 01/40] Move elbow coeffieicnt calc from blanket to pumping --- .../source/eng-models/blanket_overview.md | 22 ------ .../eng-models/generic_methods/pumping.md | 28 ++++++- process/models/blankets/blanket_library.py | 76 +------------------ process/models/engineering/pumping.py | 66 ++++++++++++++++ .../models/blankets/test_blanket_library.py | 21 ----- tests/unit/models/engineering/test_pumping.py | 14 ++++ 6 files changed, 110 insertions(+), 117 deletions(-) diff --git a/documentation/source/eng-models/blanket_overview.md b/documentation/source/eng-models/blanket_overview.md index 66a84b15f7..fa0de10aa6 100644 --- a/documentation/source/eng-models/blanket_overview.md +++ b/documentation/source/eng-models/blanket_overview.md @@ -78,27 +78,5 @@ $$ ------------------- -### 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..7bd3a1dc4e 100644 --- a/documentation/source/eng-models/generic_methods/pumping.md +++ b/documentation/source/eng-models/generic_methods/pumping.md @@ -61,4 +61,30 @@ 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) +$$ \ No newline at end of file diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index ec293bed12..b4a154e363 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -24,6 +24,7 @@ CoolantType, calculate_reynolds_number, darcy_friction_haaland, + elbow_coeff, ) from process.models.power import PumpingPowerModelTypes @@ -3243,7 +3244,7 @@ def coolant_friction_pressure_drop( f_straight = darcy_friction_factor * len_pipe / dia_pipe # 90 degree elbow pressure drop coefficient - f_elbow_90 = self.elbow_coeff( + f_elbow_90 = elbow_coeff( radius_pipe_elbow=radius_pipe_90_deg_bend, deg_pipe_elbow=90.0, darcy_friction=darcy_friction_factor, @@ -3251,7 +3252,7 @@ def coolant_friction_pressure_drop( ) # 180 degree elbow pressure drop coefficient - f_elbow_180 = self.elbow_coeff( + f_elbow_180 = elbow_coeff( radius_pipe_elbow=radius_pipe_180_deg_bend, deg_pipe_elbow=180.0, darcy_friction=darcy_friction_factor, @@ -3367,77 +3368,6 @@ def pipe_hydraulic_diameter(self, i_channel_shape): f"i_channel_shape ={i_channel_shape} is an invalid option." ) - @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 - - Raises - ------ - ProcessValueError - If 70 <= elbow angle(deg) <= 100 as there is no formula - for this range - - 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 - - # 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 - def coolant_pumping_power( self, output: bool, diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index ea6c6fb0c8..30a9d739f9 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -41,6 +41,8 @@ def full_name(self): return self._full_name_ +from process.core.exceptions import ProcessValueError + logger = logging.getLogger(__name__) @@ -200,3 +202,67 @@ def calculate_reynolds_number( # Calculate Reynolds number return den_coolant * vel_coolant * diameter / visc_coolant + + +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 diff --git a/tests/unit/models/blankets/test_blanket_library.py b/tests/unit/models/blankets/test_blanket_library.py index 3f05117eff..168c165dfa 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -1606,27 +1606,6 @@ def test_hydraulic_diameter(monkeypatch, blanket_library): 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..5e85511dda 100644 --- a/tests/unit/models/engineering/test_pumping.py +++ b/tests/unit/models/engineering/test_pumping.py @@ -3,6 +3,7 @@ from process.models.engineering.pumping import ( calculate_reynolds_number, darcy_friction_haaland, + elbow_coeff, gnielinski_heat_transfer_coefficient, ) @@ -33,3 +34,16 @@ 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) From 3c6c1ec3f871c0d4f0928a8a2920cfc898b1e73b Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 4 Jun 2026 10:40:41 +0100 Subject: [PATCH 02/40] Add coolant properties to BlanketData class --- process/data_structure/blanket_variables.py | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/process/data_structure/blanket_variables.py b/process/data_structure/blanket_variables.py index 05d2b9c7cf..cff770b8e8 100644 --- a/process/data_structure/blanket_variables.py +++ b/process/data_structure/blanket_variables.py @@ -200,5 +200,35 @@ 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""" + + 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 From ea697158726a6c1de487a84106ee637675817351 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 4 Jun 2026 16:21:28 +0100 Subject: [PATCH 03/40] Add coolant friction loss parameters and output functions for blanket models --- process/data_structure/blanket_variables.py | 42 ++ process/main.py | 2 + process/models/blankets/blanket_library.py | 478 ++++++++++++------ process/models/engineering/pumping.py | 33 ++ .../models/blankets/test_blanket_library.py | 1 - 5 files changed, 403 insertions(+), 153 deletions(-) diff --git a/process/data_structure/blanket_variables.py b/process/data_structure/blanket_variables.py index cff770b8e8..43452b356e 100644 --- a/process/data_structure/blanket_variables.py +++ b/process/data_structure/blanket_variables.py @@ -224,6 +224,48 @@ class BlanketData: 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)""" diff --git a/process/main.py b/process/main.py index d99959837f..9958897b0c 100644 --- a/process/main.py +++ b/process/main.py @@ -965,6 +965,8 @@ def write(self, data, _outfile): # DCLL model self.dcll.output() + self.blanket_library.output_blkt_pumping_variables() + # FISPACT and LOCA model (not used)- removed # Power model diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index b4a154e363..d85aebb356 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -22,6 +22,7 @@ ) from process.models.engineering.pumping import ( CoolantType, + CoolantFrictionLossParameters, calculate_reynolds_number, darcy_friction_haaland, elbow_coeff, @@ -1425,7 +1426,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, fw_inboard_friction_params = self.total_pressure_drop( output, icoolpump=1, vel_coolant=vel_fw_inboard_coolant, @@ -1440,19 +1441,21 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): label="Inboard first wall", ) - dpres_fw_outboard_coolant = self.total_pressure_drop( - output, - icoolpump=1, - vel_coolant=vel_fw_outboard_coolant, - len_pipe=self.data.fwbs.len_fw_channel, - n_pipe_90_deg_bends=N_FW_PIPE_90_DEG_BENDS, - n_pipe_180_deg_bends=N_FW_PIPE_180_DEG_BENDS, - den_coolant=self.data.fwbs.den_fw_coolant, - visc_coolant_dynamic=self.data.fwbs.visc_fw_coolant, - coolant_electrical_conductivity=0.0e0, - pol_channel_length=pollengo, - nopolchan=npoltoto, - label="Outboard first wall", + dpres_fw_outboard_coolant, fw_outboard_friction_params = ( + self.total_pressure_drop( + output, + icoolpump=1, + vel_coolant=vel_fw_outboard_coolant, + len_pipe=self.data.fwbs.len_fw_channel, + n_pipe_90_deg_bends=N_FW_PIPE_90_DEG_BENDS, + n_pipe_180_deg_bends=N_FW_PIPE_180_DEG_BENDS, + den_coolant=self.data.fwbs.den_fw_coolant, + visc_coolant_dynamic=self.data.fwbs.visc_fw_coolant, + coolant_electrical_conductivity=0.0e0, + pol_channel_length=pollengo, + nopolchan=npoltoto, + label="Outboard first wall", + ) ) # BB Presure Drops ############### @@ -1473,43 +1476,85 @@ 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", + ) ) # 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, @@ -1527,7 +1572,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, @@ -2473,6 +2518,7 @@ def thermo_hydraulic_model(self, output: bool): deltap = self.thermo_hydraulic_model_pressure_drop_calculations( output=output ) + print(deltap) deltap_fwi = deltap[0] deltap_fwo = deltap[1] deltap_blo = deltap[2] @@ -2903,9 +2949,8 @@ def total_pressure_drop( pol_channel_length: float, nopolchan: int, label: str, - ) -> float: - """Calculate the total pressure drop (Pa) for coolant flow in the - first wall (FW) and breeding blanket (BZ). + ) -> 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. @@ -2939,8 +2984,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, @@ -2949,19 +2994,20 @@ def total_pressure_drop( ) # Friction - for all coolants - dpres_friction = self.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, - n_pipe_90_deg_bends=n_pipe_90_deg_bends, - n_pipe_180_deg_bends=n_pipe_180_deg_bends, - len_pipe=len_pipe, - den_coolant=den_coolant, - visc_coolant=visc_coolant_dynamic, - vel_coolant=vel_coolant, - label=label, - output=output, + friction_params: CoolantFrictionLossParameters = ( + self.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, + n_pipe_90_deg_bends=n_pipe_90_deg_bends, + n_pipe_180_deg_bends=n_pipe_180_deg_bends, + len_pipe=len_pipe, + den_coolant=den_coolant, + visc_coolant=visc_coolant_dynamic, + vel_coolant=vel_coolant, + ) ) + dpres_friction = friction_params.dpres_total if icoolpump == 2: dpres_mhd = self.liquid_breeder_mhd_pressure_drop( @@ -2979,22 +3025,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, @@ -3168,9 +3199,7 @@ def coolant_friction_pressure_drop( den_coolant: float, visc_coolant: float, vel_coolant: float, - label: str, - output: bool = False, - ): + ) -> 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 @@ -3197,12 +3226,23 @@ def coolant_friction_pressure_drop( coolant viscosity (Pa s) vel_coolant : coolant flow velocity (m/s) - label : - component name - output : - boolean of whether to write data to output file - :Notes: + 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 @@ -3263,78 +3303,31 @@ def coolant_friction_pressure_drop( 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 - ) - - # 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 ", - ) + 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 - # 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 ", - ) + dpres_bends_total = dpres_90_total + dpres_180_total - return dpres_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(self, i_channel_shape): """Caculate the hydraulic diameter (m) for a given coolant pipe size/shape. @@ -3547,6 +3540,187 @@ def coolant_pumping_power( return pumppower + def output_blkt_pumping_variables(self): + + po.oheadr(self.outfile, "Blanket pumping variables") + + po.osubhd(self.outfile, "Inboard Blanket") + + 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 (toroidal direction) (m)", + "(len_blkt_inboard_segment_toroidal)", + self.data.blanket.len_blkt_inboard_segment_toroidal, + "OP ", + ) + 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.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.osubhd(self.outfile, "Outboard Blanket") + + 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 ", + ) + po.ovarre( + self.outfile, + "Outboard blanket coolant channel length (toroidal direction) (m)", + "(len_blkt_outboard_segment_toroidal)", + self.data.blanket.len_blkt_outboard_segment_toroidal, + "OP ", + ) + 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 ", + ) + 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 ", + ) + 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 ", + ) + po.oblnkl(self.outfile) + po.ocmmnt(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 ", + ) + + 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 ", + ) + + 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 ", + ) + + 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) + + 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 ", + ) + + 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 ", + ) + + 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 ", + ) + + 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 ", + ) + class OutboardBlanket(BlanketLibrary): """Outboard blanket routines""" diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index 30a9d739f9..f5c2971644 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -3,6 +3,7 @@ import logging from enum import IntEnum, unique from types import DynamicClassAttribute +from dataclasses import dataclass import numpy as np @@ -46,6 +47,38 @@ def full_name(self): 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 darcy_friction_haaland( reynolds: float, roughness_channel: float, radius_channel: float ) -> float: diff --git a/tests/unit/models/blankets/test_blanket_library.py b/tests/unit/models/blankets/test_blanket_library.py index 168c165dfa..3070e24bb9 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -1118,7 +1118,6 @@ def test_pressure_drop(pressuredropparam, monkeypatch, blanket_library): den_coolant=pressuredropparam.den, visc_coolant=pressuredropparam.vsc, vel_coolant=pressuredropparam.vv, - label=pressuredropparam.label, ) assert pressure_drop_out == pytest.approx( From dd1779a6ebc86bd0e31bec21c2924d4900833051 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 4 Jun 2026 16:40:38 +0100 Subject: [PATCH 04/40] Add function to plot blanket coolant channel structure and update main plot --- process/core/io/plot/summary.py | 246 ++++++++++++++++++++- process/models/blankets/blanket_library.py | 1 - process/models/blankets/hcpb.py | 47 ++++ 3 files changed, 288 insertions(+), 6 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 6d1379d3d5..297b9e7c8b 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -16461,7 +16461,241 @@ def plot_cs_von_mises_2d_contour( axis.set_xlim(r_cs_inner * 0.9, r_cs_outer * 1.1) axis.set_ylim((-dz_cs_full / 2) * 1.1, (dz_cs_full / 2) * 1.1) axis.grid(True, alpha=0.3) - axis.set_title("CS Von Mises Stress Contour at BOP") + axis.set_title("CS Radial Stress at BOP") + axis.legend(loc="best") + + +def plot_blanket_coolant_channel_structure(self, m_file: MFile, scan: int): + """Plot a schematic of the blanket coolant channel routing.""" + len_blkt_outboard_coolant_channel_radial = float( + m_file.get("len_blkt_outboard_coolant_channel_radial", scan=scan) + ) + len_blkt_outboard_segment_toroidal = float( + m_file.get("len_blkt_outboard_segment_toroidal", scan=scan) + ) + n_blkt_outboard_module_coolant_sections_radial = max( + 1, + int( + round( + m_file.get("n_blkt_outboard_module_coolant_sections_radial", scan=scan) + ) + ), + ) + n_blkt_outboard_module_coolant_sections_poloidal = max( + 1, + int( + round( + m_file.get("n_blkt_outboard_module_coolant_sections_poloidal", scan=scan) + ) + ), + ) + elbow_radius_180 = max( + float(m_file.get("radius_blkt_channel_180_bend", scan=scan)), 1e-6 + ) + r = max(float(m_file.get("radius_blkt_channel", scan=scan)), 1e-6) + elbow_radius_90 = max( + float(m_file.get("radius_blkt_channel_90_bend", scan=scan)), 1e-6 + ) + + axis = self if isinstance(self, Axes) else getattr(self, "ax", None) + figure = axis.figure if isinstance(axis, Axes) else None + + if not isinstance(axis, Axes): + figure, axis = plt.subplots(figsize=(10, 6)) + + def _plot_straight( + x0: float, + y0: float, + x1: float, + y1: float, + *, + label: str | None = None, + alpha: float = 1.0, + ): + axis.plot( + [x0, x1], + [y0, y1], + color="tab:blue", + linewidth=2.5, + solid_capstyle="round", + alpha=alpha, + label=label, + ) + + def _plot_bend( + centre_x: float, + centre_y: float, + radius: float, + theta_start: float, + theta_end: float, + *, + label: str | None = None, + alpha: float = 1.0, + ): + theta = np.linspace(theta_start, theta_end, 80) + axis.plot( + centre_x + radius * np.cos(theta), + centre_y + radius * np.sin(theta), + color="tab:orange", + linewidth=2.5, + solid_capstyle="round", + alpha=alpha, + label=label, + ) + + lane_pitch = 2.0 * elbow_radius_180 + poloidal_section_length = max(4.0 * r, 1.5 * elbow_radius_90) + + show_radial_label = True + show_poloidal_label = True + show_90_label = True + + inlet_x = 0.0 + x_left = elbow_radius_90 + x_right = elbow_radius_90 + len_blkt_outboard_coolant_channel_radial + y_lower = 0.0 + y_upper = y_lower + lane_pitch + y_inlet_lower = y_lower - elbow_radius_90 - poloidal_section_length + y_outlet_upper = y_upper + elbow_radius_90 + poloidal_section_length + + _plot_straight( + inlet_x, + y_inlet_lower, + inlet_x, + y_lower - elbow_radius_90, + label="Straight poloidal section" if show_poloidal_label else None, + ) + show_poloidal_label = False + + _plot_bend( + elbow_radius_90, + y_lower - elbow_radius_90, + elbow_radius_90, + np.pi, + np.pi / 2.0, + label="90° bend" if show_90_label else None, + ) + show_90_label = False + + _plot_straight( + x_left, + y_lower, + x_right, + y_lower, + label="Straight radial section" if show_radial_label else None, + ) + show_radial_label = False + + _plot_bend( + x_right, + y_lower + elbow_radius_180, + elbow_radius_180, + -np.pi / 2.0, + np.pi / 2.0, + label="180° bend", + ) + + _plot_straight( + x_right, + y_upper, + x_left, + y_upper, + label="Straight radial section" if show_radial_label else None, + ) + + _plot_bend( + x_left, + y_upper + elbow_radius_90, + elbow_radius_90, + 3.0 * np.pi / 2.0, + np.pi, + label="90° bend" if show_90_label else None, + ) + + _plot_straight( + inlet_x, + y_upper + elbow_radius_90, + inlet_x, + y_outlet_upper, + label="Straight poloidal section" if show_poloidal_label else None, + ) + + total_180_bends = 1 + total_90_bends = 2 + total_poloidal_sections = 2 + length_per_90_bend = 0.5 * np.pi * elbow_radius_90 + length_per_180_bend = np.pi * elbow_radius_180 + length_per_poloidal_section = poloidal_section_length + total_radial_channel_length = ( + n_blkt_outboard_module_coolant_sections_radial + * len_blkt_outboard_coolant_channel_radial + ) + total_poloidal_length = total_poloidal_sections * length_per_poloidal_section + total_bend_length = ( + total_90_bends * length_per_90_bend + total_180_bends * length_per_180_bend + ) + total_channel_length = ( + total_radial_channel_length + total_poloidal_length + total_bend_length + ) + info = textwrap.dedent( + f""" + Representative continuous channel loop + Straight radial sections: {n_blkt_outboard_module_coolant_sections_radial} + Straight radial length per channel: {total_radial_channel_length:.2f} m + Straight poloidal sections per loop: {total_poloidal_sections} + Straight poloidal length: {length_per_poloidal_section:.2f} m each + 90° bends per channel: {total_90_bends} + 90° bend length: {length_per_90_bend:.2f} m each + 180° bends per channel: {total_180_bends} + 180° bend length: {length_per_180_bend:.2f} m each + Total poloidal length per channel: {total_poloidal_length:.2f} m + Total bend length per channel: {total_bend_length:.2f} m + Approx. total channel length: {total_channel_length:.2f} m + Poloidal sections per module: {n_blkt_outboard_module_coolant_sections_poloidal} + Toroidal span per segment: {len_blkt_outboard_segment_toroidal:.2f} m + Channel radius: {r:.3f} m + """ + ).strip() + + axis.text( + 1.02, + 0.98, + info, + transform=axis.transAxes, + va="top", + ha="left", + fontsize=8, + bbox={"boxstyle": "round", "facecolor": "white", "alpha": 0.9}, + ) + + axis.text( + 0.02, + 0.02, + "Continuous loop schematic with a single 180° return bend", + transform=axis.transAxes, + fontsize=8, + color="dimgray", + ) + + axis.set_title("Blanket coolant channel structure") + axis.set_xlabel("Radial direction [m]") + axis.set_ylabel("Channel layout [schematic m]") + axis.set_aspect("equal", adjustable="box") + axis.grid(True, alpha=0.25) + axis.minorticks_on() + axis.legend(fontsize=8, loc="upper right") + + x_margin = len_blkt_outboard_coolant_channel_radial * 0.1 + 2.0 * elbow_radius_90 + axis.set_xlim(-x_margin, x_right + x_margin) + axis.set_ylim( + y_inlet_lower - r, + y_outlet_upper + r, + ) + + if figure is not None: + figure.tight_layout() + + return axis def plot_pf_dimensions( @@ -17470,10 +17704,10 @@ def _add_page(name: str | None = None): ax24 = _add_page("power_profile_over_time").add_subplot(111) # set_position([left, bottom, width, height]) -> height ~ 0.66 => ~2/3 of page height ax24.set_position([0.08, 0.35, 0.84, 0.57]) - plot_system_power_profiles_over_time( - ax24, m_file, scan, pages["power_profile_over_time"] - ) - return list(pages.values()) + plot_system_power_profiles_over_time(ax24, m_file, scan, figs[36]) + + plot_blanket_coolant_channel_structure(figs[37].add_subplot(111), m_file, scan) + def create_thickness_builds(m_file, scan: int): @@ -17580,6 +17814,8 @@ def add_page_footer( # create main plot # Increase range when adding new page + pages = [plt.figure(figsize=(12, 9), dpi=80) for i in range(38)] + # run main_plot mfile_obj = MFile(mfile) if mfile else MFile("MFILE.DAT") run_label = f"{mfile_obj.get('fileprefix', scan=-1)} | scan {scan or -1} | {mfile_obj.get('date', scan=-1)} {mfile_obj.get('time', scan=-1)} | {mfile_obj.get('tagno', scan=-1)} | Branch: {mfile_obj.get('branch_name', scan=-1)} " diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index d85aebb356..d62e3e542f 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -2518,7 +2518,6 @@ def thermo_hydraulic_model(self, output: bool): deltap = self.thermo_hydraulic_model_pressure_drop_calculations( output=output ) - print(deltap) deltap_fwi = deltap[0] deltap_fwo = deltap[1] deltap_blo = deltap[2] diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 5a300557f4..935017e6f2 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -852,6 +852,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, + ) case PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP: # Issue #503 From f1dd90d63ac025caec2211637934d5cfe73b023f Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 5 Jun 2026 10:58:10 +0100 Subject: [PATCH 05/40] Refactor pressure drop assertions in blanket tests for clarity and consistency --- .../unit/models/blankets/test_blanket_library.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/tests/unit/models/blankets/test_blanket_library.py b/tests/unit/models/blankets/test_blanket_library.py index 3070e24bb9..cb1bf19180 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -301,10 +301,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,10 +330,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 - ) + dpres_total, _ = blanket_library.total_pressure_drop(False, **data) + assert dpres_total == pytest.approx(56.95922064419226) def test_pumppower_primary_helium(monkeypatch, blanket_library): @@ -1108,7 +1104,7 @@ def test_pressure_drop(pressuredropparam, monkeypatch, blanket_library): pressuredropparam.roughness_fw_channel, ) - pressure_drop_out = blanket_library.coolant_friction_pressure_drop( + pressure_params = 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, @@ -1120,7 +1116,7 @@ def test_pressure_drop(pressuredropparam, monkeypatch, blanket_library): vel_coolant=pressuredropparam.vv, ) - assert pressure_drop_out == pytest.approx( + assert pressure_params.dpres_total == pytest.approx( pressuredropparam.expected_pressure_drop_out ) From 1fd6bc0cde545c1055795adb0220e52eeb233378 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 5 Jun 2026 14:30:55 +0100 Subject: [PATCH 06/40] Add function to plot outboard blanket coolant properties along the poloidal direction --- process/core/io/plot/summary.py | 100 ++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 297b9e7c8b..6a63d4c0bc 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -7,11 +7,14 @@ from pathlib import Path from typing import Any, Literal +import CoolProp import matplotlib as mpl import matplotlib.backends.backend_pdf as bpdf import matplotlib.image as mpimg import matplotlib.pyplot as plt import numpy as np +from CoolProp.CoolProp import PropsSI +from CoolProp.Plots import PropertyPlot from matplotlib import patches from matplotlib.patches import Circle, Rectangle from matplotlib.path import Path as mplPath @@ -17047,6 +17050,102 @@ def plot_cumulative_plasma_thermal_energy_profiles(axis, m_file: MFile, scan: in ) +def plot_outboard_blanket_coolant_properties(fig: plt.Figure, m_file: MFile, scan: int): + """Plots the properties of the outboard blanket coolant along the poloidal direction.""" + temp_blkt_coolant_in = float(m_file.get("temp_blkt_coolant_in", scan=scan)) + temp_blkt_coolant_out = float(m_file.get("temp_blkt_coolant_out", scan=scan)) + pres_blkt_coolant = float(m_file.get("pres_blkt_coolant", scan=scan)) + dens_blkt_coolant_in = PropsSI( + "Dmass", "T", temp_blkt_coolant_in, "P", pres_blkt_coolant, "Helium" + ) + dens_blkt_coolant_out = PropsSI( + "Dmass", "T", temp_blkt_coolant_out, "P", pres_blkt_coolant, "Helium" + ) + + fig.clear() + axes = fig.subplots(2, 2) + + axis = axes[0, 0] + + plot = PropertyPlot("Helium", "PH", axis=axis, unit_system="SI") + + # #axis.set_xscale('log') + axis.set_ylim(((pres_blkt_coolant) * 0.9), (pres_blkt_coolant) * 1.5) + # plot.set_axis_limits([None, None, (pres_blkt_coolant/1e3)*0.9, (pres_blkt_coolant/1e3)*1.1]) + + plot.calc_isolines(CoolProp.iT) # Temperature + plot.calc_isolines(CoolProp.iSmass) # Entropy + plot.calc_isolines(CoolProp.iDmass) # Density + # plot.axis.relim() + # plot.axis.autoscale_view() + plot.draw() + + axis.set_title("Pressure-enthalpy plot") + + axis = axes[0, 1] + plot = PropertyPlot("Helium", "TD", axis=axis, unit_system="SI") + + density_min = min(dens_blkt_coolant_in, dens_blkt_coolant_out) + density_max = max(dens_blkt_coolant_in, dens_blkt_coolant_out) + temperature_min = min(temp_blkt_coolant_in, temp_blkt_coolant_out) + temperature_max = max(temp_blkt_coolant_in, temp_blkt_coolant_out) + + density_margin = max((density_max - density_min) * 0.2, max(density_max, 1.0) * 0.05) + temperature_margin = max( + (temperature_max - temperature_min) * 0.2, + max(temperature_max, 1.0) * 0.05, + ) + + axis.set_xlim(density_min - density_margin, density_max + density_margin) + axis.set_ylim( + temperature_min - temperature_margin, temperature_max + temperature_margin + ) + + plot.calc_isolines(CoolProp.iP) # Pressure + plot.calc_isolines(CoolProp.iHmass) # Enthalpy + plot.draw() + + axis.plot( + [dens_blkt_coolant_in, dens_blkt_coolant_out], + [temp_blkt_coolant_in, temp_blkt_coolant_out], + color="C3", + marker="o", + linewidth=1.5, + label="Coolant path", + ) + axis.legend(fontsize=8, loc="best") + axis.set_title("Temperature-density plot") + + axis = axes[1, 0] + plot = PropertyPlot("Helium", "PD", axis=axis, unit_system="SI") + + pressure_margin = max(pres_blkt_coolant * 0.1, 1.0) + + axis.set_xlim(density_min - density_margin, density_max + density_margin) + axis.set_ylim( + pres_blkt_coolant - pressure_margin, pres_blkt_coolant + pressure_margin + ) + + plot.calc_isolines(CoolProp.iT) # Temperature + plot.calc_isolines(CoolProp.iHmass) # Enthalpy + plot.draw() + + axis.plot( + [dens_blkt_coolant_in, dens_blkt_coolant_out], + [pres_blkt_coolant, pres_blkt_coolant], + color="C3", + marker="o", + linewidth=1.5, + label="Coolant path", + ) + axis.legend(fontsize=8, loc="best") + axis.set_title("Pressure-density plot") + + axes[1, 1].axis("off") + + fig.tight_layout() + + def main_plot( m_file: MFile, scan: int, @@ -17708,6 +17807,7 @@ def _add_page(name: str | None = None): plot_blanket_coolant_channel_structure(figs[37].add_subplot(111), m_file, scan) + plot_outboard_blanket_coolant_properties(figs[37], m_file, scan) def create_thickness_builds(m_file, scan: int): From 336d33e3e289384e6394eb940e69a57f08d89441 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 5 Jun 2026 17:17:45 +0100 Subject: [PATCH 07/40] Tidy some variable names to match style guide --- process/models/blankets/blanket_library.py | 85 +++++++++++++--------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index d62e3e542f..b7149cf0e9 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -2322,26 +2322,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 @@ -2386,13 +2386,19 @@ def thermo_hydraulic_model(self, output: bool): 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)) + / ( + self.data.fwbs.cp_fw + * (temp_fw_coolant_out - self.data.fwbs.temp_fw_coolant_in) + ) ) # 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)) + / ( + self.data.fwbs.cp_fw + * (temp_fw_coolant_out - self.data.fwbs.temp_fw_coolant_in) + ) ) # If the blanket is dual-coolant... @@ -2403,7 +2409,7 @@ def thermo_hydraulic_model(self, output: bool): * (pnucblkto_struct) / ( self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempo) + * (self.data.fwbs.temp_blkt_coolant_out - temp_blkt_coolant_in) ) ) self.data.blanket.mfblkto_liq = ( @@ -2426,7 +2432,7 @@ def thermo_hydraulic_model(self, output: bool): * (pnucblkti_struct) / ( self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempi) + * (self.data.fwbs.temp_blkt_coolant_out - temp_blkt_coolant_in) ) ) self.data.blanket.mfblkti_liq = ( @@ -2449,7 +2455,7 @@ def thermo_hydraulic_model(self, output: bool): * (self.data.blanket.p_blkt_nuclear_heat_outboard_mw) / ( self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempo) + * (self.data.fwbs.temp_blkt_coolant_out - temp_blkt_coolant_in) ) ) @@ -2473,7 +2479,7 @@ def thermo_hydraulic_model(self, output: bool): * (self.data.blanket.p_blkt_nuclear_heat_inboard_mw) / ( self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempi) + * (self.data.fwbs.temp_blkt_coolant_out - temp_blkt_coolant_in) ) ) # Mass flow rate for inboard breeder flow (kg/s) @@ -2489,7 +2495,7 @@ def thermo_hydraulic_model(self, output: bool): * (self.data.blanket.p_blkt_nuclear_heat_outboard_mw) / ( self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempo) + * (self.data.fwbs.temp_blkt_coolant_out - temp_blkt_coolant_in) ) ) @@ -2504,7 +2510,7 @@ def thermo_hydraulic_model(self, output: bool): * (self.data.blanket.p_blkt_nuclear_heat_inboard_mw) / ( self.data.fwbs.cp_bl - * (self.data.fwbs.temp_blkt_coolant_out - inlet_tempi) + * (self.data.fwbs.temp_blkt_coolant_out - temp_blkt_coolant_in) ) ) @@ -2518,15 +2524,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: @@ -2535,7 +2541,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... @@ -2546,12 +2552,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 @@ -2586,25 +2601,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 = ( @@ -2624,7 +2643,7 @@ def thermo_hydraulic_model(self, output: bool): 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, @@ -2638,7 +2657,7 @@ def thermo_hydraulic_model(self, output: bool): 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, From 48e26e87182fe73b34444664acb28e283de509e9 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 5 Jun 2026 18:04:26 +0100 Subject: [PATCH 08/40] Create mass flow required function and implement --- .../eng-models/generic_methods/pumping.md | 30 ++++- process/models/blankets/blanket_library.py | 124 ++++++++++-------- process/models/engineering/pumping.py | 34 +++++ tests/unit/models/engineering/test_pumping.py | 29 ++++ 4 files changed, 159 insertions(+), 58 deletions(-) diff --git a/documentation/source/eng-models/generic_methods/pumping.md b/documentation/source/eng-models/generic_methods/pumping.md index 7bd3a1dc4e..7047b42199 100644 --- a/documentation/source/eng-models/generic_methods/pumping.md +++ b/documentation/source/eng-models/generic_methods/pumping.md @@ -87,4 +87,32 @@ 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) -$$ \ No newline at end of file +$$ + +-------------- + +## 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/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index b7149cf0e9..c3bdc2fc2c 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -23,6 +23,7 @@ from process.models.engineering.pumping import ( CoolantType, CoolantFrictionLossParameters, + calculate_required_mass_flow_rate, calculate_reynolds_number, darcy_friction_haaland, elbow_coeff, @@ -2384,20 +2385,29 @@ 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 - * (temp_fw_coolant_out - 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.cp_fw, + temp_in_coolant=temp_fw_coolant_out, + temp_out_coolant=self.data.fwbs.temp_fw_coolant_in, ) ) + # 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 - * (temp_fw_coolant_out - 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.cp_fw, + temp_in_coolant=temp_fw_coolant_out, + temp_out_coolant=self.data.fwbs.temp_fw_coolant_in, ) ) @@ -2405,20 +2415,19 @@ def thermo_hydraulic_model(self, output: bool): 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 - temp_blkt_coolant_in) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 * pnucblkto_struct, + heatcap_coolant=self.data.fwbs.cp_bl, + 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... @@ -2428,34 +2437,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 - temp_blkt_coolant_in) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 * pnucblkti_struct, + heatcap_coolant=self.data.fwbs.cp_bl, + 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 - temp_blkt_coolant_in) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 + * self.data.blanket.p_blkt_nuclear_heat_outboard_mw, + heatcap_coolant=self.data.fwbs.cp_bl, + temp_in_coolant=temp_blkt_coolant_in, + temp_out_coolant=self.data.fwbs.temp_blkt_coolant_out, ) ) @@ -2475,13 +2481,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 - temp_blkt_coolant_in) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 + * self.data.blanket.p_blkt_nuclear_heat_inboard_mw, + heatcap_coolant=self.data.fwbs.cp_bl, + 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 @@ -2491,11 +2499,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 - temp_blkt_coolant_in) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 + * self.data.blanket.p_blkt_nuclear_heat_outboard_mw, + heatcap_coolant=self.data.fwbs.cp_bl, + temp_in_coolant=temp_blkt_coolant_in, + temp_out_coolant=self.data.fwbs.temp_blkt_coolant_out, ) ) @@ -2506,11 +2515,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 - temp_blkt_coolant_in) + calculate_required_mass_flow_rate( + p_heat_total=1.0e6 + * self.data.blanket.p_blkt_nuclear_heat_inboard_mw, + heatcap_coolant=self.data.fwbs.cp_bl, + temp_in_coolant=temp_blkt_coolant_in, + temp_out_coolant=self.data.fwbs.temp_blkt_coolant_out, ) ) diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index f5c2971644..78384108a3 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -299,3 +299,37 @@ def elbow_coeff( # Elbow Coefficient return ximt + xift + + +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/tests/unit/models/engineering/test_pumping.py b/tests/unit/models/engineering/test_pumping.py index 5e85511dda..bc5fbee253 100644 --- a/tests/unit/models/engineering/test_pumping.py +++ b/tests/unit/models/engineering/test_pumping.py @@ -1,6 +1,7 @@ import pytest from process.models.engineering.pumping import ( + calculate_required_mass_flow_rate, calculate_reynolds_number, darcy_friction_haaland, elbow_coeff, @@ -47,3 +48,31 @@ def test_elbow_coeff(): 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, + ) From 78352ac63a8fd0c3cab2fc7ea550c49ae7593c70 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 5 Jun 2026 18:22:28 +0100 Subject: [PATCH 09/40] =?UTF-8?q?=F0=9F=94=84=20Rename=20FW=20and=20Blkt?= =?UTF-8?q?=20heat=20capacity=20variables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- documentation/source/development/standards.md | 8 ++++ process/data_structure/fwbs_variables.py | 16 +++---- process/models/blankets/blanket_library.py | 48 ++++++++++++------- .../models/blankets/test_blanket_library.py | 48 +++++++++++-------- 4 files changed, 76 insertions(+), 44 deletions(-) diff --git a/documentation/source/development/standards.md b/documentation/source/development/standards.md index 8479267753..beec712ac0 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/process/data_structure/fwbs_variables.py b/process/data_structure/fwbs_variables.py index acd52894d6..160a0d5838 100644 --- a/process/data_structure/fwbs_variables.py +++ b/process/data_structure/fwbs_variables.py @@ -635,17 +635,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 c3bdc2fc2c..094905ab91 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -736,14 +736,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: @@ -757,8 +765,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 @@ -772,8 +784,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 ( @@ -2391,7 +2407,7 @@ def thermo_hydraulic_model(self, output: bool): self.data.blanket.p_fw_inboard_nuclear_heat_mw + self.data.fwbs.psurffwi ), - heatcap_coolant=self.data.fwbs.cp_fw, + heatcap_coolant=self.data.fwbs.heatcap_pres_fw_coolant_average, temp_in_coolant=temp_fw_coolant_out, temp_out_coolant=self.data.fwbs.temp_fw_coolant_in, ) @@ -2405,7 +2421,7 @@ def thermo_hydraulic_model(self, output: bool): self.data.blanket.p_fw_outboard_nuclear_heat_mw + self.data.fwbs.psurffwo ), - heatcap_coolant=self.data.fwbs.cp_fw, + heatcap_coolant=self.data.fwbs.heatcap_pres_fw_coolant_average, temp_in_coolant=temp_fw_coolant_out, temp_out_coolant=self.data.fwbs.temp_fw_coolant_in, ) @@ -2417,7 +2433,7 @@ def thermo_hydraulic_model(self, output: bool): self.data.blanket.mflow_blkt_outboard_coolant = ( calculate_required_mass_flow_rate( p_heat_total=1.0e6 * pnucblkto_struct, - heatcap_coolant=self.data.fwbs.cp_bl, + 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, ) @@ -2439,7 +2455,7 @@ def thermo_hydraulic_model(self, output: bool): self.data.blanket.mflow_blkt_inboard_coolant = ( calculate_required_mass_flow_rate( p_heat_total=1.0e6 * pnucblkti_struct, - heatcap_coolant=self.data.fwbs.cp_bl, + 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, ) @@ -2459,7 +2475,7 @@ def thermo_hydraulic_model(self, output: bool): calculate_required_mass_flow_rate( p_heat_total=1.0e6 * self.data.blanket.p_blkt_nuclear_heat_outboard_mw, - heatcap_coolant=self.data.fwbs.cp_bl, + 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, ) @@ -2484,7 +2500,7 @@ def thermo_hydraulic_model(self, output: bool): calculate_required_mass_flow_rate( p_heat_total=1.0e6 * self.data.blanket.p_blkt_nuclear_heat_inboard_mw, - heatcap_coolant=self.data.fwbs.cp_bl, + 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, ) @@ -2502,7 +2518,7 @@ def thermo_hydraulic_model(self, output: bool): calculate_required_mass_flow_rate( p_heat_total=1.0e6 * self.data.blanket.p_blkt_nuclear_heat_outboard_mw, - heatcap_coolant=self.data.fwbs.cp_bl, + 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, ) @@ -2518,7 +2534,7 @@ def thermo_hydraulic_model(self, output: bool): calculate_required_mass_flow_rate( p_heat_total=1.0e6 * self.data.blanket.p_blkt_nuclear_heat_inboard_mw, - heatcap_coolant=self.data.fwbs.cp_bl, + 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, ) diff --git a/tests/unit/models/blankets/test_blanket_library.py b/tests/unit/models/blankets/test_blanket_library.py index cb1bf19180..b4053bbba7 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -29,9 +29,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 +47,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 +81,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 +90,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 +109,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 +118,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 +179,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 +233,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 +262,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 +278,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 ) From 244dc59bf546d45434c9e56b4bd20a787d7fa3f9 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 5 Jun 2026 23:13:55 +0100 Subject: [PATCH 10/40] Add output for outboard blanket piping --- process/core/io/plot/summary.py | 66 ++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 6a63d4c0bc..904ca25555 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -17144,7 +17144,43 @@ def plot_outboard_blanket_coolant_properties(fig: plt.Figure, m_file: MFile, sca axes[1, 1].axis("off") fig.tight_layout() + +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"Radial coolant channel length: {m_file.get('len_blkt_outboard_coolant_channel_radial', scan=scan):.4f}\n" + f"Toroidal coolant channel length: {m_file.get('len_blkt_outboard_coolant_channel_toroidal', scan=scan):.4f}\n" + f"Number of radial channels: {m_file.get('n_blkt_outboard_coolant_channels_radial', scan=scan)}\n" + f"Number of poloidal channels: {m_file.get('n_blkt_outboard_coolant_channels_poloidal', scan=scan)}\n" + f"Stored energy of all coils: {m_file.get('e_tf_magnetic_stored_total_gj', scan=scan):.4f} GJ\n" + f"Stored energy of a single coil: {m_file.get('e_tf_coil_magnetic_stored', scan=scan) / 1e9:.2f} GJ\n" + f"Total area of steel in coil: {m_file.get('a_tf_coil_inboard_steel', scan=scan):.4f} $\\mathrm{{m}}^2$\n" + f"Total area fraction of steel: {m_file.get('f_a_tf_coil_inboard_steel', scan=scan):.4f}\n" + f"Total area fraction of insulation: {m_file.get('f_a_tf_coil_inboard_insulation', scan=scan):.4f}\n" + f"$A$, all insulation in coil: {m_file.get('a_tf_coil_inboard_insulation', scan=scan):.4f} $\\mathrm{{m}}^2$\n" + ) + + fig.text( + 0.775, + 0.58, + textstr_outboard_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, @@ -17782,32 +17818,26 @@ 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") - plot_blkt_structure( - ax_blanket, - pages["blkt_pipe_bends"], - m_file, - scan, - radial_build, - colour_scheme, - ) + + ax_blanket = figs[34].add_subplot(122, aspect="equal") + plot_blkt_structure(ax_blanket, figs[34], m_file, scan, radial_build, colour_scheme) + + plot_blkt_pipe_bends(figs[35], m_file, scan) + plot_blanket_coolant_channel_structure(figs[35].add_subplot(111), m_file, scan) + plot_blanket_coolant_channel_structure_and_properties(figs[35], m_file, scan) plot_main_power_flow( - _add_page("main_power_flow").add_subplot(111, aspect="equal"), - m_file, - scan, - pages["main_power_flow"], + figs[36].add_subplot(111, aspect="equal"), m_file, scan, figs[36] ) - ax24 = _add_page("power_profile_over_time").add_subplot(111) + ax24 = figs[37].add_subplot(111) # set_position([left, bottom, width, height]) -> height ~ 0.66 => ~2/3 of page height ax24.set_position([0.08, 0.35, 0.84, 0.57]) - plot_system_power_profiles_over_time(ax24, m_file, scan, figs[36]) + plot_system_power_profiles_over_time(ax24, m_file, scan, figs[37]) - plot_blanket_coolant_channel_structure(figs[37].add_subplot(111), m_file, scan) + - plot_outboard_blanket_coolant_properties(figs[37], m_file, scan) + # plot_outboard_blanket_coolant_properties(figs[37], m_file, scan) def create_thickness_builds(m_file, scan: int): From 566a92083d91aa6ab9e5bbe0bbaf212830b681b2 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Sat, 6 Jun 2026 00:12:24 +0100 Subject: [PATCH 11/40] Add output table for outboard blanket --- process/core/io/plot/summary.py | 48 ++++++++++++---------- process/data_structure/fwbs_variables.py | 2 +- process/models/blankets/blanket_library.py | 8 ++-- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 904ca25555..8a801c1854 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -17146,27 +17146,37 @@ def plot_outboard_blanket_coolant_properties(fig: plt.Figure, m_file: MFile, sca fig.tight_layout() -def plot_blanket_coolant_channel_structure_and_properties(fig: plt.Figure, m_file: MFile, scan: int): + +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"Radial coolant channel length: {m_file.get('len_blkt_outboard_coolant_channel_radial', scan=scan):.4f}\n" - f"Toroidal coolant channel length: {m_file.get('len_blkt_outboard_coolant_channel_toroidal', scan=scan):.4f}\n" - f"Number of radial channels: {m_file.get('n_blkt_outboard_coolant_channels_radial', scan=scan)}\n" - f"Number of poloidal channels: {m_file.get('n_blkt_outboard_coolant_channels_poloidal', scan=scan)}\n" - f"Stored energy of all coils: {m_file.get('e_tf_magnetic_stored_total_gj', scan=scan):.4f} GJ\n" - f"Stored energy of a single coil: {m_file.get('e_tf_coil_magnetic_stored', scan=scan) / 1e9:.2f} GJ\n" - f"Total area of steel in coil: {m_file.get('a_tf_coil_inboard_steel', scan=scan):.4f} $\\mathrm{{m}}^2$\n" - f"Total area fraction of steel: {m_file.get('f_a_tf_coil_inboard_steel', scan=scan):.4f}\n" - f"Total area fraction of insulation: {m_file.get('f_a_tf_coil_inboard_insulation', scan=scan):.4f}\n" - f"$A$, all insulation in coil: {m_file.get('a_tf_coil_inboard_insulation', scan=scan):.4f} $\\mathrm{{m}}^2$\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"Toroidal coolant channel length: {m_file.get('len_blkt_inboard_segment_toroidal', 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_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.775, - 0.58, + 0.5, + 0.65, textstr_outboard_blkt, fontsize=9, verticalalignment="top", @@ -17179,8 +17189,7 @@ def plot_blanket_coolant_channel_structure_and_properties(fig: plt.Figure, m_fil "linewidth": 2, }, ) - - + def main_plot( m_file: MFile, @@ -17818,10 +17827,9 @@ def _add_page(name: str | None = None): ) plot_fw_90_deg_pipe_bend(pages["fw_td_cross_section"].add_subplot(337), m_file, scan) - ax_blanket = figs[34].add_subplot(122, aspect="equal") plot_blkt_structure(ax_blanket, figs[34], m_file, scan, radial_build, colour_scheme) - + plot_blkt_pipe_bends(figs[35], m_file, scan) plot_blanket_coolant_channel_structure(figs[35].add_subplot(111), m_file, scan) plot_blanket_coolant_channel_structure_and_properties(figs[35], m_file, scan) @@ -17835,8 +17843,6 @@ def _add_page(name: str | None = None): ax24.set_position([0.08, 0.35, 0.84, 0.57]) plot_system_power_profiles_over_time(ax24, m_file, scan, figs[37]) - - # plot_outboard_blanket_coolant_properties(figs[37], m_file, scan) diff --git a/process/data_structure/fwbs_variables.py b/process/data_structure/fwbs_variables.py index 160a0d5838..c1ae7cfe75 100644 --- a/process/data_structure/fwbs_variables.py +++ b/process/data_structure/fwbs_variables.py @@ -310,7 +310,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] diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 094905ab91..e4f4b72083 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -2408,8 +2408,8 @@ def thermo_hydraulic_model(self, output: bool): + self.data.fwbs.psurffwi ), heatcap_coolant=self.data.fwbs.heatcap_pres_fw_coolant_average, - temp_in_coolant=temp_fw_coolant_out, - temp_out_coolant=self.data.fwbs.temp_fw_coolant_in, + temp_in_coolant=self.data.fwbs.temp_fw_coolant_in, + temp_out_coolant=temp_fw_coolant_out, ) ) @@ -2422,8 +2422,8 @@ def thermo_hydraulic_model(self, output: bool): + self.data.fwbs.psurffwo ), heatcap_coolant=self.data.fwbs.heatcap_pres_fw_coolant_average, - temp_in_coolant=temp_fw_coolant_out, - temp_out_coolant=self.data.fwbs.temp_fw_coolant_in, + temp_in_coolant=self.data.fwbs.temp_fw_coolant_in, + temp_out_coolant=temp_fw_coolant_out, ) ) From 1c1666f8fbae0ee202068b06fca9624bae5ae4c0 Mon Sep 17 00:00:00 2001 From: chris-ashe Date: Sun, 7 Jun 2026 17:00:36 +0100 Subject: [PATCH 12/40] Refactor coolant friction loss parameters and update related tests --- process/models/blankets/blanket_library.py | 34 +++-- .../models/blankets/test_blanket_library.py | 119 +++++++++--------- 2 files changed, 80 insertions(+), 73 deletions(-) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index e4f4b72083..345fb09a38 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -3049,6 +3049,8 @@ def total_pressure_drop( den_coolant=den_coolant, visc_coolant=visc_coolant_dynamic, vel_coolant=vel_coolant, + roughness_channel=self.data.fwbs.roughness_fw_channel, + radius_channel=self.data.fwbs.radius_fw_channel, ) ) dpres_friction = friction_params.dpres_total @@ -3243,6 +3245,8 @@ def coolant_friction_pressure_drop( den_coolant: float, visc_coolant: float, vel_coolant: float, + roughness_channel: float, + radius_channel: 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 @@ -3255,26 +3259,30 @@ def coolant_friction_pressure_drop( i_ps : switch for primary or secondary coolant radius_pipe_90_deg_bend : - radius of 90 degree bend in pipe (m) + radius of 90 degree bend in pipe [m] radius_pipe_180_deg_bend : - radius of 180 degree bend in pipe (m) + 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) + total flow length along pipe [m] den_coolant : - coolant density (kg/m³) + coolant density [kg/m³] visc_coolant : - coolant viscosity (Pa s) + coolant viscosity [Pa s] vel_coolant : - coolant flow velocity (m/s) + coolant flow velocity [m/s] + roughness_channel : + roughness of the channel wall (ε) [m] + radius_channel : + radius of the channel [m] Returns ------- : - CoolantFrictionLossParameters dataclass containing: + `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) @@ -3300,7 +3308,7 @@ def coolant_friction_pressure_drop( 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) + dia_pipe = self.pipe_hydraulic_diameter(i_channel_shape=i_ps) # Reynolds number reynolds_number = calculate_reynolds_number( @@ -3311,15 +3319,15 @@ def coolant_friction_pressure_drop( ) # 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 + # 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=self.data.fwbs.roughness_fw_channel, - radius_channel=self.data.fwbs.radius_fw_channel, + roughness_channel=roughness_channel, + radius_channel=radius_channel, ) # Pressure drop coefficient diff --git a/tests/unit/models/blankets/test_blanket_library.py b/tests/unit/models/blankets/test_blanket_library.py index b4053bbba7..5c4ecde54b 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -1,4 +1,4 @@ -from typing import Any, NamedTuple +from typing import TYPE_CHECKING, Any, NamedTuple import numpy as np import pytest @@ -7,6 +7,9 @@ from process.models.blankets.blanket_library import InboardBlanket from process.models.engineering.pumping import CoolantType +if TYPE_CHECKING: + from process.models.engineering.pumping import CoolantFrictionLossParameters + @pytest.fixture def blanket_library(process_models): @@ -1022,110 +1025,106 @@ def test_liquid_breeder_properties( ) -class PressureDropParam(NamedTuple): - radius_fw_channel: Any = None +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_fw_channel: Any = None - ip: Any = None + roughness_channel: 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 + 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( - "pressuredropparam", + "coolantfrictionlossparam", [ - PressureDropParam( - radius_fw_channel=0.0060000000000000001, + 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_fw_channel=9.9999999999999995e-07, - ip=0, + roughness_channel=9.9999999999999995e-07, i_ps=1, - num_90=2, - num_180=0, - l_pipe=4, - den=10.405276820718059, - vsc=3.604452999475736e-05, - vv=32.753134225223164, + 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, ), - PressureDropParam( - radius_fw_channel=1.0, + 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_fw_channel=1e-6, - ip=0, + roughness_channel=1e-6, i_ps=2, - num_90=1.0, - num_180=1.0, - l_pipe=1.0, - den=1.0, - vsc=1.0, - vv=1.0, + 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_pressure_drop(pressuredropparam, monkeypatch, blanket_library): +def test_coolant_friction_loss(coolantfrictionlossparam, monkeypatch, blanket_library): """ Automatically generated 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 + 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. - :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 + blanket_library.data.fwbs, "a_bz_liq", coolantfrictionlossparam.a_bz_liq ) monkeypatch.setattr( - blanket_library.data.fwbs, - "roughness_fw_channel", - pressuredropparam.roughness_fw_channel, + blanket_library.data.fwbs, "b_bz_liq", coolantfrictionlossparam.b_bz_liq ) - pressure_params = 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, + pressure_params: CoolantFrictionLossParameters = ( + blanket_library.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, + ) ) assert pressure_params.dpres_total == pytest.approx( - pressuredropparam.expected_pressure_drop_out + coolantfrictionlossparam.expected_pressure_drop_out ) From 5f030f3aadd8bb66e64a2781537f4c1be11e8a74 Mon Sep 17 00:00:00 2001 From: chris-ashe Date: Sun, 7 Jun 2026 19:30:26 +0100 Subject: [PATCH 13/40] Enhance inboard blanket coolant channel output and pressure drop calculations --- process/core/io/plot/summary.py | 47 ++++++- process/models/blankets/blanket_library.py | 151 ++++++++++++++++++++- 2 files changed, 188 insertions(+), 10 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 8a801c1854..7e8aaeed25 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -17157,10 +17157,10 @@ def plot_blanket_coolant_channel_structure_and_properties( 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"Toroidal coolant channel length: {m_file.get('len_blkt_inboard_segment_toroidal', 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"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" @@ -17190,6 +17190,45 @@ def plot_blanket_coolant_channel_structure_and_properties( }, ) + # 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.2, + 0.65, + 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, diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 345fb09a38..2fa34609fe 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -1569,6 +1569,44 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): ) ) + 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( @@ -3607,11 +3645,12 @@ def output_blkt_pumping_variables(self): ) po.ovarre( self.outfile, - "Inboard blanket coolant channel length (toroidal direction) (m)", - "(len_blkt_inboard_segment_toroidal)", - self.data.blanket.len_blkt_inboard_segment_toroidal, + "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", @@ -3626,6 +3665,7 @@ def output_blkt_pumping_variables(self): 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)", @@ -3633,6 +3673,105 @@ def output_blkt_pumping_variables(self): self.data.blanket.len_blkt_inboard_channel_total, "OP ", ) + po.oblnkl(self.outfile) + po.ocmmnt(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 ", + ) + + 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 ", + ) + + 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 ", + ) + + 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) + + 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 ", + ) + + 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 ", + ) + + 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 ", + ) + + 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.osubhd(self.outfile, "Outboard Blanket") @@ -3645,9 +3784,9 @@ def output_blkt_pumping_variables(self): ) po.ovarre( self.outfile, - "Outboard blanket coolant channel length (toroidal direction) (m)", - "(len_blkt_outboard_segment_toroidal)", - self.data.blanket.len_blkt_outboard_segment_toroidal, + "Outboard blanket coolant channel length (poloidal direction) (m)", + "(len_blkt_outboard_segment_poloidal)", + self.data.blanket.len_blkt_outboard_segment_poloidal, "OP ", ) po.oblnkl(self.outfile) From e32fffd3c4bb219645a7af8c1d26f7c6e3185502 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 8 Jun 2026 10:28:12 +0100 Subject: [PATCH 14/40] Move FW number of bends to FW file --- process/models/blankets/blanket_library.py | 7 +------ process/models/fw.py | 6 ++++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 2fa34609fe..11cd81119c 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -28,6 +28,7 @@ darcy_friction_haaland, elbow_coeff, ) +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__) @@ -1143,12 +1144,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 diff --git a/process/models/fw.py b/process/models/fw.py index 44fb8c1503..418fd5259d 100644 --- a/process/models/fw.py +++ b/process/models/fw.py @@ -24,6 +24,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): """Calculate the first wall parameters""" From 1f43d47950d86448bbf83ca28bfdf6738d4f44e3 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 8 Jun 2026 10:54:17 +0100 Subject: [PATCH 15/40] Remove pipe plotting --- process/core/io/plot/summary.py | 1 - 1 file changed, 1 deletion(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 7e8aaeed25..431472868f 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -17870,7 +17870,6 @@ def _add_page(name: str | None = None): plot_blkt_structure(ax_blanket, figs[34], m_file, scan, radial_build, colour_scheme) plot_blkt_pipe_bends(figs[35], m_file, scan) - plot_blanket_coolant_channel_structure(figs[35].add_subplot(111), m_file, scan) plot_blanket_coolant_channel_structure_and_properties(figs[35], m_file, scan) plot_main_power_flow( From bba36de503ee30c89de2c89c842b3a5c58c4d4ca Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 8 Jun 2026 11:14:21 +0100 Subject: [PATCH 16/40] Update summary positions of tables --- process/core/io/plot/summary.py | 8 +++--- process/models/blankets/blanket_library.py | 30 ++++++++++------------ 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 431472868f..f6e570a816 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -17176,7 +17176,7 @@ def plot_blanket_coolant_channel_structure_and_properties( fig.text( 0.5, - 0.65, + 0.5, textstr_outboard_blkt, fontsize=9, verticalalignment="top", @@ -17214,8 +17214,8 @@ def plot_blanket_coolant_channel_structure_and_properties( ) fig.text( - 0.2, - 0.65, + 0.1, + 0.5, textstr_inboard_blkt, fontsize=9, verticalalignment="top", @@ -17881,8 +17881,6 @@ def _add_page(name: str | None = None): ax24.set_position([0.08, 0.35, 0.84, 0.57]) plot_system_power_profiles_over_time(ax24, m_file, scan, figs[37]) - # plot_outboard_blanket_coolant_properties(figs[37], m_file, scan) - def create_thickness_builds(m_file, scan: int): """Create the dictionaries of radial and vertical build values and cumulative values""" diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 11cd81119c..4ab6c8e364 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -1438,7 +1438,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): b_bz_liq=self.data.fwbs.b_bz_liq, ) - dpres_fw_inboard_coolant, fw_inboard_friction_params = self.total_pressure_drop( + dpres_fw_inboard_coolant, _ = self.total_pressure_drop( output, icoolpump=1, vel_coolant=vel_fw_inboard_coolant, @@ -1453,21 +1453,19 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): label="Inboard first wall", ) - dpres_fw_outboard_coolant, fw_outboard_friction_params = ( - self.total_pressure_drop( - output, - icoolpump=1, - vel_coolant=vel_fw_outboard_coolant, - len_pipe=self.data.fwbs.len_fw_channel, - n_pipe_90_deg_bends=N_FW_PIPE_90_DEG_BENDS, - n_pipe_180_deg_bends=N_FW_PIPE_180_DEG_BENDS, - den_coolant=self.data.fwbs.den_fw_coolant, - visc_coolant_dynamic=self.data.fwbs.visc_fw_coolant, - coolant_electrical_conductivity=0.0e0, - pol_channel_length=pollengo, - nopolchan=npoltoto, - label="Outboard first wall", - ) + dpres_fw_outboard_coolant, _ = self.total_pressure_drop( + output, + icoolpump=1, + vel_coolant=vel_fw_outboard_coolant, + len_pipe=self.data.fwbs.len_fw_channel, + n_pipe_90_deg_bends=N_FW_PIPE_90_DEG_BENDS, + n_pipe_180_deg_bends=N_FW_PIPE_180_DEG_BENDS, + den_coolant=self.data.fwbs.den_fw_coolant, + visc_coolant_dynamic=self.data.fwbs.visc_fw_coolant, + coolant_electrical_conductivity=0.0e0, + pol_channel_length=pollengo, + nopolchan=npoltoto, + label="Outboard first wall", ) # BB Presure Drops ############### From 39bd360295515379ec6622c67936a874c699be4f Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 11 Jun 2026 15:23:04 +0100 Subject: [PATCH 17/40] Post rebase fixes --- process/models/blankets/blanket_library.py | 2 +- process/models/engineering/pumping.py | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 4ab6c8e364..92184ef051 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -21,8 +21,8 @@ eshellvol, ) from process.models.engineering.pumping import ( - CoolantType, CoolantFrictionLossParameters, + CoolantType, calculate_required_mass_flow_rate, calculate_reynolds_number, darcy_friction_haaland, diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index 78384108a3..4f134d1c19 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -1,12 +1,16 @@ """Engineering models for pumping system analysis.""" import logging +from dataclasses import dataclass from enum import IntEnum, unique from types import DynamicClassAttribute -from dataclasses import dataclass import numpy as np +from process.core.exceptions import ProcessValueError + +logger = logging.getLogger(__name__) + @unique class CoolantType(IntEnum): @@ -42,11 +46,6 @@ def full_name(self): return self._full_name_ -from process.core.exceptions import ProcessValueError - -logger = logging.getLogger(__name__) - - @dataclass class CoolantFrictionLossParameters: """Parameters for calculating coolant friction losses.""" From cb5f1454802191c072c27573d5f827d48f5a84c4 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 23 Jun 2026 16:48:11 +0100 Subject: [PATCH 18/40] Remove unused CoolProp imports and related plotting functions for outboard blanket coolant properties --- process/core/io/plot/summary.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index f6e570a816..e2f870bcea 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -7,14 +7,11 @@ from pathlib import Path from typing import Any, Literal -import CoolProp import matplotlib as mpl import matplotlib.backends.backend_pdf as bpdf import matplotlib.image as mpimg import matplotlib.pyplot as plt import numpy as np -from CoolProp.CoolProp import PropsSI -from CoolProp.Plots import PropertyPlot from matplotlib import patches from matplotlib.patches import Circle, Rectangle from matplotlib.path import Path as mplPath @@ -16478,19 +16475,11 @@ def plot_blanket_coolant_channel_structure(self, m_file: MFile, scan: int): ) n_blkt_outboard_module_coolant_sections_radial = max( 1, - int( - round( - m_file.get("n_blkt_outboard_module_coolant_sections_radial", scan=scan) - ) - ), + round(m_file.get("n_blkt_outboard_module_coolant_sections_radial", scan=scan)), ) n_blkt_outboard_module_coolant_sections_poloidal = max( 1, - int( - round( - m_file.get("n_blkt_outboard_module_coolant_sections_poloidal", scan=scan) - ) - ), + round(m_file.get("n_blkt_outboard_module_coolant_sections_poloidal", scan=scan)), ) elbow_radius_180 = max( float(m_file.get("radius_blkt_channel_180_bend", scan=scan)), 1e-6 From 843e6da7ee0f304f7edc8d2f3e97a4834ecbddd2 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 2 Jul 2026 14:16:31 +0100 Subject: [PATCH 19/40] Post merge coflict fixes --- process/core/io/plot/summary.py | 237 ++------------------------------ 1 file changed, 8 insertions(+), 229 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index e2f870bcea..5e3b076aca 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -16465,231 +16465,6 @@ def plot_cs_von_mises_2d_contour( axis.legend(loc="best") -def plot_blanket_coolant_channel_structure(self, m_file: MFile, scan: int): - """Plot a schematic of the blanket coolant channel routing.""" - len_blkt_outboard_coolant_channel_radial = float( - m_file.get("len_blkt_outboard_coolant_channel_radial", scan=scan) - ) - len_blkt_outboard_segment_toroidal = float( - m_file.get("len_blkt_outboard_segment_toroidal", scan=scan) - ) - n_blkt_outboard_module_coolant_sections_radial = max( - 1, - round(m_file.get("n_blkt_outboard_module_coolant_sections_radial", scan=scan)), - ) - n_blkt_outboard_module_coolant_sections_poloidal = max( - 1, - round(m_file.get("n_blkt_outboard_module_coolant_sections_poloidal", scan=scan)), - ) - elbow_radius_180 = max( - float(m_file.get("radius_blkt_channel_180_bend", scan=scan)), 1e-6 - ) - r = max(float(m_file.get("radius_blkt_channel", scan=scan)), 1e-6) - elbow_radius_90 = max( - float(m_file.get("radius_blkt_channel_90_bend", scan=scan)), 1e-6 - ) - - axis = self if isinstance(self, Axes) else getattr(self, "ax", None) - figure = axis.figure if isinstance(axis, Axes) else None - - if not isinstance(axis, Axes): - figure, axis = plt.subplots(figsize=(10, 6)) - - def _plot_straight( - x0: float, - y0: float, - x1: float, - y1: float, - *, - label: str | None = None, - alpha: float = 1.0, - ): - axis.plot( - [x0, x1], - [y0, y1], - color="tab:blue", - linewidth=2.5, - solid_capstyle="round", - alpha=alpha, - label=label, - ) - - def _plot_bend( - centre_x: float, - centre_y: float, - radius: float, - theta_start: float, - theta_end: float, - *, - label: str | None = None, - alpha: float = 1.0, - ): - theta = np.linspace(theta_start, theta_end, 80) - axis.plot( - centre_x + radius * np.cos(theta), - centre_y + radius * np.sin(theta), - color="tab:orange", - linewidth=2.5, - solid_capstyle="round", - alpha=alpha, - label=label, - ) - - lane_pitch = 2.0 * elbow_radius_180 - poloidal_section_length = max(4.0 * r, 1.5 * elbow_radius_90) - - show_radial_label = True - show_poloidal_label = True - show_90_label = True - - inlet_x = 0.0 - x_left = elbow_radius_90 - x_right = elbow_radius_90 + len_blkt_outboard_coolant_channel_radial - y_lower = 0.0 - y_upper = y_lower + lane_pitch - y_inlet_lower = y_lower - elbow_radius_90 - poloidal_section_length - y_outlet_upper = y_upper + elbow_radius_90 + poloidal_section_length - - _plot_straight( - inlet_x, - y_inlet_lower, - inlet_x, - y_lower - elbow_radius_90, - label="Straight poloidal section" if show_poloidal_label else None, - ) - show_poloidal_label = False - - _plot_bend( - elbow_radius_90, - y_lower - elbow_radius_90, - elbow_radius_90, - np.pi, - np.pi / 2.0, - label="90° bend" if show_90_label else None, - ) - show_90_label = False - - _plot_straight( - x_left, - y_lower, - x_right, - y_lower, - label="Straight radial section" if show_radial_label else None, - ) - show_radial_label = False - - _plot_bend( - x_right, - y_lower + elbow_radius_180, - elbow_radius_180, - -np.pi / 2.0, - np.pi / 2.0, - label="180° bend", - ) - - _plot_straight( - x_right, - y_upper, - x_left, - y_upper, - label="Straight radial section" if show_radial_label else None, - ) - - _plot_bend( - x_left, - y_upper + elbow_radius_90, - elbow_radius_90, - 3.0 * np.pi / 2.0, - np.pi, - label="90° bend" if show_90_label else None, - ) - - _plot_straight( - inlet_x, - y_upper + elbow_radius_90, - inlet_x, - y_outlet_upper, - label="Straight poloidal section" if show_poloidal_label else None, - ) - - total_180_bends = 1 - total_90_bends = 2 - total_poloidal_sections = 2 - length_per_90_bend = 0.5 * np.pi * elbow_radius_90 - length_per_180_bend = np.pi * elbow_radius_180 - length_per_poloidal_section = poloidal_section_length - total_radial_channel_length = ( - n_blkt_outboard_module_coolant_sections_radial - * len_blkt_outboard_coolant_channel_radial - ) - total_poloidal_length = total_poloidal_sections * length_per_poloidal_section - total_bend_length = ( - total_90_bends * length_per_90_bend + total_180_bends * length_per_180_bend - ) - total_channel_length = ( - total_radial_channel_length + total_poloidal_length + total_bend_length - ) - info = textwrap.dedent( - f""" - Representative continuous channel loop - Straight radial sections: {n_blkt_outboard_module_coolant_sections_radial} - Straight radial length per channel: {total_radial_channel_length:.2f} m - Straight poloidal sections per loop: {total_poloidal_sections} - Straight poloidal length: {length_per_poloidal_section:.2f} m each - 90° bends per channel: {total_90_bends} - 90° bend length: {length_per_90_bend:.2f} m each - 180° bends per channel: {total_180_bends} - 180° bend length: {length_per_180_bend:.2f} m each - Total poloidal length per channel: {total_poloidal_length:.2f} m - Total bend length per channel: {total_bend_length:.2f} m - Approx. total channel length: {total_channel_length:.2f} m - Poloidal sections per module: {n_blkt_outboard_module_coolant_sections_poloidal} - Toroidal span per segment: {len_blkt_outboard_segment_toroidal:.2f} m - Channel radius: {r:.3f} m - """ - ).strip() - - axis.text( - 1.02, - 0.98, - info, - transform=axis.transAxes, - va="top", - ha="left", - fontsize=8, - bbox={"boxstyle": "round", "facecolor": "white", "alpha": 0.9}, - ) - - axis.text( - 0.02, - 0.02, - "Continuous loop schematic with a single 180° return bend", - transform=axis.transAxes, - fontsize=8, - color="dimgray", - ) - - axis.set_title("Blanket coolant channel structure") - axis.set_xlabel("Radial direction [m]") - axis.set_ylabel("Channel layout [schematic m]") - axis.set_aspect("equal", adjustable="box") - axis.grid(True, alpha=0.25) - axis.minorticks_on() - axis.legend(fontsize=8, loc="upper right") - - x_margin = len_blkt_outboard_coolant_channel_radial * 0.1 + 2.0 * elbow_radius_90 - axis.set_xlim(-x_margin, x_right + x_margin) - axis.set_ylim( - y_inlet_lower - r, - y_outlet_upper + r, - ) - - if figure is not None: - figure.tight_layout() - - return axis - - def plot_pf_dimensions( axis: plt.Axes, mfile: MFile, scan: int, colour_scheme: Literal[1, 2] = 1 ) -> None: @@ -17855,11 +17630,15 @@ def _add_page(name: str | None = None): ) plot_fw_90_deg_pipe_bend(pages["fw_td_cross_section"].add_subplot(337), m_file, scan) - ax_blanket = figs[34].add_subplot(122, aspect="equal") - plot_blkt_structure(ax_blanket, figs[34], m_file, scan, radial_build, colour_scheme) + ax_blanket = _add_page("blkt_structure").add_subplot(122, aspect="equal") + plot_blkt_structure( + ax_blanket, pages["blkt_structure"], m_file, scan, radial_build, colour_scheme + ) - plot_blkt_pipe_bends(figs[35], m_file, scan) - plot_blanket_coolant_channel_structure_and_properties(figs[35], m_file, scan) + 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( figs[36].add_subplot(111, aspect="equal"), m_file, scan, figs[36] From 95ca6b9384f9b1c81904de43d727e772fc1438b0 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Thu, 2 Jul 2026 14:39:35 +0100 Subject: [PATCH 20/40] Move all of the pumping related function from `BlanketLibrary` into the new generic Pumping class --- .../source/eng-models/blanket_overview.md | 75 ---- .../eng-models/generic_methods/pumping.md | 75 ++++ process/models/blankets/blanket_library.py | 416 ++---------------- process/models/blankets/dcll.py | 7 +- process/models/blankets/hcpb.py | 12 +- process/models/engineering/pumping.py | 321 +++++++++++++- .../models/blankets/test_blanket_library.py | 178 +------- tests/unit/models/engineering/test_pumping.py | 171 +++++++ 8 files changed, 624 insertions(+), 631 deletions(-) diff --git a/documentation/source/eng-models/blanket_overview.md b/documentation/source/eng-models/blanket_overview.md index fa0de10aa6..97bd9dc6dc 100644 --- a/documentation/source/eng-models/blanket_overview.md +++ b/documentation/source/eng-models/blanket_overview.md @@ -5,78 +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] -$$ - -------------------- - - - diff --git a/documentation/source/eng-models/generic_methods/pumping.md b/documentation/source/eng-models/generic_methods/pumping.md index 7047b42199..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. diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 92184ef051..70ef8faa5b 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -24,9 +24,7 @@ CoolantFrictionLossParameters, CoolantType, calculate_required_mass_flow_rate, - calculate_reynolds_number, - darcy_friction_haaland, - elbow_coeff, + coolant_friction_pressure_drop, ) from process.models.fw import N_FW_PIPE_90_DEG_BENDS, N_FW_PIPE_180_DEG_BENDS from process.models.power import PumpingPowerModelTypes @@ -2695,7 +2693,6 @@ 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, 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, @@ -2704,12 +2701,12 @@ def thermo_hydraulic_model(self, output: bool): 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, 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, @@ -2718,7 +2715,8 @@ def thermo_hydraulic_model(self, output: bool): 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) @@ -2753,7 +2751,6 @@ 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, i_liquid_breeder=2, temp_coolant_pump_outlet=self.data.fwbs.inlet_temp_liq, temp_coolant_pump_inlet=self.data.fwbs.outlet_temp_liq, @@ -2762,7 +2759,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 = ( @@ -3069,20 +3067,20 @@ def total_pressure_drop( ) # Friction - for all coolants - friction_params: CoolantFrictionLossParameters = ( - self.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, - n_pipe_90_deg_bends=n_pipe_90_deg_bends, - n_pipe_180_deg_bends=n_pipe_180_deg_bends, - len_pipe=len_pipe, - den_coolant=den_coolant, - visc_coolant=visc_coolant_dynamic, - vel_coolant=vel_coolant, - roughness_channel=self.data.fwbs.roughness_fw_channel, - radius_channel=self.data.fwbs.radius_fw_channel, - ) + 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, + n_pipe_90_deg_bends=n_pipe_90_deg_bends, + n_pipe_180_deg_bends=n_pipe_180_deg_bends, + len_pipe=len_pipe, + den_coolant=den_coolant, + visc_coolant=visc_coolant_dynamic, + vel_coolant=vel_coolant, + 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 @@ -3265,364 +3263,6 @@ 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, - roughness_channel: float, - radius_channel: 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] - - 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 = self.pipe_hydraulic_diameter(i_channel_shape=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, - ) - - # 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(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 - - Raises - ------ - ProcessValueError - If i_channel_shape is an invalid option (not in [1,2]) - """ - # 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." - ) - - 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. - - 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). - pres_coolant_pump_inlet : 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. - - Returns - ------- - float - Pumping power in MW. - - Raises - ------ - ProcessValueError - If calculated pressure drops in the coolant are too large to be feasible - - 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) - ) - / ( - 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) - - # 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) - ) - / ( - 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) - - # Error for dpres_coolant too large - if fp >= 1: - raise ProcessValueError( - "Pressure drops in coolant are too large to be feasible" - ) - - if output: - po.oheadr(self.outfile, "Mechanical Pumping Power for " + label) - po.osubhd(self.outfile, "Pumping power for " + label) - - 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 ", - ) - - return pumppower - def output_blkt_pumping_variables(self): po.oheadr(self.outfile, "Blanket pumping variables") @@ -3913,7 +3553,12 @@ def calculate_basic_geometry(self): """Calculate basic outboard blanket geometry""" 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, @@ -4044,7 +3689,12 @@ def calculate_basic_geometry(self): """Calculate basic inboard blanket geometry""" 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 82616d4293..8985f9d6b0 100644 --- a/process/models/blankets/dcll.py +++ b/process/models/blankets/dcll.py @@ -126,7 +126,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 935017e6f2..f6fbf4c563 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -15,7 +15,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 @@ -73,7 +76,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, diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index 4f134d1c19..a1ea52e794 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -7,6 +7,7 @@ import numpy as np +from process.core.coolprop_interface import FluidProperties from process.core.exceptions import ProcessValueError logger = logging.getLogger(__name__) @@ -78,6 +79,189 @@ class CoolantFrictionLossParameters: """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: @@ -99,8 +283,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 ---------- @@ -113,6 +297,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, @@ -207,8 +516,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. @@ -236,6 +549,7 @@ def calculate_reynolds_number( return den_coolant * vel_coolant * diameter / visc_coolant +@staticmethod def elbow_coeff( radius_pipe_elbow: float, deg_pipe_elbow: float, @@ -300,6 +614,7 @@ def elbow_coeff( return ximt + xift +@staticmethod def calculate_required_mass_flow_rate( p_heat_total: float, heatcap_coolant: float, diff --git a/tests/unit/models/blankets/test_blanket_library.py b/tests/unit/models/blankets/test_blanket_library.py index 5c4ecde54b..c80fef542e 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, NamedTuple +from typing import Any, NamedTuple import numpy as np import pytest @@ -7,9 +7,6 @@ from process.models.blankets.blanket_library import InboardBlanket from process.models.engineering.pumping import CoolantType -if TYPE_CHECKING: - from process.models.engineering.pumping import CoolantFrictionLossParameters - @pytest.fixture def blanket_library(process_models): @@ -21,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 @@ -345,50 +352,6 @@ def test_deltap_tot_outboard_blanket_breeder_liquid(monkeypatch, blanket_library assert dpres_total == pytest.approx(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 - ) - - class ComponentHalfHeightParam(NamedTuple): z_tf_inside_half: Any = None dz_xpoint_divertor: Any = None @@ -1025,109 +988,6 @@ def test_liquid_breeder_properties( ) -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 Unit Test for pressure_drop. - - 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 = ( - blanket_library.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, - ) - ) - - assert pressure_params.dpres_total == pytest.approx( - coolantfrictionlossparam.expected_pressure_drop_out - ) - - class LiquidBreederPressureDropMhdParam(NamedTuple): i_blkt_liquid_breeder_channel_type: Any = None a_bz_liq: Any = None @@ -1592,22 +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_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 bc5fbee253..ac5a6f9fbd 100644 --- a/tests/unit/models/engineering/test_pumping.py +++ b/tests/unit/models/engineering/test_pumping.py @@ -1,14 +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 @@ -76,3 +93,157 @@ def test_calculate_required_mass_flow_rate_zero_temperature_rise(): 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 From 300ff558717e9a42e3188841ed0b196fcfd72d2d Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 3 Jul 2026 14:09:44 +0100 Subject: [PATCH 21/40] Update some output formatting --- process/models/blankets/blanket_library.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 70ef8faa5b..80d648daf1 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -3267,7 +3267,7 @@ def output_blkt_pumping_variables(self): po.oheadr(self.outfile, "Blanket pumping variables") - po.osubhd(self.outfile, "Inboard Blanket") + po.osubhd(self.outfile, "Inboard Blanket:") po.ovarre( self.outfile, @@ -3308,7 +3308,7 @@ def output_blkt_pumping_variables(self): ) 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)", @@ -3405,8 +3405,10 @@ def output_blkt_pumping_variables(self): self.data.blanket.f_elbow_blkt_inboard_180_bend, "OP ", ) + po.oblnkl(self.outfile) + po.ocmmnt(self.outfile, "----------------------------") - po.osubhd(self.outfile, "Outboard Blanket") + po.osubhd(self.outfile, "Outboard Blanket:") po.ovarre( self.outfile, @@ -3447,7 +3449,7 @@ def output_blkt_pumping_variables(self): ) 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)", From a7e09c7eb2905dae2c389983ee2a8dd974e7ba9d Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 3 Jul 2026 14:51:34 +0100 Subject: [PATCH 22/40] Fix some coolant pumping power function imports --- process/models/blankets/blanket_library.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 80d648daf1..131c80ebdb 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -25,6 +25,7 @@ CoolantType, 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 @@ -2635,8 +2636,7 @@ 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, + 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, @@ -2645,7 +2645,8 @@ def thermo_hydraulic_model(self, output: bool): 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, - label="First Wall and Blanket", + etaiso=self.data.fwbs.etaiso, + etaiso_liq=self.data.fwbs.etaiso_liq ) ) @@ -2692,7 +2693,7 @@ 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( + 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, @@ -2706,7 +2707,7 @@ def thermo_hydraulic_model(self, output: bool): ) # Mechanical pumping power for the blanket (MW) - self.data.heat_transport.p_blkt_coolant_pump_mw = self.coolant_pumping_power( + 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, @@ -2750,7 +2751,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( + 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, From 9e7f961137fc54dfe63b0a9bdb454430a900ebe7 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 3 Jul 2026 15:44:26 +0100 Subject: [PATCH 23/40] Refactor pumping outputs to be specifically for inboard and outboard blankets --- process/main.py | 9 +- process/models/blankets/blanket_library.py | 255 +++++++++++---------- 2 files changed, 145 insertions(+), 119 deletions(-) diff --git a/process/main.py b/process/main.py index 9958897b0c..c0ee85b966 100644 --- a/process/main.py +++ b/process/main.py @@ -50,6 +50,7 @@ from process.core.process_output import OutputFileManager, oheadr from process.core.scan import Scan from process.data_structure.blanket_variables import BlktModelTypes +from process.data_structure.build_variables import InboardBlanketConfiguration from process.data_structure.cost_variables import CostModels from process.data_structure.numerics import PROCESSRunMode from process.data_structure.stellarator_variables import StellaratorModel @@ -965,7 +966,13 @@ def write(self, data, _outfile): # DCLL model self.dcll.output() - self.blanket_library.output_blkt_pumping_variables() + if ( + data.build.i_blkt_inboard + == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT + ): + self.blanket_library.output_inboard_blkt_pumping_variables() + + self.blanket_library.output_outboard_blkt_pumping_variables() # FISPACT and LOCA model (not used)- removed diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 131c80ebdb..b884fa2f1e 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -2635,19 +2635,17 @@ def thermo_hydraulic_model(self, output: bool): ) # Total mechanical pumping power (MW) - 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 - ) + 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... @@ -3264,47 +3262,45 @@ def liquid_breeder_mhd_pressure_drop( return liquid_breeder_pressure_drop_mhd - def output_blkt_pumping_variables(self): + def output_outboard_blkt_pumping_variables(self): - po.oheadr(self.outfile, "Blanket pumping variables") - - po.osubhd(self.outfile, "Inboard Blanket:") + po.oheadr(self.outfile, "Outboard Blanket pumping variables") 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, + "Outboard blanket coolant channel length (radial direction) (m)", + "(len_blkt_outboard_coolant_channel_radial)", + self.data.blanket.len_blkt_outboard_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, + "Outboard blanket coolant channel length (poloidal direction) (m)", + "(len_blkt_outboard_segment_poloidal)", + self.data.blanket.len_blkt_outboard_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, + "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 ", ) 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, + "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 ", ) 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, + "Total length of outboard blanket coolant channel straight sections (m)", + "(len_blkt_outboard_channel_total)", + self.data.blanket.len_blkt_outboard_channel_total, "OP ", ) po.oblnkl(self.outfile) @@ -3312,39 +3308,39 @@ def output_blkt_pumping_variables(self): 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, + "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 inboard blanket (Pa)", - "(dpres_blkt_inboard_coolant_channel_90_bend)", - self.data.blanket.dpres_blkt_inboard_coolant_channel_90_bend, + "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 inboard blanket (Pa)", - "(dpres_blkt_inboard_coolant_channel_90_bends_total)", - self.data.blanket.dpres_blkt_inboard_coolant_channel_90_bends_total, + "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 inboard blanket (Pa)", - "(dpres_blkt_inboard_coolant_channel_180_bend)", - self.data.blanket.dpres_blkt_inboard_coolant_channel_180_bend, + "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 inboard blanket (Pa)", - "(dpres_blkt_inboard_coolant_channel_180_bends_total)", - self.data.blanket.dpres_blkt_inboard_coolant_channel_180_bends_total, + "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 ", ) @@ -3352,100 +3348,111 @@ def output_blkt_pumping_variables(self): po.ovarre( self.outfile, "Total pressure drop for all bends (Pa)", - "(dpres_blkt_inboard_bends_total)", - self.data.blanket.dpres_blkt_inboard_bends_total, + "(dpres_blkt_outboard_bends_total)", + self.data.blanket.dpres_blkt_outboard_bends_total, "OP ", ) 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, + "Reynolds number of outboard blanket coolant", + "(reynolds_blkt_outboard_coolant)", + self.data.blanket.reynolds_blkt_outboard_coolant, "OP ", ) po.ovarre( self.outfile, - "Darcy friction factor of inboard blanket coolant", - "(darcy_frict_blkt_inboard_coolant)", - self.data.blanket.darcy_frict_blkt_inboard_coolant, + "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) 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, + "Pressure drop coefficient for straight sections of outboard blanket", + "(f_straight_blkt_outboard_coolant)", + self.data.blanket.f_straight_blkt_outboard_coolant, "OP ", ) 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, + "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 ", ) 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, + "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 ", ) 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, + "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) - po.osubhd(self.outfile, "Outboard Blanket:") + 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 ", + ) + + def output_inboard_blkt_pumping_variables(self): + + po.oheadr(self.outfile, "Inboard Blanket pumping variables") 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, + "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, - "Outboard blanket coolant channel length (poloidal direction) (m)", - "(len_blkt_outboard_segment_poloidal)", - self.data.blanket.len_blkt_outboard_segment_poloidal, + "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 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, + "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 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, + "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 outboard blanket coolant channel straight sections (m)", - "(len_blkt_outboard_channel_total)", - self.data.blanket.len_blkt_outboard_channel_total, + "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) @@ -3453,39 +3460,39 @@ def output_blkt_pumping_variables(self): 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, + "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 outboard blanket (Pa)", - "(dpres_blkt_outboard_coolant_channel_90_bend)", - self.data.blanket.dpres_blkt_outboard_coolant_channel_90_bend, + "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 outboard blanket (Pa)", - "(dpres_blkt_outboard_coolant_channel_90_bends_total)", - self.data.blanket.dpres_blkt_outboard_coolant_channel_90_bends_total, + "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 outboard blanket (Pa)", - "(dpres_blkt_outboard_coolant_channel_180_bend)", - self.data.blanket.dpres_blkt_outboard_coolant_channel_180_bend, + "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 outboard blanket (Pa)", - "(dpres_blkt_outboard_coolant_channel_180_bends_total)", - self.data.blanket.dpres_blkt_outboard_coolant_channel_180_bends_total, + "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 ", ) @@ -3493,58 +3500,70 @@ def output_blkt_pumping_variables(self): po.ovarre( self.outfile, "Total pressure drop for all bends (Pa)", - "(dpres_blkt_outboard_bends_total)", - self.data.blanket.dpres_blkt_outboard_bends_total, + "(dpres_blkt_inboard_bends_total)", + self.data.blanket.dpres_blkt_inboard_bends_total, "OP ", ) 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, + "Reynolds number of inboard blanket coolant", + "(reynolds_blkt_inboard_coolant)", + self.data.blanket.reynolds_blkt_inboard_coolant, "OP ", ) po.ovarre( self.outfile, - "Darcy friction factor of outboard blanket coolant", - "(darcy_frict_blkt_outboard_coolant)", - self.data.blanket.darcy_frict_blkt_outboard_coolant, + "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) 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, + "Pressure drop coefficient for straight sections of inboard blanket", + "(f_straight_blkt_inboard_coolant)", + self.data.blanket.f_straight_blkt_inboard_coolant, "OP ", ) 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, + "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 ", ) 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, + "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 ", ) 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, + "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.oblnkl(self.outfile) + po.ocmmnt(self.outfile, "----------------------------") + po.oblnkl(self.outfile) + + 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 ", ) From caf239e326c534b8c2212e05f174c8f47c2bbdd9 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 15 Jul 2026 08:52:09 +0100 Subject: [PATCH 24/40] Add coolant mass flow rate output --- process/core/io/plot/summary.py | 6 ++++-- process/models/engineering/pumping.py | 6 ++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 5e3b076aca..59e8c9a1d1 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -16935,7 +16935,8 @@ def plot_blanket_coolant_channel_structure_and_properties( 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" + f"Friction drop coefficient for 180° bends: {m_file.get('f_elbow_blkt_outboard_180_bend', scan=scan):.4f}\n\n" + f"Total coolant mass flow rate: {m_file.get('mflow_blkt_outboard_coolant', scan=scan):.4f} kg/s\n" ) fig.text( @@ -16974,7 +16975,8 @@ def plot_blanket_coolant_channel_structure_and_properties( 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" + f"Friction drop coefficient for 180° bends: {m_file.get('f_elbow_blkt_inboard_180_bend', scan=scan):.4f}\n\n" + f"Total coolant mass flow rate: {m_file.get('mflow_blkt_inboard_coolant', scan=scan):.4f} kg/s\n" ) fig.text( diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index a1ea52e794..4904930fea 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -313,16 +313,14 @@ def coolant_pumping_power( 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). + pres_coolant_pump_inlet : float + Pump inlet pressure (Pa). dpres_coolant : float Coolant pressure drop (Pa). mflow_coolant_total : float From c49eaa8b651513d98ec8f80f4cf6e299ad85bd65 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 15 Jul 2026 08:56:26 +0100 Subject: [PATCH 25/40] Refactor coolant pumping power types to use CALCULATE_PRESSURE_DROP and INPUT_PRESSURE_DROP --- process/models/blankets/blanket_library.py | 23 +++++++--------------- process/models/blankets/dcll.py | 8 ++++---- process/models/blankets/hcpb.py | 6 +++--- process/models/power.py | 12 +++++------ 4 files changed, 20 insertions(+), 29 deletions(-) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index b884fa2f1e..f8366a32dc 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -2576,7 +2576,7 @@ def thermo_hydraulic_model(self, output: bool): # load in pressures if primary pumping == 2 i_p_coolant_pumping = PumpingPowerModelTypes(self.data.fwbs.i_p_coolant_pumping) - if i_p_coolant_pumping == PumpingPowerModelTypes.MECHANICAL: + if i_p_coolant_pumping == PumpingPowerModelTypes.CALCULATE_PRESSURE_DROP: deltap = self.thermo_hydraulic_model_pressure_drop_calculations( output=output ) @@ -2603,7 +2603,7 @@ def thermo_hydraulic_model(self, output: bool): # If FW and BB have the same coolant... if self.data.fwbs.i_fw_blkt_shared_coolant == FWBlktCoolantLoopTypes.SHARED_LOOP: # Total pressure drop in the first wall/blanket (Pa) - if i_p_coolant_pumping == PumpingPowerModelTypes.MECHANICAL: + if i_p_coolant_pumping == PumpingPowerModelTypes.CALCULATE_PRESSURE_DROP: if ( self.data.build.i_blkt_inboard == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT @@ -2623,10 +2623,7 @@ def thermo_hydraulic_model(self, output: bool): + dpres_fw_outboard_coolant + dpres_blkt_outboard_coolant ) - elif ( - i_p_coolant_pumping - == PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP - ): + elif i_p_coolant_pumping == PumpingPowerModelTypes.INPUT_PRESSURE_DROP: deltap_fw_blkt = self.data.primary_pumping.dp_fw_blkt # Total coolant mass flow rate in the first wall/blanket (kg/s) self.data.blanket.mftotal = ( @@ -2653,7 +2650,7 @@ def thermo_hydraulic_model(self, output: bool): self.data.fwbs.i_fw_blkt_shared_coolant == FWBlktCoolantLoopTypes.SEPARATE_LOOPS ): - if i_p_coolant_pumping == PumpingPowerModelTypes.MECHANICAL: + if i_p_coolant_pumping == PumpingPowerModelTypes.CALCULATE_PRESSURE_DROP: # Total pressure drop in the first wall (Pa) dpres_fw_coolant_total = ( dpres_fw_inboard_coolant + dpres_fw_outboard_coolant @@ -2672,10 +2669,7 @@ def thermo_hydraulic_model(self, output: bool): == InboardBlanketConfiguration.NO_INBOARD_BLANKET ): dpres_blkt_coolant_total = dpres_blkt_outboard_coolant - elif ( - i_p_coolant_pumping - == PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP - ): + elif i_p_coolant_pumping == PumpingPowerModelTypes.INPUT_PRESSURE_DROP: dpres_fw_coolant_total = self.data.primary_pumping.dp_fw dpres_blkt_coolant_total = self.data.primary_pumping.dp_blkt @@ -2727,7 +2721,7 @@ def thermo_hydraulic_model(self, output: bool): # If the blanket has a liquid metal breeder... if self.data.fwbs.i_blkt_dual_coolant > 0: # Total pressure drop in the blanket (Pa) - if i_p_coolant_pumping == PumpingPowerModelTypes.MECHANICAL: + if i_p_coolant_pumping == PumpingPowerModelTypes.CALCULATE_PRESSURE_DROP: if ( self.data.build.i_blkt_inboard == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT @@ -2738,10 +2732,7 @@ def thermo_hydraulic_model(self, output: bool): == InboardBlanketConfiguration.NO_INBOARD_BLANKET ): deltap_bl_liq = deltap_blo_liq - elif ( - i_p_coolant_pumping - == PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP - ): + elif i_p_coolant_pumping == PumpingPowerModelTypes.INPUT_PRESSURE_DROP: deltap_bl_liq = self.data.primary_pumping.dp_liq # Total liquid metal breeder/coolant mass flow rate in the blanket (kg/s) self.data.blanket.mfblkt_liq = ( diff --git a/process/models/blankets/dcll.py b/process/models/blankets/dcll.py index 8985f9d6b0..9bdb454a6d 100644 --- a/process/models/blankets/dcll.py +++ b/process/models/blankets/dcll.py @@ -375,8 +375,8 @@ def dcll_power_and_heating(self, output: bool): ) elif i_p_coolant_pumping in { - PumpingPowerModelTypes.MECHANICAL, - PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP, + PumpingPowerModelTypes.CALCULATE_PRESSURE_DROP, + PumpingPowerModelTypes.INPUT_PRESSURE_DROP, }: # Mechanical pumping power is calculated for first wall and blanket self.thermo_hydraulic_model(output=output) @@ -401,8 +401,8 @@ def dcll_power_and_heating(self, output: bool): po.osubhd(self.outfile, "DCLL model: Thermal-hydraulics Component Totals") if self.data.fwbs.i_p_coolant_pumping not in { - PumpingPowerModelTypes.MECHANICAL, - PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP, + PumpingPowerModelTypes.CALCULATE_PRESSURE_DROP, + PumpingPowerModelTypes.INPUT_PRESSURE_DROP, }: po.ovarre( self.outfile, diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index f6fbf4c563..7281580a89 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -837,7 +837,7 @@ def powerflow_calc(self, output: bool): p_div_rad_total_mw=self.data.fwbs.p_div_rad_total_mw, ) - case PumpingPowerModelTypes.MECHANICAL: + case PumpingPowerModelTypes.CALCULATE_PRESSURE_DROP: # Calculate the required material properties of the FW and BB coolant. self.primary_coolant_properties(output=output) # Mechanical pumping power is calculated for first wall and blanket @@ -908,7 +908,7 @@ def powerflow_calc(self, output: bool): self.data.fwbs.radius_blkt_channel_180_bend, ) - case PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP: + case PumpingPowerModelTypes.INPUT_PRESSURE_DROP: # Issue #503 # Mechanical pumping power is calculated using specified pressure drop # for first wall and blanket circuit, including heat exchanger and pipes @@ -1606,7 +1606,7 @@ def write_output(self): if ( self.data.fwbs.i_p_coolant_pumping - != PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP + != PumpingPowerModelTypes.INPUT_PRESSURE_DROP ): po.ovarre( self.outfile, diff --git a/process/models/power.py b/process/models/power.py index b1eb59308c..2c8bfeec08 100644 --- a/process/models/power.py +++ b/process/models/power.py @@ -22,8 +22,8 @@ class PumpingPowerModelTypes(IntEnum): USER_INPUT = 0 FRACTION_OF_HEAT = 1 - MECHANICAL = 2 - MECHANICAL_WITH_PRESSURE_DROP = 3 + CALCULATE_PRESSURE_DROP = 2 + INPUT_PRESSURE_DROP = 3 logger = logging.getLogger(__name__) @@ -822,8 +822,8 @@ def component_thermal_powers(self): """ i_p_coolant_pumping = PumpingPowerModelTypes(self.data.fwbs.i_p_coolant_pumping) if i_p_coolant_pumping not in { - PumpingPowerModelTypes.MECHANICAL, - PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP, + PumpingPowerModelTypes.CALCULATE_PRESSURE_DROP, + PumpingPowerModelTypes.INPUT_PRESSURE_DROP, }: self.data.primary_pumping.p_fw_blkt_coolant_pump_mw = ( self.data.heat_transport.p_fw_coolant_pump_mw @@ -956,7 +956,7 @@ def component_thermal_powers(self): # Heat removal from first wall and divertor (MW) (only used in costs.f90) i_p_coolant_pumping = PumpingPowerModelTypes(self.data.fwbs.i_p_coolant_pumping) - if i_p_coolant_pumping != PumpingPowerModelTypes.MECHANICAL_WITH_PRESSURE_DROP: + if i_p_coolant_pumping != PumpingPowerModelTypes.INPUT_PRESSURE_DROP: self.data.heat_transport.p_fw_div_heat_deposited_mw = ( self.data.power.p_fw_heat_deposited_mw + self.data.power.p_div_heat_deposited_mw @@ -1694,7 +1694,7 @@ def plant_electric_production(self): ) if ( self.data.fwbs.i_blkt_dual_coolant > 0 - and i_p_coolant_pumping == PumpingPowerModelTypes.MECHANICAL + and i_p_coolant_pumping == PumpingPowerModelTypes.CALCULATE_PRESSURE_DROP ): self.data.heat_transport.p_plant_electric_gross_mw = ( ( From b66dfecb3a646eba5810705a61980c70daa1b7e2 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 15 Jul 2026 09:06:37 +0100 Subject: [PATCH 26/40] Rename mass flow rate variables for clarity in blanket model --- process/data_structure/blanket_variables.py | 4 ++-- process/models/blankets/blanket_library.py | 24 ++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/process/data_structure/blanket_variables.py b/process/data_structure/blanket_variables.py index 43452b356e..ee28b9b8b7 100644 --- a/process/data_structure/blanket_variables.py +++ b/process/data_structure/blanket_variables.py @@ -149,10 +149,10 @@ class BlanketData: n_blkt_outboard_channels: float = 0.0 """Outboard total number of blanket coolant pipes""" - mfblktpi: float = 0.0 + mflow_blkt_inboard_coolant_channel: float = 0.0 """Inboard mass flow rate per coolant pipe (kg/s)""" - mfblktpo: float = 0.0 + mflow_blkt_outboard_coolant_channel: float = 0.0 """Outboard mass flow rate per coolant pipe (kg/s)""" vel_blkt_inboard_coolant: float = 0.0 diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index f8366a32dc..1d138070ac 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -1200,7 +1200,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): ) # Mass flow rate per coolant pipe - self.data.blanket.mfblktpo = ( + self.data.blanket.mflow_blkt_outboard_coolant_channel = ( self.data.blanket.mflow_blkt_outboard_coolant / self.data.blanket.n_blkt_outboard_channels ) @@ -1209,7 +1209,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): # Assume BZ structure has same channel width as FW self.data.blanket.vel_blkt_outboard_coolant = self.flow_velocity( i_channel_shape=1, - mass_flow_rate=self.data.blanket.mfblktpo, + mass_flow_rate=self.data.blanket.mflow_blkt_outboard_coolant_channel, flow_density=self.data.fwbs.den_blkt_coolant, ) velblkto_liq = self.flow_velocity( @@ -1242,7 +1242,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): ) # Mass flow rate per coolant pipe - self.data.blanket.mfblktpi = ( + self.data.blanket.mflow_blkt_inboard_coolant_channel = ( self.data.blanket.mflow_blkt_inboard_coolant / self.data.blanket.n_blkt_inboard_channels ) @@ -1254,7 +1254,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): # Assume BZ structure has same channel width as FW self.data.blanket.vel_blkt_inboard_coolant = self.flow_velocity( i_channel_shape=1, - mass_flow_rate=self.data.blanket.mfblktpi, + mass_flow_rate=self.data.blanket.mflow_blkt_inboard_coolant_channel, flow_density=self.data.fwbs.den_blkt_coolant, ) velblkti_liq = self.flow_velocity( @@ -1284,7 +1284,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): ) # Mass flow rate per coolant pipe - self.data.blanket.mfblktpo = ( + self.data.blanket.mflow_blkt_outboard_coolant_channel = ( self.data.blanket.mflow_blkt_outboard_coolant / self.data.blanket.n_blkt_outboard_channels ) @@ -1293,7 +1293,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): # Assume BZ structure has same channel width as FW self.data.blanket.vel_blkt_outboard_coolant = self.flow_velocity( i_channel_shape=1, - mass_flow_rate=self.data.blanket.mfblktpo, + mass_flow_rate=self.data.blanket.mflow_blkt_outboard_coolant_channel, flow_density=self.data.fwbs.den_blkt_coolant, ) @@ -1336,7 +1336,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): ) # Mass flow rate per coolant pipe - self.data.blanket.mfblktpi = ( + self.data.blanket.mflow_blkt_inboard_coolant_channel = ( self.data.blanket.mflow_blkt_inboard_coolant / self.data.blanket.n_blkt_inboard_channels ) @@ -1345,7 +1345,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): # Assume BZ structure has same channel width as FW self.data.blanket.vel_blkt_inboard_coolant = self.flow_velocity( i_channel_shape=1, - mass_flow_rate=self.data.blanket.mfblktpi, + mass_flow_rate=self.data.blanket.mflow_blkt_inboard_coolant_channel, flow_density=self.data.fwbs.den_blkt_coolant, ) @@ -1382,7 +1382,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): ) # Mass flow rate per coolant pipe - self.data.blanket.mfblktpo = ( + self.data.blanket.mflow_blkt_outboard_coolant_channel = ( self.data.blanket.mflow_blkt_outboard_coolant / self.data.blanket.n_blkt_outboard_channels ) @@ -1391,7 +1391,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): # Assume BZ structure has same channel width as FW self.data.blanket.vel_blkt_outboard_coolant = self.flow_velocity( i_channel_shape=1, - mass_flow_rate=self.data.blanket.mfblktpo, + mass_flow_rate=self.data.blanket.mflow_blkt_outboard_coolant_channel, flow_density=self.data.fwbs.den_blkt_coolant, ) @@ -1413,7 +1413,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): ) # Mass flow rate per coolant pipe - self.data.blanket.mfblktpi = ( + self.data.blanket.mflow_blkt_inboard_coolant_channel = ( self.data.blanket.mflow_blkt_inboard_coolant / self.data.blanket.n_blkt_inboard_channels ) @@ -1422,7 +1422,7 @@ def thermo_hydraulic_model_pressure_drop_calculations(self, output: bool): # Assume BZ structure has same channel width as FW self.data.blanket.vel_blkt_inboard_coolant = self.flow_velocity( i_channel_shape=1, - mass_flow_rate=self.data.blanket.mfblktpi, + mass_flow_rate=self.data.blanket.mflow_blkt_inboard_coolant_channel, flow_density=self.data.fwbs.den_blkt_coolant, ) From b6dabed5be454f3b03bca02f6027ec8f95618f19 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 15 Jul 2026 09:18:56 +0100 Subject: [PATCH 27/40] Add coolant mass flow rate and velocity outputs for single channels in blanket model --- process/core/io/plot/summary.py | 4 ++++ process/models/blankets/blanket_library.py | 28 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 59e8c9a1d1..b4c46d2e0b 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -16937,6 +16937,8 @@ def plot_blanket_coolant_channel_structure_and_properties( 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\n" f"Total coolant mass flow rate: {m_file.get('mflow_blkt_outboard_coolant', scan=scan):.4f} kg/s\n" + f"Coolant mass flow rate in single channel: {m_file.get('mflow_blkt_outboard_coolant_channel', scan=scan):.4f} kg/s\n" + f"Coolant velocity in single channel: {m_file.get('vel_blkt_outboard_coolant', scan=scan):.4f} m/s" ) fig.text( @@ -16977,6 +16979,8 @@ def plot_blanket_coolant_channel_structure_and_properties( 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\n" f"Total coolant mass flow rate: {m_file.get('mflow_blkt_inboard_coolant', scan=scan):.4f} kg/s\n" + f"Coolant mass flow rate in single channel: {m_file.get('mflow_blkt_inboard_coolant_channel', scan=scan):.4f} kg/s\n" + f"Velocity of inboard blanket coolant in single channel: {m_file.get('vel_blkt_inboard_coolant', scan=scan):.4f} m/s" ) fig.text( diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 1d138070ac..38f3865888 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -3404,6 +3404,20 @@ def output_outboard_blkt_pumping_variables(self): self.data.blanket.mflow_blkt_outboard_coolant, "OP ", ) + po.ovarre( + self.outfile, + "Mass flow rate of outboard blanket coolant in single channel (kg/s)", + "(mflow_blkt_outboard_coolant_channel)", + self.data.blanket.mflow_blkt_outboard_coolant_channel, + "OP ", + ) + po.ovarre( + self.outfile, + "Velocity of outboard blanket coolant in single channel (m/s)", + "(vel_blkt_outboard_coolant)", + self.data.blanket.vel_blkt_outboard_coolant, + "OP ", + ) def output_inboard_blkt_pumping_variables(self): @@ -3557,6 +3571,20 @@ def output_inboard_blkt_pumping_variables(self): self.data.blanket.mflow_blkt_inboard_coolant, "OP ", ) + po.ovarre( + self.outfile, + "Mass flow rate of inboard blanket coolant in single channel (kg/s)", + "(mflow_blkt_inboard_coolant_channel)", + self.data.blanket.mflow_blkt_inboard_coolant_channel, + "OP ", + ) + po.ovarre( + self.outfile, + "Velocity of inboard blanket coolant in single channel (m/s)", + "(vel_blkt_inboard_coolant)", + self.data.blanket.vel_blkt_inboard_coolant, + "OP ", + ) class OutboardBlanket(BlanketLibrary): From 4db51cef5f43eb1c34a585a4151decb373260741 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 3 Aug 2026 10:49:26 +0100 Subject: [PATCH 28/40] Post rebase fixes --- process/core/io/plot/summary.py | 16 +++++++++------- process/models/blankets/blanket_library.py | 3 ++- process/models/blankets/hcpb.py | 3 ++- process/models/engineering/pumping.py | 12 ++++++++---- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index b4c46d2e0b..5e4cf7fb87 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -17647,13 +17647,19 @@ def _add_page(name: str | None = None): ) plot_main_power_flow( - figs[36].add_subplot(111, aspect="equal"), m_file, scan, figs[36] + _add_page("main_power_flow").add_subplot(111, aspect="equal"), + m_file, + scan, + pages["main_power_flow"], ) - ax24 = figs[37].add_subplot(111) + ax24 = _add_page("power_profile_over_time").add_subplot(111) # set_position([left, bottom, width, height]) -> height ~ 0.66 => ~2/3 of page height ax24.set_position([0.08, 0.35, 0.84, 0.57]) - plot_system_power_profiles_over_time(ax24, m_file, scan, figs[37]) + plot_system_power_profiles_over_time( + ax24, m_file, scan, pages["power_profile_over_time"] + ) + return list(pages.values()) def create_thickness_builds(m_file, scan: int): @@ -17758,10 +17764,6 @@ def add_page_footer( color="dimgray", ) - # create main plot - # Increase range when adding new page - pages = [plt.figure(figsize=(12, 9), dpi=80) for i in range(38)] - # run main_plot mfile_obj = MFile(mfile) if mfile else MFile("MFILE.DAT") run_label = f"{mfile_obj.get('fileprefix', scan=-1)} | scan {scan or -1} | {mfile_obj.get('date', scan=-1)} {mfile_obj.get('time', scan=-1)} | {mfile_obj.get('tagno', scan=-1)} | Branch: {mfile_obj.get('branch_name', scan=-1)} " diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index 38f3865888..d5349f6d11 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -3013,7 +3013,8 @@ def total_pressure_drop( nopolchan: int, label: str, ) -> tuple[float, CoolantFrictionLossParameters]: - """Calculate the total pressure drop (Pa) for coolant flow in the first wall (FW) and breeding blanket (BZ). + """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. diff --git a/process/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 7281580a89..785b4145f9 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -863,7 +863,8 @@ def powerflow_calc(self, output: bool): if output: po.ovarre( self.outfile, - "Mechanical pumping power for FW and blanket cooling loop including heat exchanger (MW)", + "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 ", diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index 4904930fea..71566070da 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -309,12 +309,14 @@ def coolant_pumping_power( etaiso: float, etaiso_liq: float, ) -> float: - """Calculate the coolant pumping power in MW for the first wall (FW) or breeding blanket (BZ) coolant. + """Calculate the coolant pumping power in MW for the first wall (FW) or breeding + blanket (BZ) coolant. Parameters ---------- i_liquid_breeder : int - Switch for primary coolant or secondary coolant/breeder (1=primary He/H2O, 2=secondary PbLi/Li). + 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 @@ -365,7 +367,8 @@ def coolant_pumping_power( # 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 + # 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, @@ -585,7 +588,8 @@ def elbow_coeff( 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." + "No formula for 70 <= elbow angle(deg) <= 100, only 90 deg option available " + "in this range." ) r_ratio = radius_pipe_elbow / dia_pipe From 4d245fc270dc29b627236660973d05f7092b9bb0 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 3 Aug 2026 10:59:27 +0100 Subject: [PATCH 29/40] Only output pumping variables if pressure drop is calculated --- process/core/io/plot/summary.py | 317 +++++++++++++++++++++++++++++++- process/main.py | 14 +- 2 files changed, 320 insertions(+), 11 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 5e4cf7fb87..752de3fc2b 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -91,6 +91,7 @@ PlasmaProfileShapeType, calculate_profile_shell_contributions, ) +from process.models.power import PumpingPowerModelTypes from process.models.pulse import PulseTimings from process.models.superconductors import SuperconductorModel from process.models.tfcoil.base import ( @@ -16910,10 +16911,312 @@ def plot_outboard_blanket_coolant_properties(fig: plt.Figure, m_file: MFile, sca fig.tight_layout() +def plot_plasma_thermal_energy_profiles(axis, m_file: MFile, scan: int): + """Function to plot plasma thermal energy profiles on the given axis. -def plot_blanket_coolant_channel_structure_and_properties( - fig: plt.Figure, m_file: MFile, scan: int -): + Parameters + ---------- + axis : + Matplotlib axis to plot on + m_file : + MFILE + scan : + scan to read from MFILE + """ + n_plasma_profile_elements = int(m_file.get("n_plasma_profile_elements", scan=scan)) + + eden_plasma_electrons_thermal_profile_mj = [ + m_file.get(f"eden_plasma_electrons_thermal_profile{i}", scan=scan) / 1e6 + for i in range(n_plasma_profile_elements) + ] + eden_plasma_ions_thermal_profile_mj = [ + m_file.get(f"eden_plasma_ions_thermal_profile{i}", scan=scan) / 1e6 + for i in range(n_plasma_profile_elements) + ] + eden_plasma_thermal_profile_mj = [ + m_file.get(f"eden_plasma_thermal_profile{i}", scan=scan) / 1e6 + for i in range(n_plasma_profile_elements) + ] + e_plasma_electrons_thermal_profile_mj = [ + m_file.get(f"e_plasma_electrons_thermal_profile{i}", scan=scan) / 1e6 + for i in range(n_plasma_profile_elements) + ] + + e_plasma_ions_thermal_profile_mj = [ + m_file.get(f"e_plasma_ions_thermal_profile{i}", scan=scan) / 1e6 + for i in range(n_plasma_profile_elements) + ] + e_plasma_thermal_profile_mj = [ + m_file.get(f"e_plasma_thermal_profile{i}", scan=scan) / 1e6 + for i in range(n_plasma_profile_elements) + ] + + axis.plot( + np.linspace(0, 1, n_plasma_profile_elements), + e_plasma_electrons_thermal_profile_mj, + label="$W_{\\text{e}}$", + color="tab:blue", + linestyle=":", + ) + axis.plot( + np.linspace(0, 1, n_plasma_profile_elements), + e_plasma_ions_thermal_profile_mj, + label="$W_{\\text{i}}$", + color="tab:blue", + linestyle="--", + ) + axis.plot( + np.linspace(0, 1, n_plasma_profile_elements), + e_plasma_thermal_profile_mj, + label="$W_{\\text{total}}$", + color="tab:blue", + linestyle="-", + ) + + total_thermal_energy_max_index = int(np.argmax(e_plasma_thermal_profile_mj)) + total_thermal_energy_max_rho = np.linspace(0, 1, n_plasma_profile_elements)[ + total_thermal_energy_max_index + ] + total_thermal_energy_max = e_plasma_thermal_profile_mj[ + total_thermal_energy_max_index + ] + axis.axvline( + total_thermal_energy_max_rho, + color="tab:red", + alpha=0.7, + label="$W_{\\text{total, peak}}$", + ) + axis.axhline( + total_thermal_energy_max, + color="tab:red", + alpha=0.7, + ) + + density_axis = axis.twinx() + density_axis.plot( + np.linspace(0, 1, n_plasma_profile_elements), + eden_plasma_electrons_thermal_profile_mj, + label="$W_{\\text{density, e}}$", + color="tab:orange", + linestyle=":", + ) + density_axis.plot( + np.linspace(0, 1, n_plasma_profile_elements), + eden_plasma_ions_thermal_profile_mj, + label="$W_{\\text{density, i}}$", + color="tab:orange", + linestyle="--", + ) + density_axis.plot( + np.linspace(0, 1, n_plasma_profile_elements), + eden_plasma_thermal_profile_mj, + label="$W_{\\text{density, total}}$", + color="tab:orange", + linestyle="-", + ) + + axis.grid(True, alpha=0.3) + axis.minorticks_on() + axis.set_xlabel(r"$\rho \quad [r/a]$") + axis.set_xlim(left=0.0, right=1.0) + axis.set_ylabel( + "Thermal Energy [MJ]", + color="tab:blue", + ) + axis.tick_params(axis="y", colors="tab:blue") + density_axis.set_ylabel( + "Thermal Energy Density [MJ/m$^3$]", + color="tab:orange", + ) + density_axis.tick_params(axis="y", colors="tab:orange") + handles, labels = axis.get_legend_handles_labels() + density_handles, density_labels = density_axis.get_legend_handles_labels() + axis.legend(handles + density_handles, labels + density_labels) + + +def plot_cumulative_plasma_thermal_energy_profiles(axis, m_file: MFile, scan: int): + """Function to plot the cumulative plasma thermal energy profiles on the given axis. + + Parameters + ---------- + axis : + Matplotlib axis to plot on + m_file : + MFILE + scan : + scan to read from MFILE + """ + n_plasma_profile_elements = int(m_file.get("n_plasma_profile_elements", scan=scan)) + e_plasma_thermal_total_mj = m_file.get("e_plasma_thermal_total", scan=scan) / 1e6 + e_plasma_electrons_thermal_profile_mj = [ + m_file.get(f"e_plasma_electrons_thermal_profile{i}", scan=scan) / 1e6 + for i in range(n_plasma_profile_elements) + ] + + e_plasma_ions_thermal_profile_mj = [ + m_file.get(f"e_plasma_ions_thermal_profile{i}", scan=scan) / 1e6 + for i in range(n_plasma_profile_elements) + ] + e_plasma_thermal_profile_mj = [ + m_file.get(f"e_plasma_thermal_profile{i}", scan=scan) / 1e6 + for i in range(n_plasma_profile_elements) + ] + + axis.plot( + np.linspace(0, 1, n_plasma_profile_elements), + np.cumsum(e_plasma_electrons_thermal_profile_mj), + label="$\\Sigma W_{\\text{e}}$", + color="tab:blue", + linestyle=":", + ) + axis.plot( + np.linspace(0, 1, n_plasma_profile_elements), + np.cumsum(e_plasma_ions_thermal_profile_mj), + label="$\\Sigma W_{\\text{i}}$", + color="tab:blue", + linestyle="--", + ) + axis.plot( + np.linspace(0, 1, n_plasma_profile_elements), + np.cumsum(e_plasma_thermal_profile_mj), + label="$\\Sigma W_{\\text{total}}$", + color="tab:blue", + linestyle="-", + ) + axis.axhline( + y=e_plasma_thermal_total_mj, + label="$W_{\\text{thermal,total}}$", + color="tab:red", + linestyle="--", + ) + cumulative_thermal_energy_mj = np.cumsum(e_plasma_thermal_profile_mj) + half_thermal_energy_mj = 0.5 * e_plasma_thermal_total_mj + half_thermal_energy_position = np.interp( + half_thermal_energy_mj, + cumulative_thermal_energy_mj, + np.linspace(0, 1, n_plasma_profile_elements), + ) + axis.axhline( + y=half_thermal_energy_mj, + label="$50\\%\\ W_{\\text{thermal,total}}$", + color="tab:green", + linestyle=":", + ) + axis.axvline( + x=half_thermal_energy_position, + color="tab:green", + linestyle=":", + ) + + axis.legend() + axis.set_title("Thermal Energy Profiles and Cumulative Distribution") + axis.grid(True, alpha=0.3) + axis.minorticks_on() + axis.tick_params(axis="x", labelbottom=False) + axis.set_xlim(left=0.0, right=1.0) + axis.set_ylabel( + "Cumulative Thermal Energy [MJ]", + ) + + +def plot_outboard_blanket_coolant_properties(fig: plt.Figure, m_file: MFile, scan: int): + """Plots the properties of the outboard blanket coolant along the poloidal direction.""" + temp_blkt_coolant_in = float(m_file.get("temp_blkt_coolant_in", scan=scan)) + temp_blkt_coolant_out = float(m_file.get("temp_blkt_coolant_out", scan=scan)) + pres_blkt_coolant = float(m_file.get("pres_blkt_coolant", scan=scan)) + dens_blkt_coolant_in = PropsSI( + "Dmass", "T", temp_blkt_coolant_in, "P", pres_blkt_coolant, "Helium" + ) + dens_blkt_coolant_out = PropsSI( + "Dmass", "T", temp_blkt_coolant_out, "P", pres_blkt_coolant, "Helium" + ) + + fig.clear() + axes = fig.subplots(2, 2) + + axis = axes[0, 0] + + plot = PropertyPlot("Helium", "PH", axis=axis, unit_system="SI") + + # #axis.set_xscale('log') + axis.set_ylim(((pres_blkt_coolant) * 0.9), (pres_blkt_coolant) * 1.5) + # plot.set_axis_limits([None, None, (pres_blkt_coolant/1e3)*0.9, (pres_blkt_coolant/1e3)*1.1]) + + plot.calc_isolines(CoolProp.iT) # Temperature + plot.calc_isolines(CoolProp.iSmass) # Entropy + plot.calc_isolines(CoolProp.iDmass) # Density + # plot.axis.relim() + # plot.axis.autoscale_view() + plot.draw() + + axis.set_title("Pressure-enthalpy plot") + + axis = axes[0, 1] + plot = PropertyPlot("Helium", "TD", axis=axis, unit_system="SI") + + density_min = min(dens_blkt_coolant_in, dens_blkt_coolant_out) + density_max = max(dens_blkt_coolant_in, dens_blkt_coolant_out) + temperature_min = min(temp_blkt_coolant_in, temp_blkt_coolant_out) + temperature_max = max(temp_blkt_coolant_in, temp_blkt_coolant_out) + + density_margin = max((density_max - density_min) * 0.2, max(density_max, 1.0) * 0.05) + temperature_margin = max( + (temperature_max - temperature_min) * 0.2, + max(temperature_max, 1.0) * 0.05, + ) + + axis.set_xlim(density_min - density_margin, density_max + density_margin) + axis.set_ylim( + temperature_min - temperature_margin, temperature_max + temperature_margin + ) + + plot.calc_isolines(CoolProp.iP) # Pressure + plot.calc_isolines(CoolProp.iHmass) # Enthalpy + plot.draw() + + axis.plot( + [dens_blkt_coolant_in, dens_blkt_coolant_out], + [temp_blkt_coolant_in, temp_blkt_coolant_out], + color="C3", + marker="o", + linewidth=1.5, + label="Coolant path", + ) + axis.legend(fontsize=8, loc="best") + axis.set_title("Temperature-density plot") + + axis = axes[1, 0] + plot = PropertyPlot("Helium", "PD", axis=axis, unit_system="SI") + + pressure_margin = max(pres_blkt_coolant * 0.1, 1.0) + + axis.set_xlim(density_min - density_margin, density_max + density_margin) + axis.set_ylim( + pres_blkt_coolant - pressure_margin, pres_blkt_coolant + pressure_margin + ) + + plot.calc_isolines(CoolProp.iT) # Temperature + plot.calc_isolines(CoolProp.iHmass) # Enthalpy + plot.draw() + + axis.plot( + [dens_blkt_coolant_in, dens_blkt_coolant_out], + [pres_blkt_coolant, pres_blkt_coolant], + color="C3", + marker="o", + linewidth=1.5, + label="Coolant path", + ) + axis.legend(fontsize=8, loc="best") + axis.set_title("Pressure-density plot") + + axes[1, 1].axis("off") + + fig.tight_layout() + + + +def plot_blanket_coolant_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 = ( @@ -17642,9 +17945,11 @@ def _add_page(name: str | None = None): ) plot_blkt_pipe_bends(_add_page("blkt_cooling"), m_file, scan) - plot_blanket_coolant_channel_structure_and_properties( - pages["blkt_cooling"], m_file, scan - ) + if ( + m_file.get("i_p_coolant_pumping", scan=scan) + == PumpingPowerModelTypes.CALCULATE_PRESSURE_DROP + ): + plot_blanket_coolant_properties(pages["blkt_cooling"], m_file, scan) plot_main_power_flow( _add_page("main_power_flow").add_subplot(111, aspect="equal"), diff --git a/process/main.py b/process/main.py index c0ee85b966..843fbebe60 100644 --- a/process/main.py +++ b/process/main.py @@ -110,7 +110,7 @@ IonTemperatureProfile, ) from process.models.physics.scrape_off_layer import ScrapeOffLayer -from process.models.power import Power +from process.models.power import Power, PumpingPowerModelTypes from process.models.pulse import Pulse from process.models.shield import Shield from process.models.stellarator.neoclassics import Neoclassics @@ -967,12 +967,16 @@ def write(self, data, _outfile): self.dcll.output() if ( - data.build.i_blkt_inboard - == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT + data.fwbs.i_p_coolant_pumping + == PumpingPowerModelTypes.CALCULATE_PRESSURE_DROP ): - self.blanket_library.output_inboard_blkt_pumping_variables() + if ( + data.build.i_blkt_inboard + == InboardBlanketConfiguration.INBOARD_BLANKET_PRESENT + ): + self.blanket_library.output_inboard_blkt_pumping_variables() - self.blanket_library.output_outboard_blkt_pumping_variables() + self.blanket_library.output_outboard_blkt_pumping_variables() # FISPACT and LOCA model (not used)- removed From f490d43eb92f993c2d829ddf3e26f68d2d10980f Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 5 Aug 2026 10:12:33 +0100 Subject: [PATCH 30/40] Add pumping power calculation option output in first wall pumping details --- process/models/fw.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/process/models/fw.py b/process/models/fw.py index 418fd5259d..2f134dced1 100644 --- a/process/models/fw.py +++ b/process/models/fw.py @@ -788,7 +788,13 @@ def output_fw_geometry(self): def output_fw_pumping(self): """Outputs the first wall pumping details to the output file.""" po.oheadr(self.outfile, "First wall pumping") - + po.ovarre( + self.outfile, + "Pumping power calculation option", + "(i_p_coolant_pumping)", + self.data.fwbs.i_p_coolant_pumping, + "OP ", + ) po.ovarre( self.outfile, "First wall coolant type", From 095ba18bdce0fc7655334f8eb9cdf13b191afedb Mon Sep 17 00:00:00 2001 From: Christopher Ashe <91618944+chris-ashe@users.noreply.github.com> Date: Fri, 21 Aug 2026 16:00:07 +0100 Subject: [PATCH 31/40] Update process/models/blankets/blanket_library.py Co-authored-by: Timothy <75321887+timothy-nunn@users.noreply.github.com> --- process/models/blankets/blanket_library.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index d5349f6d11..bf671e9964 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -3058,7 +3058,7 @@ def total_pressure_drop( ) # Friction - for all coolants - friction_params: CoolantFrictionLossParameters = coolant_friction_pressure_drop( + friction_params = 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, From f060e2612c0f5b79176123d9caf0b3f3f466e1f3 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 25 Aug 2026 13:49:18 +0100 Subject: [PATCH 32/40] Refactor blanket models to use pipe_hydraulic_diameter function and add docstring improvements --- process/models/blankets/blanket_library.py | 9 +++++---- process/models/blankets/dcll.py | 5 ++++- process/models/engineering/pumping.py | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/process/models/blankets/blanket_library.py b/process/models/blankets/blanket_library.py index bf671e9964..106aae93c3 100644 --- a/process/models/blankets/blanket_library.py +++ b/process/models/blankets/blanket_library.py @@ -26,6 +26,7 @@ calculate_required_mass_flow_rate, coolant_friction_pressure_drop, coolant_pumping_power, + pipe_hydraulic_diameter, ) from process.models.fw import N_FW_PIPE_90_DEG_BENDS, N_FW_PIPE_180_DEG_BENDS from process.models.power import PumpingPowerModelTypes @@ -3255,7 +3256,7 @@ def liquid_breeder_mhd_pressure_drop( return liquid_breeder_pressure_drop_mhd def output_outboard_blkt_pumping_variables(self): - + """Output the outboard blanket pumping variables to the output file.""" po.oheadr(self.outfile, "Outboard Blanket pumping variables") po.ovarre( @@ -3421,7 +3422,7 @@ def output_outboard_blkt_pumping_variables(self): ) def output_inboard_blkt_pumping_variables(self): - + """Output the inboard blanket pumping variables to the output file.""" po.oheadr(self.outfile, "Inboard Blanket pumping variables") po.ovarre( @@ -3595,7 +3596,7 @@ def calculate_basic_geometry(self): """Calculate basic outboard blanket geometry""" self.component_volumes() - dia_blkt_channel = self.pipe_hydraulic_diameter( + 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, @@ -3731,7 +3732,7 @@ def calculate_basic_geometry(self): """Calculate basic inboard blanket geometry""" self.component_volumes() - dia_blkt_channel = self.pipe_hydraulic_diameter( + 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, diff --git a/process/models/blankets/dcll.py b/process/models/blankets/dcll.py index 9bdb454a6d..e2cfa26c7c 100644 --- a/process/models/blankets/dcll.py +++ b/process/models/blankets/dcll.py @@ -10,6 +10,9 @@ calculate_pipe_bend_radius, pumping_powers_as_fractions, ) +from process.models.engineering.pumping import ( + pipe_hydraulic_diameter, +) from process.models.power import PumpingPowerModelTypes @@ -126,7 +129,7 @@ def run(self, output: bool = False): self.data.blanket.deg_blkt_inboard_poloidal_plasma / 360.0 ) - dia_blkt_channel = self.pipe_hydraulic_diameter( + 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, diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index 71566070da..43d0f17bf7 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -249,6 +249,11 @@ def pipe_hydraulic_diameter( i_channel_shape : switch for circular or rectangular channel crossection. Shape depends on whether primary or secondary coolant + + Raises + ------ + ProcessValueError + If i_channel_shape is not 1 or 2. """ # If primary coolant then circular channels assumed if i_channel_shape == 1: @@ -341,6 +346,11 @@ def coolant_pumping_power( float Pumping power in MW. + Raises + ------ + ProcessValueError + If the pressure drops in the coolant are too large to be feasible. + References ---------- - Idel'Cik, I. E. (1969), Memento des pertes de charge @@ -575,6 +585,12 @@ def elbow_coeff( float Elbow coefficient for pressure drop calculation + Raises + ------ + ProcessValueError + If deg_pipe_elbow is between 70 and 100 degrees, as no formula is + available for this range. + References ---------- [1] Idel'Cik, I. E. (1969), Memento des pertes de charge, From ddedc01bbefc9aa17887312a30be62717e6f2c13 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Tue, 25 Aug 2026 13:50:37 +0100 Subject: [PATCH 33/40] Remove unnecessary @staticmethod decorators from calculate_reynolds_number and elbow_coeff functions --- process/models/engineering/pumping.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index 43d0f17bf7..7dc3f691e1 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -527,7 +527,6 @@ def gnielinski_heat_transfer_coefficient( return heat_transfer_coefficient -@staticmethod def calculate_reynolds_number( den_coolant: float, vel_coolant: float, @@ -560,7 +559,6 @@ def calculate_reynolds_number( return den_coolant * vel_coolant * diameter / visc_coolant -@staticmethod def elbow_coeff( radius_pipe_elbow: float, deg_pipe_elbow: float, From 42a80d15e79b296adbc3a30177eafd6d7903dfe2 Mon Sep 17 00:00:00 2001 From: Christopher Ashe <91618944+chris-ashe@users.noreply.github.com> Date: Wed, 2 Sep 2026 09:16:36 +0100 Subject: [PATCH 34/40] Update process/models/engineering/pumping.py Co-authored-by: Timothy <75321887+timothy-nunn@users.noreply.github.com> --- process/models/engineering/pumping.py | 1 - 1 file changed, 1 deletion(-) diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index 7dc3f691e1..6bd6df617e 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -266,7 +266,6 @@ def pipe_hydraulic_diameter( 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: From 7e8e0d6fc62b8b517efe95b0936b2aa3e737934a Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 2 Sep 2026 09:20:59 +0100 Subject: [PATCH 35/40] Refactor pressure drop output formatting in plot_blanket_coolant_properties and remove unnecessary @staticmethod decorator from calculate_required_mass_flow_rate --- process/core/io/plot/summary.py | 28 +++++++++++++-------------- process/models/engineering/pumping.py | 1 - 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 752de3fc2b..944c82b794 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -17228,13 +17228,13 @@ def plot_blanket_coolant_properties(fig: plt.Figure, m_file: MFile, scan: int): 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"Pressure drop for straight sections: {m_file.get('dpres_blkt_outboard_coolant_channel_straight_total', scan=scan):,.2f} Pa\n" + f"Pressure drop for 90° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_90_bend', scan=scan):,.2f} Pa\n" + f"Total pressure drop for 90° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_90_bends_total', scan=scan):,.2f} Pa\n" + f"Pressure drop for 180° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_180_bend', scan=scan):,.2f} Pa\n" + f"Total pressure drop for 180° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_180_bends_total', scan=scan):,.2f} Pa\n" + f"Total pressure drop for all bends: {m_file.get('dpres_blkt_outboard_bends_total', scan=scan):,.2f} 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" @@ -17270,13 +17270,13 @@ def plot_blanket_coolant_properties(fig: plt.Figure, m_file: MFile, scan: int): 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"Pressure drop for straight sections: {m_file.get('dpres_blkt_inboard_coolant_channel_straight_total', scan=scan):,.2f} Pa\n" + f"Pressure drop for 90° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_90_bend', scan=scan):,.2f} Pa\n" + f"Total pressure drop for 90° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_90_bends_total', scan=scan):,.2f} Pa\n" + f"Pressure drop for 180° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_180_bend', scan=scan):,.2f} Pa\n" + f"Total pressure drop for 180° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_180_bends_total', scan=scan):,.2f} Pa\n" + f"Total pressure drop for all bends: {m_file.get('dpres_blkt_inboard_bends_total', scan=scan):,.2f} 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" diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index 6bd6df617e..119ce670f6 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -629,7 +629,6 @@ def elbow_coeff( return ximt + xift -@staticmethod def calculate_required_mass_flow_rate( p_heat_total: float, heatcap_coolant: float, From f11c93a4662409e793ce963ddbcd50cf3e2caac6 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 14 Sep 2026 14:23:18 +0100 Subject: [PATCH 36/40] Calculate adiabatic index directly from CoolProp properties. and catch error for liquid pump flow that dosent have a compressable flow --- .../eng-models/generic_methods/pumping.md | 6 +++-- process/models/engineering/pumping.py | 24 ++++++++----------- tests/unit/models/engineering/test_pumping.py | 6 ++--- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/documentation/source/eng-models/generic_methods/pumping.md b/documentation/source/eng-models/generic_methods/pumping.md index 543756144a..c27fc0a147 100644 --- a/documentation/source/eng-models/generic_methods/pumping.md +++ b/documentation/source/eng-models/generic_methods/pumping.md @@ -9,13 +9,15 @@ To calculate the coolant pumping power we use the change in enthalpies of the co 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)} +P = \frac{\frac{\dot{m} \times \left(H_{\text{out}}-H_{\text{in}}\right)}{\eta}}{\left(1-f_p\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. +$f_p$ is a seocondary correction accounting for the fact that some of the pump's mechanical work reheats the coolant, and that reheat is already implicitly present in the prescribed $\left(T_{\text{pump,in}}-T_{\text{pump,out}}\right)$ used elsewhere in the plant energy balance, without this correction that portion of the pump work would be double counted. The term uses the classical isentropic ideal-gas relation $T_2/T_1 = (P_2/P_1)^{(\gamma-1)/\gamma}$ + $$ -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)} +f_p = \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)} $$ ------------------ diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index 119ce670f6..a966068ab7 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -359,9 +359,6 @@ def coolant_pumping_power( # 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, @@ -373,6 +370,13 @@ def coolant_pumping_power( pressure=pres_coolant_pump_outlet, ) + # Real gamma (cp/cv) at the pump outlet state, so liquid water (gamma~1) + # and superheated steam (gamma~1.3) are both handled correctly + gamma = ( + pump_outlet_fluid_properties.specific_heat_const_p + / pump_outlet_fluid_properties.specific_heat_const_v + ) + # Assume isentropic pump so that s1 = s2 s1 = pump_outlet_fluid_properties.entropy @@ -410,17 +414,9 @@ def coolant_pumping_power( # 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)) - ) + # Liquid metal breeders (PbLi/Li) are incompressible, so there is no + # gas-law reheat effect from compression: fp is negligible (gamma~1). + fp = 0.0 pumppower = ( 1e-6 * mflow_coolant_total * spec_vol * dpres_coolant / etaiso_liq ) / (1 - fp) diff --git a/tests/unit/models/engineering/test_pumping.py b/tests/unit/models/engineering/test_pumping.py index ac5a6f9fbd..a532047f7e 100644 --- a/tests/unit/models/engineering/test_pumping.py +++ b/tests/unit/models/engineering/test_pumping.py @@ -213,7 +213,7 @@ def test_coolant_friction_loss(coolantfrictionlossparam, monkeypatch, blanket_li ) -def test_pumppower_primary_helium(): +def test_pumppower_secondary_pb_li(): data = { "i_liquid_breeder": 2, @@ -228,10 +228,10 @@ def test_pumppower_primary_helium(): "etaiso_liq": 0.85, } - assert pytest.approx(coolant_pumping_power(**data)) == 1.8251284651310427 + assert pytest.approx(coolant_pumping_power(**data)) == 1.3062070319498325 -def test_pumppower_secondary_pb_li(): +def test_pumppower_primary_helium(): data = { "i_liquid_breeder": 1, From 8d193fc71d54cf003569d27763179d31f87381b2 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Mon, 14 Sep 2026 14:34:47 +0100 Subject: [PATCH 37/40] Clarify explanation of specific heat capacity usage in pumping systems for open, steady-flow conditions --- .../eng-models/generic_methods/pumping.md | 6 +- process/models/blankets/hcpb.py | 96 +++++++++---------- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/documentation/source/eng-models/generic_methods/pumping.md b/documentation/source/eng-models/generic_methods/pumping.md index c27fc0a147..829f36bf8e 100644 --- a/documentation/source/eng-models/generic_methods/pumping.md +++ b/documentation/source/eng-models/generic_methods/pumping.md @@ -190,6 +190,10 @@ where $\dot{m}$ is the required mass flow rate in, $P$ is the heating power to b !!! 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. + For pumping, the specific heat capacity for constant pressure $(c_{\text{p}})$ is used because + cooling loops are open, steady-flow systems: coolant flows continuously through pipes, heat + exchangers and pumps at a system pressure that is actively maintained by the pump's operating + point and any pressure-control components (e.g. pressurisers, control valves), rather than + varying freely with the coolant's volume. 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/models/blankets/hcpb.py b/process/models/blankets/hcpb.py index 785b4145f9..ee7cc67ad3 100644 --- a/process/models/blankets/hcpb.py +++ b/process/models/blankets/hcpb.py @@ -860,54 +860,54 @@ 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, - ) + 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, + ) case PumpingPowerModelTypes.INPUT_PRESSURE_DROP: # Issue #503 From 9096884153f3e937cdfc323e79305b8b7e91a7ba Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 30 Sep 2026 08:51:07 +0100 Subject: [PATCH 38/40] :bug: Fix haaland equation not using the full pipe hydraulic diameter --- process/models/engineering/pumping.py | 14 +++++++------- tests/unit/models/engineering/test_pumping.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index a966068ab7..c52116c9b3 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -183,7 +183,7 @@ def coolant_friction_pressure_drop( darcy_friction_factor = darcy_friction_haaland( reynolds=reynolds_number, roughness_channel=roughness_channel, - radius_channel=radius_channel, + dia_channel=dia_pipe, ) # Pressure drop coefficient @@ -267,7 +267,7 @@ def pipe_hydraulic_diameter( def darcy_friction_haaland( - reynolds: float, roughness_channel: float, radius_channel: float + reynolds: float, roughness_channel: float, dia_channel: float ) -> float: """Calculate Darcy friction factor using the Haaland equation. @@ -276,9 +276,9 @@ def darcy_friction_haaland( reynolds: Reynolds number. roughness_channel: - Roughness of the first wall coolant channel (m). - radius_channel: - Radius of the first wall coolant channel (m). + Roughness of the coolant channel (m). + dia_channel: + Hydraulic diameter of the coolant channel (m). Returns ------- @@ -295,7 +295,7 @@ def darcy_friction_haaland( [1] https://en.wikipedia.org/wiki/Darcy_friction_factor_formulae#Haaland_equation """ # Bracketed term in Haaland equation - bracket = (roughness_channel / radius_channel / 3.7) ** 1.11 + 6.9 / reynolds + bracket = (roughness_channel / dia_channel / 3.7) ** 1.11 + 6.9 / reynolds # Calculate Darcy friction factor return (1.8 * np.log10(bracket)) ** (-2) @@ -492,7 +492,7 @@ def gnielinski_heat_transfer_coefficient( f = darcy_friction_haaland( reynolds=reynolds, roughness_channel=roughness_channel, - radius_channel=radius_channel, + dia_channel=2 * radius_channel, ) # Calculate the Nusselt number diff --git a/tests/unit/models/engineering/test_pumping.py b/tests/unit/models/engineering/test_pumping.py index a532047f7e..f84fb806e4 100644 --- a/tests/unit/models/engineering/test_pumping.py +++ b/tests/unit/models/engineering/test_pumping.py @@ -28,7 +28,7 @@ def blanket_library(process_models): def test_darcy_friction_haaland(): assert darcy_friction_haaland( - reynolds=5500, roughness_channel=1e-6, radius_channel=0.1 + reynolds=5500, roughness_channel=1e-6, dia_channel=0.1 ) == pytest.approx(0.0366668931278784) From c63ddfa52e9ee05ebd841b39e33a1e3cf31d67e9 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Wed, 30 Sep 2026 08:58:50 +0100 Subject: [PATCH 39/40] Tidy some docstrings --- process/core/io/plot/summary.py | 404 +----------------- process/models/engineering/pumping.py | 127 +++--- .../models/blankets/test_blanket_library.py | 4 +- tests/unit/models/engineering/test_pumping.py | 4 +- tests/unit/models/test_fw.py | 4 +- 5 files changed, 74 insertions(+), 469 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 944c82b794..9ec6a67f5c 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -16462,8 +16462,7 @@ def plot_cs_von_mises_2d_contour( axis.set_xlim(r_cs_inner * 0.9, r_cs_outer * 1.1) axis.set_ylim((-dz_cs_full / 2) * 1.1, (dz_cs_full / 2) * 1.1) axis.grid(True, alpha=0.3) - axis.set_title("CS Radial Stress at BOP") - axis.legend(loc="best") + axis.set_title("CS Von Mises Stress Contour at BOP") def plot_pf_dimensions( @@ -16815,407 +16814,6 @@ def plot_cumulative_plasma_thermal_energy_profiles(axis, m_file: MFile, scan: in ) -def plot_outboard_blanket_coolant_properties(fig: plt.Figure, m_file: MFile, scan: int): - """Plots the properties of the outboard blanket coolant along the poloidal direction.""" - temp_blkt_coolant_in = float(m_file.get("temp_blkt_coolant_in", scan=scan)) - temp_blkt_coolant_out = float(m_file.get("temp_blkt_coolant_out", scan=scan)) - pres_blkt_coolant = float(m_file.get("pres_blkt_coolant", scan=scan)) - dens_blkt_coolant_in = PropsSI( - "Dmass", "T", temp_blkt_coolant_in, "P", pres_blkt_coolant, "Helium" - ) - dens_blkt_coolant_out = PropsSI( - "Dmass", "T", temp_blkt_coolant_out, "P", pres_blkt_coolant, "Helium" - ) - - fig.clear() - axes = fig.subplots(2, 2) - - axis = axes[0, 0] - - plot = PropertyPlot("Helium", "PH", axis=axis, unit_system="SI") - - # #axis.set_xscale('log') - axis.set_ylim(((pres_blkt_coolant) * 0.9), (pres_blkt_coolant) * 1.5) - # plot.set_axis_limits([None, None, (pres_blkt_coolant/1e3)*0.9, (pres_blkt_coolant/1e3)*1.1]) - - plot.calc_isolines(CoolProp.iT) # Temperature - plot.calc_isolines(CoolProp.iSmass) # Entropy - plot.calc_isolines(CoolProp.iDmass) # Density - # plot.axis.relim() - # plot.axis.autoscale_view() - plot.draw() - - axis.set_title("Pressure-enthalpy plot") - - axis = axes[0, 1] - plot = PropertyPlot("Helium", "TD", axis=axis, unit_system="SI") - - density_min = min(dens_blkt_coolant_in, dens_blkt_coolant_out) - density_max = max(dens_blkt_coolant_in, dens_blkt_coolant_out) - temperature_min = min(temp_blkt_coolant_in, temp_blkt_coolant_out) - temperature_max = max(temp_blkt_coolant_in, temp_blkt_coolant_out) - - density_margin = max((density_max - density_min) * 0.2, max(density_max, 1.0) * 0.05) - temperature_margin = max( - (temperature_max - temperature_min) * 0.2, - max(temperature_max, 1.0) * 0.05, - ) - - axis.set_xlim(density_min - density_margin, density_max + density_margin) - axis.set_ylim( - temperature_min - temperature_margin, temperature_max + temperature_margin - ) - - plot.calc_isolines(CoolProp.iP) # Pressure - plot.calc_isolines(CoolProp.iHmass) # Enthalpy - plot.draw() - - axis.plot( - [dens_blkt_coolant_in, dens_blkt_coolant_out], - [temp_blkt_coolant_in, temp_blkt_coolant_out], - color="C3", - marker="o", - linewidth=1.5, - label="Coolant path", - ) - axis.legend(fontsize=8, loc="best") - axis.set_title("Temperature-density plot") - - axis = axes[1, 0] - plot = PropertyPlot("Helium", "PD", axis=axis, unit_system="SI") - - pressure_margin = max(pres_blkt_coolant * 0.1, 1.0) - - axis.set_xlim(density_min - density_margin, density_max + density_margin) - axis.set_ylim( - pres_blkt_coolant - pressure_margin, pres_blkt_coolant + pressure_margin - ) - - plot.calc_isolines(CoolProp.iT) # Temperature - plot.calc_isolines(CoolProp.iHmass) # Enthalpy - plot.draw() - - axis.plot( - [dens_blkt_coolant_in, dens_blkt_coolant_out], - [pres_blkt_coolant, pres_blkt_coolant], - color="C3", - marker="o", - linewidth=1.5, - label="Coolant path", - ) - axis.legend(fontsize=8, loc="best") - axis.set_title("Pressure-density plot") - - axes[1, 1].axis("off") - - fig.tight_layout() - - -def plot_plasma_thermal_energy_profiles(axis, m_file: MFile, scan: int): - """Function to plot plasma thermal energy profiles on the given axis. - - Parameters - ---------- - axis : - Matplotlib axis to plot on - m_file : - MFILE - scan : - scan to read from MFILE - """ - n_plasma_profile_elements = int(m_file.get("n_plasma_profile_elements", scan=scan)) - - eden_plasma_electrons_thermal_profile_mj = [ - m_file.get(f"eden_plasma_electrons_thermal_profile{i}", scan=scan) / 1e6 - for i in range(n_plasma_profile_elements) - ] - eden_plasma_ions_thermal_profile_mj = [ - m_file.get(f"eden_plasma_ions_thermal_profile{i}", scan=scan) / 1e6 - for i in range(n_plasma_profile_elements) - ] - eden_plasma_thermal_profile_mj = [ - m_file.get(f"eden_plasma_thermal_profile{i}", scan=scan) / 1e6 - for i in range(n_plasma_profile_elements) - ] - e_plasma_electrons_thermal_profile_mj = [ - m_file.get(f"e_plasma_electrons_thermal_profile{i}", scan=scan) / 1e6 - for i in range(n_plasma_profile_elements) - ] - - e_plasma_ions_thermal_profile_mj = [ - m_file.get(f"e_plasma_ions_thermal_profile{i}", scan=scan) / 1e6 - for i in range(n_plasma_profile_elements) - ] - e_plasma_thermal_profile_mj = [ - m_file.get(f"e_plasma_thermal_profile{i}", scan=scan) / 1e6 - for i in range(n_plasma_profile_elements) - ] - - axis.plot( - np.linspace(0, 1, n_plasma_profile_elements), - e_plasma_electrons_thermal_profile_mj, - label="$W_{\\text{e}}$", - color="tab:blue", - linestyle=":", - ) - axis.plot( - np.linspace(0, 1, n_plasma_profile_elements), - e_plasma_ions_thermal_profile_mj, - label="$W_{\\text{i}}$", - color="tab:blue", - linestyle="--", - ) - axis.plot( - np.linspace(0, 1, n_plasma_profile_elements), - e_plasma_thermal_profile_mj, - label="$W_{\\text{total}}$", - color="tab:blue", - linestyle="-", - ) - - total_thermal_energy_max_index = int(np.argmax(e_plasma_thermal_profile_mj)) - total_thermal_energy_max_rho = np.linspace(0, 1, n_plasma_profile_elements)[ - total_thermal_energy_max_index - ] - total_thermal_energy_max = e_plasma_thermal_profile_mj[ - total_thermal_energy_max_index - ] - axis.axvline( - total_thermal_energy_max_rho, - color="tab:red", - alpha=0.7, - label="$W_{\\text{total, peak}}$", - ) - axis.axhline( - total_thermal_energy_max, - color="tab:red", - alpha=0.7, - ) - - density_axis = axis.twinx() - density_axis.plot( - np.linspace(0, 1, n_plasma_profile_elements), - eden_plasma_electrons_thermal_profile_mj, - label="$W_{\\text{density, e}}$", - color="tab:orange", - linestyle=":", - ) - density_axis.plot( - np.linspace(0, 1, n_plasma_profile_elements), - eden_plasma_ions_thermal_profile_mj, - label="$W_{\\text{density, i}}$", - color="tab:orange", - linestyle="--", - ) - density_axis.plot( - np.linspace(0, 1, n_plasma_profile_elements), - eden_plasma_thermal_profile_mj, - label="$W_{\\text{density, total}}$", - color="tab:orange", - linestyle="-", - ) - - axis.grid(True, alpha=0.3) - axis.minorticks_on() - axis.set_xlabel(r"$\rho \quad [r/a]$") - axis.set_xlim(left=0.0, right=1.0) - axis.set_ylabel( - "Thermal Energy [MJ]", - color="tab:blue", - ) - axis.tick_params(axis="y", colors="tab:blue") - density_axis.set_ylabel( - "Thermal Energy Density [MJ/m$^3$]", - color="tab:orange", - ) - density_axis.tick_params(axis="y", colors="tab:orange") - handles, labels = axis.get_legend_handles_labels() - density_handles, density_labels = density_axis.get_legend_handles_labels() - axis.legend(handles + density_handles, labels + density_labels) - - -def plot_cumulative_plasma_thermal_energy_profiles(axis, m_file: MFile, scan: int): - """Function to plot the cumulative plasma thermal energy profiles on the given axis. - - Parameters - ---------- - axis : - Matplotlib axis to plot on - m_file : - MFILE - scan : - scan to read from MFILE - """ - n_plasma_profile_elements = int(m_file.get("n_plasma_profile_elements", scan=scan)) - e_plasma_thermal_total_mj = m_file.get("e_plasma_thermal_total", scan=scan) / 1e6 - e_plasma_electrons_thermal_profile_mj = [ - m_file.get(f"e_plasma_electrons_thermal_profile{i}", scan=scan) / 1e6 - for i in range(n_plasma_profile_elements) - ] - - e_plasma_ions_thermal_profile_mj = [ - m_file.get(f"e_plasma_ions_thermal_profile{i}", scan=scan) / 1e6 - for i in range(n_plasma_profile_elements) - ] - e_plasma_thermal_profile_mj = [ - m_file.get(f"e_plasma_thermal_profile{i}", scan=scan) / 1e6 - for i in range(n_plasma_profile_elements) - ] - - axis.plot( - np.linspace(0, 1, n_plasma_profile_elements), - np.cumsum(e_plasma_electrons_thermal_profile_mj), - label="$\\Sigma W_{\\text{e}}$", - color="tab:blue", - linestyle=":", - ) - axis.plot( - np.linspace(0, 1, n_plasma_profile_elements), - np.cumsum(e_plasma_ions_thermal_profile_mj), - label="$\\Sigma W_{\\text{i}}$", - color="tab:blue", - linestyle="--", - ) - axis.plot( - np.linspace(0, 1, n_plasma_profile_elements), - np.cumsum(e_plasma_thermal_profile_mj), - label="$\\Sigma W_{\\text{total}}$", - color="tab:blue", - linestyle="-", - ) - axis.axhline( - y=e_plasma_thermal_total_mj, - label="$W_{\\text{thermal,total}}$", - color="tab:red", - linestyle="--", - ) - cumulative_thermal_energy_mj = np.cumsum(e_plasma_thermal_profile_mj) - half_thermal_energy_mj = 0.5 * e_plasma_thermal_total_mj - half_thermal_energy_position = np.interp( - half_thermal_energy_mj, - cumulative_thermal_energy_mj, - np.linspace(0, 1, n_plasma_profile_elements), - ) - axis.axhline( - y=half_thermal_energy_mj, - label="$50\\%\\ W_{\\text{thermal,total}}$", - color="tab:green", - linestyle=":", - ) - axis.axvline( - x=half_thermal_energy_position, - color="tab:green", - linestyle=":", - ) - - axis.legend() - axis.set_title("Thermal Energy Profiles and Cumulative Distribution") - axis.grid(True, alpha=0.3) - axis.minorticks_on() - axis.tick_params(axis="x", labelbottom=False) - axis.set_xlim(left=0.0, right=1.0) - axis.set_ylabel( - "Cumulative Thermal Energy [MJ]", - ) - - -def plot_outboard_blanket_coolant_properties(fig: plt.Figure, m_file: MFile, scan: int): - """Plots the properties of the outboard blanket coolant along the poloidal direction.""" - temp_blkt_coolant_in = float(m_file.get("temp_blkt_coolant_in", scan=scan)) - temp_blkt_coolant_out = float(m_file.get("temp_blkt_coolant_out", scan=scan)) - pres_blkt_coolant = float(m_file.get("pres_blkt_coolant", scan=scan)) - dens_blkt_coolant_in = PropsSI( - "Dmass", "T", temp_blkt_coolant_in, "P", pres_blkt_coolant, "Helium" - ) - dens_blkt_coolant_out = PropsSI( - "Dmass", "T", temp_blkt_coolant_out, "P", pres_blkt_coolant, "Helium" - ) - - fig.clear() - axes = fig.subplots(2, 2) - - axis = axes[0, 0] - - plot = PropertyPlot("Helium", "PH", axis=axis, unit_system="SI") - - # #axis.set_xscale('log') - axis.set_ylim(((pres_blkt_coolant) * 0.9), (pres_blkt_coolant) * 1.5) - # plot.set_axis_limits([None, None, (pres_blkt_coolant/1e3)*0.9, (pres_blkt_coolant/1e3)*1.1]) - - plot.calc_isolines(CoolProp.iT) # Temperature - plot.calc_isolines(CoolProp.iSmass) # Entropy - plot.calc_isolines(CoolProp.iDmass) # Density - # plot.axis.relim() - # plot.axis.autoscale_view() - plot.draw() - - axis.set_title("Pressure-enthalpy plot") - - axis = axes[0, 1] - plot = PropertyPlot("Helium", "TD", axis=axis, unit_system="SI") - - density_min = min(dens_blkt_coolant_in, dens_blkt_coolant_out) - density_max = max(dens_blkt_coolant_in, dens_blkt_coolant_out) - temperature_min = min(temp_blkt_coolant_in, temp_blkt_coolant_out) - temperature_max = max(temp_blkt_coolant_in, temp_blkt_coolant_out) - - density_margin = max((density_max - density_min) * 0.2, max(density_max, 1.0) * 0.05) - temperature_margin = max( - (temperature_max - temperature_min) * 0.2, - max(temperature_max, 1.0) * 0.05, - ) - - axis.set_xlim(density_min - density_margin, density_max + density_margin) - axis.set_ylim( - temperature_min - temperature_margin, temperature_max + temperature_margin - ) - - plot.calc_isolines(CoolProp.iP) # Pressure - plot.calc_isolines(CoolProp.iHmass) # Enthalpy - plot.draw() - - axis.plot( - [dens_blkt_coolant_in, dens_blkt_coolant_out], - [temp_blkt_coolant_in, temp_blkt_coolant_out], - color="C3", - marker="o", - linewidth=1.5, - label="Coolant path", - ) - axis.legend(fontsize=8, loc="best") - axis.set_title("Temperature-density plot") - - axis = axes[1, 0] - plot = PropertyPlot("Helium", "PD", axis=axis, unit_system="SI") - - pressure_margin = max(pres_blkt_coolant * 0.1, 1.0) - - axis.set_xlim(density_min - density_margin, density_max + density_margin) - axis.set_ylim( - pres_blkt_coolant - pressure_margin, pres_blkt_coolant + pressure_margin - ) - - plot.calc_isolines(CoolProp.iT) # Temperature - plot.calc_isolines(CoolProp.iHmass) # Enthalpy - plot.draw() - - axis.plot( - [dens_blkt_coolant_in, dens_blkt_coolant_out], - [pres_blkt_coolant, pres_blkt_coolant], - color="C3", - marker="o", - linewidth=1.5, - label="Coolant path", - ) - axis.legend(fontsize=8, loc="best") - axis.set_title("Pressure-density plot") - - axes[1, 1].axis("off") - - fig.tight_layout() - - - def plot_blanket_coolant_properties(fig: plt.Figure, m_file: MFile, scan: int): """Combined plot of blanket coolant channel structure and properties.""" # Add info about the Winding Pack diff --git a/process/models/engineering/pumping.py b/process/models/engineering/pumping.py index c52116c9b3..fd134fc9ca 100644 --- a/process/models/engineering/pumping.py +++ b/process/models/engineering/pumping.py @@ -52,19 +52,19 @@ class CoolantFrictionLossParameters: """Parameters for calculating coolant friction losses.""" dpres_total: float - """Total pressure drop across the coolant channel (Pa)""" + """Total pressure drop across the coolant channel [Pa]""" dpres_straight: float - """Pressure drop due to straight length of the coolant channel (Pa)""" + """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)""" + """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)""" + """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)""" + """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)""" + """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)""" + """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 @@ -72,7 +72,7 @@ class CoolantFrictionLossParameters: f_straight: float """Friction factor for straight length of the coolant channel""" len_straight: float - """Length of straight sections of the coolant channel (m)""" + """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 @@ -166,7 +166,7 @@ def coolant_friction_pressure_drop( b_bz_liq=b_bz_liq, ) - # Reynolds number + # Reynolds number (Re) reynolds_number = calculate_reynolds_number( den_coolant=den_coolant, vel_coolant=vel_coolant, @@ -241,14 +241,21 @@ def coolant_friction_pressure_drop( 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. + """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 + Shape depends on whether primary or secondary coolant is used. + 1 = circular channel (primary coolant) + 2 = rectangular channel (secondary coolant) + + Returns + ------- + : + Hydraulic diameter of the coolant channel [m]. Raises ------ @@ -276,9 +283,9 @@ def darcy_friction_haaland( reynolds: Reynolds number. roughness_channel: - Roughness of the coolant channel (m). + Roughness of the coolant channel [m]. dia_channel: - Hydraulic diameter of the coolant channel (m). + Hydraulic diameter of the coolant channel [m]. Returns ------- @@ -287,8 +294,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 ---------- @@ -322,19 +329,19 @@ def coolant_pumping_power( 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). + Pump outlet temperature [K]. temp_coolant_pump_inlet : float - Pump inlet temperature (K). + Pump inlet temperature [K]. pres_coolant_pump_inlet : float - Pump inlet pressure (Pa). + Pump inlet pressure [Pa]. dpres_coolant : float - Coolant pressure drop (Pa). + Coolant pressure drop [Pa]. mflow_coolant_total : float - Total coolant mass flow rate in (kg/s). + Total coolant mass flow rate [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³). + Density of coolant or liquid breeder [kg/m³]. etaiso : float Isentropic efficiency of the pump for primary coolant. etaiso_liq : float @@ -342,8 +349,8 @@ def coolant_pumping_power( Returns ------- - float - Pumping power in MW. + :float + Pumping power [MW]. Raises ------ @@ -352,10 +359,10 @@ def coolant_pumping_power( References ---------- - - Idel'Cik, I. E. (1969), Memento des pertes de charge - - S.P. Sukhatme (2005), A Textbook on Heat Transfer + [1] Idel'Cik, I. E. (1969), Memento des pertes de charge + [2] S.P. Sukhatme (2005), A Textbook on Heat Transfer """ - # Pump outlet pressure (Pa) + # 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 @@ -370,8 +377,8 @@ def coolant_pumping_power( pressure=pres_coolant_pump_outlet, ) - # Real gamma (cp/cv) at the pump outlet state, so liquid water (gamma~1) - # and superheated steam (gamma~1.3) are both handled correctly + # Real gamma (cp/cv) at the pump outlet state, so liquid water (γ~1) # noqa: RUF003 + # and superheated steam (γ~1.3) are both handled correctly # noqa: RUF003 gamma = ( pump_outlet_fluid_properties.specific_heat_const_p / pump_outlet_fluid_properties.specific_heat_const_v @@ -380,7 +387,7 @@ def coolant_pumping_power( # 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 + # 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, @@ -388,7 +395,7 @@ def coolant_pumping_power( entropy=s1, ) - # Pumping power (MW) is given by enthalpy change, with a correction for + # Pumping power [MW] is given by enthalpy change, with a correction for # the isentropic efficiency of the pump. fp = ( temp_coolant_pump_outlet @@ -442,28 +449,28 @@ def gnielinski_heat_transfer_coefficient( Parameters ---------- mflux_coolant: - Coolant mass flux in a single channel (kg/m²/s). + Coolant mass flux in a single channel [kg/m²/s]. den_coolant: - Coolant density (average of inlet and outlet) (kg/m³). + Coolant density (average of inlet and outlet) [kg/m³]. radius_channel: - Coolant pipe radius (m). + Coolant pipe radius [m]. heatcap_coolant: - Coolant specific heat capacity (average of inlet and outlet) (J/kg/K). + Coolant specific heat capacity (average of inlet and outlet) [J/kg/K]. visc_coolant: - Coolant viscosity (average of inlet and outlet) (Pa.s). + Coolant viscosity (average of inlet and outlet) [Pa.s]. thermcond_coolant: - Thermal conductivity of coolant (average of inlet and outlet) (W/m.K). + Thermal conductivity of coolant (average of inlet and outlet) [W/m.K]. roughness_channel: - Roughness of the coolant channel (m). + Roughness of the coolant channel [m]. Returns ------- : - Heat transfer coefficient (W/m²K). + Heat transfer coefficient [W/m²K]. Notes ----- - Gnielinski correlation. Ignore the distinction between wall and + - Gnielinski correlation. Ignore the distinction between wall and bulk temperatures. Valid for: 3000 < Re < 5e6, 0.5 < Pr < 2000 References @@ -477,7 +484,7 @@ def gnielinski_heat_transfer_coefficient( # Calculate flow velocity (m/s) vel_coolant = mflux_coolant / den_coolant - # Calculate Reynolds number + # Calculate Reynolds number (Re) reynolds = calculate_reynolds_number( den_coolant=den_coolant, vel_coolant=vel_coolant, @@ -485,7 +492,7 @@ def gnielinski_heat_transfer_coefficient( visc_coolant=visc_coolant, ) - # Calculate Prandtl number + # Calculate Prandtl number (Pr) pr = heatcap_coolant * visc_coolant / thermcond_coolant # Calculate Darcy friction factor, using Haaland equation @@ -495,7 +502,7 @@ def gnielinski_heat_transfer_coefficient( dia_channel=2 * radius_channel, ) - # Calculate the Nusselt number + # Calculate the Nusselt number (Nu) nusselt = ( (f / 8.0) * (reynolds - 1000.0) @@ -503,7 +510,7 @@ def gnielinski_heat_transfer_coefficient( / (1 + 12.7 * np.sqrt(f / 8.0) * (pr ** (2 / 3) - 1.0)) ) - # Calculate the heat transfer coefficient (W/m^2K) + # Calculate the heat transfer coefficient [W/m²K] heat_transfer_coefficient = nusselt * thermcond_coolant / (2.0 * radius_channel) # Check that Reynolds number is in valid range for the Gnielinski correlation @@ -528,23 +535,23 @@ def calculate_reynolds_number( radius_channel: float, visc_coolant: float, ) -> float: - """Calculate Reynolds number for flow in a pipe. + """Calculate Reynolds number (Re) for flow in a pipe. Parameters ---------- den_coolant: - Coolant density (average of inlet and outlet) (kg/m³). + Coolant density (average of inlet and outlet) [kg/m³]. vel_coolant: - Coolant velocity in a single channel (m/s). + Coolant velocity in a single channel [m/s]. radius_channel: - Coolant pipe radius (m). + Coolant pipe radius [m]. visc_coolant: - Coolant viscosity (average of inlet and outlet) (Pa.s). + Coolant viscosity (average of inlet and outlet) [Pa.s]. Returns ------- : - Reynolds number. + Reynolds number (Re). """ # Calculate pipe diameter (m) @@ -565,17 +572,17 @@ def elbow_coeff( Parameters ---------- radius_pipe_elbow : float - Pipe elbow radius (m) + Pipe elbow radius [m]. deg_pipe_elbow : float - Pipe elbow angle (degrees) + Pipe elbow angle [degrees] darcy_friction : float Darcy friction factor dia_pipe : float - Pipe diameter (m) + Pipe diameter [m] Returns ------- - float + :float Elbow coefficient for pressure drop calculation Raises @@ -637,22 +644,22 @@ def calculate_required_mass_flow_rate( Parameters ---------- p_heat_total: - Total heat load to be removed (W). + Total heat load to be removed [W]. heatcap_coolant: - Specific heat capacity of the coolant (J/kg/K). + Specific heat capacity of the coolant [J/kg/K]. temp_in_coolant: - Inlet temperature of the coolant (K). + Inlet temperature of the coolant [K]. temp_out_coolant: - Outlet temperature of the coolant (K). + Outlet temperature of the coolant [K]. Returns ------- - float - Required mass flow rate of the coolant (kg/s). + :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 + - The heat capacity is assumed to be constant over the temperature range of the coolant. """ diff --git a/tests/unit/models/blankets/test_blanket_library.py b/tests/unit/models/blankets/test_blanket_library.py index c80fef542e..c30b798dcb 100644 --- a/tests/unit/models/blankets/test_blanket_library.py +++ b/tests/unit/models/blankets/test_blanket_library.py @@ -320,7 +320,7 @@ def test_deltap_tot_inboard_first_wall(monkeypatch, blanket_library): } dpres_total, _ = blanket_library.total_pressure_drop(False, **data) - assert dpres_total == pytest.approx(5884.982168510442) + assert dpres_total == pytest.approx(5839.298437354936) def test_deltap_tot_outboard_blanket_breeder_liquid(monkeypatch, blanket_library): @@ -349,7 +349,7 @@ def test_deltap_tot_outboard_blanket_breeder_liquid(monkeypatch, blanket_library } dpres_total, _ = blanket_library.total_pressure_drop(False, **data) - assert dpres_total == pytest.approx(56.95922064419226) + assert dpres_total == pytest.approx(56.66392220444523) class ComponentHalfHeightParam(NamedTuple): diff --git a/tests/unit/models/engineering/test_pumping.py b/tests/unit/models/engineering/test_pumping.py index f84fb806e4..7371edb3c8 100644 --- a/tests/unit/models/engineering/test_pumping.py +++ b/tests/unit/models/engineering/test_pumping.py @@ -41,7 +41,7 @@ def test_gnielinski_heat_transfer_coefficient(): visc_coolant=4.0416219836935569e-05, thermcond_coolant=0.3211653052986152, roughness_channel=6e-8, - ) == pytest.approx(1929.2042015869506) + ) == pytest.approx(1928.3502714369304) def test_calculate_reynolds_number(): @@ -147,7 +147,7 @@ class CoolantFrictionLossParam(NamedTuple): visc_coolant=3.604452999475736e-05, vel_coolant=32.753134225223164, label="Inboard first wall", - expected_pressure_drop_out=36213.58989742931, + expected_pressure_drop_out=35416.899017164345, ), CoolantFrictionLossParam( radius_channel=1.0, diff --git a/tests/unit/models/test_fw.py b/tests/unit/models/test_fw.py index 88ec3e88ae..7c5e620d4d 100644 --- a/tests/unit/models/test_fw.py +++ b/tests/unit/models/test_fw.py @@ -77,7 +77,7 @@ class FwTempParam(NamedTuple): area=612.23369764444396, prad_incident=97.271629070225231, label="Inboard first wall", - expected_tpeakfw=846.4965128389427, + expected_tpeakfw=846.6822343235608, expected_cfmean=5188.6731857247905, expected_rhofmean=5.7616377393642955, expected_massrate=0.0054299309578952218, @@ -99,7 +99,7 @@ class FwTempParam(NamedTuple): area=988.92586580655245, prad_incident=176.95628839065773, label="Outboard first wall", - expected_tpeakfw=850.851622254886, + expected_tpeakfw=851.051204263975, expected_cfmean=5188.6731857247905, expected_rhofmean=5.7616377393642955, expected_massrate=0.005816503189155049, From 8eca27e3eb6582610a95c24a1c3663969f39ccd1 Mon Sep 17 00:00:00 2001 From: mn3981 Date: Fri, 2 Oct 2026 09:38:19 +0100 Subject: [PATCH 40/40] Refactor blanket coolant properties plotting to reduce code duplication and improve readability --- process/core/io/plot/summary.py | 126 +++++++++++--------------------- 1 file changed, 44 insertions(+), 82 deletions(-) diff --git a/process/core/io/plot/summary.py b/process/core/io/plot/summary.py index 9ec6a67f5c..6471685831 100644 --- a/process/core/io/plot/summary.py +++ b/process/core/io/plot/summary.py @@ -16816,89 +16816,51 @@ def plot_cumulative_plasma_thermal_energy_profiles(axis, m_file: MFile, scan: in def plot_blanket_coolant_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):,.2f} Pa\n" - f"Pressure drop for 90° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_90_bend', scan=scan):,.2f} Pa\n" - f"Total pressure drop for 90° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_90_bends_total', scan=scan):,.2f} Pa\n" - f"Pressure drop for 180° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_180_bend', scan=scan):,.2f} Pa\n" - f"Total pressure drop for 180° bends: {m_file.get('dpres_blkt_outboard_coolant_channel_180_bends_total', scan=scan):,.2f} Pa\n" - f"Total pressure drop for all bends: {m_file.get('dpres_blkt_outboard_bends_total', scan=scan):,.2f} 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\n" - f"Total coolant mass flow rate: {m_file.get('mflow_blkt_outboard_coolant', scan=scan):.4f} kg/s\n" - f"Coolant mass flow rate in single channel: {m_file.get('mflow_blkt_outboard_coolant_channel', scan=scan):.4f} kg/s\n" - f"Coolant velocity in single channel: {m_file.get('vel_blkt_outboard_coolant', scan=scan):.4f} m/s" - ) - - 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, - }, - ) + for side, x_position in (("inboard", 0.1), ("outboard", 0.5)): + + def get(variable: str): + return m_file.get(variable, scan=scan) + + text = ( + f"$\\mathbf{{{side.capitalize()} \\ 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: {get(f'len_blkt_{side}_coolant_channel_radial'):.4f} m\n" + f"Poloidal coolant channel length: {get(f'len_blkt_{side}_segment_poloidal'):.4f} m\n" + f"Number of radial channels: {get(f'n_blkt_{side}_module_coolant_sections_radial')}\n" + f"Number of poloidal channels: {get(f'n_blkt_{side}_module_coolant_sections_poloidal')}\n" + f"Total length of coolant channel straight sections: {get(f'len_blkt_{side}_channel_total'):.4f} m\n\n" + f"Pressure drop for straight sections: {get(f'dpres_blkt_{side}_coolant_channel_straight_total'):,.2f} Pa\n" + f"Pressure drop for 90° bends: {get(f'dpres_blkt_{side}_coolant_channel_90_bend'):,.2f} Pa\n" + f"Total pressure drop for 90° bends: {get(f'dpres_blkt_{side}_coolant_channel_90_bends_total'):,.2f} Pa\n" + f"Pressure drop for 180° bends: {get(f'dpres_blkt_{side}_coolant_channel_180_bend'):,.2f} Pa\n" + f"Total pressure drop for 180° bends: {get(f'dpres_blkt_{side}_coolant_channel_180_bends_total'):,.2f} Pa\n" + f"Total pressure drop for all bends: {get(f'dpres_blkt_{side}_bends_total'):,.2f} Pa\n\n" + f"Reynolds number ($Re$): {get(f'reynolds_blkt_{side}_coolant'):,.4f}\n" + f"Darcy Friction factor ($f$): {get(f'darcy_frict_blkt_{side}_coolant'):.4f}\n\n" + f"Friction drop coefficient for straight sections: {get(f'f_straight_blkt_{side}_coolant'):.4f}\n" + f"Friction drop coefficient for 90° bends: {get(f'f_elbow_blkt_{side}_90_bend'):.4f}\n" + f"Friction drop coefficient for 180° bends: {get(f'f_elbow_blkt_{side}_180_bend'):.4f}\n\n" + f"Total coolant mass flow rate: {get(f'mflow_blkt_{side}_coolant'):.4f} kg/s\n" + f"Coolant mass flow rate in single channel: {get(f'mflow_blkt_{side}_coolant_channel'):.4f} kg/s\n" + f"Coolant velocity in single channel: {get(f'vel_blkt_{side}_coolant'):.4f} m/s" + ) - # 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):,.2f} Pa\n" - f"Pressure drop for 90° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_90_bend', scan=scan):,.2f} Pa\n" - f"Total pressure drop for 90° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_90_bends_total', scan=scan):,.2f} Pa\n" - f"Pressure drop for 180° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_180_bend', scan=scan):,.2f} Pa\n" - f"Total pressure drop for 180° bends: {m_file.get('dpres_blkt_inboard_coolant_channel_180_bends_total', scan=scan):,.2f} Pa\n" - f"Total pressure drop for all bends: {m_file.get('dpres_blkt_inboard_bends_total', scan=scan):,.2f} 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\n" - f"Total coolant mass flow rate: {m_file.get('mflow_blkt_inboard_coolant', scan=scan):.4f} kg/s\n" - f"Coolant mass flow rate in single channel: {m_file.get('mflow_blkt_inboard_coolant_channel', scan=scan):.4f} kg/s\n" - f"Velocity of inboard blanket coolant in single channel: {m_file.get('vel_blkt_inboard_coolant', scan=scan):.4f} m/s" - ) - - 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, - }, - ) + fig.text( + x_position, + 0.5, + text, + fontsize=9, + verticalalignment="top", + horizontalalignment="left", + transform=fig.transFigure, + bbox={ + "boxstyle": "round", + "facecolor": "wheat", + "alpha": 1.0, + "linewidth": 2, + }, + ) def main_plot(