def to_cherab_equilibrium(self): """ Function for converting this Fiesta object to a CHERAB equilibrium. rtype: EFITEquilibrium """ try: from raysect.core import Point2D from cherab.tools.equilibrium import EFITEquilibrium except ImportError: raise RuntimeError("CHERAB integration not installed.") r_vec = self.r_vec z_vec = self.z_vec psi = np.swapaxes(self.psi, 0, 1) psi_axis = self.psi_axis psi_lcfs = self.psi_lcfs magnetic_axis = Point2D(self.mag_axis[0], self.mag_axis[1]) x_points = [] for point in self.x_points: x_points.append(Point2D(point[0], point[1])) strike_points = [] f_profile = self.f_profile q_profile = self.q_profile b_vacuum_radius = self.b_vac_radius b_vacuum_magnitude = self.b_vac lcfs_polygon = self.lcfs_polygon # shape 2xM, indexing to remove duplicated point if np.all(lcfs_polygon[:, 0] == lcfs_polygon[:, -1]): lcfs_polygon = lcfs_polygon[:, 0:-1] limiter_polygon = np.array([self.r_limiter, self.z_limiter]) # 2xM time = 0.0 equilibrium = EFITEquilibrium(r_vec, z_vec, psi, psi_axis, psi_lcfs, magnetic_axis, x_points, strike_points, f_profile, q_profile, b_vacuum_radius, b_vacuum_magnitude, lcfs_polygon, limiter_polygon, time) return equilibrium
def voxel_matches_polygon(self, coordinate_list): for voxel_coords in coordinate_list: voxel_coords = np.asarray(voxel_coords) rmax = voxel_coords[:, 0].max() rmin = voxel_coords[:, 0].min() zmax = voxel_coords[:, 1].max() zmin = voxel_coords[:, 1].min() router = 1.5 * rmax rinner = 0.5 * rmin zupper = 1.5 * zmax if zmax > 0 else 0.5 * zmax zlower = 0.5 * zmin if zmin > 0 else 1.5 * zmin test_rs = np.linspace(rinner, router, int(100 * (router - rinner))) test_zs = np.linspace(zlower, zupper, int(100 * (zupper - zlower))) voxel_vertex_points = [Point2D(*v) for v in voxel_coords] voxel = AxisymmetricVoxel(voxel_vertex_points, parent=None, primitive_type='csg') polygon = Polygon(voxel_coords, closed=True) test_verts = list(itertools.product(test_rs, test_zs)) try: inside_poly = polygon.contains_points(test_verts) except AttributeError: # Polygon.contains_points was only introduced in Matplotlib 2.2. # Before that we need to convert to Path inside_poly = Path( polygon.get_verts()).contains_points(test_verts) inside_csg = [ any( child.contains(Point3D(r, 0, z)) for child in voxel.children) for (r, z) in test_verts ] self.assertSequenceEqual(inside_csg, inside_poly.tolist())
def test_rectangle_centroid(self): for rectangle in RECTANGULAR_VOXEL_COORDS: coords = np.asarray(rectangle) voxel = AxisymmetricVoxel(coords) x0, y0 = coords.mean(axis=0) expected_centroid = Point2D(x0, y0) self.assertEqual(voxel.cross_section_centroid, expected_centroid)
def _process_efit_points(self, point_r, point_z): #avoid using the point if any coordinate is nan point_list = [] for i in range(point_r.shape[0]): if not np.isnan(point_r[i]) and not np.isnan(point_z[i]): point_list.append(Point2D(point_r[i], point_z[i])) return point_list
def load_kb1_voxel_grid(parent=None, name=None): grid_description = _read_grid_pickle() voxel_coordinates = [] for voxel in grid_description['voxels']: v1 = Point2D(voxel[0][0], voxel[0][1]) v2 = Point2D(voxel[1][0], voxel[1][1]) v3 = Point2D(voxel[2][0], voxel[2][1]) v4 = Point2D(voxel[3][0], voxel[3][1]) voxel_coordinates.append((v1, v2, v3, v4)) voxel_grid = ToroidalVoxelGrid(voxel_coordinates, parent=parent, name=name, primitive_type="csg") return voxel_grid
def _build_reference_grid(): index = 0 voxels = [] drs = np.diff(_VOXEL_RS) dzs = np.diff(_VOXEL_ZS) dr = np.abs(np.median(drs)) dz = np.abs(np.median(dzs)) for r in _VOXEL_RS: row = [] for z in _VOXEL_ZS: top_left = (r - dr / 2, z + dz / 2) bottom_left = (r - dr / 2, z - dz / 2) bottom_right = (r + dr / 2, z - dz / 2) top_right = (r + dr / 2, z + dz / 2) row.append((index, Point2D(*top_left), Point2D(*bottom_left), Point2D(*bottom_right), Point2D(*top_right))) index += 1 voxels.append(row) return voxels
def test_triangle_centroid(self): for triangle in TRIANGLE_VOXEL_COORDS: coords = np.asarray(triangle) voxel = AxisymmetricVoxel(coords) # The centroid for a triangle is simply the mean position # of the vertices expected_centroid = Point2D(*coords.mean(axis=0)) if voxel.cross_sectional_area == 0: self.assertRaises(ZeroDivisionError, getattr, voxel, "cross_section_centroid") else: self.assertEqual(voxel.cross_section_centroid, expected_centroid)
def example_equilibrium(): """ Return a populated instance of the example equilibrium. .. code-block:: pycon >>> from cherab.tools.equilibrium import example_equilibrium >>> equilibrium = example_equilibrium() """ directory = os.path.split(__file__)[0] example_file = os.path.join(directory, 'example.json') with open(example_file, 'r') as fh: eq_data = json.load(fh) r = eq_data['r'] z = eq_data['z'] psi = eq_data['psi'] psi_axis = eq_data['psi_axis'] psi_lcfs = eq_data['psi_lcfs'] ac = eq_data['axis_coord'] axis_coord = Point2D(ac[0], ac[1]) xp = eq_data['x_points'] x_points = [Point2D(xp[0][0], xp[0][1])] sp = eq_data['strike_points'] strike_points = [Point2D(sp[0][0], sp[0][1]), Point2D(sp[1][0], sp[1][1])] f_profile = eq_data['f_profile'] q_profile = eq_data['q_profile'] b_vacuum_radius = eq_data['b_vacuum_radius'] b_vacuum_magnitude = eq_data['b_vacuum_magnitude'] lcfs_polygon = eq_data['lcfs_polygon'] limiter_polygon = eq_data['limiter_polygon'] time = eq_data['time'] equilibrium = EFITEquilibrium(r, z, psi, psi_axis, psi_lcfs, axis_coord, x_points, strike_points, f_profile, q_profile, b_vacuum_radius, b_vacuum_magnitude, lcfs_polygon, limiter_polygon, time) return equilibrium
def test_polygon_centroid(self): for polygon in ARBITRARY_VOXEL_COORDS: coords = np.asarray(polygon) voxel = AxisymmetricVoxel(coords) x = coords[:, 0] y = coords[:, 1] signed_area = (np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1))) / 2 xroll = np.roll(x, 1) yroll = np.roll(y, 1) cx = np.sum((x + xroll) * (x * yroll - xroll * y)) / (6 * signed_area) cy = np.sum((y + yroll) * (x * yroll - xroll * y)) / (6 * signed_area) expected_centroid = Point2D(cx, cy) self.assertEqual(voxel.cross_section_centroid, expected_centroid)
def load_kl11_voxel_grid(parent=None, name=None): directory = os.path.split(__file__)[0] voxel_grid_file = os.path.join(directory, "kl11_voxel_grid.csv") voxel_coordinates = [] with open(voxel_grid_file, 'r') as fh: reader = csv.reader(fh) for row in reader: v1 = Point2D(float(row[1]), float(row[2])) v2 = Point2D(float(row[3]), float(row[4])) v3 = Point2D(float(row[5]), float(row[6])) v4 = Point2D(float(row[7]), float(row[8])) voxel_coordinates.append((v1, v2, v3, v4)) voxel_grid = ToroidalVoxelGrid(voxel_coordinates, parent=parent, name=name, primitive_type='csg') return voxel_grid
def test_voxel_indexing(self): grid = ToroidalVoxelGrid(self.voxel_grid_coords) nvoxels = self.voxel_grid_coords.shape[0] with self.assertRaises(IndexError, msg="Negative index did not raise IndexError"): grid[-2] with self.assertRaises(IndexError, msg="index > length did not raise IndexError"): grid[nvoxels + 3] with self.assertRaises(TypeError, msg="String index did not raise TypeError"): grid["blah"] # Check the correct voxel is returned, by looking at its vertices for i in range(grid.count): self.assertSequenceEqual( grid[i].vertices, [Point2D(r, z) for r, z in self.voxel_grid_coords[i]], msg="Wrong voxel returned for index {}".format(i) )
def load_bul_voxel_grid(parent=None, name=None, active=None): directory = os.path.split(__file__)[0] voxel_grid_file = os.path.join(directory, "bul_voxel_grid.csv") voxel_coordinates = [] with open(voxel_grid_file, 'r') as fh: reader = csv.reader(fh, ) for row in reader: if row[0][0] == '#': continue v1 = Point2D(float(row[1]), float(row[2])) v2 = Point2D(float(row[3]), float(row[4])) v3 = Point2D(float(row[5]), float(row[6])) v4 = Point2D(float(row[7]), float(row[8])) voxel_coordinates.append((v1, v2, v3, v4)) voxel_grid = ToroidalVoxelGrid(voxel_coordinates, parent=parent, name=name, active=active) return voxel_grid
def time(self, time): """ Returns an equilibrium object for the time-slice closest to the requested time. The specific time-slice returned is held in the time attribute of the returned object. :param time: The equilibrium time point. :returns: An EFITEquilibrium object. """ B_VACUUM_RADIUS = 2.96 # meters # locate the nearest time point and fail early if we are outside the time range of the data try: index = self._find_nearest(self.time_slices, time) except IndexError: raise ValueError( 'Requested time lies outside the range of the data: [{}, {}]s.' .format(*self.time_range)) # slice data for selected time point time = self.time_slices[index] f_profile_psin = self._f.dimensions[1].data f_profile_magnitude = self._f.data[index, :] psi_lcfs = self._psi_lcfs.data[index] psi_axis = self._psi_axis.data[index] axis_coord = Point2D(self._axis_coord_r.data[index], self._axis_coord_z.data[index]) b_vacuum_magnitude = self._b_vacuum_magnitude.data[index] lcfs_poly_r = self._lcfs_poly_r.data[index, :] lcfs_poly_z = self._lcfs_poly_z.data[index, :] # slice and reshape psi data for specified time point # the original data is 3D, packed into a 2D array, this must be reshaped psi = np.reshape(self._packed_psi.data[index, :], (len(self._r.data), len(self._z.data)), order='F') # convert raw lcfs poly coordinates into a polygon object lcfs_polygon = self._process_efit_lcfs_polygon(lcfs_poly_r, lcfs_poly_z) return EFITEquilibrium(self._r, self._z, psi, psi_axis, psi_lcfs, axis_coord, f_profile_psin, f_profile_magnitude, B_VACUUM_RADIUS, b_vacuum_magnitude, lcfs_polygon, time)
def time(self, time): """ Returns an equilibrium object for the time-slice closest to the requested time. The specific time-slice returned is held in the time attribute of the returned object. :param time: The equilibrium time point. :returns: An EFITEquilibrium object. """ time_index, time_slice = self._find_nearest(self._time_slice, time) r = self._r[time_index, :] # get the R coords for psi_grid z = self._z[time_index, :] # get the Z coords for psi_grid psi_grid = self._psi_grid[time_index, :, :] psi_axis = self._psi_axis[time_index] psi_lcfs = self._psi_lcfs[time_index] magnetic_axis = Point2D(self._magnetic_axis_r[time_index], self._magnetic_axis_z[time_index]) b_vacuum_radius = self._b_vacuum_radius b_vacuum_magnitude = self._b_vacuum_magnitude[time_index] lcfs_polygon = self._process_efit_polygon(self._lcfs_r[time_index, :], self._lcfs_z[time_index, :]) limiter_polygon = self._process_efit_polygon(self._limiter_r[time_index, :], self._limiter_z[time_index, :]) strikepoints = self._process_efit_points(self._strikepoint_r[time_index, :], self._strikepoint_z[time_index, :]) xpoints = self._process_efit_points(self._xpoint_r[time_index, :], self._xpoint_z[time_index, :]) f_profile = np.array([self._psin, self._rBphi[time_index, :]]) equilibrium = EFITEquilibrium(r=r, z=z, psi_grid=psi_grid, psi_axis=psi_axis, psi_lcfs=psi_lcfs, magnetic_axis=magnetic_axis, x_points = xpoints, strike_points=strikepoints, f_profile=f_profile, b_vacuum_radius=b_vacuum_radius, b_vacuum_magnitude=b_vacuum_magnitude, lcfs_polygon=lcfs_polygon, limiter_polygon=limiter_polygon, time=time_slice) return equilibrium
def load_standard_voxel_grid(system, parent=None, name='', voxel_range=None, primitive_type='csg'): """ Load the standard bolometry reconstruction grid. :param system: the name of the bolometer system used Currently 'core', 'core_high_res' and 'sxdl' are supported. :param parent: the parent node for the grid object. :param name: the name of the grid object :param voxel_range: if not none, return a subset of the grid cells, `voxels[voxel_range]`. Can be either a slice object or a range object. :param str primitive_type: specifies the representation type of the voxels in the grid. 'mesh' and 'csg' are supported. 'mesh' generally works well but can have extremely high memory usage on higher resolution grids, whereas 'csg' has much lower memory usage but can be slower on grids with many non-rectilinear voxels. Both types support voxels with arbitrary polygon cross sections. Before this function is called, the corresponding pickle file for the grid must have already been generated. This can be done by running the `/examples/bholometry/grid_generation/make_<system>_grid.py` scripts. """ directory = os.path.split(__file__)[0] grid_file = os.path.join(directory, '{}_rectilinear_grid.pickle'.format(system)) with open(grid_file, 'rb') as f: grid_data = pickle.load(f) grid_voxels = grid_data['voxels'] if voxel_range is not None: grid_voxels = grid_voxels[voxel_range] voxel_list = [[Point2D(*vertex) for vertex in voxel] for voxel in grid_voxels] grid = ToroidalVoxelGrid(voxel_list, parent=parent, name=name, primitive_type=primitive_type) return grid
def rad_function(r, z): sample_point = Point2D(r, z) direction = PLASMA_AXIS.vector_to(sample_point) bearing = np.arctan2(direction.y, direction.x) # calculate radius of coordinate from magnetic axis radius_from_axis = direction.length closest_ring_point = PLASMA_AXIS + (direction.normalise() * 0.5) radius_from_ring = sample_point.distance_to(closest_ring_point) # evaluate pedestal-> core function if radius_from_axis <= LCFS_RADIUS: central_radiatior = RADIATION_PEAK * np.exp( -(radius_from_axis**2) / CENTRE_PEAK_WIDTH) ring_radiator = RADIATION_PEAK * np.cos(bearing) * np.exp( -(radius_from_ring**2) / RING_WIDTH) ring_radiator = max(0, ring_radiator) return central_radiatior + ring_radiator else: return 0
import numpy as np from raysect.core import Point2D from raysect.primitive import export_obj from cherab.tools.primitives import axisymmetric_mesh_from_polygon PLASMA_AXIS = Point2D(1.5, 0) LCFS_RADIUS = 1 RING_RADIUS = 0.5 RADIATION_PEAK = 1 CENTRE_PEAK_WIDTH = 0.05 RING_WIDTH = 0.025 # distance of wall from LCFS WALL_LCFS_OFFSET = 0.1 CYLINDER_RADIUS = PLASMA_AXIS.x + LCFS_RADIUS + WALL_LCFS_OFFSET * 1.1 CYLINDER_HEIGHT = (LCFS_RADIUS + WALL_LCFS_OFFSET) * 2 WALL_RADIUS = LCFS_RADIUS + WALL_LCFS_OFFSET ########################################## # make toroidal wall wrapping the plasma # # number of poloidal wall elements num = 250 d_angle = (2 * np.pi) / num wall_polygon = np.zeros((num, 2)) for i in range(num): pr = PLASMA_AXIS.x + WALL_RADIUS * np.sin(d_angle * i)
def __init__(self, pulse): self.client = pyuda.Client() # get the pyuda client # Poloidal magnetic flux per toroidal radian as a function of (Z,R) and timebase self.psi = self.client.get("efm_psi(r,z)", pulse) self.time_slices = self.psi.dims[0].data # Psi grid axes f(nr), f(nz) self.r = self.client.get("efm_grid(r)", pulse) self.z = self.client.get("efm_grid(z)", pulse) # f profile self.f = self.client.get("efm_f(psi)_(c)", pulse)# Poloidal current flux function, f=R*Bphi; f(psin, C) # q profile self.q = self.client.get("efm_q(psi)_(c)", pulse) self.psi_r = self.client.get("efm_psi(r)", pulse) #poloidal magnetic flux per toroidal radian as a function of radius at Z=0 # Poloidal magnetic flux per toroidal radian at the plasma boundary and magnetic axis self.psi_lcfs = self.client.get("efm_psi_boundary", pulse) self.psi_axis = self.client.get("efm_psi_axis", pulse) # Plasma current self.plasma_current = self.client.get("efm_plasma_curr(C)", pulse) # Reference vaccuum toroidal B field at R = efm_bvac_r self.b_vacuum_magnitude = self.client.get("efm_bvac_val", pulse) self.b_vacuum_radius = self.client.get("efm_bvac_r", pulse) # Magnetic axis co-ordinates self.axis_coord_r = self.client.get("efm_magnetic_axis_r", pulse) self.axis_coord_z = self.client.get("efm_magnetic_axis_z", pulse) # X point coordinates xpoint1r = self.client.get("efm_xpoint1_r(c)", pulse).data xpoint1z = self.client.get("efm_xpoint1_z(c)", pulse).data xpoint2r = self.client.get("efm_xpoint2_r(c)", pulse).data xpoint2z = self.client.get("efm_xpoint2_z(c)", pulse).data self.xpoints = [ (Point2D(r1, z1), Point2D(r2, z2)) for (r1, z1, r2, z2) in zip(xpoint1r, xpoint1z, xpoint2r, xpoint2z) ] #minor radius self.minor_radius = self.client.get("efm_minor_radius", pulse) #lcfs boundary polygon self.lcfs_poly_r = self.client.get("efm_lcfs(r)_(c)", pulse) self.lcfs_poly_z = self.client.get("efm_lcfs(z)_(c)", pulse) # Number of LCFS co-ordinates self.nlcfs = self.client.get("efm_lcfs(n)_(c)", pulse) # limiter boundary polygon self.limiter_poly_r = self.client.get("efm_limiter(r)", pulse) self.limiter_poly_z = self.client.get("efm_limiter(z)", pulse) # Number of limiter co-ordinates self.nlimiter = self.client.get("efm_limiter(n)", pulse) # time slices when plasma is present self.plasma_times = self.client.get("efm_ip_times", pulse) self.time_range = self.time_slices.min(), self.time_slices.max()
def time(self, time): """ Returns an equilibrium object for the time-slice closest to the requested time. The specific time-slice returned is held in the time attribute of the returned object. :param time: The equilibrium time point. :returns: An EFIT Equilibrium object. """ # locate the nearest time point and fail early if we are outside the time range of the data try: index = self._find_nearest(self.time_slices, time) # Find the index in the time array defined as when the plasma is present plasma_index = self._find_nearest(self.plasma_times.data, time) except IndexError: raise ValueError('Requested time lies outside the range of the data: [{}, {}]s.'.format(*self.time_range)) b_vacuum_radius = self.b_vacuum_radius.data[index] time = self.time_slices[index] psi = np.transpose(self.psi.data[index,:,:]) #transpose psi to get psi(R,Z) instead of psi(Z,R) psi_lcfs = self.psi_lcfs.data[plasma_index] psi_axis = self.psi_axis.data[plasma_index] print('psi_axis', psi_axis) f_profile_psin = self.f.dims[1].data self.f_profile_psin = f_profile_psin f_profile_magnitude = self.f.data[plasma_index, :] f_profile = np.asarray([f_profile_psin, f_profile_magnitude]) q_profile_magnitude = self.q.data[plasma_index] q_profile_psin = self.q.dims[1].data q_profile = np.asarray([q_profile_psin, q_profile_magnitude]) axis_coord = Point2D(self.axis_coord_r.data[plasma_index], self.axis_coord_z.data[plasma_index]) b_vacuum_magnitude = self.b_vacuum_magnitude.data[index] lcfs_poly_r = self.lcfs_poly_r.data[plasma_index,:] lcfs_poly_z = self.lcfs_poly_z.data[plasma_index,:] # Get the actual co-ordinates of the LCFS lcfs_points = self.nlcfs.data[plasma_index] #Filter out padding in the LCFS coordinate arrays lcfs_poly_r = lcfs_poly_r[0:lcfs_points] lcfs_poly_z = lcfs_poly_z[0:lcfs_points] # convert raw lcfs poly coordinates into a polygon object lcfs_polygon = self._process_efit_lcfs_polygon(lcfs_poly_r, lcfs_poly_z) lcfs_polygon = np.ascontiguousarray(lcfs_polygon.T) # 2xN contiguous self.lcfs_polygon = lcfs_polygon limiter_poly_r = self.limiter_poly_r.data.squeeze() limiter_poly_z = self.limiter_poly_z.data.squeeze() # Get the actual co-ordinates of the limiter limiter_points = self.nlimiter.data.item() #Filter out padding in the LIMITER coordinate arrays limiter_poly_r = limiter_poly_r[0:limiter_points] limiter_poly_z = limiter_poly_z[0:limiter_points] # convert raw limiter poly coordinates into a polygon object limiter_polygon = self._process_efit_lcfs_polygon(limiter_poly_r, limiter_poly_z) limiter_polygon = np.ascontiguousarray(limiter_polygon.T) # 2xN contiguous self.limiter_polygon = limiter_polygon r = self.r.data.squeeze() z = self.z.data.squeeze() xpoints = self.xpoints[plasma_index] # MAST-U EFIT has no reliably-available strike point data (ASF not always available) strike_points = [] minor_radius = self.minor_radius.data[plasma_index] print('minor radius', minor_radius) return EFITEquilibrium(r, z, psi, psi_axis, psi_lcfs, axis_coord, xpoints, strike_points, f_profile, q_profile, b_vacuum_radius, b_vacuum_magnitude, lcfs_polygon, limiter_polygon, time)
# HFS mapping footprint = Eich(1, 0.0001) # lambda_q=2.5, S=0.5 x = np.linspace(-1, 10, 100) footprint.set_coordinates(x) footprint.s_disconnected_dn_max = 2.1 footprint.fx_in_out = 5. footprint.calculate_heat_flux_density("hfs") field_tracer = FieldlineTracer(b_field, method=RK2(step_size=0.0001, direction="negative")) # HFS interface POINT_A = Point2D(0.25, -0.5) POINT_B = Point2D(0.3304, -0.5833) power_profile = sample_power_at_surface(POINT_A, POINT_B, equilibrium, footprint, lcfs_radii_min=0.15, lcfs_radii_max=0.25, side="HFS") interface_power = 5e5 # 0.5MW angle_period = 45 interface_surface = InterfaceSurface(POINT_A, POINT_B, power_profile, interface_power) interface_surface.map_power(interface_power,
def plot_kernel(kernel, voxel_vertices): """Plot a 1D grid function as a 2D image""" voxels = [[Point2D(p[0], p[1]) for p in voxel] for voxel in voxel_vertices] grid = ToroidalVoxelGrid(voxels) grid.plot(voxel_values=abs(kernel)) plt.show()
def time(self, time): """ Returns an equilibrium object for the time-slice closest to the requested time. The specific time-slice returned is held in the time attribute of the returned object. :param time: The equilibrium time point. :returns: An EFIT Equilibrium object. """ # locate the nearest time point and fail early if we are outside the time range of the data try: index = self._find_nearest(self.time_slices, time) # Find the index in the time array defined as when the plasma is present plasma_index = self._find_nearest(self.plasma_times.data, time) except IndexError: raise ValueError( 'Requested time lies outside the range of the data: [{}, {}]s.' .format(*self.time_range)) B_VACUUM_RADIUS = self.b_vacuum_radius.data[index] time = self.time_slices[index] psi = np.transpose(self.psi.data[ index, :, :]) #transpose psi to get psi(R,Z) instead of psi(Z,R) psi_lcfs = self.psi_lcfs.data[plasma_index] psi_axis = self.psi_axis.data[plasma_index] print('psi_axis', psi_axis) psi_r = self.psi_r.data[:, plasma_index] f_profile_psin = self.f.dims[1].data self.f_profile_psin = f_profile_psin f_profile_magnitude = self.f.data[plasma_index, :] axis_coord = Point2D(self.axis_coord_r.data[plasma_index], self.axis_coord_z.data[plasma_index]) b_vacuum_magnitude = self.b_vacuum_magnitude.data[index] lcfs_poly_r = self.lcfs_poly_r.data[plasma_index, :] lcfs_poly_z = self.lcfs_poly_z.data[plasma_index, :] # Get the actual co-ordinates of the LCFS lcfs_points = self.nlcfs.data[plasma_index] #Filter out padding in the LCFS coordinate arrays lcfs_poly_r = lcfs_poly_r[0:lcfs_points] lcfs_poly_z = lcfs_poly_z[0:lcfs_points] # convert raw lcfs poly coordinates into a polygon object lcfs_polygon = self._process_efit_lcfs_polygon(lcfs_poly_r, lcfs_poly_z) self.lcfs_polygon = lcfs_polygon r = self.r.data[0, :] z = self.z.data[0, :] minor_radius = self.minor_radius.data[plasma_index] print('minor radius', minor_radius) return EFITEquilibrium(r, z, psi, psi_axis, psi_lcfs, axis_coord, f_profile_psin, f_profile_magnitude, B_VACUUM_RADIUS, b_vacuum_magnitude, lcfs_polygon, time)
num_voxels = nx * ny # Coordinate of vertices xpoints = np.linspace(xrange[0], xrange[1], num=nx + 1) ypoints = np.linspace(yrange[0], yrange[1], num=ny + 1) grid_index_2D_to_1D_map = {} grid_index_1D_to_2D_map = {} grid_cells = [] unwrapped_cell_index = 0 for ix in range(nx - 1): for jy in range(ny - 1): p1 = Point2D(xpoints[ix], ypoints[jy]) p2 = Point2D(xpoints[ix], ypoints[jy + 1]) p3 = Point2D(xpoints[ix + 1], ypoints[jy + 1]) p4 = Point2D(xpoints[ix + 1], ypoints[jy]) if wall_mask(*p1) or wall_mask(*p2) or wall_mask(*p3) or wall_mask( *p4): grid_cells.append((p1, p2, p3, p4)) grid_index_2D_to_1D_map[(ix, jy)] = unwrapped_cell_index grid_index_1D_to_2D_map[unwrapped_cell_index] = (ix, jy) unwrapped_cell_index += 1 num_cells = len(grid_cells) cell_data = np.zeros((num_cells, 4, 2)) for i in range(num_cells): p1, p2, p3, p4 = grid_cells[i]
def test_list_of_point2d(self): voxel_coords = [Point2D(*v) for v in RECTANGULAR_VOXEL_COORDS[0]] voxel = AxisymmetricVoxel(voxel_coords) for coord, vertex in zip(voxel_coords, voxel.vertices): self.assertEqual([coord[0], coord[1]], [vertex.x, vertex.y], "Nx2 array coordinate mismatch")
def load_mesh_from_mdsplus(mds_connection): """ Load the SOLPS mesh geometry for a given MDSplus connection. :param mds_connection: MDSplus connection object. Already set to the SOLPS tree with pulse #ID. """ # Load the R, Z coordinates of the cell vertices, original coordinates are (4, 38, 98) # re-arrange axes (4, 38, 98) => (98, 38, 4) x = np.swapaxes(mds_connection.get('\TOP.SNAPSHOT.GRID:R').data(), 0, 2) z = np.swapaxes(mds_connection.get('\TOP.SNAPSHOT.GRID:Z').data(), 0, 2) vol = np.swapaxes( mds_connection.get('\SOLPS::TOP.SNAPSHOT.VOL').data(), 0, 1) # build mesh object mesh = SOLPSMesh(x, z, vol) ############################# # Add additional parameters # ############################# # add the vessel geometry mesh.vessel = mds_connection.get('\SOLPS::TOP.SNAPSHOT.GRID:VESSEL').data() # Load the centre points of the grid cells. cr = np.swapaxes(mds_connection.get('\TOP.SNAPSHOT.GRID:CR').data(), 0, 1) cz = np.swapaxes(mds_connection.get('\TOP.SNAPSHOT.GRID:CZ').data(), 0, 1) mesh._cr = cr mesh._cz = cz # Load cell basis vectors nx = mesh.nx ny = mesh.ny cell_poloidal_basis = np.empty((nx, ny, 2), dtype=object) for i in range(nx): for j in range(ny): # Work out cell's 2D parallel vector in the poloidal plane if i == nx - 1: # Special case for end of array, repeat previous calculation. # This is because I don't have access to the gaurd cells. xp_x = cr[i, j] - cr[i - 1, j] xp_y = cz[i, j] - cz[i - 1, j] norm = np.sqrt(xp_x**2 + xp_y**2) cell_poloidal_basis[i, j, 0] = Point2D(xp_x / norm, xp_y / norm) else: xp_x = cr[i + 1, j] - cr[i, j] xp_y = cz[i + 1, j] - cz[i, j] norm = np.sqrt(xp_x**2 + xp_y**2) cell_poloidal_basis[i, j, 0] = Point2D(xp_x / norm, xp_y / norm) # Work out cell's 2D radial vector in the poloidal plane if j == ny - 1: # Special case for end of array, repeat previous calculation. yr_x = cr[i, j] - cr[i, j - 1] yr_y = cz[i, j] - cz[i, j - 1] norm = np.sqrt(yr_x**2 + yr_y**2) cell_poloidal_basis[i, j, 1] = Point2D(yr_x / norm, yr_y / norm) else: yr_x = cr[i, j + 1] - cr[i, j] yr_y = cz[i, j + 1] - cz[i, j] norm = np.sqrt(yr_x**2 + yr_y**2) cell_poloidal_basis[i, j, 1] = Point2D(yr_x / norm, yr_y / norm) mesh._poloidal_grid_basis = cell_poloidal_basis return mesh
############################## # setup the heatflux profile # # specify and load heatflux profile footprint = Eich(1.0e-3, 0.0001e-3) x = np.linspace(-0.001, 0.01, 100) footprint.set_coordinates(x) footprint.s_disconnected_dn_max = 2.1 footprint.fx_in_out = 5. footprint.calculate_heat_flux_density("lfs") footprint.plot_heat_power_density() # make a mesh of the interface surface interface_point_a = Point2D(0.345941, -0.593439) interface_point_b = Point2D(0.51091, -0.757166) interface_vector = interface_point_a.vector_to(interface_point_b) interface_polygon = np.zeros((100, 2)) psi_on_interface = [] for i in range(100): point = interface_point_a + interface_vector * i / 100 interface_polygon[i, :] = point.x, point.y psi_on_interface.append(psin2d(point.x, point.y)) interface_mesh = axisymmetric_mesh_from_polygon(interface_polygon) interface_mesh.parent = world interface_mesh.name = "interface_mesh" # find LCFS midplane radius def psin(r, offset=0):
# interface_surface.map_power(interface_power, angle_period, field_tracer, world, # num_of_fieldlines=5000, phi_offset=-angle_period/2, debug_output=True) # HFS mapping footprint = Eich(1, 0.0001) # lambda_q=2.5, S=0.5 x = np.linspace(-1, 10, 100) footprint.set_coordinates(x) footprint.s_disconnected_dn_max = 2.1 footprint.fx_in_out = 5. footprint.calculate_heat_flux_density("hfs") field_tracer = FieldlineTracer(b_field, method=RK2(step_size=0.0001, direction="negative")) # HFS interface POINT_A = Point2D(0.25, -0.5) POINT_B = Point2D(0.340, -0.600) power_profile = sample_power_at_surface(POINT_A, POINT_B, equilibrium, footprint, lcfs_radii_min=0.15, lcfs_radii_max=0.25, side="HFS") interface_power = 5e5 # 0.5MW angle_period = 45 interface_surface = InterfaceSurface(POINT_A, POINT_B, power_profile, interface_power) # new function interface_surface.histogram_plot() interface_surface.map_power(interface_power, angle_period, field_tracer, world, num_of_fieldlines=5000, phi_offset=-angle_period/2, debug_output=True)
return [r_div, z_div, power] ########################## # add machine components # config_file = get_resource("ST40-IVC1", "configuration", 'st40_ivc1_config') ######################## # load the equilibrium # eq = get_resource("ST40-IVC1", "equilibrium", "eq_006_2T_export") fiesta = Fiesta(eq) ############################## # setup the heatflux profile # # specify and load heatflux profile lcfs = fiesta.get_midplane_lcfs(psi_p=1.00001)[1] footprint = Eich(1.0e-3, 0.0001e-3, r0_lfs=lcfs) # lambda_q=2.5, S=0.5 x = np.linspace(-1, 10, 100) * 1e-3 footprint.set_coordinates(x) footprint.calculate_heat_flux_density("lfs") POINT_A = Point2D(0.345941, -0.593439) POINT_B = Point2D(0.51091, -0.757166) power_profile = sample_power_at_surface(POINT_A, POINT_B, fiesta, footprint) s = np.sqrt(power_profile[0]**2 + power_profile[1]**2) - np.sqrt(POINT_A[0]**2 + POINT_A[1]**2) plt.plot(s, power_profile[2] / max(power_profile[2]) * 40)
equilibrium = fiesta.to_cherab_equilibrium() psin2d = equilibrium.psi_normalised ############################## # setup the heatflux profile # # specify and load heatflux profile footprint = Eich(1, 0.0001) # lambda_q=2.5, S=0.5 x = np.linspace(-1, 10, 100) footprint.set_coordinates(x) footprint.s_disconnected_dn_max = 2.1 footprint.fx_in_out = 5. footprint.calculate_heat_flux_density("lfs") POINT_A = Point2D(0.340, -0.600) POINT_B = Point2D(0.500, -0.810) power_profile = sample_power_at_surface(POINT_A, POINT_B, equilibrium, footprint) interface_power = 1e6 # 1MW angle_period = 45 interface_surface = InterfaceSurface(POINT_A, POINT_B, power_profile, interface_power) # new function interface_surface.histogram_plot() interface_surface.map_power(interface_power, angle_period, field_tracer, world,
equilibrium = fiesta.to_cherab_equilibrium() psin2d = equilibrium.psi_normalised ############################## # setup the heatflux profile # # specify and load heatflux profile footprint = Eich(2.5, 0.5) # lambda_q=2.5, S=0.5 x = np.linspace(-1, 10, 100) footprint.set_coordinates(x) footprint.s_disconnected_dn_max = 2.1 footprint.fx_in_out = 5. footprint.calculate_heat_flux_density("lfs") POINT_A = Point2D(0.44, -0.79) POINT_B = Point2D(0.57, -0.798) power_profile = sample_power_at_surface(POINT_A, POINT_B, equilibrium, footprint) interface_power = 1e6 # 1MW angle_period = 45 interface_surface = InterfaceSurface(POINT_A, POINT_B, power_profile, interface_power) interface_surface.map_power(interface_power, angle_period, field_tracer, world, num_of_fieldlines=100, debug_output=True)