Coverage for python/gsfit/database_readers/st40_astra_mdsplus/setup_plasma.py: 0%

61 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-14 15:25 +0000

1import typing 

2from typing import TYPE_CHECKING 

3 

4import gsfit_rs 

5import numpy as np 

6import numpy.typing as npt 

7from gsfit_rs import Plasma 

8 

9if TYPE_CHECKING: 

10 from . import DatabaseReader 

11 

12 

13def setup_plasma( 

14 self: "DatabaseReader", 

15 pulseNo: int, 

16 settings: dict[str, typing.Any], 

17 times_to_reconstruct: npt.NDArray[np.float64], 

18) -> Plasma: 

19 """ 

20 This method initialises the Rust `Plasma` class. 

21 

22 :param pulseNo: Pulse number, used to read from the database 

23 :param settings: Dictionary containing the JSON settings read from the `settings` directory 

24 :param times_to_reconstruct: Times the equilibrium will be solved at [second] 

25 

26 **This method is specific to ST40's experimental MDSplus database.** 

27 

28 See `python/gsfit/database_readers/interface.py` for more details on how a new database_reader should be implemented. 

29 """ 

30 

31 # Initial plasma conditions 

32 initial_guess_ip = settings["GSFIT_code_settings.json"]["initial_guess"]["ip"] 

33 initial_guess_cur_r = settings["GSFIT_code_settings.json"]["initial_guess"]["cur_r"] 

34 initial_guess_cur_z = settings["GSFIT_code_settings.json"]["initial_guess"]["cur_z"] 

35 initial_guess_minor_radius = settings["GSFIT_code_settings.json"]["initial_guess"]["minor_radius"] 

36 initial_guess_elongation = settings["GSFIT_code_settings.json"]["initial_guess"]["elongation"] 

37 

38 # Numerical settings the Grad-Shafranov solve is run with 

39 n_iter_max = settings["GSFIT_code_settings.json"]["numerics"]["n_iter_max"] 

40 n_iter_min = settings["GSFIT_code_settings.json"]["numerics"]["n_iter_min"] 

41 n_iter_no_vertical_feedback = settings["GSFIT_code_settings.json"]["numerics"]["n_iter_no_vertical_feedback"] 

42 grad_shafranov_deviation_tolerance = settings["GSFIT_code_settings.json"]["numerics"]["grad_shafranov_deviation_tolerance"] 

43 use_anderson_mixing = settings["GSFIT_code_settings.json"]["numerics"]["anderson_mixing"]["use"] 

44 anderson_mixing_from_previous_iter = settings["GSFIT_code_settings.json"]["numerics"]["anderson_mixing"]["mixing_from_previous_iter"] 

45 # The pressure at the plasma boundary. Integrating `p'` leaves the pressure defined up to a constant, which 

46 # GSFit historically fixed by demanding zero pressure at the boundary. Setting `free` makes it a degree of 

47 # freedom of the fit instead. `.get` is used so that older settings directories keep working 

48 pressure_edge_settings = settings["source_function_p_prime.json"].get( 

49 "pressure_edge", settings["GSFIT_code_settings.json"]["numerics"].get("pressure_edge", {}) 

50 ) 

51 pressure_edge_free = pressure_edge_settings.get("free", False) 

52 pressure_edge_regularisation_weight = pressure_edge_settings.get("regularisation_weight", 1.0e-4) 

53 

54 # Set the source functions types 

55 p_prime_source_function: gsfit_rs.EfitPolynomial | gsfit_rs.TensionedCubicBSpline 

56 ff_prime_source_function: gsfit_rs.EfitPolynomial | gsfit_rs.TensionedCubicBSpline 

57 

58 if settings["source_function_p_prime.json"]["method"] == "efit_polynomial": 

59 n_dof = settings["source_function_p_prime.json"]["efit_polynomial"]["n_dof"] 

60 regularisations = np.array(settings["source_function_p_prime.json"]["efit_polynomial"]["regularizations"]) 

61 # If `regularisations` is [[]] in the json file, will be interpreted by numpy as having size (1, 0). 

62 # Which would be interpreted as (n_regularisations, n_dof). So it would cause an error 

63 if regularisations.shape == (1, 0): 

64 regularisations = np.zeros((0, n_dof), dtype=np.float64) 

65 p_prime_source_function = gsfit_rs.EfitPolynomial(n_dof, regularisations) 

66 elif settings["source_function_p_prime.json"]["method"] == "tensioned_cubic_b_spline": 

67 regularisations = np.array(settings["source_function_p_prime.json"]["tensioned_cubic_b_spline"]["regularizations"]) 

68 # If `regularisations` is [[]] in the json file, will be interpreted by numpy as having size (1, 0). 

69 # Which would be interpreted as (n_regularisations, n_dof). So it would cause an error 

70 interior_knots = np.array(settings["source_function_p_prime.json"]["tensioned_cubic_b_spline"]["interior_knots"]) 

71 n_dof = len(interior_knots) + 4 

72 if regularisations.shape == (1, 0): 

73 regularisations = np.zeros((0, n_dof), dtype=np.float64) 

74 interval_tensions = np.array(settings["source_function_p_prime.json"]["tensioned_cubic_b_spline"]["interval_tensions"]) 

75 p_prime_source_function = gsfit_rs.TensionedCubicBSpline(regularisations, interior_knots, interval_tensions) 

76 else: 

77 raise ValueError(f"Unknown method for p_prime source function: {settings['source_function_p_prime.json']['method']}") 

78 

79 if settings["source_function_ff_prime.json"]["method"] == "efit_polynomial": 

80 n_dof = settings["source_function_ff_prime.json"]["efit_polynomial"]["n_dof"] 

81 regularisations = np.array(settings["source_function_ff_prime.json"]["efit_polynomial"]["regularizations"]) 

82 # If `regularisations` is [[]] in the json file, will be interpreted by numpy as having size (1, 0). 

83 # Which would be interpreted as (n_regularisations, n_dof). So it would cause an error 

84 if regularisations.shape == (1, 0): 

85 regularisations = np.zeros((0, n_dof), dtype=np.float64) 

86 ff_prime_source_function = gsfit_rs.EfitPolynomial(n_dof, regularisations) 

87 elif settings["source_function_ff_prime.json"]["method"] == "tensioned_cubic_b_spline": 

88 regularisations = np.array(settings["source_function_ff_prime.json"]["tensioned_cubic_b_spline"]["regularizations"]) 

89 # If `regularisations` is [[]] in the json file, will be interpreted by numpy as having size (1, 0). 

90 # Which would be interpreted as (n_regularisations, n_dof). So it would cause an error 

91 interior_knots = np.array(settings["source_function_ff_prime.json"]["tensioned_cubic_b_spline"]["interior_knots"]) 

92 n_dof = len(interior_knots) + 4 

93 if regularisations.shape == (1, 0): 

94 regularisations = np.zeros((0, n_dof), dtype=np.float64) 

95 interval_tensions = np.array(settings["source_function_ff_prime.json"]["tensioned_cubic_b_spline"]["interval_tensions"]) 

96 ff_prime_source_function = gsfit_rs.TensionedCubicBSpline(regularisations, interior_knots, interval_tensions) 

97 else: 

98 raise ValueError(f"Unknown method for ff_prime source function: {settings['source_function_ff_prime.json']['method']}") 

99 

100 # Grid size and shape 

101 n_r = settings["GSFIT_code_settings.json"]["grid"]["n_r"] 

102 n_z = settings["GSFIT_code_settings.json"]["grid"]["n_z"] 

103 r_min = settings["GSFIT_code_settings.json"]["grid"]["r_min"] 

104 r_max = settings["GSFIT_code_settings.json"]["grid"]["r_max"] 

105 z_min = settings["GSFIT_code_settings.json"]["grid"]["z_min"] 

106 z_max = settings["GSFIT_code_settings.json"]["grid"]["z_max"] 

107 

108 # Normalised poloidal flux grid 

109 n_psi_n = settings["GSFIT_code_settings.json"]["n_psi_n"] 

110 psi_n = np.linspace(0.0, 1.0, n_psi_n).astype(np.float64) 

111 

112 # Initialise the Plasma Rust class 

113 plasma = Plasma( 

114 n_r, 

115 n_z, 

116 r_min, 

117 r_max, 

118 z_min, 

119 z_max, 

120 psi_n, # BUXTON: perhaps better to send in `n_psi_n` 

121 p_prime_source_function, 

122 ff_prime_source_function, 

123 initial_guess_ip, 

124 initial_guess_cur_r, 

125 initial_guess_cur_z, 

126 initial_guess_minor_radius, 

127 initial_guess_elongation, 

128 n_iter_max, 

129 n_iter_min, 

130 n_iter_no_vertical_feedback, 

131 grad_shafranov_deviation_tolerance, 

132 use_anderson_mixing, 

133 anderson_mixing_from_previous_iter, 

134 times_to_reconstruct, 

135 pressure_edge_free, 

136 pressure_edge_regularisation_weight, 

137 ) 

138 

139 return plasma 

140 

141 

142# import typing 

143# from typing import TYPE_CHECKING 

144 

145# import gsfit_rs 

146# import numpy as np 

147# import numpy.typing as npt 

148# from gsfit_rs import Plasma 

149# from st40_database import GetData # type: ignore[import-not-found] 

150 

151# if TYPE_CHECKING: 

152# from . import DatabaseReader 

153 

154 

155# def setup_plasma( 

156# self: "DatabaseReader", 

157# pulseNo: int, 

158# settings: dict[str, typing.Any], 

159# ) -> Plasma: 

160# """ 

161# This method initialises the Rust `Plasma` class. 

162 

163# :param pulseNo: Pulse number, used to read from the database 

164# :param settings: Dictionary containing the JSON settings read from the `settings` directory 

165 

166# **This method is specific to ST40's ASTRA stored on MDSplus.** 

167 

168# See `python/gsfit/database_readers/interface.py` for more details on how a new database_reader should be implemented. 

169# """ 

170 

171# # Initial plasma conditions 

172# initial_guess_ip = settings["GSFIT_code_settings.json"]["initial_guess"]["ip"] 

173# initial_guess_cur_r = settings["GSFIT_code_settings.json"]["initial_guess"]["cur_r"] 

174# initial_guess_cur_z = settings["GSFIT_code_settings.json"]["initial_guess"]["cur_z"] 

175 

176# # Set the source functions types 

177# p_prime_source_function: gsfit_rs.EfitPolynomial 

178# ff_prime_source_function: gsfit_rs.EfitPolynomial 

179 

180# if settings["source_function_p_prime.json"]["method"] == "efit_polynomial": 

181# n_dof = settings["source_function_p_prime.json"]["efit_polynomial"]["n_dof"] 

182# regularisations = np.array(settings["source_function_p_prime.json"]["efit_polynomial"]["regularizations"]) 

183# p_prime_source_function = gsfit_rs.EfitPolynomial(n_dof, regularisations) 

184# elif settings["source_function_p_prime.json"]["method"] == "efit_polynomial": 

185# n_dof = settings["source_function_p_prime.json"]["efit_polynomial"]["n_dof"] 

186# regularisations = np.array(settings["source_function_p_prime.json"]["efit_polynomial"]["regularizations"]) 

187# p_prime_source_function = gsfit_rs.EfitPolynomial(n_dof, regularisations) 

188# else: 

189# raise ValueError(f"Unknown method for p_prime source function: {settings['source_function_p_prime.json']['method']}") 

190 

191# if settings["source_function_ff_prime.json"]["method"] == "efit_polynomial": 

192# n_dof = settings["source_function_ff_prime.json"]["efit_polynomial"]["n_dof"] 

193# regularisations = np.array(settings["source_function_ff_prime.json"]["efit_polynomial"]["regularizations"]) 

194# ff_prime_source_function = gsfit_rs.EfitPolynomial(n_dof, regularisations) 

195# elif settings["source_function_ff_prime.json"]["method"] == "efit_polynomial": 

196# n_dof = settings["source_function_ff_prime.json"]["efit_polynomial"]["n_dof"] 

197# regularisations = np.array(settings["source_function_ff_prime.json"]["efit_polynomial"]["regularizations"]) 

198# ff_prime_source_function = gsfit_rs.EfitPolynomial(n_dof, regularisations) 

199 

200# # Grid size and shape 

201# n_r = settings["GSFIT_code_settings.json"]["grid"]["n_r"] 

202# n_z = settings["GSFIT_code_settings.json"]["grid"]["n_z"] 

203# r_min = settings["GSFIT_code_settings.json"]["grid"]["r_min"] 

204# r_max = settings["GSFIT_code_settings.json"]["grid"]["r_max"] 

205# z_min = settings["GSFIT_code_settings.json"]["grid"]["z_min"] 

206# z_max = settings["GSFIT_code_settings.json"]["grid"]["z_max"] 

207 

208# # Normalised poloidal flux grid 

209# n_psi_n = settings["GSFIT_code_settings.json"]["n_psi_n"] 

210# psi_n = np.linspace(0.0, 1.0, n_psi_n).astype(np.float64) 

211 

212# # Limiter 

213# elmag_run_name = settings["GSFIT_code_settings.json"]["database_reader"]["st40_astra_mdsplus"]["elmag_run_name"] 

214# elmag = GetData(12050, f"ELMAG#{elmag_run_name}", is_fail_quiet=False) 

215# limit_pts_r = typing.cast(npt.NDArray[np.float64], elmag.get("LIMITER.LIMIT_PTS.R")) 

216# limit_pts_z = typing.cast(npt.NDArray[np.float64], elmag.get("LIMITER.LIMIT_PTS.Z")) 

217 

218# # Vacuum vessel where the plasma is allowed to be 

219# vessel_r = limit_pts_r 

220# vessel_z = limit_pts_z 

221 

222# # Add lower MC tiles 

223# limit_pts_r = np.append(limit_pts_r, 0.7103) 

224# limit_pts_z = np.append(limit_pts_z, -0.3131) 

225# # Add upper MC tiles 

226# limit_pts_r = np.append(limit_pts_r, 0.7103) 

227# limit_pts_z = np.append(limit_pts_z, 0.3031) 

228 

229# # Initialise the Plasma Rust class 

230# plasma = Plasma( 

231# n_r, 

232# n_z, 

233# r_min, 

234# r_max, 

235# z_min, 

236# z_max, 

237# psi_n, # BUXTON: perhaps better to send in `n_psi_n` 

238# limit_pts_r, 

239# limit_pts_z, 

240# vessel_r, 

241# vessel_z, 

242# p_prime_source_function, 

243# ff_prime_source_function, 

244# initial_guess_ip, 

245# initial_guess_cur_r, 

246# initial_guess_cur_z, 

247# ) 

248 

249# return plasma