Coverage for tcvx21/grillix_post/lineouts/lineouts_m.py: 100%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Implementations of lineouts specific to the tcvx21 analysis
3"""
4import numpy as np
5import warnings
6from pathlib import Path
7from numpy.polynomial import Polynomial
8import matplotlib.pyplot as plt
9import xarray as xr
10import pint
12from .lineout_m import Lineout, on_grid_interpolator
13from tcvx21.analysis.contour_finding_m import find_contours
14from tcvx21.file_io.json_io_m import read_from_json
15from tcvx21.file_io.matlab_file_reader_m import read_struct_from_file
16from tcvx21.units_m import Quantity, convert_xarray_to_quantity
19def outboard_midplane_chord(grid, equi, n_points: int = 1000):
20 """
21 Returns n_points of equally spaced points along the outboard zaxis, which is a horizontal line from the magnetic axis
22 outwards (to large radius). N.b. the outboard midplane is usually to largest radius, but this is roughly equivalent
23 """
25 r_start = equi.axis_r_norm
26 z_start = equi.axis_z_norm
28 r_end = grid.r_s.max()
29 z_end = equi.axis_z_norm
31 lineout = Lineout([r_start, r_end], [z_start, z_end])
32 lineout.find_points_on_grid(grid, n_points=n_points, trim_points=False)
34 return lineout
37def penalisation_contour(grid, equi, level: float, contour_index: int = None, epsilon: float = 1E-10,
38 smoothing: float = 0.1, n_points: int = 1000):
39 """
40 Returns a lineout along a contour of the penalisation characteristic
42 Should use torx.specialisations.*.penalisation_m.filled_penalisation_characteristic to get the
43 penalisation_characteristic filled to include pghost points
45 smoothing is recommended, since this removes the grid staircase pattern of the contour
46 """
47 penalisation_characteristic = grid.shape(equi.penalisation_characteristic)
49 assert 'R' in penalisation_characteristic.dims and 'Z' in penalisation_characteristic.dims, "Call vector_to_matrix on\
50 penalisation_chacteristic before calling penalisation_contour"
52 # Shift the level slightly in from [0, 1] to ensure a clear contour
53 level = max(level, epsilon)
54 level = min(level, 1.0 - epsilon)
56 contours = find_contours(grid.r_s, grid.z_s, penalisation_characteristic, level=level)
58 if equi.flipped_z:
59 # Reverse the contour list is using flipped_z, since contours are returned from bottom to top
60 contours.reverse()
62 if contour_index is None: # pragma: no cover
63 plt.pcolormesh(grid.r_s, grid.z_s, penalisation_characteristic, shading='nearest')
64 for i, contour in enumerate(contours):
65 r_points, z_points = contour.T
67 plt.plot(r_points, z_points, label=f'contour_index = {i}')
68 plt.legend()
69 plt.show()
71 raise ValueError("Select a contour index for penalisation_contour")
73 else:
74 r_points, z_points = contours[contour_index].T
76 lineout = Lineout(r_points, z_points, smoothing=smoothing)
77 lineout.find_points_on_grid(grid, trim_points=True, n_points=n_points)
79 return lineout
82def thomson_scattering(grid, equi, thomson_position: Path, n_points: int = 1000):
83 """
84 Returns a lineout for the Thomson scattering points below the magnetic axis
85 """
86 thomson_position_ = read_from_json(thomson_position)
88 # For the simulation, we only want divertor entrance points (below the magnetic axis)
89 # Use the ts_below_axis mask to limit to this region
90 ts_below_axis = thomson_position_['Z'] < 0
91 thomson_position_ = {'R': thomson_position_['R'][ts_below_axis],
92 'Z': thomson_position_['Z'][ts_below_axis], }
94 R0 = convert_xarray_to_quantity(equi.axis_r)
96 ts_r = (Quantity(thomson_position_['R'], 'm') / R0).to('').magnitude
97 ts_z = (Quantity(thomson_position_['Z'], 'm') / R0).to('').magnitude * (-1.0 if equi.flipped_z else 1.0)
99 ts = Lineout(ts_r, ts_z)
100 ts.find_points_on_grid(grid, n_points=n_points, trim_points=False)
102 return ts
105def rdpa(grid, equi, omp_map, norm, rdpa_coordinates: Path):
106 """
107 Returns a lineout for the 2D RDPA (reciprocating divertor probe array). Despite being a 2D diagnostic,
108 since observables are not on a regular rectangular grid we calculate values as tricolumn (R^u-R^_sep, Z, values)
109 data and then reshape.
110 """
111 warnings.simplefilter('ignore', category=pint.errors.UnitStrippedWarning)
113 rdpa_coords = read_from_json(rdpa_coordinates)
115 R0 = norm.R0
117 rdpa_r = xr.DataArray(Quantity(rdpa_coords['R'] , rdpa_coords['R_units'] ) / R0).assign_attrs(norm=R0)
118 rdpa_zx = xr.DataArray(Quantity(rdpa_coords['Zx'] , rdpa_coords['Zx_units'] ) / R0).assign_attrs(norm=R0)
119 rdpa_rsep = xr.DataArray(Quantity(rdpa_coords['Rsep'], rdpa_coords['Rsep_units']) / R0).assign_attrs(norm=R0)
120 rdpa_z = xr.DataArray(Quantity(rdpa_coords['Z'] , rdpa_coords['Z_units'] ) / R0).assign_attrs(norm=R0)
122 if equi.flipped_z:
123 rdpa_z *= -1
125 # Some points cannot be matched in the reference equilibrium. These are marked by NaN
126 nan_value = ~np.isnan(rdpa_r).values
127 rdpa_on_grid = on_grid_interpolator(grid)(rdpa_r, rdpa_z, grid=False) > 0.99
128 mask = np.logical_and.reduce((nan_value, rdpa_on_grid))
129 print(f"Masking {np.count_nonzero(~mask)} of {rdpa_r.size} RDPA points")
130 rdpa_r, rdpa_z, rdpa_zx, rdpa_rsep = rdpa_r[mask], rdpa_z[mask], rdpa_zx[mask], rdpa_rsep[mask]
132 rdpa_rho = equi.normalised_flux_surface_label(rdpa_r, rdpa_z, grid=False)
133 assert np.allclose(rdpa_rsep, omp_map.convert_rho_to_distance(rdpa_rho), atol=1E-6), \
134 f"Max difference for rdpa Rsep was {np.max(np.abs(rdpa_rsep - omp_map.convert_rho_to_distance(rdpa_rho)))}"
136 rdpa_lineout = Lineout(rdpa_r, rdpa_z)
137 rdpa_lineout.setup_interpolation_matrix(grid, use_source_points=True)
139 # Store the observable positions to allow mapping to 2D data
140 # We store according to machine coordinates, regardless of whether we have flipped the equilibrium
141 rdpa_lineout.coords = {
142 'R': convert_xarray_to_quantity(rdpa_r),
143 'Rsep': convert_xarray_to_quantity(rdpa_rsep),
144 'Z': convert_xarray_to_quantity(rdpa_z) * (-1.0 if equi.flipped_z else 1.0),
145 'Zx': convert_xarray_to_quantity(rdpa_zx),
146 }
148 return rdpa_lineout
151def xpoint(grid, equi, norm, r_range: float = 0.1, z_range: float = 0.1,
152 rhomin=0.97, rhomax=1.03, r_samples: int = 50, z_samples: int = 50):
153 """
154 Returns a lineout of the region around the X-point
155 """
156 warnings.simplefilter('ignore', category=pint.errors.UnitStrippedWarning)
158 r_min = float(equi.x_point_r_norm.values - r_range)
159 r_max = float(equi.x_point_r_norm.values + r_range)
160 z_min = float(equi.x_point_z_norm.values - z_range)
161 z_max = float(equi.x_point_z_norm.values + z_range)
163 r_sample, z_sample = np.linspace(r_min, r_max, num=r_samples), np.linspace(z_min, z_max, num=z_samples)
165 xpoint_on_grid = (on_grid_interpolator(grid)(r_sample, z_sample, grid=True) > 0.99).T
166 xpoint_rho = equi.normalised_flux_surface_label(r_sample, z_sample, grid=True).values
168 r_mesh, z_mesh = np.meshgrid(r_sample, z_sample)
169 mask = np.logical_and.reduce((xpoint_on_grid, xpoint_rho > rhomin, xpoint_rho < rhomax))
171 xpoint_lineout = Lineout(r_mesh[mask].ravel(), z_mesh[mask].ravel())
172 xpoint_lineout.setup_interpolation_matrix(grid, use_source_points=True)
174 xpoint_lineout.coords = {
175 'R': xpoint_lineout.r_points * norm.R0,
176 'Z': xpoint_lineout.z_points * norm.R0 * (-1.0 if equi.flipped_z else 1.0),
177 'Zx': xpoint_lineout.z_points * norm.R0 * (-1.0 if equi.flipped_z else 1.0) \
178 - convert_xarray_to_quantity(equi.axis_r)
179 }
181 return xpoint_lineout