Coverage for python/gsfit/database_writers/tokamak_energy_mdsplus/map_results_to_database.py: 0%

122 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 numpy as np 

5import numpy.typing as npt 

6from gsfit_rs.imas import equilibrium_paths as ep 

7from scipy.constants import mu_0 

8 

9# from st40_database import GetData 

10 

11if TYPE_CHECKING: 

12 from ...gsfit import Gsfit 

13 from . import DatabaseWriterTokamakEnergyMDSplus 

14 

15 

16# Pairs of (MDSplus node path, IMAS path) for everything which comes out of the `equilibrium` IDS 

17# one value per time-slice. See the same tables in `tokamak_energy_mdsplus_new`; this writer maps 

18# the same quantities onto the older MDSplus node layout. 

19_TIME_SERIES_PATH_PAIRS: list[tuple[tuple[str, ...], typing.Any]] = [ 

20 # Two-d. `profiles_2d(0)` because GSFit solves on a single rectangular (R, Z) grid 

21 (("TWO_D", "BR"), ep.time_slice[:].profiles_2d[0].b_field_r), 

22 (("TWO_D", "BT"), ep.time_slice[:].profiles_2d[0].b_field_phi), 

23 (("TWO_D", "BZ"), ep.time_slice[:].profiles_2d[0].b_field_z), 

24 (("TWO_D", "MASK"), ep.time_slice[:].profiles_2d[0].mask), 

25 (("TWO_D", "P"), ep.time_slice[:].profiles_2d[0].pressure), 

26 (("TWO_D", "PSI"), ep.time_slice[:].profiles_2d[0].psi), 

27 # Global 

28 (("GLOBAL", "BETA_N"), ep.time_slice[:].global_quantities.beta_tor_norm), 

29 (("GLOBAL", "BETA_P_1"), ep.time_slice[:].global_quantities.beta_pol_1), 

30 (("GLOBAL", "BETA_P_2"), ep.time_slice[:].global_quantities.beta_pol_2), 

31 (("GLOBAL", "BETA_P_3"), ep.time_slice[:].global_quantities.beta_pol_3), 

32 (("GLOBAL", "BT_VAC_RGEO"), ep.time_slice[:].global_quantities.bt_vac_at_r_geo), 

33 (("GLOBAL", "CHI_MAG"), ep.time_slice[:].constraints.chi_squared_reduced), 

34 (("GLOBAL", "LI_1"), ep.time_slice[:].global_quantities.li_1), 

35 (("GLOBAL", "LI_2"), ep.time_slice[:].global_quantities.li_2), 

36 (("GLOBAL", "LI_3"), ep.time_slice[:].global_quantities.li_3), 

37 (("GLOBAL", "DELTA_Z"), ep.time_slice[:].convergence.delta_z), 

38 (("GLOBAL", "ELON"), ep.time_slice[:].boundary.elongation), 

39 (("GLOBAL", "PHI_DIA"), ep.time_slice[:].constraints.diamagnetic_flux.reconstructed), 

40 (("GLOBAL", "GS_ERROR"), ep.time_slice[:].convergence.grad_shafranov_deviation_value), 

41 (("GLOBAL", "IP"), ep.time_slice[:].global_quantities.ip), 

42 (("GLOBAL", "N_ITER"), ep.time_slice[:].convergence.iterations_n), 

43 (("GLOBAL", "PSI_A"), ep.time_slice[:].global_quantities.psi_magnetic_axis), 

44 (("GLOBAL", "PSI_B"), ep.time_slice[:].boundary.psi), 

45 (("GLOBAL", "Q0"), ep.time_slice[:].global_quantities.q_axis), 

46 (("GLOBAL", "Q95"), ep.time_slice[:].global_quantities.q_95), 

47 (("GLOBAL", "R_CUR"), ep.time_slice[:].global_quantities.current_centre.r), 

48 (("GLOBAL", "Z_CUR"), ep.time_slice[:].global_quantities.current_centre.z), 

49 (("GLOBAL", "R_GEO"), ep.time_slice[:].boundary.geometric_axis.r), 

50 (("GLOBAL", "Z_GEO"), ep.time_slice[:].boundary.geometric_axis.z), 

51 (("GLOBAL", "R_MAG"), ep.time_slice[:].global_quantities.magnetic_axis.r), 

52 (("GLOBAL", "Z_MAG"), ep.time_slice[:].global_quantities.magnetic_axis.z), 

53 (("GLOBAL", "R_MINOR"), ep.time_slice[:].boundary.minor_radius), 

54 (("GLOBAL", "V_LOOP"), ep.time_slice[:].global_quantities.v_loop), 

55 (("GLOBAL", "VPLASMA"), ep.time_slice[:].global_quantities.volume), 

56 (("GLOBAL", "W_MHD"), ep.time_slice[:].global_quantities.energy_mhd), 

57 (("GLOBAL", "XPT_DIVERTED"), ep.time_slice[:].boundary.type), 

58 # Plasma boundary 

59 (("P_BOUNDARY", "RBND"), ep.time_slice[:].boundary.outline.r), 

60 (("P_BOUNDARY", "ZBND"), ep.time_slice[:].boundary.outline.z), 

61 (("P_BOUNDARY", "BOUNDING_R"), ep.time_slice[:].boundary.bounding.r), 

62 (("P_BOUNDARY", "BOUNDING_Z"), ep.time_slice[:].boundary.bounding.z), 

63 # Profiles, on the psi_norm grid 

64 (("PROFILES", "RHO", "AREA"), ep.time_slice[:].profiles_1d.area), 

65 (("PROFILES", "RHO", "AREA_PRIME"), ep.time_slice[:].profiles_1d.darea_dpsi), 

66 (("PROFILES", "RHO", "F"), ep.time_slice[:].profiles_1d.f), 

67 (("PROFILES", "RHO", "FF_PRIME"), ep.time_slice[:].profiles_1d.f_df_dpsi), 

68 (("PROFILES", "RHO", "FLUX_TOR"), ep.time_slice[:].profiles_1d.phi), 

69 (("PROFILES", "RHO", "P"), ep.time_slice[:].profiles_1d.pressure), 

70 (("PROFILES", "RHO", "P_PRIME"), ep.time_slice[:].profiles_1d.dpressure_dpsi), 

71 (("PROFILES", "RHO", "Q"), ep.time_slice[:].profiles_1d.q), 

72 (("PROFILES", "RHO", "RHO_POL"), ep.time_slice[:].profiles_1d.rho_pol), 

73 (("PROFILES", "RHO", "RHO_TOR"), ep.time_slice[:].profiles_1d.rho_tor), 

74 (("PROFILES", "RHO", "VOL"), ep.time_slice[:].profiles_1d.volume), 

75 (("PROFILES", "RHO", "VOL_PRIME"), ep.time_slice[:].profiles_1d.dvolume_dpsi), 

76 (("PROFILES", "R_MIDPLANE", "P"), ep.time_slice[:].profiles_1d_r_midplane.pressure), 

77 # Scrape off layer (SOL) 

78 (("SOL", "HFS", "CONTOUR", "R"), ep.time_slice[:].sol.hfs.contour.r), 

79 (("SOL", "HFS", "CONTOUR", "Z"), ep.time_slice[:].sol.hfs.contour.z), 

80 (("SOL", "HFS", "STRIKE_POINT", "R"), ep.time_slice[:].sol.hfs.strike_point.r), 

81 (("SOL", "HFS", "STRIKE_POINT", "Z"), ep.time_slice[:].sol.hfs.strike_point.z), 

82 (("SOL", "LFS", "CONTOUR", "R"), ep.time_slice[:].sol.lfs.contour.r), 

83 (("SOL", "LFS", "CONTOUR", "Z"), ep.time_slice[:].sol.lfs.contour.z), 

84 (("SOL", "LFS", "STRIKE_POINT", "R"), ep.time_slice[:].sol.lfs.strike_point.r), 

85 (("SOL", "LFS", "STRIKE_POINT", "Z"), ep.time_slice[:].sol.lfs.strike_point.z), 

86] 

87 

88# The same, for quantities which are the same on every time-slice 

89_TIME_INDEPENDENT_PATH_PAIRS: list[tuple[tuple[str, ...], typing.Any]] = [ 

90 (("TWO_D", "RGRID"), ep.time_slice[:].profiles_2d[0].grid.dim1), 

91 (("TWO_D", "ZGRID"), ep.time_slice[:].profiles_2d[0].grid.dim2), 

92 (("PROFILES", "RHO", "PSI_N"), ep.time_slice[:].profiles_1d.psi_norm), 

93 (("PROFILES", "R_MIDPLANE", "R"), ep.time_slice[:].profiles_1d_r_midplane.r), 

94] 

95 

96 

97def _assign(results: typing.Any, mdsplus_path: tuple[str, ...], value: typing.Any) -> None: 

98 """Assign `value` into the nested `results` object at `mdsplus_path`.""" 

99 node = results 

100 for key in mdsplus_path[:-1]: 

101 node = node[key] 

102 node[mdsplus_path[-1]] = value 

103 

104 

105def _n_points_per_time(padded: npt.NDArray[np.float64]) -> npt.NDArray[np.int32]: 

106 """Number of real points in each row of a NaN-padded `[n_time, n_points]` array. 

107 

108 The IDS stores one contour per time-slice, each its own length; gathering over time pads the 

109 short rows with NaN to make a rectangle. MDSplus needs that rectangle plus a separate count of 

110 how much of each row is real. Contour coordinates are always finite, so a NaN can only be 

111 padding. 

112 """ 

113 n_points: npt.NDArray[np.int32] = np.isfinite(padded).sum(axis=1).astype(np.int32) 

114 

115 return n_points 

116 

117 

118def map_results_to_database( 

119 self: "DatabaseWriterTokamakEnergyMDSplus", 

120 gsfit_controller: "Gsfit", 

121) -> None: 

122 """Map the results to MDSplus structure. 

123 `gsfit_controller.results` is a `NestedDict` object, which is a 1:1 mapping to the MDSplus structure. 

124 This function will mutate the `gsfit_controller` object. 

125 """ 

126 

127 # Take class object out of the `gsfit_controller` object 

128 pulseNo = gsfit_controller.pulseNo 

129 settings = gsfit_controller.settings 

130 plasma = gsfit_controller.plasma 

131 bp_probes = gsfit_controller.bp_probes 

132 flux_loops = gsfit_controller.flux_loops 

133 rogowski_coils = gsfit_controller.rogowski_coils 

134 passives = gsfit_controller.passives 

135 results = gsfit_controller.results 

136 pressure_sensors = gsfit_controller.pressure_sensors 

137 

138 # Everything below comes out of the `equilibrium` IDS through the (MDSplus path, IMAS path) 

139 # tables at the top of this file. `plasma.get` reads in place, so reading quantity by quantity 

140 # costs nothing extra 

141 

142 for mdsplus_path, imas_path in _TIME_SERIES_PATH_PAIRS: 

143 _assign(results, mdsplus_path, plasma.get(imas_path)) 

144 

145 for mdsplus_path, imas_path in _TIME_INDEPENDENT_PATH_PAIRS: 

146 _assign(results, mdsplus_path, np.asarray(plasma.get(imas_path))[0]) 

147 

148 results["SOL"]["HFS"]["CONTOUR"]["N"] = _n_points_per_time(results["SOL"]["HFS"]["CONTOUR"]["R"]) 

149 results["SOL"]["LFS"]["CONTOUR"]["N"] = _n_points_per_time(results["SOL"]["LFS"]["CONTOUR"]["R"]) 

150 

151 # The data dictionary defines `beta_tor` as a fraction, but this MDSplus node has always held a 

152 # percentage, so the factor of 100 is put back here rather than changing what consumers read 

153 results["GLOBAL"]["BETA_T"] = 100.0 * np.asarray(plasma.get(ep.time_slice[:].global_quantities.beta_tor)) 

154 

155 # The rod current is not a data dictionary node. It is recovered from the vacuum toroidal 

156 # field, `f_vac = r0 * b0 = mu_0 * i_rod / (2 * pi)` 

157 r0 = plasma.get(ep.vacuum_toroidal_field.r0) 

158 b0 = np.asarray(plasma.get(ep.vacuum_toroidal_field.b0)) 

159 results["GLOBAL"]["I_ROD"] = 2.0 * np.pi * r0 * b0 / mu_0 

160 

161 

162 # Bp probes (note, this is all the sensors, both the ones we fit and the ones we don't) 

163 bp_names = bp_probes.keys() # list of strings; len(bp_names) = n_sensors 

164 bp_names = [bp_name.replace("P", "B_BPPROBE_") for bp_name in bp_names] 

165 results["CONSTRAINTS"]["BPPROBE"]["NAME"] = np.array(bp_names) # MDSplus requires numpy objects, not lists of strings; shape = [n_sensors] 

166 results["CONSTRAINTS"]["BPPROBE"]["CVALUE"] = bp_probes.get_array2(["*", "b", "calculated", "value"]) # shape = [n_time, n_sensors] 

167 results["CONSTRAINTS"]["BPPROBE"]["INCLUDE"] = np.array(bp_probes.get_vec_bool(["*", "fit_settings", "include"])).astype(np.int32) 

168 results["CONSTRAINTS"]["BPPROBE"]["MVALUE"] = bp_probes.get_array2(["*", "b", "measured", "value"]) # shape = [n_time, n_sensors] 

169 results["CONSTRAINTS"]["BPPROBE"]["WEIGHT"] = bp_probes.get_array1(["*", "fit_settings", "weight"]) # shape = [n_sensors] 

170 

171 # Flux loops (note, this is all the sensors, both the ones we fit and the ones we don't) 

172 fl_names = flux_loops.keys() # list of strings; len(fl_names) = n_sensors 

173 fl_names = [fl_name.replace("L", "PSI_FLOOP_") for fl_name in fl_names] 

174 results["CONSTRAINTS"]["FLOOP"]["NAME"] = np.array(fl_names) # MDSplus requires numpy objects, not lists of strings; shape = [n_sensors] 

175 results["CONSTRAINTS"]["FLOOP"]["CVALUE"] = flux_loops.get_array2(["*", "psi", "calculated", "value"]) # shape = [n_time, n_sensors] 

176 results["CONSTRAINTS"]["FLOOP"]["INCLUDE"] = np.array(flux_loops.get_vec_bool(["*", "fit_settings", "include"])).astype(np.int32) 

177 results["CONSTRAINTS"]["FLOOP"]["MVALUE"] = flux_loops.get_array2(["*", "psi", "measured", "value"]) # shape = [n_time, n_sensors] 

178 results["CONSTRAINTS"]["FLOOP"]["WEIGHT"] = flux_loops.get_array1(["*", "fit_settings", "weight"]) # shape = [n_sensors] 

179 

180 # Rogowski coils (note, this is all the sensors, both the ones we fit and the ones we don't) 

181 rog_names = rogowski_coils.keys() # list of strings; len(rog_names) = n_sensors 

182 rog_names = [f"I_ROG_{rog_name}" for rog_name in rog_names] 

183 results["CONSTRAINTS"]["ROG"]["NAME"] = np.array(rog_names) # MDSplus requires numpy objects, not lists of strings; shape = [n_sensors] 

184 results["CONSTRAINTS"]["ROG"]["CVALUE"] = rogowski_coils.get_array2(["*", "i", "calculated", "value"]) # shape = [n_time, n_sensors] 

185 results["CONSTRAINTS"]["ROG"]["INCLUDE"] = np.array(rogowski_coils.get_vec_bool(["*", "fit_settings", "include"])).astype(np.int32) 

186 results["CONSTRAINTS"]["ROG"]["MVALUE"] = rogowski_coils.get_array2(["*", "i", "measured", "value"]) # shape = [n_time, n_sensors] 

187 results["CONSTRAINTS"]["ROG"]["WEIGHT"] = rogowski_coils.get_array1(["*", "fit_settings", "weight"]) # shape = [n_sensors] 

188 

189 # Plasma boundary 

190 # The IDS stores each contour at its own length and the gather pads them into a rectangle; 

191 # MDSplus wants the rectangle plus the count of real points in each row 

192 results["P_BOUNDARY"]["NBND"] = _n_points_per_time(results["P_BOUNDARY"]["RBND"]) 

193 

194 # Passives 

195 for passive_name in passives.keys(): 

196 if passive_name == "IVC": 

197 results["PASSIVES"]["IVC"]["DOF"]["EIG_01"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_01", "calculated"]) 

198 results["PASSIVES"]["IVC"]["DOF"]["EIG_01"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_01", "current_distribution"]) 

199 results["PASSIVES"]["IVC"]["DOF"]["EIG_02"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_02", "calculated"]) 

200 results["PASSIVES"]["IVC"]["DOF"]["EIG_02"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_02", "current_distribution"]) 

201 results["PASSIVES"]["IVC"]["DOF"]["EIG_03"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_03", "calculated"]) 

202 results["PASSIVES"]["IVC"]["DOF"]["EIG_03"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_03", "current_distribution"]) 

203 results["PASSIVES"]["IVC"]["DOF"]["EIG_04"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_04", "calculated"]) 

204 results["PASSIVES"]["IVC"]["DOF"]["EIG_04"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_04", "current_distribution"]) 

205 results["PASSIVES"]["IVC"]["DOF"]["EIG_05"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_05", "calculated"]) 

206 results["PASSIVES"]["IVC"]["DOF"]["EIG_05"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_05", "current_distribution"]) 

207 results["PASSIVES"]["IVC"]["DOF"]["EIG_06"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_06", "calculated"]) 

208 results["PASSIVES"]["IVC"]["DOF"]["EIG_06"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_06", "current_distribution"]) 

209 results["PASSIVES"]["IVC"]["DOF"]["EIG_07"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_07", "calculated"]) 

210 results["PASSIVES"]["IVC"]["DOF"]["EIG_07"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_07", "current_distribution"]) 

211 results["PASSIVES"]["IVC"]["DOF"]["EIG_08"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_08", "calculated"]) 

212 results["PASSIVES"]["IVC"]["DOF"]["EIG_08"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_08", "current_distribution"]) 

213 results["PASSIVES"]["IVC"]["DOF"]["EIG_09"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_09", "calculated"]) 

214 results["PASSIVES"]["IVC"]["DOF"]["EIG_09"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_09", "current_distribution"]) 

215 results["PASSIVES"]["IVC"]["DOF"]["EIG_10"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_10", "calculated"]) 

216 results["PASSIVES"]["IVC"]["DOF"]["EIG_10"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_10", "current_distribution"]) 

217 results["PASSIVES"]["IVC"]["DOF"]["EIG_11"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_11", "calculated"]) 

218 results["PASSIVES"]["IVC"]["DOF"]["EIG_11"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_11", "current_distribution"]) 

219 results["PASSIVES"]["IVC"]["DOF"]["EIG_12"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_12", "calculated"]) 

220 results["PASSIVES"]["IVC"]["DOF"]["EIG_12"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_12", "current_distribution"]) 

221 results["PASSIVES"]["IVC"]["DOF"]["EIG_13"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_13", "calculated"]) 

222 results["PASSIVES"]["IVC"]["DOF"]["EIG_13"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_13", "current_distribution"]) 

223 results["PASSIVES"]["IVC"]["DOF"]["EIG_14"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_14", "calculated"]) 

224 results["PASSIVES"]["IVC"]["DOF"]["EIG_14"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_14", "current_distribution"]) 

225 results["PASSIVES"]["IVC"]["DOF"]["EIG_15"]["CVALUE"] = passives.get_array1(["IVC", "dof", "eig_15", "calculated"]) 

226 results["PASSIVES"]["IVC"]["DOF"]["EIG_15"]["I_DIST"] = passives.get_array1(["IVC", "dof", "eig_15", "current_distribution"]) 

227 results["PASSIVES"]["IVC"]["GEOMETRY"]["ANGLE_1"] = passives.get_array1(["IVC", "geometry", "angle_1"]) 

228 results["PASSIVES"]["IVC"]["GEOMETRY"]["ANGLE_2"] = passives.get_array1(["IVC", "geometry", "angle_2"]) 

229 results["PASSIVES"]["IVC"]["GEOMETRY"]["D_R"] = passives.get_array1(["IVC", "geometry", "d_r"]) 

230 results["PASSIVES"]["IVC"]["GEOMETRY"]["D_Z"] = passives.get_array1(["IVC", "geometry", "d_z"]) 

231 results["PASSIVES"]["IVC"]["GEOMETRY"]["R"] = passives.get_array1(["IVC", "geometry", "r"]) 

232 results["PASSIVES"]["IVC"]["GEOMETRY"]["Z"] = passives.get_array1(["IVC", "geometry", "z"]) 

233 else: 

234 results["PASSIVES"][passive_name]["DOF"]["CONSTANT_J"]["CVALUE"] = passives.get_array1( 

235 [passive_name, "dof", "constant_current_density", "calculated"] 

236 ) 

237 results["PASSIVES"][passive_name]["DOF"]["CONSTANT_J"]["I_DIST"] = passives.get_array1( 

238 [passive_name, "dof", "constant_current_density", "current_distribution"] 

239 ) 

240 results["PASSIVES"][passive_name]["GEOMETRY"]["ANGLE_1"] = passives.get_array1([passive_name, "geometry", "angle_1"]) 

241 results["PASSIVES"][passive_name]["GEOMETRY"]["ANGLE_2"] = passives.get_array1([passive_name, "geometry", "angle_2"]) 

242 results["PASSIVES"][passive_name]["GEOMETRY"]["D_R"] = passives.get_array1([passive_name, "geometry", "d_r"]) 

243 results["PASSIVES"][passive_name]["GEOMETRY"]["D_Z"] = passives.get_array1([passive_name, "geometry", "d_z"]) 

244 results["PASSIVES"][passive_name]["GEOMETRY"]["R"] = passives.get_array1([passive_name, "geometry", "r"]) 

245 results["PASSIVES"][passive_name]["GEOMETRY"]["Z"] = passives.get_array1([passive_name, "geometry", "z"]) 

246 

247 # Scrape off layer (SOL) 

248 

249 if len(pressure_sensors.keys()) > 0: 

250 results["CONSTRAINTS"]["PRESSURE"]["RECONSTRUCTED"] = pressure_sensors.get_array2( 

251 ["*", "pressure", "calculated", "value"] 

252 ) # shape = [n_time, n_points] 

253 results["CONSTRAINTS"]["PRESSURE"]["MEASURED"] = pressure_sensors.get_array2(["*", "pressure", "measured", "value"]) # shape = [n_time, n_points] 

254 results["CONSTRAINTS"]["PRESSURE"]["WEIGHT"] = pressure_sensors.get_array1(["*", "fit_settings", "weight"]) # shape = [n_points] 

255 results["CONSTRAINTS"]["PRESSURE"]["POSITION"]["R"] = pressure_sensors.get_array1(["*", "geometry", "r"]) # shape = [n_points] 

256 results["CONSTRAINTS"]["PRESSURE"]["POSITION"]["Z"] = pressure_sensors.get_array1(["*", "geometry", "z"]) # shape = [n_points] 

257 results["CONSTRAINTS"]["PRESSURE"]["POSITION"]["PSI"] = pressure_sensors.get_array2( 

258 ["*", "pressure", "calculated", "psi"] 

259 ) # shape = [n_time, n_points] 

260 

261 # Store "WORKFLOW" 

262 database_reader_method = settings["GSFIT_code_settings.json"]["database_reader"]["method"] 

263 

264 code_names = settings["GSFIT_code_settings.json"]["database_reader"][database_reader_method]["workflow"].keys() 

265 

266 for code_name in code_names: 

267 pulseNo_json = settings["GSFIT_code_settings.json"]["database_reader"][database_reader_method]["workflow"][code_name]["pulseNo"] 

268 if pulseNo_json is not None: 

269 results["INPUT"]["WORKFLOW"][code_name]["PULSE"] = pulseNo_json 

270 else: 

271 results["INPUT"]["WORKFLOW"][code_name]["PULSE"] = pulseNo 

272 

273 run_name = settings["GSFIT_code_settings.json"]["database_reader"][database_reader_method]["workflow"][code_name]["run_name"] 

274 results["INPUT"]["WORKFLOW"][code_name]["RUN"] = run_name 

275 

276 usage = settings["GSFIT_code_settings.json"]["database_reader"][database_reader_method]["workflow"][code_name]["usage"] 

277 results["INPUT"]["WORKFLOW"][code_name]["USAGE"] = usage