From 437ffaec757fc89e062c2ff02ebc5abfa7a55f59 Mon Sep 17 00:00:00 2001 From: james <81617086+je-cook@users.noreply.github.com> Date: Thu, 13 Aug 2026 10:30:14 +0100 Subject: [PATCH 1/5] availability refactor --- process/models/availability.py | 589 +++++++++++++++++---------------- 1 file changed, 301 insertions(+), 288 deletions(-) diff --git a/process/models/availability.py b/process/models/availability.py index a6f804476e..6a0ed97f94 100644 --- a/process/models/availability.py +++ b/process/models/availability.py @@ -116,6 +116,82 @@ def run(self, output: bool = False): else: self.avail(output) # Taylor and Ward model (1999) + def calc_dpa_fpy(self): + """ + Calculate DPA per FPY - based on neutronics-derived fusion power relation + to DEMO blanket lifetime provided by Matti Coleman + + Detailed and cited in T. Franke 2020, + "The EU DEMO equatorial outboard limiter, + Design and port integration concept" + https://www.sciencedirect.com/science/article/pii/S0920379620301952#bib0075 + + Scaling w.r.t. fusion power drops out a large number of factors relating + to neutronics, such as: + - the actual neutron flux + - the geometry and material composition leading to the neutron flux at the + EUROfer FW OMP + - the neutron energy spectrum + - all of the above and more leading to the dpa/fpy in EUROfer at the FW OMP + + About a relatively "constant" reference point, we can reasonably assume + they all equal to 1.0. + """ + ref_fusion_power = 2.0e3 # (MW) fusion power for EU-DEMO + f_scale = self.data.physics.p_fusion_total_mw / ref_fusion_power + ref_dpa_fpy = 10.0e0 # dpa per fpy from T. Franke 2020 states up to 10 dpa/FPY + dpa_fpy = f_scale * ref_dpa_fpy + + # First wall / blanket lifetime (years) + # TODO MDK Do this calculation whatever the value of blktmodel + # (whatever that is) + # For some reason life_fw_fpy is not always calculated, + # so ignore it if it is still zero. + if self.data.fwbs.life_fw_fpy < 0.0001e0: + # Calculate blanket lifetime using neutron fluence model (ibkt_life=0) + # or DEMO fusion power model (ibkt_life=1) + if self.data.costs.ibkt_life == 0: + self.data.fwbs.life_blkt_fpy = ( + self.data.costs.life_plant + if self.data.physics.pflux_fw_neutron_mw == 0.0 # noqa: RUF069 + else min( + ( + self.data.costs.abktflnc + / self.data.physics.pflux_fw_neutron_mw + ), + self.data.costs.life_plant, + ) + ) + else: + self.data.fwbs.life_blkt_fpy = min( + self.data.costs.life_dpa / dpa_fpy, self.data.costs.life_plant + ) # DEMO + elif self.data.costs.ibkt_life == 0: + self.data.fwbs.life_blkt_fpy = min( + self.data.fwbs.life_fw_fpy, + self.data.costs.abktflnc / self.data.physics.pflux_fw_neutron_mw, + self.data.costs.life_plant, + ) + else: + self.data.fwbs.life_blkt_fpy = min( + self.data.fwbs.life_fw_fpy, + self.data.costs.life_dpa / dpa_fpy, + self.data.costs.life_plant, + ) # DEMO + + # TODO Issue #834 + # Add a test for pflux_div_heat_load_mw=0 + self.data.divertor.pflux_div_heat_load_mw = max( + self.data.divertor.pflux_div_heat_load_mw, 1.0e-10 + ) + + # Divertor lifetime (years) + self.data.costs.life_div_fpy = self.divertor_lifetime() + + # Centrepost lifetime (years) (ST machines only) + if self.data.physics.itart == 1: + self.data.costs.cplife = self.cp_lifetime() + def avail(self, output: bool): """Routine to calculate component lifetimes and the overall plant availability @@ -131,78 +207,7 @@ def avail(self, output: bool): """ # Full power lifetime (in years) if self.data.ife.ife != 1: - # Calculate DPA per FPY - based on neutronics-derived fusion power relation - # to DEMO blanket lifetime provided by Matti Coleman - # Detailed and cited in T. Franke 2020, - # "The EU DEMO equatorial outboard limiter, - # Design and port integration concept" - # https://www.sciencedirect.com/science/article/pii/S0920379620301952#bib0075 - # Scaling w.r.t. fusion power drops out a large number of factors relating - # to neutronics, such as: - # - the actual neutron flux - # - the geometry and material composition leading to the neutron flux at the - # EUROfer FW OMP - # - the neutron energy spectrum - # - all of the above and more leading to the dpa/fpy in EUROfer at the FW OMP - # About a relatively "constant" reference point, we can reasonably assume - # they all equal to 1.0. - ref_fusion_power = 2.0e3 # (MW) fusion power for EU-DEMO - f_scale = self.data.physics.p_fusion_total_mw / ref_fusion_power - ref_dpa_fpy = ( - 10.0e0 # dpa per fpy from T. Franke 2020 states up to 10 dpa/FPY - ) - dpa_fpy = f_scale * ref_dpa_fpy - - # First wall / blanket lifetime (years) - # TODO MDK Do this calculation whatever the value of blktmodel - # (whatever that is) - # For some reason life_fw_fpy is not always calculated, - # so ignore it if it is still zero. - if self.data.fwbs.life_fw_fpy < 0.0001e0: - # Calculate blanket lifetime using neutron fluence model (ibkt_life=0) - # or DEMO fusion power model (ibkt_life=1) - if self.data.costs.ibkt_life == 0: - self.data.fwbs.life_blkt_fpy = ( - self.data.costs.life_plant - if self.data.physics.pflux_fw_neutron_mw == 0.0 # noqa: RUF069 - else min( - ( - self.data.costs.abktflnc - / self.data.physics.pflux_fw_neutron_mw - ), - self.data.costs.life_plant, - ) - ) - else: - self.data.fwbs.life_blkt_fpy = min( - self.data.costs.life_dpa / dpa_fpy, self.data.costs.life_plant - ) # DEMO - elif self.data.costs.ibkt_life == 0: - self.data.fwbs.life_blkt_fpy = min( - self.data.fwbs.life_fw_fpy, - self.data.costs.abktflnc / self.data.physics.pflux_fw_neutron_mw, - self.data.costs.life_plant, - ) - else: - self.data.fwbs.life_blkt_fpy = min( - self.data.fwbs.life_fw_fpy, - self.data.costs.life_dpa / dpa_fpy, - self.data.costs.life_plant, - ) # DEMO - - # TODO Issue #834 - # Add a test for pflux_div_heat_load_mw=0 - self.data.divertor.pflux_div_heat_load_mw = max( - self.data.divertor.pflux_div_heat_load_mw, 1.0e-10 - ) - - # Divertor lifetime (years) - self.data.costs.life_div_fpy = self.divertor_lifetime() - - # Centrepost lifetime (years) (ST machines only) - if self.data.physics.itart == 1: - self.data.costs.cplife = self.cp_lifetime() - + self.calc_dpa_fpy() # Plant Availability (i_plant_availability=0,1) # Calculate the number of fusion cycles for a given blanket lifetime @@ -289,123 +294,127 @@ def avail(self, output: bool): # Output section if output: - po.oheadr(self.outfile, "Plant Availability") - if self.data.fwbs.blktmodel == 0: + self.avail_output(uplanned, uutot) + + def avail_output(self, uplanned, uutot): + """Output for availability model 1""" + po.oheadr(self.outfile, "Plant Availability") + if self.data.fwbs.blktmodel == 0: + po.ovarre( + self.outfile, + "Allowable blanket neutron fluence (MW-yr/m2)", + "(abktflnc)", + self.data.costs.abktflnc, + ) + + po.ovarre( + self.outfile, + "Allowable divertor heat fluence (MW-yr/m2)", + "(adivflnc)", + self.data.costs.adivflnc, + ) + po.ovarre( + self.outfile, + "First wall / blanket lifetime (years)", + "(life_blkt_fpy)", + self.data.fwbs.life_blkt_fpy, + "OP ", + ) + po.ovarre( + self.outfile, + "Divertor lifetime (years)", + "(life_div_fpy)", + self.data.costs.life_div_fpy, + "OP ", + ) + + if self.data.physics.itart == 1: + po.ovarre( + self.outfile, + "Centrepost lifetime (years)", + "(cplife)", + self.data.costs.cplife, + "OP ", + ) + + po.ovarre( + self.outfile, + "Heating/CD system lifetime (years)", + "(life_hcd_fpy)", + self.data.costs.life_hcd_fpy, + "OP ", + ) + po.ovarre( + self.outfile, + "Total plant lifetime (years)", + "(life_plant)", + self.data.costs.life_plant, + ) + + if ( + AvailabilityModel(self.data.costs.i_plant_availability) + == AvailabilityModel.WARD_TAYLOR + ): + if self.data.costs.life_div_fpy < self.data.fwbs.life_blkt_fpy: + po.ovarre( + self.outfile, + "Time needed to replace divertor (years)", + "(t_div_replace_yrs)", + self.data.costs.t_div_replace_yrs, + ) + else: po.ovarre( self.outfile, - "Allowable blanket neutron fluence (MW-yr/m2)", - "(abktflnc)", - self.data.costs.abktflnc, + "Time needed to replace blanket (years)", + "(t_blkt_replace_yrs)", + self.data.costs.t_blkt_replace_yrs, ) po.ovarre( self.outfile, - "Allowable divertor heat fluence (MW-yr/m2)", - "(adivflnc)", - self.data.costs.adivflnc, + "Time needed to replace blkt + div (years)", + "(tcomrepl)", + self.data.costs.tcomrepl, ) po.ovarre( self.outfile, - "First wall / blanket lifetime (years)", - "(life_blkt_fpy)", - self.data.fwbs.life_blkt_fpy, + "Planned unavailability fraction", + "(uplanned)", + uplanned, "OP ", ) po.ovarre( self.outfile, - "Divertor lifetime (years)", - "(life_div_fpy)", - self.data.costs.life_div_fpy, + "Unplanned unavailability fraction", + "(uutot)", + uutot, "OP ", ) - if self.data.physics.itart == 1: - po.ovarre( - self.outfile, - "Centrepost lifetime (years)", - "(cplife)", - self.data.costs.cplife, - "OP ", - ) - + if ( + AvailabilityModel(self.data.costs.i_plant_availability) + == AvailabilityModel.USER_INPUT + ): po.ovarre( self.outfile, - "Heating/CD system lifetime (years)", - "(life_hcd_fpy)", - self.data.costs.life_hcd_fpy, - "OP ", + "Total plant availability fraction", + "(f_t_plant_available)", + self.data.costs.f_t_plant_available, ) po.ovarre( self.outfile, - "Total plant lifetime (years)", - "(life_plant)", - self.data.costs.life_plant, + "Number of fusion cycles to reach allowable fw/blanket DPA", + "(bktcycles)", + self.data.costs.bktcycles, + ) + else: + po.ovarre( + self.outfile, + "Total plant availability fraction", + "(f_t_plant_available)", + self.data.costs.f_t_plant_available, + "OP ", ) - - if ( - AvailabilityModel(self.data.costs.i_plant_availability) - == AvailabilityModel.WARD_TAYLOR - ): - if self.data.costs.life_div_fpy < self.data.fwbs.life_blkt_fpy: - po.ovarre( - self.outfile, - "Time needed to replace divertor (years)", - "(t_div_replace_yrs)", - self.data.costs.t_div_replace_yrs, - ) - else: - po.ovarre( - self.outfile, - "Time needed to replace blanket (years)", - "(t_blkt_replace_yrs)", - self.data.costs.t_blkt_replace_yrs, - ) - - po.ovarre( - self.outfile, - "Time needed to replace blkt + div (years)", - "(tcomrepl)", - self.data.costs.tcomrepl, - ) - po.ovarre( - self.outfile, - "Planned unavailability fraction", - "(uplanned)", - uplanned, - "OP ", - ) - po.ovarre( - self.outfile, - "Unplanned unavailability fraction", - "(uutot)", - uutot, - "OP ", - ) - - if ( - AvailabilityModel(self.data.costs.i_plant_availability) - == AvailabilityModel.USER_INPUT - ): - po.ovarre( - self.outfile, - "Total plant availability fraction", - "(f_t_plant_available)", - self.data.costs.f_t_plant_available, - ) - po.ovarre( - self.outfile, - "Number of fusion cycles to reach allowable fw/blanket DPA", - "(bktcycles)", - self.data.costs.bktcycles, - ) - else: - po.ovarre( - self.outfile, - "Total plant availability fraction", - "(f_t_plant_available)", - self.data.costs.f_t_plant_available, - "OP ", - ) def avail_2(self, output: bool): """Routine to calculate component lifetimes and the overall plant availability @@ -1396,143 +1405,147 @@ def avail_st(self, output: bool): ) if output: - po.ocmmnt(self.outfile, "Plant Availability") - po.oblnkl(self.outfile) - po.ovarre( - self.outfile, - "Allowable blanket neutron fluence (MW-yr/m2)", - "(abktflnc)", - self.data.costs.abktflnc, - ) - po.ovarre( - self.outfile, - "Allowable divertor heat fluence (MW-yr/m2)", - "(adivflnc)", - self.data.costs.adivflnc, - ) - po.ovarre( - self.outfile, - "First wall / blanket lifetime (FPY)", - "(life_blkt_fpy)", - self.data.fwbs.life_blkt_fpy, - "OP ", - ) - po.ovarre( - self.outfile, - "Divertor lifetime (FPY)", - "(life_div_fpy)", - self.data.costs.life_div_fpy, - "OP ", - ) - if self.data.tfcoil.i_tf_sup == TFConductorModel.SUPERCONDUCTING: - po.ovarre( - self.outfile, - "Max allowed fast neutron fluence on TF coil (n/m²)", - "(flu_tf_neutron_fast_max)", - self.data.constraints.flu_tf_neutron_fast_max, - "OP ", - ) - po.ovarre( - self.outfile, - "Centrepost TF fast neutron flux (E > 0.1 MeV) (m^(-2).^(-1))", - "(neut_flux_cp)", - self.data.fwbs.neut_flux_cp, - "OP ", - ) - else: - po.ovarre( - self.outfile, - "Allowable ST centrepost neutron fluence (MW-yr/m2)", - "(cpstflnc)", - self.data.costs.cpstflnc, - "OP ", - ) - po.ovarre( - self.outfile, - "Average neutron wall load (MW/m2)", - "(pflux_fw_neutron_mw)", - self.data.physics.pflux_fw_neutron_mw, - "OP ", - ) - po.ovarre( - self.outfile, - "Centrepost lifetime (years)", - "(cplife)", - self.data.costs.cplife, - "OP ", - ) - po.oblnkl(self.outfile) - po.ovarre( - self.outfile, - "Maintenance time for replacing CP (years)", - "(tmain)", - self.data.costs.tmain, - "OP ", - ) - po.ovarre( - self.outfile, - "Length of maintenance cycle (years)", - "(maint_cycle)", - maint_cycle, - "OP ", - ) - po.ovarre( - self.outfile, - "Number of maintenance cycles over lifetime", - "(n_cycles_main)", - n_cycles_main, - "OP ", - ) - po.ovarre( - self.outfile, - "Number of centre columns over lifetime", - "(n_centre_cols)", - n_centre_cols, - "OP ", - ) - po.oblnkl(self.outfile) + self.avail_st_output( + maint_cycle, n_cycles_main, n_centre_cols, u_planned, u_unplanned + ) + + def avail_st_output( + self, maint_cycle, n_cycles_main, n_centre_cols, u_planned, u_unplanned + ): + """Output for st availability model""" + po.ocmmnt(self.outfile, "Plant Availability") + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Allowable blanket neutron fluence (MW-yr/m2)", + "(abktflnc)", + self.data.costs.abktflnc, + ) + po.ovarre( + self.outfile, + "Allowable divertor heat fluence (MW-yr/m2)", + "(adivflnc)", + self.data.costs.adivflnc, + ) + po.ovarre( + self.outfile, + "First wall / blanket lifetime (FPY)", + "(life_blkt_fpy)", + self.data.fwbs.life_blkt_fpy, + "OP ", + ) + po.ovarre( + self.outfile, + "Divertor lifetime (FPY)", + "(life_div_fpy)", + self.data.costs.life_div_fpy, + "OP ", + ) + if self.data.tfcoil.i_tf_sup == TFConductorModel.SUPERCONDUCTING: po.ovarre( self.outfile, - "Total planned unavailability", - "(u_planned)", - u_planned, + "Max allowed fast neutron fluence on TF coil (n/m²)", + "(flu_tf_neutron_fast_max)", + self.data.constraints.flu_tf_neutron_fast_max, "OP ", ) po.ovarre( self.outfile, - "Total unplanned unavailability", - "(u_unplanned)", - u_unplanned, - "OP ", - ) - po.ovarre( - self.outfile, - "Total plant availability fraction", - "(f_t_plant_available)", - self.data.costs.f_t_plant_available, + "Centrepost TF fast neutron flux (E > 0.1 MeV) (m^(-2).^(-1))", + "(neut_flux_cp)", + self.data.fwbs.neut_flux_cp, "OP ", ) + else: po.ovarre( self.outfile, - "Capacity factor: total lifetime elec. energy output / output power", - "(cpfact)", - self.data.costs.cpfact, + "Allowable ST centrepost neutron fluence (MW-yr/m2)", + "(cpstflnc)", + self.data.costs.cpstflnc, "OP ", ) po.ovarre( self.outfile, - "Total DT operational time (years)", - "(t_plant_operational_total_yrs)", - self.data.costs.t_plant_operational_total_yrs, + "Average neutron wall load (MW/m2)", + "(pflux_fw_neutron_mw)", + self.data.physics.pflux_fw_neutron_mw, "OP ", ) - po.ovarre( - self.outfile, - "Total plant lifetime (years)", - "(life_plant)", - self.data.costs.life_plant, - "OP", - ) + po.ovarre( + self.outfile, + "Centrepost lifetime (years)", + "(cplife)", + self.data.costs.cplife, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Maintenance time for replacing CP (years)", + "(tmain)", + self.data.costs.tmain, + "OP ", + ) + po.ovarre( + self.outfile, + "Length of maintenance cycle (years)", + "(maint_cycle)", + maint_cycle, + "OP ", + ) + po.ovarre( + self.outfile, + "Number of maintenance cycles over lifetime", + "(n_cycles_main)", + n_cycles_main, + "OP ", + ) + po.ovarre( + self.outfile, + "Number of centre columns over lifetime", + "(n_centre_cols)", + n_centre_cols, + "OP ", + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, "Total planned unavailability", "(u_planned)", u_planned, "OP " + ) + po.ovarre( + self.outfile, + "Total unplanned unavailability", + "(u_unplanned)", + u_unplanned, + "OP ", + ) + po.ovarre( + self.outfile, + "Total plant availability fraction", + "(f_t_plant_available)", + self.data.costs.f_t_plant_available, + "OP ", + ) + po.ovarre( + self.outfile, + "Capacity factor: total lifetime elec. energy output / output power", + "(cpfact)", + self.data.costs.cpfact, + "OP ", + ) + po.ovarre( + self.outfile, + "Total DT operational time (years)", + "(t_plant_operational_total_yrs)", + self.data.costs.t_plant_operational_total_yrs, + "OP ", + ) + po.ovarre( + self.outfile, + "Total plant lifetime (years)", + "(life_plant)", + self.data.costs.life_plant, + "OP", + ) def cp_lifetime(self): """Calculate Centrepost Lifetime From 3089a776fc4b0f46b372d44fb40951b1db61fc58 Mon Sep 17 00:00:00 2001 From: james <81617086+je-cook@users.noreply.github.com> Date: Thu, 13 Aug 2026 11:24:25 +0100 Subject: [PATCH 2/5] vertical build refactor --- process/models/build.py | 792 ++++++++++------------------------------ 1 file changed, 192 insertions(+), 600 deletions(-) diff --git a/process/models/build.py b/process/models/build.py index 7fa2b7b3e3..c40b90a85d 100644 --- a/process/models/build.py +++ b/process/models/build.py @@ -149,6 +149,196 @@ def calculate_beam_port_size( return radius_beam_tangency, radius_beam_tangency_max + def _vertical_build_out(self, i_single_null: DivertorNumberModels): + bld = self.data.build + sn = i_single_null == i_single_null.SINGLE_NULL + + po.ocmmnt(self.outfile, f"{'Single' if sn else 'Double'} null case") + bld.dz_vv_upper = 0.5 * (bld.dz_vv_upper + bld.dz_vv_lower) + bld.dz_fw_upper = 0.5 * (bld.dr_fw_inboard + bld.dr_fw_outboard) + + vbuild = ( + self.data.buildings.dz_tf_cryostat + + bld.dr_tf_inboard + + bld.dr_tf_shld_gap + + bld.dz_shld_thermal + + bld.dz_shld_vv_gap + + bld.dz_vv_upper + + (bld.dr_shld_blkt_gap if sn else 0) + + bld.dz_shld_upper + + (bld.dz_blkt_upper if sn else 0) + + ( + (bld.dz_fw_upper + bld.dz_fw_plasma_gap) + if sn + else ( + self.data.divertor.dz_divertor + self.data.build.dz_xpoint_divertor + ) + ) + + bld.z_plasma_xpoint_upper + ) + + # To calculate vertical offset between TF coil centre and plasma centre + vbuile1 = vbuild + + dz_fw_upper = 0.5e0 * (bld.dr_fw_inboard + bld.dr_fw_outboard) + + # Top of TF coil + tf_top = vbuild - self.data.buildings.dz_tf_cryostat + vbuild = self.write_obuild( + vbuild, + [ + ( + "Cryostat roof structure*", + self.data.buildings.dz_tf_cryostat, + "(dz_tf_cryostat)", + ), + ("TF coil", bld.dr_tf_inboard, "(dr_tf_inboard)"), + ("Gap", bld.dr_tf_shld_gap, "(dr_tf_shld_gap)"), + ("Thermal shield, vertical", bld.dz_shld_thermal, "(dz_shld_thermal)"), + ("Gap", bld.dz_shld_vv_gap, "(dz_shld_vv_gap)"), + ( + "Vacuum vessel (and shielding)", + bld.dz_vv_upper + bld.dz_shld_upper, + "(dz_vv_upper+dz_shld_upper)", + ), + ("Gap", bld.dr_shld_blkt_gap, "(dr_shld_blkt_gap)"), + ("Top blanket", bld.dz_blkt_upper, "(dz_blkt_upper)"), + ("Top first wall", dz_fw_upper, "(dz_fw_upper)") + if sn + else ( + "Divertor structure", + self.data.divertor.dz_divertor, + "(dz_divertor)", + ), + ("Top scrape-off", bld.dz_fw_plasma_gap, "(dz_fw_plasma_gap)"), + ( + "Plasma upper X-point height (m)", + bld.z_plasma_xpoint_upper, + "(z_plasma_xpoint_upper)", + ), + ], + ) + + for desc, name, val in [ + ( + "Cryostat roof structure*", + "(dz_tf_cryostat)", + self.data.buildings.dz_tf_cryostat, + ), + ("Thermal shield, vertical (m)", "(dz_shld_thermal)", bld.dz_shld_thermal), + ( + "Vessel - TF coil vertical gap (m)", + "(dz_shld_vv_gap)", + bld.dz_shld_vv_gap, + ), + ( + "Topside vacuum vessel radial thickness (m)", + "(dz_vv_upper)", + bld.dz_vv_upper, + ), + ("Top radiation shield thickness (m)", "(dz_shld_upper)", bld.dz_shld_upper), + ("Top blanket vertical thickness (m)", "(dz_blkt_upper)", bld.dz_blkt_upper), + ("Top first wall vertical thickness (m)", "(dz_fw_upper)", dz_fw_upper) + if sn + else ( + "Divertor structure vertical thickness (m)", + "(dz_divertor)", + self.data.divertor.dz_divertor, + ), + ( + "Top scrape-off vertical thickness (m)", + "(dz_fw_plasma_gap)", + bld.dz_fw_plasma_gap, + ), + ( + "Plasma upper X-point height (m)", + "(z_plasma_xpoint_upper)", + bld.z_plasma_xpoint_upper, + ), + ]: + po.ovarre(self.mfile, desc, name, val) + + po.obuild(self.outfile, "Midplane", 0.0e0, vbuild) + + vbuild = self.write_obuild( + vbuild, + [ + ( + "Plasma lower X-point height (m)", + bld.z_plasma_xpoint_lower, + "(z_plasma_xpoint_lower)", + ), + ("Lower scrape-off", bld.dz_xpoint_divertor, "(dz_xpoint_divertor)"), + ("Divertor structure", self.data.divertor.dz_divertor, "(dz_divertor)"), + ( + "Vacuum vessel (and shielding)", + bld.dz_vv_lower + bld.dz_shld_lower, + "(dz_vv_lower+dz_shld_lower)", + ), + ("Gap", bld.dz_shld_vv_gap, "(dz_shld_vv_gap)"), + ("Thermal shield, vertical", bld.dz_shld_thermal, "(dz_shld_thermal)"), + ("Gap", bld.dr_tf_shld_gap, "(dr_tf_shld_gap)"), + ("TF coil", bld.dr_tf_inboard, "(dr_tf_inboard)"), + ( + "Cryostat floor structure**", + self.data.buildings.dz_tf_cryostat, + "(dz_tf_cryostat)", + ), + ], + before=True, + ) + for desc, name, val in [ + ( + "Plasma lower X-point height (m)", + "(z_plasma_xpoint_lower)", + bld.z_plasma_xpoint_lower, + ), + ( + "Bottom scrape-off vertical thickness (m)", + "(dz_xpoint_divertor)", + bld.dz_xpoint_divertor, + ), + ( + "Divertor structure vertical thickness (m)", + "(dz_divertor)", + self.data.divertor.dz_divertor, + ), + ( + "Bottom radiation shield thickness (m)", + "(dz_shld_lower)", + bld.dz_shld_lower, + ), + ( + "Underside vacuum vessel radial thickness (m)", + "(dz_vv_lower)", + bld.dz_vv_lower, + ), + ]: + po.ovarre(self.mfile, desc, name, val) + + # Total height of TF coil + tf_height = tf_top - vbuild + self.data.buildings.dz_tf_cryostat + # Inner vertical dimension of TF coil + bld.dh_tf_inner_bore = tf_height - 2 * bld.dr_tf_inboard + + # To calculate vertical offset between TF coil centre and plasma centre + bld.dz_tf_plasma_centre_offset = (vbuile1 + vbuild) / 2.0e0 + + # end of Single null case + + def write_obuild(self, vbuild, entry: tuple | list[tuple], *, before=False): + """Write obuild entry""" + if not isinstance(entry, list): + entry = [entry] + for desc, var, name in entry: + if before: + vbuild -= var + po.obuild(self.outfile, desc, var, vbuild, name) + if not before: + vbuild -= var + + return vbuild + def calculate_vertical_build(self, output: bool): """Determines the vertical build of the machine. @@ -170,6 +360,7 @@ def calculate_vertical_build(self, output: bool): self.data.build.z_plasma_xpoint_lower = ( self.data.physics.rminor * self.data.physics.kappa ) + i_single_null = DivertorNumberModels(self.data.physics.i_single_null) if output: po.oheadr(self.outfile, "Vertical Build") @@ -181,605 +372,7 @@ def calculate_vertical_build(self, output: bool): self.data.physics.i_single_null, ) - i_single_null = DivertorNumberModels(self.data.physics.i_single_null) - if i_single_null == DivertorNumberModels.DOUBLE_NULL: - po.ocmmnt(self.outfile, "Double null case") - - # Start at the top and work down. - - vertical_build_upper = ( - self.data.buildings.dz_tf_cryostat - + self.data.build.dr_tf_inboard - + self.data.build.dr_tf_shld_gap - + self.data.build.dz_shld_thermal - + self.data.build.dz_shld_vv_gap - + self.data.build.dz_vv_upper - + self.data.build.dz_shld_upper - + self.data.divertor.dz_divertor - + self.data.build.dz_xpoint_divertor - + self.data.build.z_plasma_xpoint_upper - ) - - # To calculate vertical offset between TF coil centre and plasma centre - vbuile1 = vertical_build_upper - - po.obuild( - self.outfile, - "Cryostat roof structure*", - self.data.buildings.dz_tf_cryostat, - vertical_build_upper, - "(dz_tf_cryostat)", - ) - po.ovarre( - self.mfile, - "Cryostat roof structure*", - "(dz_tf_cryostat)", - self.data.buildings.dz_tf_cryostat, - ) - vertical_build_upper -= self.data.buildings.dz_tf_cryostat - - # Top of TF coil - tf_top = vertical_build_upper - - po.obuild( - self.outfile, - "TF coil", - self.data.build.dr_tf_inboard, - vertical_build_upper, - "(dr_tf_inboard)", - ) - vertical_build_upper -= self.data.build.dr_tf_inboard - - po.obuild( - self.outfile, - "Gap", - self.data.build.dr_tf_shld_gap, - vertical_build_upper, - "(dr_tf_shld_gap)", - ) - vertical_build_upper -= self.data.build.dr_tf_shld_gap - - po.obuild( - self.outfile, - "Thermal shield, vertical", - self.data.build.dz_shld_thermal, - vertical_build_upper, - "(dz_shld_thermal)", - ) - - po.ovarre( - self.mfile, - "Thermal shield, vertical (m)", - "(dz_shld_thermal)", - self.data.build.dz_shld_thermal, - ) - vertical_build_upper -= self.data.build.dz_shld_thermal - - po.obuild( - self.outfile, - "Gap", - self.data.build.dz_shld_vv_gap, - vertical_build_upper, - "(dz_shld_vv_gap)", - ) - po.ovarre( - self.mfile, - "Vessel - TF coil vertical gap (m)", - "(dz_shld_vv_gap)", - self.data.build.dz_shld_vv_gap, - ) - vertical_build_upper -= self.data.build.dz_shld_vv_gap - - po.obuild( - self.outfile, - "Vacuum vessel (and shielding)", - self.data.build.dz_vv_upper + self.data.build.dz_shld_upper, - vertical_build_upper, - "(dz_vv_upper+dz_shld_upper)", - ) - vertical_build_upper = ( - vertical_build_upper - - self.data.build.dz_vv_upper - - self.data.build.dz_shld_upper - ) - po.ovarre( - self.mfile, - "Topside vacuum vessel radial thickness (m)", - "(dz_vv_upper)", - self.data.build.dz_vv_upper, - ) - po.ovarre( - self.mfile, - "Top radiation shield thickness (m)", - "(dz_shld_upper)", - self.data.build.dz_shld_upper, - ) - - po.obuild( - self.outfile, - "Divertor structure", - self.data.divertor.dz_divertor, - vertical_build_upper, - "(dz_divertor)", - ) - po.ovarre( - self.mfile, - "Divertor structure vertical thickness (m)", - "(dz_divertor)", - self.data.divertor.dz_divertor, - ) - vertical_build_upper -= self.data.divertor.dz_divertor - - po.obuild( - self.outfile, - "Top scrape-off", - self.data.build.dz_xpoint_divertor, - vertical_build_upper, - "(dz_xpoint_divertor)", - ) - po.ovarre( - self.mfile, - "Top scrape-off vertical thickness (m)", - "(dz_xpoint_divertor)", - self.data.build.dz_xpoint_divertor, - ) - vertical_build_upper -= self.data.build.dz_xpoint_divertor - - po.obuild( - self.outfile, - "Plasma upper X-point height (m)", - self.data.build.z_plasma_xpoint_upper, - vertical_build_upper, - "(z_plasma_xpoint_upper)", - ) - po.ovarre( - self.mfile, - "Plasma upper X-point height (m)", - "(z_plasma_xpoint_upper)", - self.data.build.z_plasma_xpoint_upper, - ) - vertical_build_upper -= self.data.build.z_plasma_xpoint_upper - - po.obuild(self.outfile, "Midplane", 0.0e0, vertical_build_upper) - - vertical_build_upper -= self.data.build.z_plasma_xpoint_lower - po.obuild( - self.outfile, - "Plasma lower X-point height (m)", - self.data.build.z_plasma_xpoint_lower, - vertical_build_upper, - "(z_plasma_xpoint_lower)", - ) - po.ovarre( - self.mfile, - "Plasma lower X-point height (m)", - "(z_plasma_xpoint_lower)", - self.data.build.z_plasma_xpoint_lower, - ) - - vertical_build_upper -= self.data.build.dz_xpoint_divertor - po.obuild( - self.outfile, - "Lower scrape-off", - self.data.build.dz_xpoint_divertor, - vertical_build_upper, - "(dz_xpoint_divertor)", - ) - po.ovarre( - self.mfile, - "Bottom scrape-off vertical thickness (m)", - "(dz_xpoint_divertor)", - self.data.build.dz_xpoint_divertor, - ) - - vertical_build_upper -= self.data.divertor.dz_divertor - po.obuild( - self.outfile, - "Divertor structure", - self.data.divertor.dz_divertor, - vertical_build_upper, - "(dz_divertor)", - ) - po.ovarre( - self.mfile, - "Divertor structure vertical thickness (m)", - "(dz_divertor)", - self.data.divertor.dz_divertor, - ) - - vertical_build_upper -= self.data.build.dz_shld_lower - - vertical_build_upper -= self.data.build.dz_vv_lower - po.obuild( - self.outfile, - "Vacuum vessel (and shielding)", - self.data.build.dz_vv_lower + self.data.build.dz_shld_lower, - vertical_build_upper, - "(dz_vv_lower+dz_shld_lower)", - ) - po.ovarre( - self.mfile, - "Bottom radiation shield thickness (m)", - "(dz_shld_lower)", - self.data.build.dz_shld_lower, - ) - po.ovarre( - self.mfile, - "Underside vacuum vessel radial thickness (m)", - "(dz_vv_lower)", - self.data.build.dz_vv_lower, - ) - - vertical_build_upper -= self.data.build.dz_shld_vv_gap - po.obuild( - self.outfile, - "Gap", - self.data.build.dz_shld_vv_gap, - vertical_build_upper, - "(dz_shld_vv_gap)", - ) - - vertical_build_upper -= self.data.build.dz_shld_thermal - po.obuild( - self.outfile, - "Thermal shield, vertical", - self.data.build.dz_shld_thermal, - vertical_build_upper, - "(dz_shld_thermal)", - ) - - vertical_build_upper -= self.data.build.dr_tf_shld_gap - po.obuild( - self.outfile, - "Gap", - self.data.build.dr_tf_shld_gap, - vertical_build_upper, - "(dr_tf_shld_gap)", - ) - - vertical_build_upper -= self.data.build.dr_tf_inboard - po.obuild( - self.outfile, - "TF coil", - self.data.build.dr_tf_inboard, - vertical_build_upper, - "(dr_tf_inboard)", - ) - - # Total height of TF coil - tf_height = tf_top - vertical_build_upper - # Inner vertical dimension of TF coil - self.data.build.dh_tf_inner_bore = ( - tf_height - 2 * self.data.build.dr_tf_inboard - ) - - vertical_build_upper -= self.data.buildings.dz_tf_cryostat - po.obuild( - self.outfile, - "Cryostat floor structure**", - self.data.buildings.dz_tf_cryostat, - vertical_build_upper, - "(dz_tf_cryostat)", - ) - - # To calculate vertical offset between TF coil centre and plasma centre - self.data.build.dz_tf_plasma_centre_offset = ( - vbuile1 + vertical_build_upper - ) / 2.0e0 - - # End of Double null case - else: - po.ocmmnt(self.outfile, "Single null case") - self.data.build.dz_vv_upper = 0.5 * ( - self.data.build.dz_vv_upper + self.data.build.dz_vv_lower - ) - - self.data.build.dz_fw_upper = 0.5 * ( - self.data.build.dr_fw_inboard + self.data.build.dr_fw_outboard - ) - - vbuild = ( - self.data.buildings.dz_tf_cryostat - + self.data.build.dr_tf_inboard - + self.data.build.dr_tf_shld_gap - + self.data.build.dz_shld_thermal - + self.data.build.dz_shld_vv_gap - + self.data.build.dz_vv_upper - + self.data.build.dr_shld_blkt_gap - + self.data.build.dz_shld_upper - + self.data.build.dz_blkt_upper - + self.data.build.dz_fw_upper - + self.data.build.dz_fw_plasma_gap - + self.data.build.z_plasma_xpoint_upper - ) - - # To calculate vertical offset between TF coil centre and plasma centre - vbuile1 = vbuild - - po.obuild( - self.outfile, - "Cryostat roof structure*", - self.data.buildings.dz_tf_cryostat, - vbuild, - "(dz_tf_cryostat)", - ) - po.ovarre( - self.mfile, - "Cryostat roof structure*", - "(dz_tf_cryostat)", - self.data.buildings.dz_tf_cryostat, - ) - vbuild -= self.data.buildings.dz_tf_cryostat - - # Top of TF coil - tf_top = vbuild - - po.obuild( - self.outfile, - "TF coil", - self.data.build.dr_tf_inboard, - vbuild, - "(dr_tf_inboard)", - ) - vbuild -= self.data.build.dr_tf_inboard - - po.obuild( - self.outfile, - "Gap", - self.data.build.dr_tf_shld_gap, - vbuild, - "(dr_tf_shld_gap)", - ) - vbuild -= self.data.build.dr_tf_shld_gap - - po.obuild( - self.outfile, - "Thermal shield, vertical", - self.data.build.dz_shld_thermal, - vbuild, - "(dz_shld_thermal)", - ) - po.ovarre( - self.mfile, - "Thermal shield, vertical (m)", - "(dz_shld_thermal)", - self.data.build.dz_shld_thermal, - ) - vbuild -= self.data.build.dz_shld_thermal - - po.obuild( - self.outfile, - "Gap", - self.data.build.dz_shld_vv_gap, - vbuild, - "(dz_shld_vv_gap)", - ) - po.ovarre( - self.mfile, - "Vessel - TF coil vertical gap (m)", - "(dz_shld_vv_gap)", - self.data.build.dz_shld_vv_gap, - ) - vbuild -= self.data.build.dz_shld_vv_gap - - po.obuild( - self.outfile, - "Vacuum vessel (and shielding)", - self.data.build.dz_vv_upper + self.data.build.dz_shld_upper, - vbuild, - "(dz_vv_upper+dz_shld_upper)", - ) - vbuild = ( - vbuild - self.data.build.dz_vv_upper - self.data.build.dz_shld_upper - ) - po.ovarre( - self.mfile, - "Topside vacuum vessel radial thickness (m)", - "(dz_vv_upper)", - self.data.build.dz_vv_upper, - ) - po.ovarre( - self.mfile, - "Top radiation shield thickness (m)", - "(dz_shld_upper)", - self.data.build.dz_shld_upper, - ) - - po.obuild( - self.outfile, - "Gap", - self.data.build.dr_shld_blkt_gap, - vbuild, - "(dr_shld_blkt_gap)", - ) - vbuild -= self.data.build.dr_shld_blkt_gap - - po.obuild( - self.outfile, - "Top blanket", - self.data.build.dz_blkt_upper, - vbuild, - "(dz_blkt_upper)", - ) - po.ovarre( - self.mfile, - "Top blanket vertical thickness (m)", - "(dz_blkt_upper)", - self.data.build.dz_blkt_upper, - ) - vbuild -= self.data.build.dz_blkt_upper - - dz_fw_upper = 0.5e0 * ( - self.data.build.dr_fw_inboard + self.data.build.dr_fw_outboard - ) - po.obuild( - self.outfile, "Top first wall", dz_fw_upper, vbuild, "(dz_fw_upper)" - ) - po.ovarre( - self.mfile, - "Top first wall vertical thickness (m)", - "(dz_fw_upper)", - dz_fw_upper, - ) - vbuild -= dz_fw_upper - - po.obuild( - self.outfile, - "Top scrape-off", - self.data.build.dz_fw_plasma_gap, - vbuild, - "(dz_fw_plasma_gap)", - ) - po.ovarre( - self.mfile, - "Top scrape-off vertical thickness (m)", - "(dz_fw_plasma_gap)", - self.data.build.dz_fw_plasma_gap, - ) - vbuild -= self.data.build.dz_fw_plasma_gap - - po.obuild( - self.outfile, - "Plasma upper X-point height (m)", - self.data.build.z_plasma_xpoint_upper, - vbuild, - "(z_plasma_xpoint_upper)", - ) - po.ovarre( - self.mfile, - "Plasma upper X-point height (m)", - "(z_plasma_xpoint_upper)", - self.data.build.z_plasma_xpoint_upper, - ) - vbuild -= self.data.build.z_plasma_xpoint_upper - - po.obuild(self.outfile, "Midplane", 0.0e0, vbuild) - - vbuild -= self.data.build.z_plasma_xpoint_lower - po.obuild( - self.outfile, - "Plasma lower X-point height (m)", - self.data.build.z_plasma_xpoint_lower, - vbuild, - "(z_plasma_xpoint_lower)", - ) - po.ovarre( - self.mfile, - "Plasma lower X-point height (m)", - "(z_plasma_xpoint_lower)", - self.data.build.z_plasma_xpoint_lower, - ) - - vbuild -= self.data.build.dz_xpoint_divertor - po.obuild( - self.outfile, - "Lower scrape-off", - self.data.build.dz_xpoint_divertor, - vbuild, - "(dz_xpoint_divertor)", - ) - po.ovarre( - self.mfile, - "Bottom scrape-off vertical thickness (m)", - "(dz_xpoint_divertor)", - self.data.build.dz_xpoint_divertor, - ) - - vbuild -= self.data.divertor.dz_divertor - po.obuild( - self.outfile, - "Divertor structure", - self.data.divertor.dz_divertor, - vbuild, - "(dz_divertor)", - ) - po.ovarre( - self.mfile, - "Divertor structure vertical thickness (m)", - "(dz_divertor)", - self.data.divertor.dz_divertor, - ) - - vbuild -= self.data.build.dz_shld_lower - - vbuild -= self.data.build.dz_vv_lower - po.obuild( - self.outfile, - "Vacuum vessel (and shielding)", - self.data.build.dz_vv_lower + self.data.build.dz_shld_lower, - vbuild, - "(dz_vv_lower+dz_shld_lower)", - ) - po.ovarre( - self.mfile, - "Bottom radiation shield thickness (m)", - "(dz_shld_lower)", - self.data.build.dz_shld_lower, - ) - po.ovarre( - self.mfile, - "Underside vacuum vessel radial thickness (m)", - "(dz_vv_lower)", - self.data.build.dz_vv_lower, - ) - - vbuild -= self.data.build.dz_shld_vv_gap - po.obuild( - self.outfile, - "Gap", - self.data.build.dz_shld_vv_gap, - vbuild, - "(dz_shld_vv_gap)", - ) - - vbuild -= self.data.build.dz_shld_thermal - po.obuild( - self.outfile, - "Thermal shield, vertical", - self.data.build.dz_shld_thermal, - vbuild, - "(dz_shld_thermal)", - ) - - vbuild -= self.data.build.dr_tf_shld_gap - po.obuild( - self.outfile, - "Gap", - self.data.build.dr_tf_shld_gap, - vbuild, - "(dr_tf_shld_gap)", - ) - - vbuild -= self.data.build.dr_tf_inboard - po.obuild( - self.outfile, - "TF coil", - self.data.build.dr_tf_inboard, - vbuild, - "(dr_tf_inboard)", - ) - - # Total height of TF coil - tf_height = tf_top - vbuild - # Inner vertical dimension of TF coil - self.data.build.dh_tf_inner_bore = ( - tf_height - 2 * self.data.build.dr_tf_inboard - ) - - vbuild -= self.data.buildings.dz_tf_cryostat - - po.obuild( - self.outfile, - "Cryostat floor structure**", - self.data.buildings.dz_tf_cryostat, - vbuild, - "(dz_tf_cryostat)", - ) - - # To calculate vertical offset between TF coil centre and plasma centre - self.data.build.dz_tf_plasma_centre_offset = (vbuile1 + vbuild) / 2.0e0 - - # end of Single null case + self._vertical_build_out(i_single_null) po.ovarre( self.mfile, @@ -816,7 +409,6 @@ def calculate_vertical_build(self, output: bool): ) # Vertical locations of divertor coils - i_single_null = DivertorNumberModels(self.data.physics.i_single_null) if i_single_null == DivertorNumberModels.DOUBLE_NULL: self.data.build.z_tf_top = ( self.data.build.z_tf_inside_half + self.data.build.dr_tf_inboard From a64bd007dfb23aec0c146e47b7428542049c0090 Mon Sep 17 00:00:00 2001 From: james <81617086+je-cook@users.noreply.github.com> Date: Thu, 13 Aug 2026 15:48:59 +0100 Subject: [PATCH 3/5] divertor and radial build refactor --- process/models/build.py | 1564 +++++++++++++-------------------------- 1 file changed, 531 insertions(+), 1033 deletions(-) diff --git a/process/models/build.py b/process/models/build.py index c40b90a85d..f3eb452017 100644 --- a/process/models/build.py +++ b/process/models/build.py @@ -457,8 +457,7 @@ def divgeom(self, output: bool): # options for separate upper and lower self.data.physics.triangularity kap = self.data.physics.kappa - triu = self.data.physics.triang - tril = self.data.physics.triang + triu = tril = self.data.physics.triang # New method, assuming straight legs -- superceded by new method 26/5/2016 # Assumed 90 degrees at X-pt -- wrong# @@ -471,599 +470,299 @@ def divgeom(self, output: bool): # Method 26/05/2016 # Find radius of inner and outer plasma arcs + def rc(trilio): + return 0.5 * np.sqrt( + (self.data.physics.rminor**2 * (trilio**2 + kap**2) ** 2) + / ((tril + 1.0e0) ** 2) + ) - rco = 0.5 * np.sqrt( - (self.data.physics.rminor**2 * ((tril + 1.0e0) ** 2 + kap**2) ** 2) - / ((tril + 1.0e0) ** 2) - ) - rci = 0.5 * np.sqrt( - (self.data.physics.rminor**2 * ((tril - 1.0e0) ** 2 + kap**2) ** 2) - / ((tril - 1.0e0) ** 2) - ) + rco = rc(tril + 1) + rci = rc(tril - 1) # Find angles between vertical and legs - # Inboard arc angle = outboard leg angle - - thetao = np.arcsin(1.0e0 - (self.data.physics.rminor * (1.0e0 - tril)) / rci) + def theta(trilio, rcio): + np.arcsin(1.0e0 - (self.data.physics.rminor * (1.0e0 - trilio)) / rcio) + # Inboard arc angle = outboard leg angle + thetai = theta(1 - tril, rci) # Outboard arc angle = inboard leg angle - - thetai = np.arcsin(1.0e0 - (self.data.physics.rminor * (1.0e0 + tril)) / rco) + thetao = theta(1 + tril, rco) # Position of lower x-pt rxpt = self.data.physics.rmajor - tril * self.data.physics.rminor zxpt = -1.0e0 * kap * self.data.physics.rminor - # Position of inner strike point - # rspi = rxpt - self.data.build.plsepi*cos(alphad) - # zspi = zxpt - self.data.build.plsepi*sin(alphad) - rspi = rxpt - self.data.build.plsepi * np.cos(thetai) - zspi = zxpt - self.data.build.plsepi * np.sin(thetai) - # Position of outer strike point self.data.build.rspo = rxpt + self.data.build.plsepo * np.cos(thetao) zspo = zxpt - self.data.build.plsepo * np.sin(thetao) # Position of inner plate ends - rplti = rspi + (self.data.build.plleni / 2.0e0) * np.cos( - thetai + self.data.divertor.betai - ) - zplti = zspi + (self.data.build.plleni / 2.0e0) * np.sin( - thetai + self.data.divertor.betai - ) - rplbi = rspi - (self.data.build.plleni / 2.0e0) * np.cos( - thetai + self.data.divertor.betai - ) - zplbi = zspi - (self.data.build.plleni / 2.0e0) * np.sin( - thetai + self.data.divertor.betai - ) + def plate_cos(l_div_plate, theta, beta): + return (l_div_plate / 2) * np.cos(theta + beta) - # Position of outer plate ends - rplto = self.data.build.rspo - (self.data.build.plleno / 2.0e0) * np.cos( - thetao + self.data.divertor.betao + def plate_sin(l_div_plate, theta, beta): + return (l_div_plate / 2) * np.sin(theta + beta) + + inner_plte_cos = plate_cos( + self.data.build.plleni, thetai, self.data.divertor.betai ) - zplto = zspo + (self.data.build.plleno / 2.0e0) * np.sin( - thetao + self.data.divertor.betao + inner_plte_sin = plate_sin( + self.data.build.plleni, thetai, self.data.divertor.betai ) - rplbo = self.data.build.rspo + (self.data.build.plleno / 2.0e0) * np.cos( - thetao + self.data.divertor.betao + outer_plte_cos = plate_cos( + self.data.build.plleno, thetao, self.data.divertor.betao ) - zplbo = zspo - (self.data.build.plleno / 2.0e0) * np.sin( - thetao + self.data.divertor.betao + outer_plte_sin = plate_sin( + self.data.build.plleno, thetao, self.data.divertor.betao ) + # Position of inner strike point + # rspi = rxpt - self.data.build.plsepi*cos(alphad) + # zspi = zxpt - self.data.build.plsepi*sin(alphad) + rspi = rxpt - self.data.build.plsepi * np.cos(thetai) + zspi = zxpt - self.data.build.plsepi * np.sin(thetai) + zplti = zspi + inner_plte_sin + zplbi = zspi - inner_plte_sin + zplto = zspo + outer_plte_sin + zplbo = zspo - outer_plte_sin + divht = max(zplti, zplto) - min(zplbo, zplbi) if output: - if self.data.divertor.n_divertors == 1: - po.oheadr(self.outfile, "Divertor build and plasma position") - po.ocmmnt(self.outfile, "Divertor Configuration = Single Null Divertor") - po.oblnkl(self.outfile) - ptop_radial = self.data.physics.rmajor - triu * self.data.physics.rminor - ptop_vertical = kap * self.data.physics.rminor - po.ovarre( - self.outfile, + self.divertor_geom_output( + kap, + divht, + tril, + triu, + thetai, + thetao, + rco, + rci, + rxpt, + zxpt, + rspi, + zspi, + zspo, + # Position of inner strike points + rplti=rspi + inner_plte_cos, + rplbi=rspi - inner_plte_cos, + zplti=zplti, + zplbi=zplbi, + # Position of outer plate ends + rplto=self.data.build.rspo - outer_plte_cos, + rplbo=self.data.build.rspo + outer_plte_cos, + zplto=zplto, + zplbo=zplbo, + ) + + return divht + + def divertor_geom_output( + self, + kap, + divht, + tril, + triu, + thetai, + thetao, + rco, + rci, + rxpt, + zxpt, + rspi, + zspi, + zspo, + rplti, + rplbi, + zplti, + zplbi, + rplto, + rplbo, + zplto, + zplbo, + ): + """Divertor geometry output""" + po.oheadr(self.outfile, "Divertor build and plasma position") + + if self.data.divertor.n_divertors == 1: + po.ocmmnt(self.outfile, "Divertor Configuration = Single Null Divertor") + po.oblnkl(self.outfile) + + for desc, name, var in [ + ( "Plasma top position, radial (m)", "(ptop_radial)", - ptop_radial, - "OP ", - ) - po.ovarre( - self.outfile, + self.data.physics.rmajor - triu * self.data.physics.rminor, + ), + ( "Plasma top position, vertical (m)", "(ptop_vertical)", - ptop_vertical, - "OP ", - ) - po.ovarre( - self.outfile, + kap * self.data.physics.rminor, + ), + ( "Plasma geometric centre, radial (m)", "(rmajor.)", self.data.physics.rmajor, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma geometric centre, vertical (m)", - "(0.0)", - 0.0e0, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma lower triangularity", - "(tril)", - tril, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma elongation", - "(kappa.)", - kap, - "OP ", - ) - po.ovarre( - self.outfile, + ), + ("Plasma geometric centre, vertical (m)", "(0.0)", 0.0e0), + ("Plasma lower triangularity", "(tril)", tril), + ("Plasma elongation", "(kappa.)", kap), + ( "TF coil vertical offset (m)", "(dz_tf_plasma_centre_offset)", self.data.build.dz_tf_plasma_centre_offset, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma outer arc radius of curvature (m)", - "(rco)", - rco, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma inner arc radius of curvature (m)", - "(rci)", - rci, - "OP ", - ) - po.ovarre( - self.outfile, "Plasma lower X-pt, radial (m)", "(rxpt)", rxpt, "OP " - ) - po.ovarre( - self.outfile, - "Plasma lower X-pt, vertical (m)", - "(zxpt)", - zxpt, - "OP ", - ) - po.ovarre( - self.outfile, + ), + ("Plasma outer arc radius of curvature (m)", "(rco)", rco), + ("Plasma inner arc radius of curvature (m)", "(rci)", rci), + ("Plasma lower X-pt, radial (m)", "(rxpt)", rxpt), + ("Plasma lower X-pt, vertical (m)", "(zxpt)", zxpt), + ( "Poloidal plane angle between vertical and inner leg (rad)", "(thetai)", thetai, - "OP ", - ) - po.ovarre( - self.outfile, + ), + ( "Poloidal plane angle between vertical and outer leg (rad)", "(thetao)", thetao, - "OP ", - ) - po.ovarre( - self.outfile, + ), + ( "Poloidal plane angle between inner leg and plate (rad)", "(betai)", self.data.divertor.betai, - ) - po.ovarre( - self.outfile, + ), + ( "Poloidal plane angle between outer leg and plate (rad)", "(betao)", self.data.divertor.betao, - ) - po.ovarre( - self.outfile, + ), + ( "Inner divertor leg poloidal length (m)", "(plsepi)", self.data.build.plsepi, - ) - po.ovarre( - self.outfile, + ), + ( "Outer divertor leg poloidal length (m)", "(plsepo)", self.data.build.plsepo, - ) - po.ovarre( - self.outfile, - "Inner divertor plate length (m)", - "(plleni)", - self.data.build.plleni, - ) - po.ovarre( - self.outfile, - "Outer divertor plate length (m)", - "(plleno)", - self.data.build.plleno, - ) - po.ovarre( - self.outfile, - "Inner strike point, radial (m)", - "(rspi)", - rspi, - "OP ", - ) - po.ovarre( - self.outfile, - "Inner strike point, vertical (m)", - "(zspi)", - zspi, - "OP ", - ) - po.ovarre( - self.outfile, "Inner plate top, radial (m)", "(rplti)", rplti, "OP " - ) - po.ovarre( - self.outfile, - "Inner plate top, vertical (m)", - "(zplti)", - zplti, - "OP ", - ) - po.ovarre( - self.outfile, - "Inner plate bottom, radial (m)", - "(rplbi)", - rplbi, - "OP ", - ) - po.ovarre( - self.outfile, - "Inner plate bottom, vertical (m)", - "(zplbi)", - zplbi, - "OP ", - ) - po.ovarre( - self.outfile, - "Outer strike point, radial (m)", - "(rspo)", - self.data.build.rspo, - "OP ", - ) - po.ovarre( - self.outfile, - "Outer strike point, vertical (m)", - "(zspo)", - zspo, - "OP ", - ) - po.ovarre( - self.outfile, "Outer plate top, radial (m)", "(rplto)", rplto, "OP " - ) - po.ovarre( - self.outfile, - "Outer plate top, vertical (m)", - "(zplto)", - zplto, - "OP ", - ) - po.ovarre( - self.outfile, - "Outer plate bottom, radial (m)", - "(rplbo)", - rplbo, - "OP ", - ) - po.ovarre( - self.outfile, - "Outer plate bottom, vertical (m)", - "(zplbo)", - zplbo, - "OP ", - ) - po.ovarre( - self.outfile, - "Calculated maximum divertor height (m)", - "(divht)", - divht, - "OP ", - ) - - elif self.data.divertor.n_divertors == 2: - po.oheadr(self.outfile, "Divertor build and plasma position") - po.ocmmnt(self.outfile, "Divertor Configuration = Double Null Divertor") - po.oblnkl(self.outfile) - # Assume upper and lower divertors geometries are symmetric. - ptop_radial = self.data.physics.rmajor - triu * self.data.physics.rminor - ptop_vertical = kap * self.data.physics.rminor - po.ovarre( - self.outfile, + ), + ("Inner divertor plate length (m)", "(plleni)", self.data.build.plleni), + ("Outer divertor plate length (m)", "(plleno)", self.data.build.plleno), + ("Inner strike point, radial (m)", "(rspi)", rspi), + ("Inner strike point, vertical (m)", "(zspi)", zspi), + ("Inner plate top, radial (m)", "(rplti)", rplti), + ("Inner plate top, vertical (m)", "(zplti)", zplti), + ("Inner plate bottom, radial (m)", "(rplbi)", rplbi), + ("Inner plate bottom, vertical (m)", "(zplbi)", zplbi), + ("Outer strike point, radial (m)", "(rspo)", self.data.build.rspo), + ("Outer strike point, vertical (m)", "(zspo)", zspo), + ("Outer plate top, radial (m)", "(rplto)", rplto), + ("Outer plate top, vertical (m)", "(zplto)", zplto), + ("Outer plate bottom, radial (m)", "(rplbo)", rplbo), + ("Outer plate bottom, vertical (m)", "(zplbo)", zplbo), + ("Calculated maximum divertor height (m)", "(divht)", divht), + ]: + po.ovarre(self.outfile, desc, name, var, "OP ") + + elif self.data.divertor.n_divertors == 2: + po.ocmmnt(self.outfile, "Divertor Configuration = Double Null Divertor") + po.oblnkl(self.outfile) + # Assume upper and lower divertors geometries are symmetric. + for desc, name, var in [ + ( "Plasma top position, radial (m)", "(ptop_radial)", - ptop_radial, - "OP ", - ) - po.ovarre( - self.outfile, + self.data.physics.rmajor - triu * self.data.physics.rminor, + ), + ( "Plasma top position, vertical (m)", "(ptop_vertical)", - ptop_vertical, - "OP ", - ) - po.ovarre( - self.outfile, + kap * self.data.physics.rminor, + ), + ( "Plasma geometric centre, radial (m)", "(rmajor.)", self.data.physics.rmajor, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma geometric centre, vertical (m)", - "(0.0)", - 0.0e0, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma data.physics.triangularity", - "(tril)", - tril, - "OP ", - ) - po.ovarre(self.outfile, "Plasma elongation", "(kappa.)", kap, "OP ") - po.ovarre( - self.outfile, + ), + ("Plasma geometric centre, vertical (m)", "(0.0)", 0.0e0), + ("Plasma data.physics.triangularity", "(tril)", tril), + ("Plasma elongation", "(kappa.)", kap), + ( "TF coil vertical offset (m)", "(dz_tf_plasma_centre_offset)", self.data.build.dz_tf_plasma_centre_offset, - "OP ", - ) - po.ovarre( - self.outfile, "Plasma upper X-pt, radial (m)", "(rxpt)", rxpt, "OP " - ) - po.ovarre( - self.outfile, - "Plasma upper X-pt, vertical (m)", - "(-zxpt)", - -zxpt, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma outer arc radius of curvature (m)", - "(rco)", - rco, - "OP ", - ) - po.ovarre( - self.outfile, - "Plasma inner arc radius of curvature (m)", - "(rci)", - rci, - "OP ", - ) - po.ovarre( - self.outfile, "Plasma lower X-pt, radial (m)", "(rxpt)", rxpt, "OP " - ) - po.ovarre( - self.outfile, - "Plasma lower X-pt, vertical (m)", - "(zxpt)", - zxpt, - "OP ", - ) - po.ovarre( - self.outfile, + ), + ("Plasma upper X-pt, radial (m)", "(rxpt)", rxpt), + ("Plasma upper X-pt, vertical (m)", "(-zxpt)", -zxpt), + ("Plasma outer arc radius of curvature (m)", "(rco)", rco), + ("Plasma inner arc radius of curvature (m)", "(rci)", rci), + ("Plasma lower X-pt, radial (m)", "(rxpt)", rxpt), + ("Plasma lower X-pt, vertical (m)", "(zxpt)", zxpt), + ( "Poloidal plane angle between vertical and inner leg (rad)", "(thetai)", thetai, - "OP ", - ) - po.ovarre( - self.outfile, + ), + ( "Poloidal plane angle between vertical and outer leg (rad)", "(thetao)", thetao, - "OP ", - ) - po.ovarre( - self.outfile, + ), + ( "Poloidal plane angle between inner leg and plate (rad)", "(betai)", self.data.divertor.betai, - ) - po.ovarre( - self.outfile, + ), + ( "Poloidal plane angle between outer leg and plate (rad)", "(betao)", self.data.divertor.betao, - ) - po.ovarre( - self.outfile, + ), + ( "Inner divertor leg poloidal length (m)", "(plsepi)", self.data.build.plsepi, - ) - po.ovarre( - self.outfile, + ), + ( "Outer divertor leg poloidal length (m)", "(plsepo)", self.data.build.plsepo, - ) - po.ovarre( - self.outfile, - "Inner divertor plate length (m)", - "(lleni)", - self.data.build.plleni, - ) - po.ovarre( - self.outfile, - "Outer divertor plate length (m)", - "(plleno)", - self.data.build.plleno, - ) - po.ovarre( - self.outfile, - "Upper inner strike point, radial (m)", - "(rspi)", - rspi, - "OP ", - ) - po.ovarre( - self.outfile, - "Upper inner strike point, vertical (m)", - "(-zspi)", - -zspi, - "OP ", - ) - po.ovarre( - self.outfile, - "Upper inner plate top, radial (m)", - "(rplti)", - rplti, - "OP ", - ) - po.ovarre( - self.outfile, - "Upper inner plate top, vertical (m)", - "(-zplti)", - -zplti, - "OP ", - ) - po.ovarre( - self.outfile, - "Upper inner plate bottom, radial (m)", - "(rplbi)", - rplbi, - "OP ", - ) - po.ovarre( - self.outfile, - "Upper inner plate bottom, vertical (m)", - "(-zplbi)", - -zplbi, - "OP ", - ) - po.ovarre( - self.outfile, - "Upper outer strike point, radial (m)", - "(rspo)", - self.data.build.rspo, - "OP ", - ) - po.ovarre( - self.outfile, - "Upper outer strike point, vertical (m)", - "(-zspo)", - -zspo, - "OP ", - ) - po.ovarre( - self.outfile, - "Upper outer plate top, radial (m)", - "(rplto)", - rplto, - "OP ", - ) - po.ovarre( - self.outfile, - "Upper outer plate top, vertical (m)", - "(-zplto)", - -zplto, - "OP ", - ) - po.ovarre( - self.outfile, - "Upper outer plate bottom, radial (m)", - "(rplbo)", - rplbo, - "OP ", - ) - po.ovarre( - self.outfile, - "Upper outer plate bottom, vertical (m)", - "(-zplbo)", - -zplbo, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower inner strike point, radial (m)", - "(rspi)", - rspi, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower inner strike point, vertical (m)", - "(zspi)", - zspi, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower inner plate top, radial (m)", - "(rplti)", - rplti, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower inner plate top, vertical (m)", - "(zplti)", - zplti, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower inner plate bottom, radial (m)", - "(rplbi)", - rplbi, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower inner plate bottom, vertical (m)", - "(zplbi)", - zplbi, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower outer strike point, radial (m)", - "(rspo)", - self.data.build.rspo, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower outer strike point, vertical (m)", - "(zspo)", - zspo, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower outer plate top, radial (m)", - "(rplto)", - rplto, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower outer plate top, vertical (m)", - "(zplto)", - zplto, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower outer plate bottom, radial (m)", - "(rplbo)", - rplbo, - "OP ", - ) - po.ovarre( - self.outfile, - "Lower outer plate bottom, vertical (m)", - "(zplbo)", - zplbo, - "OP ", - ) - po.ovarre( - self.outfile, - "Calculated maximum divertor height (m)", - "(divht)", - divht, - "OP ", - ) - else: - po.oheadr(self.outfile, "Divertor build and plasma position") - po.ocmmnt( - self.outfile, - "ERROR: null value not supported, check i_single_null value.", - ) - return divht + ), + ("Inner divertor plate length (m)", "(plleni)", self.data.build.plleni), + ("Outer divertor plate length (m)", "(plleno)", self.data.build.plleno), + ("Upper inner strike point, radial (m)", "(rspi)", rspi), + ("Upper inner strike point, vertical (m)", "(-zspi)", -zspi), + ("Upper inner plate top, radial (m)", "(rplti)", rplti), + ("Upper inner plate top, vertical (m)", "(-zplti)", -zplti), + ("Upper inner plate bottom, radial (m)", "(rplbi)", rplbi), + ("Upper inner plate bottom, vertical (m)", "(-zplbi)", -zplbi), + ("Upper outer strike point, radial (m)", "(rspo)", self.data.build.rspo), + ("Upper outer strike point, vertical (m)", "(-zspo)", -zspo), + ("Upper outer plate top, radial (m)", "(rplto)", rplto), + ("Upper outer plate top, vertical (m)", "(-zplto)", -zplto), + ("Upper outer plate bottom, radial (m)", "(rplbo)", rplbo), + ("Upper outer plate bottom, vertical (m)", "(-zplbo)", -zplbo), + ("Lower inner strike point, radial (m)", "(rspi)", rspi), + ("Lower inner strike point, vertical (m)", "(zspi)", zspi), + ("Lower inner plate top, radial (m)", "(rplti)", rplti), + ("Lower inner plate top, vertical (m)", "(zplti)", zplti), + ("Lower inner plate bottom, radial (m)", "(rplbi)", rplbi), + ("Lower inner plate bottom, vertical (m)", "(zplbi)", zplbi), + ("Lower outer strike point, radial (m)", "(rspo)", self.data.build.rspo), + ("Lower outer strike point, vertical (m)", "(zspo)", zspo), + ("Lower outer plate top, radial (m)", "(rplto)", rplto), + ("Lower outer plate top, vertical (m)", "(zplto)", zplto), + ("Lower outer plate bottom, radial (m)", "(rplbo)", rplbo), + ("Lower outer plate bottom, vertical (m)", "(zplbo)", zplbo), + ("Calculated maximum divertor height (m)", "(divht)", divht), + ]: + po.ovarre(self.outfile, desc, name, var, "OP ") + + else: + po.ocmmnt( + self.outfile, + "ERROR: null value not supported, check i_single_null value.", + ) @staticmethod def plasma_outboard_edge_toroidal_ripple( @@ -1238,277 +937,241 @@ def calculate_radial_build(self, output: bool): output : bool Flag indicating whether to output the results """ + bld = self.data.build if self.data.fwbs.blktmodel > 0: - self.data.build.dr_blkt_inboard = ( - self.data.build.blbuith - + self.data.build.blbmith - + self.data.build.blbpith - ) - self.data.build.dr_blkt_outboard = ( - self.data.build.blbuoth - + self.data.build.blbmoth - + self.data.build.blbpoth - ) - self.data.build.dz_shld_upper = 0.5e0 * ( - self.data.build.dr_shld_inboard + self.data.build.dr_shld_outboard - ) + bld.dr_blkt_inboard = bld.blbuith + bld.blbmith + bld.blbpith + bld.dr_blkt_outboard = bld.blbuoth + bld.blbmoth + bld.blbpoth + bld.dz_shld_upper = 0.5e0 * (bld.dr_shld_inboard + bld.dr_shld_outboard) # Top/bottom blanket thickness - self.data.build.dz_blkt_upper = 0.5e0 * ( - self.data.build.dr_blkt_inboard + self.data.build.dr_blkt_outboard - ) + bld.dz_blkt_upper = 0.5e0 * (bld.dr_blkt_inboard + bld.dr_blkt_outboard) i_single_null = DivertorNumberModels(self.data.physics.i_single_null) if i_single_null == DivertorNumberModels.SINGLE_NULL: - # Check if self.data.build.dz_fw_plasma_gap has been set too small - self.data.build.dz_fw_plasma_gap = max( - 0.5e0 - * ( - self.data.build.dr_fw_plasma_gap_inboard - + self.data.build.dr_fw_plasma_gap_outboard - ), - self.data.build.dz_fw_plasma_gap, + # Check if bld.dz_fw_plasma_gap has been set too small + bld.dz_fw_plasma_gap = max( + 0.5e0 * (bld.dr_fw_plasma_gap_inboard + bld.dr_fw_plasma_gap_outboard), + bld.dz_fw_plasma_gap, ) # Issue #514 Radial dimensions of inboard leg - # Calculate self.data.build.dr_tf_inboard if + # Calculate bld.dr_tf_inboard if # self.data.tfcoil.dr_tf_wp_with_insulation is an iteration variable (140) if 140 in self.data.numerics.ixc[0 : self.data.numerics.nvar]: - self.data.build.dr_tf_inboard = ( + bld.dr_tf_inboard = ( self.data.tfcoil.dr_tf_wp_with_insulation + self.data.tfcoil.dr_tf_plasma_case + self.data.tfcoil.dr_tf_nose_case ) - if self.data.build.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS: - self.data.build.r_tf_inboard_in = self.data.build.dr_bore + if bld.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS: + bld.r_tf_inboard_in = bld.dr_bore # CS bore radius [m] - self.data.build.dr_cs_bore = ( - self.data.build.dr_bore - + self.data.build.dr_tf_inboard - + self.data.build.dr_cs_tf_gap - ) + bld.dr_cs_bore = bld.dr_bore + bld.dr_tf_inboard + bld.dr_cs_tf_gap else: - self.data.build.dr_cs_bore = self.data.build.dr_bore + bld.dr_cs_bore = bld.dr_bore # Calculate pre-compression structure thickness if ( - CSPrecompressionConfiguration(self.data.build.i_cs_precomp) + CSPrecompressionConfiguration(bld.i_cs_precomp) == CSPrecompressionConfiguration.CS_PRECOMPRESSION_STRUCTURE_PRESENT ): - self.data.build.dr_cs_precomp = self.data.build.fseppc / ( + bld.dr_cs_precomp = bld.fseppc / ( 2.0e0 * np.pi - * self.data.build.fcspc - * self.data.build.sigallpc - * (2.0 * self.data.build.dr_cs_bore + self.data.build.dr_cs) + * bld.fcspc + * bld.sigallpc + * (2.0 * bld.dr_cs_bore + bld.dr_cs) ) else: - self.data.build.dr_cs_precomp = 0.0e0 + bld.dr_cs_precomp = 0.0e0 - if self.data.build.i_tf_inside_cs != TFCSRadialConfiguration.TF_INSIDE_CS: + if bld.i_tf_inside_cs != TFCSRadialConfiguration.TF_INSIDE_CS: # Inboard side inner radius [m] # This is not calculated above because it requires the dr_cs_precomp - self.data.build.r_tf_inboard_in = ( - self.data.build.dr_bore - + self.data.build.dr_cs - + self.data.build.dr_cs_precomp - + self.data.build.dr_cs_tf_gap + bld.r_tf_inboard_in = ( + bld.dr_bore + bld.dr_cs + bld.dr_cs_precomp + bld.dr_cs_tf_gap ) # Radial build to tfcoil middle [m] - self.data.build.r_tf_inboard_mid = ( - self.data.build.r_tf_inboard_in + 0.5e0 * self.data.build.dr_tf_inboard - ) + bld.r_tf_inboard_mid = bld.r_tf_inboard_in + 0.5e0 * bld.dr_tf_inboard # Radial build to tfcoil plasma facing side [m] - self.data.build.r_tf_inboard_out = ( - self.data.build.r_tf_inboard_in + self.data.build.dr_tf_inboard - ) + bld.r_tf_inboard_out = bld.r_tf_inboard_in + bld.dr_tf_inboard # WP radial thickness [m] # Calculated only if not used as an iteration variable if 140 not in self.data.numerics.ixc[0 : self.data.numerics.nvar]: self.data.tfcoil.dr_tf_wp_with_insulation = ( - self.data.build.dr_tf_inboard + bld.dr_tf_inboard - self.data.tfcoil.dr_tf_plasma_case - self.data.tfcoil.dr_tf_nose_case ) # Radius of the centrepost at the top of the machine if self.data.physics.itart == 1 and self.data.tfcoil.i_tf_sup != 1: - # self.data.build.r_cp_top is set using the plasma shape - if self.data.build.i_r_cp_top == 0: - self.data.build.r_cp_top = ( + # bld.r_cp_top is set using the plasma shape + if bld.i_r_cp_top == 0: + bld.r_cp_top = ( self.data.physics.rmajor - self.data.physics.rminor * self.data.physics.triang - ( - self.data.build.dr_tf_shld_gap - + self.data.build.dr_shld_thermal_inboard - + self.data.build.dr_shld_inboard - + self.data.build.dr_shld_blkt_gap - + self.data.build.dr_blkt_inboard - + self.data.build.dr_fw_inboard - + 3.0e0 * self.data.build.dr_fw_plasma_gap_inboard + bld.dr_tf_shld_gap + + bld.dr_shld_thermal_inboard + + bld.dr_shld_inboard + + bld.dr_shld_blkt_gap + + bld.dr_blkt_inboard + + bld.dr_fw_inboard + + 3.0e0 * bld.dr_fw_plasma_gap_inboard ) + self.data.tfcoil.drtop ) - # Notify user that self.data.build.r_cp_top has been set to - # 1.01*self.data.build.r_tf_inboard_out (lvl 2 error) - if self.data.build.r_cp_top < 1.01e0 * self.data.build.r_tf_inboard_out: + # Notify user that bld.r_cp_top has been set to + # 1.01*bld.r_tf_inboard_out (lvl 2 error) + if bld.r_cp_top < 1.01e0 * bld.r_tf_inboard_out: logger.error( "TF CP top radius (r_cp_top) replaced by 1.01*r_tf_inboard_out " "-> potential top rbuild issue" - f"{self.data.build.r_cp_top=} " - f"{self.data.build.r_tf_inboard_out=}" + f"{bld.r_cp_top=} " + f"{bld.r_tf_inboard_out=}" ) - # self.data.build.r_cp_top correction - self.data.build.r_cp_top = self.data.build.r_tf_inboard_out * 1.01e0 + # bld.r_cp_top correction + bld.r_cp_top = bld.r_tf_inboard_out * 1.01e0 # Top and mid-plane TF coil CP radius ratio - self.data.build.f_r_cp = ( - self.data.build.r_cp_top / self.data.build.r_tf_inboard_out - ) + bld.f_r_cp = bld.r_cp_top / bld.r_tf_inboard_out - # User defined self.data.build.r_cp_top - elif self.data.build.i_r_cp_top == 1: - # Notify user that self.data.build.r_cp_top has been set to - # 1.01*self.data.build.r_tf_inboard_out (lvl 2 error) - if self.data.build.r_cp_top < 1.01e0 * self.data.build.r_tf_inboard_out: + # User defined bld.r_cp_top + elif bld.i_r_cp_top == 1: + # Notify user that bld.r_cp_top has been set to + # 1.01*bld.r_tf_inboard_out (lvl 2 error) + if bld.r_cp_top < 1.01e0 * bld.r_tf_inboard_out: logger.error( "TF CP top radius (r_cp_top) replaced by 1.01*r_tf_inboard_out " "-> potential top rbuild issue" - f"{self.data.build.r_cp_top=} " - f"{self.data.build.r_tf_inboard_out=}" + f"{bld.r_cp_top=} " + f"{bld.r_tf_inboard_out=}" ) - # self.data.build.r_cp_top correction - self.data.build.r_cp_top = self.data.build.r_tf_inboard_out * 1.01e0 + # bld.r_cp_top correction + bld.r_cp_top = bld.r_tf_inboard_out * 1.01e0 # Top / mid-plane TF CP radius ratio - self.data.build.f_r_cp = ( - self.data.build.r_cp_top / self.data.build.r_tf_inboard_out - ) + bld.f_r_cp = bld.r_cp_top / bld.r_tf_inboard_out - # self.data.build.r_cp_top set as a fraction of the outer TF midplane radius - elif self.data.build.i_r_cp_top == 2: - self.data.build.r_cp_top = ( - self.data.build.f_r_cp * self.data.build.r_tf_inboard_out - ) + # bld.r_cp_top set as a fraction of the outer TF midplane radius + elif bld.i_r_cp_top == 2: + bld.r_cp_top = bld.f_r_cp * bld.r_tf_inboard_out else: # End of self.data.physics.itart == 1 .and. self.data.tfcoil.i_tf_sup /= 1 - self.data.build.r_cp_top = self.data.build.r_tf_inboard_out + bld.r_cp_top = bld.r_tf_inboard_out - if self.data.build.i_r_cp_top != 0 and ( - self.data.build.r_cp_top + if bld.i_r_cp_top != 0 and ( + bld.r_cp_top > self.data.physics.rmajor - self.data.physics.rminor * self.data.physics.triang - ( - self.data.build.dr_tf_shld_gap - + self.data.build.dr_shld_thermal_inboard - + self.data.build.dr_shld_inboard - + self.data.build.dr_shld_blkt_gap - + self.data.build.dr_blkt_inboard - + self.data.build.dr_fw_inboard - + 3.0e0 * self.data.build.dr_fw_plasma_gap_inboard + bld.dr_tf_shld_gap + + bld.dr_shld_thermal_inboard + + bld.dr_shld_inboard + + bld.dr_shld_blkt_gap + + bld.dr_blkt_inboard + + bld.dr_fw_inboard + + 3.0e0 * bld.dr_fw_plasma_gap_inboard ) + self.data.tfcoil.drtop ): logger.error( "Top CP radius larger that its value determined with plasma shape " - f"{self.data.build.r_cp_top=}" + f"{bld.r_cp_top=}" ) - if self.data.build.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS: + if bld.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS: # Radial position of vacuum vessel [m] - self.data.build.r_vv_inboard_out = ( - self.data.build.r_tf_inboard_out - + self.data.build.dr_cs - + self.data.build.dr_cs_tf_gap - + self.data.build.dr_cs_precomp - + self.data.build.dr_tf_shld_gap - + self.data.build.dr_shld_thermal_inboard - + self.data.build.dr_shld_vv_gap_inboard - + self.data.build.dr_vv_inboard + bld.r_vv_inboard_out = ( + bld.r_tf_inboard_out + + bld.dr_cs + + bld.dr_cs_tf_gap + + bld.dr_cs_precomp + + bld.dr_tf_shld_gap + + bld.dr_shld_thermal_inboard + + bld.dr_shld_vv_gap_inboard + + bld.dr_vv_inboard ) else: - self.data.build.r_vv_inboard_out = ( - self.data.build.r_tf_inboard_out - + self.data.build.dr_tf_shld_gap - + self.data.build.dr_shld_thermal_inboard - + self.data.build.dr_shld_vv_gap_inboard - + self.data.build.dr_vv_inboard + bld.r_vv_inboard_out = ( + bld.r_tf_inboard_out + + bld.dr_tf_shld_gap + + bld.dr_shld_thermal_inboard + + bld.dr_shld_vv_gap_inboard + + bld.dr_vv_inboard ) # Radial position of the inner side of inboard neutronic shield [m] - self.data.build.r_sh_inboard_in = self.data.build.r_vv_inboard_out + bld.r_sh_inboard_in = bld.r_vv_inboard_out # Radial position of the plasma facing side of inboard neutronic shield [m] - self.data.build.r_sh_inboard_out = ( - self.data.build.r_sh_inboard_in + self.data.build.dr_shld_inboard - ) + bld.r_sh_inboard_out = bld.r_sh_inboard_in + bld.dr_shld_inboard # Radial build to centre of plasma (should be equal to self.data.physics.rmajor) - self.data.build.rbld = ( - self.data.build.r_sh_inboard_out - + self.data.build.dr_shld_blkt_gap - + self.data.build.dr_blkt_inboard - + self.data.build.dr_fw_inboard - + self.data.build.dr_fw_plasma_gap_inboard + bld.rbld = ( + bld.r_sh_inboard_out + + bld.dr_shld_blkt_gap + + bld.dr_blkt_inboard + + bld.dr_fw_inboard + + bld.dr_fw_plasma_gap_inboard + self.data.physics.rminor ) # Radius to inner edge of inboard shield - self.data.build.r_shld_inboard_inner = ( + bld.r_shld_inboard_inner = ( self.data.physics.rmajor - self.data.physics.rminor - - self.data.build.dr_fw_plasma_gap_inboard - - self.data.build.dr_fw_inboard - - self.data.build.dr_blkt_inboard - - self.data.build.dr_shld_inboard + - bld.dr_fw_plasma_gap_inboard + - bld.dr_fw_inboard + - bld.dr_blkt_inboard + - bld.dr_shld_inboard ) # Radius to outer edge of outboard shield - self.data.build.r_shld_outboard_outer = ( + bld.r_shld_outboard_outer = ( self.data.physics.rmajor + self.data.physics.rminor - + self.data.build.dr_fw_plasma_gap_outboard - + self.data.build.dr_fw_outboard - + self.data.build.dr_blkt_outboard - + self.data.build.dr_shld_outboard + + bld.dr_fw_plasma_gap_outboard + + bld.dr_fw_outboard + + bld.dr_blkt_outboard + + bld.dr_shld_outboard ) # Thickness of outboard TF coil legs if self.data.tfcoil.i_tf_sup != 1: - self.data.build.dr_tf_outboard = ( - self.data.build.f_dr_tf_outboard_inboard * self.data.build.dr_tf_inboard - ) + bld.dr_tf_outboard = bld.f_dr_tf_outboard_inboard * bld.dr_tf_inboard else: - self.data.build.dr_tf_outboard = self.data.build.dr_tf_inboard + bld.dr_tf_outboard = bld.dr_tf_inboard # Radius to centre of outboard TF coil legs - self.data.build.r_tf_outboard_mid = ( - self.data.build.r_shld_outboard_outer - + self.data.build.dr_shld_blkt_gap - + self.data.build.dr_vv_outboard - + self.data.build.gapomin - + self.data.build.dr_shld_thermal_outboard - + self.data.build.dr_tf_shld_gap - + 0.5e0 * self.data.build.dr_tf_outboard + bld.r_tf_outboard_mid = ( + bld.r_shld_outboard_outer + + bld.dr_shld_blkt_gap + + bld.dr_vv_outboard + + bld.gapomin + + bld.dr_shld_thermal_outboard + + bld.dr_tf_shld_gap + + 0.5e0 * bld.dr_tf_outboard ) # TF coil horizontal bore at mid-plane [m] - self.data.build.dr_tf_inner_bore = ( - self.data.build.r_tf_outboard_mid - 0.5e0 * self.data.build.dr_tf_outboard - ) - (self.data.build.r_tf_inboard_mid - 0.5e0 * self.data.build.dr_tf_inboard) + bld.dr_tf_inner_bore = (bld.r_tf_outboard_mid - 0.5e0 * bld.dr_tf_outboard) - ( + bld.r_tf_inboard_mid - 0.5e0 * bld.dr_tf_inboard + ) ( self.data.tfcoil.ripple_b_tf_plasma_edge, r_tf_outboard_midl, - self.data.build.ripflag, + bld.ripflag, ) = self.plasma_outboard_edge_toroidal_ripple( ripple_b_tf_plasma_edge_max=self.data.tfcoil.ripple_b_tf_plasma_edge_max, - r_tf_outboard_mid=self.data.build.r_tf_outboard_mid, + r_tf_outboard_mid=bld.r_tf_outboard_mid, n_tf_coils=self.data.tfcoil.n_tf_coils, rmajor=self.data.physics.rmajor, rminor=self.data.physics.rminor, @@ -1524,29 +1187,26 @@ def calculate_radial_build(self, output: bool): ) # If the self.data.tfcoil.ripple is too large then move the outboard TF coil leg - if r_tf_outboard_midl > self.data.build.r_tf_outboard_mid: - self.data.build.r_tf_outboard_mid = r_tf_outboard_midl - self.data.build.dr_shld_vv_gap_outboard = ( - self.data.build.r_tf_outboard_mid - - 0.5e0 * self.data.build.dr_tf_outboard - - self.data.build.dr_vv_outboard - - self.data.build.r_shld_outboard_outer - - self.data.build.dr_shld_thermal_outboard - - self.data.build.dr_tf_shld_gap - - self.data.build.dr_shld_blkt_gap - ) - self.data.build.dr_tf_inner_bore = ( - self.data.build.r_tf_outboard_mid - - 0.5e0 * self.data.build.dr_tf_outboard - ) - ( - self.data.build.r_tf_inboard_mid - 0.5e0 * self.data.build.dr_tf_inboard + if r_tf_outboard_midl > bld.r_tf_outboard_mid: + bld.r_tf_outboard_mid = r_tf_outboard_midl + bld.dr_shld_vv_gap_outboard = ( + bld.r_tf_outboard_mid + - 0.5e0 * bld.dr_tf_outboard + - bld.dr_vv_outboard + - bld.r_shld_outboard_outer + - bld.dr_shld_thermal_outboard + - bld.dr_tf_shld_gap + - bld.dr_shld_blkt_gap ) + bld.dr_tf_inner_bore = ( + bld.r_tf_outboard_mid - 0.5e0 * bld.dr_tf_outboard + ) - (bld.r_tf_inboard_mid - 0.5e0 * bld.dr_tf_inboard) else: - self.data.build.dr_shld_vv_gap_outboard = self.data.build.gapomin + bld.dr_shld_vv_gap_outboard = bld.gapomin ( self.data.tfcoil.ripple_b_tf_plasma_edge, - r_tf_outboard_midl, + _r_tf_outboard_midl, self.data.build.ripflag, ) = self.plasma_outboard_edge_toroidal_ripple( ripple_b_tf_plasma_edge_max=self.data.tfcoil.ripple_b_tf_plasma_edge_max, @@ -1566,384 +1226,222 @@ def calculate_radial_build(self, output: bool): ) if output: - # Print out device build + self.radial_build_output() - po.oheadr(self.outfile, "Radial Build") + def _ripple_flag_validation(self): + po.ocmmnt( + self.outfile, + "(Ripple result may not be accurate, as the fit was outside", + ) + po.ocmmnt(self.outfile, " its range of applicability.)") + po.oblnkl(self.outfile) + logger.warning( + "Ripple result may be inaccurate, as the fit has been extrapolated" + ) - if self.data.build.ripflag != 0: - po.ocmmnt( - self.outfile, - "(Ripple result may not be accurate, as the fit was outside", - ) - po.ocmmnt(self.outfile, " its range of applicability.)") - po.oblnkl(self.outfile) - logger.warning( - "Ripple result may be inaccurate, as the fit has been extrapolated" - ) + if self.data.build.ripflag == 1: + warning_str = ( + "(TF coil ripple calculation) " + "Dimensionless coil width X out of fitted range. %s" + ) + diagnostic = ( + self.data.tfcoil.dx_tf_wp_primary_toroidal + * self.data.tfcoil.n_tf_coils + / self.data.physics.rmajor + ) + elif self.data.build.ripflag == 2: + warning_str = ( + "(TF coil ripple calculation) " + "No of TF coils not between 16 and 20 inclusive " + ) + diagnostic = f"{self.data.tfcoil.n_tf_coils=}" + else: + diagnostic = ( + self.data.physics.rmajor + self.data.physics.rminor + ) / self.data.build.r_tf_outboard_mid + warning_str = ( + "(TF coil ripple calculation) (R+a)/rtot=%s out of fitted range.", + ) - if self.data.build.ripflag == 1: - diagnostic = ( - self.data.tfcoil.dx_tf_wp_primary_toroidal - * self.data.tfcoil.n_tf_coils - / self.data.physics.rmajor - ) - logger.warning( - "(TF coil ripple calculation) " - "Dimensionless coil width X out of fitted range. %s", - diagnostic, - ) - elif self.data.build.ripflag == 2: - logger.warning( - "(TF coil ripple calculation) " - "No of TF coils not between 16 and 20 inclusive " - f"{self.data.tfcoil.n_tf_coils=}" - ) - else: - diagnostic = ( - self.data.physics.rmajor + self.data.physics.rminor - ) / self.data.build.r_tf_outboard_mid - - logger.warning( - "(TF coil ripple calculation) " - "(R+a)/rtot=%s out of fitted range.", - diagnostic, - ) + logger.warning(warning_str, diagnostic) - po.ovarre( - self.outfile, - "TF coil radial placement switch", - "(i_tf_inside_cs)", - self.data.build.i_tf_inside_cs, - ) + def radial_build_output(self): + """Print out device build""" + bld = self.data.build + po.oheadr(self.outfile, "Radial Build") + + if self.data.build.ripflag != 0: + self._ripple_flag_validation() + + po.ovarre( + self.outfile, + "TF coil radial placement switch", + "(i_tf_inside_cs)", + self.data.build.i_tf_inside_cs, + ) + po.ocmmnt( + self.outfile, + ( + " -> " + f"{TFCSRadialConfiguration(self.data.build.i_tf_inside_cs).description}" + ), + ) + po.oblnkl(self.outfile) + po.ovarre( + self.outfile, + "Inboard build thickness (m)", + "(dr_inboard_build)", + self.data.physics.rmajor - self.data.physics.rminor, + "OP ", + ) + + if ( + self.data.build.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS + and self.data.tfcoil.i_tf_bucking >= 2 + ): po.ocmmnt( self.outfile, - ( - " -> " - f"{TFCSRadialConfiguration(self.data.build.i_tf_inside_cs).description}" - ), - ) - po.oblnkl(self.outfile) - po.ovarre( - self.outfile, - "Inboard build thickness (m)", - "(dr_inboard_build)", - self.data.physics.rmajor - self.data.physics.rminor, - "OP ", + "(Bore hollow space has been filled with a solid metal cyclinder to" + " act as wedge support)\n", ) - if ( - self.data.build.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS - and self.data.tfcoil.i_tf_bucking >= 2 - ): - po.ocmmnt( - self.outfile, - "(Bore hollow space has been filled with a solid metal cyclinder to" - " act as wedge support)\n", - ) + # an array that holds the following information + # description, variable name, thickness, radius + radial_build_data = [] - # an array that holds the following information - # description, variable name, thickness, radius - radial_build_data = [] + radius = 0.0e0 + radial_build_data.append(["Device centreline", None, 0.0, radius]) - radius = 0.0e0 - radial_build_data.append(["Device centreline", None, 0.0, radius]) - if ( - self.data.build.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS - and self.data.tfcoil.i_tf_bucking >= 2 - ): - radius += self.data.build.dr_bore - - radial_build_data.append([ - "Machine dr_bore wedge support cylinder", - "dr_bore", - self.data.build.dr_bore, - radius, - ]) - elif ( - self.data.build.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS - and self.data.tfcoil.i_tf_bucking < 2 - ): - radius += self.data.build.dr_bore - radial_build_data.append([ - "Machine dr_bore hole", - "dr_bore", - self.data.build.dr_bore, - radius, - ]) - else: - radius += self.data.build.dr_bore - radial_build_data.append([ - "Machine dr_bore", - "dr_bore", - self.data.build.dr_bore, - radius, - ]) - if self.data.build.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS: - radius += self.data.build.dr_tf_inboard - radial_build_data.append([ + bore_descr = "Machine dr_bore" + if bld.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS: + bore_descr += ( + "wedge support cylinder" + if self.data.tfcoil.i_tf_bucking >= 2 + else "hole" + ) + + radial_build_data.append([bore_descr, "dr_bore", bld.dr_bore]) + + if bld.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS: + radial_build_data.extend(( + [ "TF coil inboard leg (in dr_bore)", "dr_tf_inboard", - self.data.build.dr_tf_inboard, - radius, - ]) - - radius += self.data.build.dr_cs_tf_gap - radial_build_data.append([ + bld.dr_tf_inboard, + ], + [ "CS precompresion to TF coil radial gap", "dr_cs_tf_gap", - self.data.build.dr_cs_tf_gap, - radius, - ]) - - radius += self.data.build.dr_cs - radial_build_data.append([ - "Central solenoid", - "dr_cs", - self.data.build.dr_cs, - radius, - ]) - - radius += self.data.build.dr_cs_precomp - radial_build_data.append([ - "CS precompression", - "dr_cs_precomp", - self.data.build.dr_cs_precomp, - radius, - ]) - if self.data.build.i_tf_inside_cs == TFCSRadialConfiguration.TF_OUTSIDE_CS: - radius += self.data.build.dr_cs_tf_gap - radial_build_data.append([ + bld.dr_cs_tf_gap, + ], + )) + + radial_build_data.extend(( + ["Central solenoid", "dr_cs", bld.dr_cs], + ["CS precompression", "dr_cs_precomp", bld.dr_cs_precomp], + )) + if bld.i_tf_inside_cs == TFCSRadialConfiguration.TF_OUTSIDE_CS: + radial_build_data.extend(( + [ "CS precompresion to TF coil radial gap", "dr_cs_tf_gap", - self.data.build.dr_cs_tf_gap, - radius, - ]) - - radius += self.data.build.dr_tf_inboard - radial_build_data.append([ + bld.dr_cs_tf_gap, + ], + [ "TF coil inboard leg", "dr_tf_inboard", - self.data.build.dr_tf_inboard, - radius, - ]) - - radius += self.data.build.dr_tf_shld_gap - radial_build_data.append([ - "TF coil inboard leg insulation gap", - "dr_tf_shld_gap", - self.data.build.dr_tf_shld_gap, - radius, - ]) + bld.dr_tf_inboard, + ], + )) - radius += self.data.build.dr_shld_thermal_inboard - radial_build_data.append([ + radial_build_data.extend(( + ["TF coil inboard leg insulation gap", "dr_tf_shld_gap", bld.dr_tf_shld_gap], + [ "Thermal shield, inboard", "dr_shld_thermal_inboard", - self.data.build.dr_shld_thermal_inboard, - radius, - ]) - - radius += self.data.build.dr_shld_vv_gap_inboard - radial_build_data.append([ + bld.dr_shld_thermal_inboard, + ], + [ "Thermal shield to vessel radial gap", "dr_shld_vv_gap_inboard", - self.data.build.dr_shld_vv_gap_inboard, - radius, - ]) - - radius += self.data.build.dr_vv_inboard - radial_build_data.append([ - "Inboard vacuum vessel", - "dr_vv_inboard", - self.data.build.dr_vv_inboard, - radius, - ]) - - radius += self.data.build.dr_shld_inboard - radial_build_data.append([ - "Inner radiation shield", - "dr_shld_inboard", - self.data.build.dr_shld_inboard, - radius, - ]) - - radius += self.data.build.dr_shld_blkt_gap - radial_build_data.append([ - "Gap", - "dr_shld_blkt_gap", - self.data.build.dr_shld_blkt_gap, - radius, - ]) - - radius += self.data.build.dr_blkt_inboard - radial_build_data.append([ - "Inboard blanket", - "dr_blkt_inboard", - self.data.build.dr_blkt_inboard, - radius, - ]) - - radius += self.data.build.dr_fw_inboard - radial_build_data.append([ - "Inboard first wall", - "dr_fw_inboard", - self.data.build.dr_fw_inboard, - radius, - ]) - - radius += self.data.build.dr_fw_plasma_gap_inboard - radial_build_data.append([ + bld.dr_shld_vv_gap_inboard, + ], + ["Inboard vacuum vessel", "dr_vv_inboard", bld.dr_vv_inboard], + ["Inner radiation shield", "dr_shld_inboard", bld.dr_shld_inboard], + ["Gap", "dr_shld_blkt_gap", bld.dr_shld_blkt_gap], + ["Inboard blanket", "dr_blkt_inboard", bld.dr_blkt_inboard], + ["Inboard first wall", "dr_fw_inboard", bld.dr_fw_inboard], + [ "Inboard scrape-off", "dr_fw_plasma_gap_inboard", - self.data.build.dr_fw_plasma_gap_inboard, - radius, - ]) - - radius += self.data.physics.rminor - radial_build_data.append([ - "Plasma geometric centre", - "rminor", - self.data.physics.rminor, - radius, - ]) - - radius += self.data.physics.rminor - radial_build_data.append([ - "Plasma outboard edge", - "rminor", - self.data.physics.rminor, - radius, - ]) - - radius += self.data.build.dr_fw_plasma_gap_outboard - radial_build_data.append([ + bld.dr_fw_plasma_gap_inboard, + ], + ["Plasma geometric centre", "rminor", self.data.physics.rminor], + ["Plasma outboard edge", "rminor", self.data.physics.rminor], + [ "Outboard scrape-off", "dr_fw_plasma_gap_outboard", - self.data.build.dr_fw_plasma_gap_outboard, - radius, - ]) - - radius += self.data.build.dr_fw_outboard - radial_build_data.append([ - "Outboard first wall", - "dr_fw_outboard", - self.data.build.dr_fw_outboard, - radius, - ]) - - radius += self.data.build.dr_blkt_outboard - radial_build_data.append([ - "Outboard blanket", - "dr_blkt_outboard", - self.data.build.dr_blkt_outboard, - radius, - ]) - - radius += self.data.build.dr_shld_blkt_gap - radial_build_data.append([ - "Gap", - "dr_shld_blkt_gap", - self.data.build.dr_shld_blkt_gap, - radius, - ]) - - radius += self.data.build.dr_shld_outboard - radial_build_data.append([ - "Outer radiation shield", - "dr_shld_outboard", - self.data.build.dr_shld_outboard, - radius, - ]) - - radius += self.data.build.dr_vv_outboard - radial_build_data.append([ - "Outboard vacuum vessel", - "dr_vv_outboard", - self.data.build.dr_vv_outboard, - radius, - ]) - - radius += self.data.build.dr_shld_vv_gap_outboard - radial_build_data.append([ - "Vessel to TF gap", - "dr_shld_vv_gap_outboard", - self.data.build.dr_shld_vv_gap_outboard, - radius, - ]) - - radius += self.data.build.dr_shld_thermal_outboard - radial_build_data.append([ + bld.dr_fw_plasma_gap_outboard, + ], + ["Outboard first wall", "dr_fw_outboard", bld.dr_fw_outboard], + ["Outboard blanket", "dr_blkt_outboard", bld.dr_blkt_outboard], + ["Gap", "dr_shld_blkt_gap", bld.dr_shld_blkt_gap], + ["Outer radiation shield", "dr_shld_outboard", bld.dr_shld_outboard], + ["Outboard vacuum vessel", "dr_vv_outboard", bld.dr_vv_outboard], + ["Vessel to TF gap", "dr_shld_vv_gap_outboard", bld.dr_shld_vv_gap_outboard], + [ "Outboard thermal shield", "dr_shld_thermal_outboard", - self.data.build.dr_shld_thermal_outboard, - radius, - ]) + bld.dr_shld_thermal_outboard, + ], + ["Gap", "dr_tf_shld_gap", bld.dr_tf_shld_gap], + ["TF coil outboard leg", "dr_tf_outboard", bld.dr_tf_outboard], + )) - radius += self.data.build.dr_tf_shld_gap - radial_build_data.append([ - "Gap", - "dr_tf_shld_gap", - self.data.build.dr_tf_shld_gap, - radius, - ]) + for description, variable, thickness in radial_build_data: + radius += thickness + var = f"({variable})" if variable else "" + po.obuild(self.outfile, description, thickness, radius, var) - radius += self.data.build.dr_tf_outboard - radial_build_data.append([ - "TF coil outboard leg", - "dr_tf_outboard", - self.data.build.dr_tf_outboard, - radius, - ]) - - for description, variable, thickness, radius in radial_build_data: - po.obuild( - self.outfile, - description, - thickness, - radius, - f"({variable})" if variable else "", - ) - - # use manual index to ensure count is contiguous in the event - # of a `None` variable component - index = 0 - for description, variable, thickness, radius in radial_build_data: - if variable is None: - continue + # use manual index to ensure count is contiguous in the event + # of a `None` variable component + index = 0 + for description, variable, thickness, radius in radial_build_data: + if variable is None: + continue - index += 1 + index += 1 - po.ovarre( - self.mfile, - f"{description} radial thickness (m)", - f"({variable})", - thickness, - ) + po.ovarre( + self.mfile, + f"{description} radial thickness (m)", + f"({variable})", + thickness, + ) - po.ovarre( - self.mfile, - f"Radial build component {index}", - f"(radial_label({index}))", - f'"{variable}"', - ) - po.ovarre( - self.mfile, - f"Radial build cumulative radius {index}", - f"(radial_cum({index}))", - radius, - ) + po.ovarre( + self.mfile, + f"Radial build component {index}", + f"(radial_label({index}))", + f'"{variable}"', + ) + po.ovarre( + self.mfile, + f"Radial build cumulative radius {index}", + f"(radial_cum({index}))", + radius, + ) - if ( - CurrentDriveModel( - self.data.current_drive.i_hcd_primary - or self.data.current_drive.i_hcd_secondary - ).method - == CurrentDriveMethodType.NEUTRAL_BEAM - ): - po.ovarre( - self.mfile, - "Width of neutral beam duct where it passes between the " - "TF coils (m)", - "(dx_beam_duct)", - self.data.current_drive.dx_beam_duct, - ) + if ( + CurrentDriveModel( + self.data.current_drive.i_hcd_primary + or self.data.current_drive.i_hcd_secondary + ).method + == CurrentDriveMethodType.NEUTRAL_BEAM + ): + po.ovarre( + self.mfile, + "Width of neutral beam duct where it passes between the TF coils (m)", + "(dx_beam_duct)", + self.data.current_drive.dx_beam_duct, + ) From d48149e275d5081df1e278482f672bffac8d6d0a Mon Sep 17 00:00:00 2001 From: james <81617086+je-cook@users.noreply.github.com> Date: Thu, 13 Aug 2026 15:55:25 +0100 Subject: [PATCH 4/5] missed return --- process/models/build.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/process/models/build.py b/process/models/build.py index f3eb452017..927be6bba1 100644 --- a/process/models/build.py +++ b/process/models/build.py @@ -481,7 +481,9 @@ def rc(trilio): # Find angles between vertical and legs def theta(trilio, rcio): - np.arcsin(1.0e0 - (self.data.physics.rminor * (1.0e0 - trilio)) / rcio) + return np.arcsin( + 1.0e0 - (self.data.physics.rminor * (1.0e0 - trilio)) / rcio + ) # Inboard arc angle = outboard leg angle thetai = theta(1 - tril, rci) From 2950a08198aed31720cfc83a2f2d36d20022e18b Mon Sep 17 00:00:00 2001 From: james <81617086+je-cook@users.noreply.github.com> Date: Thu, 13 Aug 2026 16:07:49 +0100 Subject: [PATCH 5/5] oops --- process/models/availability.py | 2 + process/models/build.py | 120 +++++++++++++++++++-------------- 2 files changed, 71 insertions(+), 51 deletions(-) diff --git a/process/models/availability.py b/process/models/availability.py index 6a0ed97f94..0a7e9b36c9 100644 --- a/process/models/availability.py +++ b/process/models/availability.py @@ -255,6 +255,8 @@ def avail(self, output: bool): self.data.costs.f_t_plant_available = 1.0e0 - ( uplanned + uutot - (uplanned * uutot) ) + else: + uplanned = uutot = 0 # Capacity factor # Using the amount of time burning for a given pulse cycle diff --git a/process/models/build.py b/process/models/build.py index 927be6bba1..2c7f5fa423 100644 --- a/process/models/build.py +++ b/process/models/build.py @@ -184,6 +184,21 @@ def _vertical_build_out(self, i_single_null: DivertorNumberModels): # Top of TF coil tf_top = vbuild - self.data.buildings.dz_tf_cryostat + + top = ( + ( + ("Top blanket", bld.dz_blkt_upper, "(dz_blkt_upper)"), + ("Top first wall", dz_fw_upper, "(dz_fw_upper)"), + ) + if sn + else ( + ( + "Divertor structure", + self.data.divertor.dz_divertor, + "(dz_divertor)", + ), + ) + ) vbuild = self.write_obuild( vbuild, [ @@ -202,15 +217,14 @@ def _vertical_build_out(self, i_single_null: DivertorNumberModels): "(dz_vv_upper+dz_shld_upper)", ), ("Gap", bld.dr_shld_blkt_gap, "(dr_shld_blkt_gap)"), - ("Top blanket", bld.dz_blkt_upper, "(dz_blkt_upper)"), - ("Top first wall", dz_fw_upper, "(dz_fw_upper)") + *top, + ("Top scrape-off", bld.dz_fw_plasma_gap, "(dz_fw_plasma_gap)") if sn else ( - "Divertor structure", - self.data.divertor.dz_divertor, - "(dz_divertor)", + "Top scrape-off", + self.data.build.dz_xpoint_divertor, + "(dz_xpoint_divertor)", ), - ("Top scrape-off", bld.dz_fw_plasma_gap, "(dz_fw_plasma_gap)"), ( "Plasma upper X-point height (m)", bld.z_plasma_xpoint_upper, @@ -218,7 +232,24 @@ def _vertical_build_out(self, i_single_null: DivertorNumberModels): ), ], ) - + top = ( + ( + ( + "Top blanket vertical thickness (m)", + "(dz_blkt_upper)", + bld.dz_blkt_upper, + ), + ("Top first wall vertical thickness (m)", "(dz_fw_upper)", dz_fw_upper), + ) + if sn + else ( + ( + "Divertor structure vertical thickness (m)", + "(dz_divertor)", + self.data.divertor.dz_divertor, + ), + ) + ) for desc, name, val in [ ( "Cryostat roof structure*", @@ -237,18 +268,17 @@ def _vertical_build_out(self, i_single_null: DivertorNumberModels): bld.dz_vv_upper, ), ("Top radiation shield thickness (m)", "(dz_shld_upper)", bld.dz_shld_upper), - ("Top blanket vertical thickness (m)", "(dz_blkt_upper)", bld.dz_blkt_upper), - ("Top first wall vertical thickness (m)", "(dz_fw_upper)", dz_fw_upper) - if sn - else ( - "Divertor structure vertical thickness (m)", - "(dz_divertor)", - self.data.divertor.dz_divertor, - ), + *top, ( "Top scrape-off vertical thickness (m)", "(dz_fw_plasma_gap)", bld.dz_fw_plasma_gap, + ) + if sn + else ( + "Top scrape-off vertical thickness (m)", + "(dz_xpoint_divertor)", + self.data.build.dz_xpoint_divertor, ), ( "Plasma upper X-point height (m)", @@ -472,8 +502,7 @@ def divgeom(self, output: bool): # Find radius of inner and outer plasma arcs def rc(trilio): return 0.5 * np.sqrt( - (self.data.physics.rminor**2 * (trilio**2 + kap**2) ** 2) - / ((tril + 1.0e0) ** 2) + (self.data.physics.rminor**2 * (trilio**2 + kap**2) ** 2) / (trilio**2) ) rco = rc(tril + 1) @@ -481,14 +510,12 @@ def rc(trilio): # Find angles between vertical and legs def theta(trilio, rcio): - return np.arcsin( - 1.0e0 - (self.data.physics.rminor * (1.0e0 - trilio)) / rcio - ) + return np.arcsin(1 - (self.data.physics.rminor * trilio) / rcio) # Inboard arc angle = outboard leg angle - thetai = theta(1 - tril, rci) + thetai = theta(1 + tril, rco) # Outboard arc angle = inboard leg angle - thetao = theta(1 + tril, rco) + thetao = theta(1 - tril, rci) # Position of lower x-pt rxpt = self.data.physics.rmajor - tril * self.data.physics.rminor @@ -1309,10 +1336,7 @@ def radial_build_output(self): # an array that holds the following information # description, variable name, thickness, radius - radial_build_data = [] - - radius = 0.0e0 - radial_build_data.append(["Device centreline", None, 0.0, radius]) + radial_build_data = [["Device centreline", None, 0.0]] bore_descr = "Machine dr_bore" if bld.i_tf_inside_cs == TFCSRadialConfiguration.TF_INSIDE_CS: @@ -1400,39 +1424,33 @@ def radial_build_output(self): ["TF coil outboard leg", "dr_tf_outboard", bld.dr_tf_outboard], )) - for description, variable, thickness in radial_build_data: - radius += thickness - var = f"({variable})" if variable else "" - po.obuild(self.outfile, description, thickness, radius, var) - # use manual index to ensure count is contiguous in the event # of a `None` variable component index = 0 - for description, variable, thickness, radius in radial_build_data: + radius = 0.0e0 + for description, variable, thickness in radial_build_data: if variable is None: continue - index += 1 + radius += thickness - po.ovarre( - self.mfile, - f"{description} radial thickness (m)", - f"({variable})", - thickness, - ) + var = f"({variable})" if variable else "" + po.obuild(self.outfile, description, thickness, radius, var) - po.ovarre( - self.mfile, - f"Radial build component {index}", - f"(radial_label({index}))", - f'"{variable}"', - ) - po.ovarre( - self.mfile, - f"Radial build cumulative radius {index}", - f"(radial_cum({index}))", - radius, - ) + for d, n, v in [ + (f"{description} radial thickness (m)", f"({variable})", thickness), + ( + f"Radial build component {index}", + f"(radial_label({index}))", + f'"{variable}"', + ), + ( + f"Radial build cumulative radius {index}", + f"(radial_cum({index}))", + radius, + ), + ]: + po.ovarre(self.mfile, d, n, v) if ( CurrentDriveModel(