def test_value(self): density = lambda x, y, z: 6e19 * (1 + 0.1 * np.sin(x) * np.sin(y) * np. sin(z)) # m^-3 temperature = lambda x, y, z: 3e3 * (1 + 0.1 * np.sin(x + 1) * np.sin( y + 1) * np.sin(z + 1)) # eV velocity = lambda x, y, z: 1.6e5 * (1 + 0.1 * np.sin(x + 2) * np.sin( y + 2) * np.sin(z + 2)) * Vector3D(1, 2, 3).normalise() # m/s mass = 4 * atomic_mass # kg maxwellian = Maxwellian(density, temperature, velocity, mass) sigma = lambda x, y, z: np.sqrt( temperature(x, y, z) * elementary_charge / mass) # m/s phase_space_density = lambda x, y, z, vx, vy, vz: density(x, y, z) / (np.sqrt(2 * np.pi) * sigma(x, y, z)) ** 3 \ * np.exp(-(Vector3D(vx, vy, vz) - velocity(x, y, z)).length ** 2 / (2 * sigma(x, y, z) ** 2)) # s^3/m^6 # testing only half the values to avoid huge execution time for x in self.x[::2]: for y in self.y[::2]: for z in self.z[::2]: for vx in self.vx[::2]: for vy in self.vy[::2]: for vz in self.vz[::2]: self.assertAlmostEqual( maxwellian(x, y, z, vx, vy, vz), phase_space_density(x, y, z, vx, vy, vz), delta=1e-10, msg= 'call method gives a wrong phase space density at ({}, {}, {}, {}, {}, {}).' .format(x, y, z, vx, vy, vz))
def point(self, value): if not (self._direction.x == 0 and self._direction.y == 0 and self._direction.z == 1): up = Vector3D(0, 0, 1) else: up = Vector3D(1, 0, 0) self._point = value self._observer.transform = translate( value.x, value.y, value.z) * rotate_basis(self._direction, up)
def direction(self, value): if value.x != 0 and value.y != 0 and value.z != 1: up = Vector3D(0, 0, 1) else: up = Vector3D(1, 0, 0) self._direction = value self._observer.transform = translate(self._point.x, self._point.y, self._point.z) * rotate_basis( value, up)
def load_dms_output(config, world, plasma, spec, fibgeom): from raysect.optical.observer import FibreOptic, PowerPipeline0D, SpectralRadiancePipeline0D from raysect.optical import translate, rotate, rotate_basis from raysect.core import Vector3D, Point3D power_arr = np.zeros(fibgeom.numfibres) spectra_arr = np.zeros((spec.pixels, fibgeom.numfibres)) for i, f in enumerate(power_arr): print("Analysing fibre: ", int(i + 1)) fibgeom.set_fibre(number=int(i + 1)) start_point = Point3D(fibgeom.origin[0], fibgeom.origin[1], fibgeom.origin[2]) forward_vector = Vector3D(fibgeom.xhat(), fibgeom.yhat(), fibgeom.zhat()).normalise() up_vector = Vector3D(0, 0, 1.0) if config['dms']['power_pipeline']: power = PowerPipeline0D() fibre = FibreOptic([power], acceptance_angle=1, radius=0.001, spectral_bins=spec.pixels, spectral_rays=1, pixel_samples=5, transform=translate(*start_point) * rotate_basis(forward_vector, up_vector), parent=world) fibre.min_wavelength = spec.wlower fibre.max_wavelength = spec.wupper fibre.observe() power_arr[i] = power.value.mean else: power_arr[i] = None if config['dms']['radiance_pipeline']: spectra = SpectralRadiancePipeline0D(display_progress=False) fibre = FibreOptic([spectra], acceptance_angle=1, radius=0.001, spectral_bins=spec.pixels, spectral_rays=1, pixel_samples=5, transform=translate(*start_point) * rotate_basis(forward_vector, up_vector), parent=world) fibre.min_wavelength = spec.wlower fibre.max_wavelength = spec.wupper fibre.observe() spectra_arr[:, i] = spectra.samples.mean else: spectra_arr[:, i] = None return power_arr, spectra_arr
def visualise_scenegraph(camera, focal_distance=1, zoom=1): if not isinstance(camera, Observer): raise TypeError("The vtk visualisation function takes a Raysect Observer object as its argument.") world = camera.root if not isinstance(world, World): raise TypeError("The vtk visualisation function requires the Raysect Observer object to be connected to a valid scene-graph.") # Add the actors to the renderer renderer = vtk.vtkRenderer() renderer.SetBackground(0, 0, 0) for child in world.children: vtk_element = map_raysect_element_to_vtk(child) if isinstance(vtk_element, VTKAssembly): renderer.AddActor(vtk_element.assembly) else: renderer.AddActor(vtk_element.actor) axes = vtk.vtkAxesActor() renderer.AddActor(axes) renWin = vtk.vtkRenderWindow() renWin.AddRenderer(renderer) renWin.SetSize(512, 512) iren = vtk.vtkRenderWindowInteractor() iren.SetRenderWindow(renWin) iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera()) iren.Initialize() camera_origin = Point3D(0, 0, 0).transform(camera.transform) camera_direction = Vector3D(0, 0, 1).transform(camera.transform) up_direction = Vector3D(0, 1, 0).transform(camera.transform) focal_point = camera_origin + camera_direction * focal_distance vtk_camera = vtk.vtkCamera() vtk_camera.SetPosition(camera_origin.x, camera_origin.y, camera_origin.z) vtk_camera.SetFocalPoint(focal_point.x, focal_point.y, focal_point.z) vtk_camera.SetViewUp(up_direction.x, up_direction.y, up_direction.z) vtk_camera.ComputeViewPlaneNormal() vtk_camera.SetDistance(focal_distance) vtk_camera.Zoom(zoom) renderer.SetActiveCamera(vtk_camera) renderer.SetBackground(1.0, 0.9688, 0.8594) renderer.SetBackground(vtk_colors.GetColor3d("SlateGray")) # Start the event loop. iren.Start()
def __init__(self, point, direction, parent=None, name=""): if not isinstance(point, Point3D): raise TypeError( "point argument for SpectroscopicSightLine must be of type Point3D." ) if not isinstance(direction, Vector3D): raise TypeError( "direction argument for SpectroscopicSightLine must be of type Vector3D." ) self._point = Point3D(0, 0, 0) self._direction = Vector3D(1, 0, 0) self._transform = AffineMatrix3D() self._spectral_pipeline = SpectralRadiancePipeline0D(accumulate=False) # TODO - carry over wavelength range and resolution settings self._observer = SightLine(pipelines=[self._spectral_pipeline], parent=parent, name=name) self.name = name self.point = point self.direction = direction
def plot_brdf(light_angle): light_position = Point3D(np.sin(np.deg2rad(light_angle)), 0, np.cos(np.deg2rad(light_angle))) light_direction = origin.vector_to(light_position).normalise() phis = np.linspace(0, 360, 200) num_phis = len(phis) thetas = np.linspace(0, 90, 100) num_thetas = len(thetas) values = np.zeros((num_thetas, num_phis)) for i in range(num_thetas): for j in range(num_phis): theta = np.deg2rad(thetas[i]) phi = np.deg2rad(phis[j]) outgoing = Vector3D( np.cos(phi) * np.sin(theta), np.sin(phi) * np.sin(theta), np.cos(theta)) values[i, j] = aluminium.bsdf(light_direction, outgoing, 500.0) fig, ax = plt.subplots(subplot_kw=dict(projection='polar')) cs = ax.contourf(np.deg2rad(phis), thetas, values, extend="both") cs.cmap.set_under('k') plt.title("Light angle: {} degrees".format(light_angle))
def calculate_brdf_surface(light_vector): thetas = np.arange(0, 91, step=5) num_thetas = len(thetas) phis = np.arange(0, 361, step=10) num_phis = len(phis) thetas, phis = np.meshgrid(thetas, phis) X = np.zeros((num_phis, num_thetas)) Y = np.zeros((num_phis, num_thetas)) Z = np.zeros((num_phis, num_thetas)) for i in range(num_phis): for j in range(num_thetas): theta = np.deg2rad(thetas[i, j]) phi = np.deg2rad(phis[i, j]) outgoing = Vector3D( np.cos(phi) * np.sin(theta), np.sin(phi) * np.sin(theta), np.cos(theta)) radius = aluminium.bsdf(light_vector, outgoing, 500) X[i, j] = radius * np.cos(phi) * np.sin(theta) Y[i, j] = radius * np.sin(phi) * np.sin(theta) Z[i, j] = radius * np.cos(theta) return X, Y, Z
def test_bulk_velocity(self): density = lambda x, y, z: 6e19 * (1 + 0.1 * np.sin(x) * np.sin(y) * np. sin(z)) # m^-3 temperature = lambda x, y, z: 3e3 * (1 + 0.1 * np.sin(x + 1) * np.sin( y + 1) * np.sin(z + 1)) # eV velocity = lambda x, y, z: 1.6e5 * (1 + 0.1 * np.sin(x + 2) * np.sin( y + 2) * np.sin(z + 2)) * Vector3D(1, 2, 3).normalise() # m/s mass = 4 * atomic_mass # kg maxwellian = Maxwellian(density, temperature, velocity, mass) for x in self.x: for y in self.y: for z in self.z: self.assertAlmostEqual( maxwellian.bulk_velocity(x, y, z).x, velocity(x, y, z).x, delta=1e-10, msg= 'bulk_velocity method gives a wrong value at ({}, {}, {}).' .format(x, y, z)) self.assertAlmostEqual( maxwellian.bulk_velocity(x, y, z).y, velocity(x, y, z).y, delta=1e-10, msg= 'bulk_velocity method gives a wrong value at ({}, {}, {}).' .format(x, y, z)) self.assertAlmostEqual( maxwellian.bulk_velocity(x, y, z).z, velocity(x, y, z).z, delta=1e-10, msg= 'bulk_velocity method gives a wrong value at ({}, {}, {}).' .format(x, y, z))
def __call__(self, x, y, z): direction = Vector3D(1, 0, 0) spectrum = Spectrum(400, 750, 1) observed = self.model.emission(Point3D(x, y, z), direction, spectrum) return observed.total()
def test_no_distribution_setter(self): species = Species(elements.carbon, 3, self.get_maxwellian()) with self.assertRaises( AttributeError, msg= 'It must not be possible to change the distribution of a species!' ): species.distribution = Maxwellian( lambda x, y, z: 2e20, lambda x, y, z: 3e3, lambda x, y, z: Vector3D(1.6e5, 0, 0), 4.1 * atomic_mass)
def vectorfunction3d(x, y, z): r = sqrt(x**2 + y**2) phi = np.arctan2(y, x) b_mag = 1 / r b_vec = Vector3D(r * np.cos(phi), r * np.sin(phi), 0) b_vec = b_vec.transform(rotate_z(90)) b_vec = b_vec.normalise() * b_mag return b_vec
def fibre_distance_world(self, world): from cherab.tools.observers.intersections import find_wall_intersection from raysect.core import Vector3D, Point3D start_point = Point3D(self.origin[0] + 1.0 * self.xhat(), self.origin[1] + 1.0 * self.yhat(), self.origin[2] + 1.0 * self.zhat()) forward_vector = Vector3D(self.xhat(), self.yhat(), self.zhat()) hit_point, primitive = find_wall_intersection(world, start_point, forward_vector, delta=1E-3) return abs((self.origin[0] - hit_point[0]) / self.xhat())
def _check_barrel_surface(self, lens, azimuths, barrel_z): """ Checks barrel surface of a lens by calculating ray-lens intersection. Hit point position and angle of incidence are compared to predicted ones. :param lens: Spherical lens object to test plane surface of. :param azimuths: Azimuth angles to test lens surface at. :param barrel_z: """ lens_radius = lens.diameter / 2 for z in barrel_z: for ta in azimuths: # get x-y coordinates of the surface point from azimuth x = lens_radius * cos(ta) y = lens_radius * sin(ta) # get origin by surface point offset and calculate ray direction surface_point = Point3D(x, y, z) direction = Vector3D(-x, -y, 0) origin = Point3D(1.1 * x, 1.1 * y, z) # calculate ray-lens intersection intersection = lens.hit(CoreRay(origin, direction)) hit_point = intersection.hit_point.transform( intersection.primitive_to_world) # distance of expected surface point and the ray hit point distance = hit_point.vector_to(surface_point).length self.assertAlmostEqual( distance, 0, self.tolerance_distance, msg= "Ray-curved surface hit point and predicted surface point difference" " is larger than tolerance.") # angle of incidence on the sphere surface should be perpendicular cos_angle_incidence = intersection.normal.dot( intersection.ray.direction.normalise()) self.assertAlmostEqual( fabs(cos_angle_incidence), 1, self.tolerance_angle, msg="Angle of incidence differs from perpendicular.")
def plot_line_of_sight(self, x_pixel, y_pixel, ax, **kwargs): """ :param x_pixel: :param y_pixel: :param ax: :return: """ direction = Vector3D(*list(self.view_vectors.isel(x=x_pixel, y=y_pixel, ).values)).normalise() ray_length = float(self.ray_lengths.isel(x=x_pixel, y=y_pixel, ).values) n_steps = 50 increment = (ray_length / n_steps) * direction rs, zs = np.zeros(n_steps), np.zeros(n_steps) point_i = self.pupil_point # initialise at pupil for i in range(n_steps): point_i += increment rs[i], zs[i], _ = cart2cyl(*[point_i[j] for j in range(3)]) ax.plot(rs, zs, **kwargs)
def get_pini_alignment(pulse, oct8_pini): import idlbridge as idl global _idl_was_setup if not _idl_was_setup: _setup_idl() _idl_was_setup = True # Note: array index starts at zero, so actual pini index equals pini number - 1/. oct8_pini -= 1 idl.execute("ret = get_cherab_pinialignment(pulse={})".format(pulse)) ret = idl.get("ret") # Pull out the origin points from the IDL structure, convert to Point3D origin = Point3D(ret['origin'][oct8_pini][0] / 1000, ret['origin'][oct8_pini][1] / 1000, ret['origin'][oct8_pini][2] / 1000) # Pull out the direction vector from the IDL structure, convert to Vector3D direction = Vector3D(ret['vector'][oct8_pini][0], ret['vector'][oct8_pini][1], ret['vector'][oct8_pini][2]) # TODO - note divergence numbers are different between Carine and Corentin. div_u = ret['divu'][oct8_pini] / (2 * PI) * 360 div_v = ret['divv'][oct8_pini] / (2 * PI) * 360 divergence = (div_u, div_v) # Minimal 1/e width (at the source) of the beam (scalar in meters) initial_width = 0.001 # Approximate with 1mm as an effective point source. pini_length = PINI_LENGTHS[oct8_pini] pini_geometry = (origin, direction, divergence, initial_width, pini_length) return pini_geometry
src.n_pulse = PULSE_PLASMA psi = src.get_psi_normalised(cached2d=True) inside = lambda x, y, z: not isnan(psi(x, y, z)) ppfsetdevice("JET") ppfuid('cgiroud', rw='R') ppfgo(pulse=PULSE_PLASMA, seq=0) psi_coord = np.array(ppfget(PULSE_PLASMA, 'PRFL', 'C6')[3], dtype=np.float64) mask = psi_coord <= 1.0 psi_coord = psi_coord[mask] flow_velocity_tor_data = np.array(ppfget(PULSE_PLASMA, 'PRFL', 'VT')[2], dtype=np.float64)[mask] flow_velocity_tor_psi = Interpolate1DCubic(psi_coord, flow_velocity_tor_data) flow_velocity_tor = IsoMapper3D(psi, flow_velocity_tor_psi) flow_velocity = lambda x, y, z: Vector3D(y * flow_velocity_tor(x, y, z), - x * flow_velocity_tor(x, y, z), 0.) / np.sqrt(x*x + y*y) ion_temperature_data = np.array(ppfget(PULSE_PLASMA, 'PRFL', 'TI')[2], dtype=np.float64)[mask] print("Ti between {} and {} eV".format(ion_temperature_data.min(), ion_temperature_data.max())) ion_temperature_psi = Interpolate1DCubic(psi_coord, ion_temperature_data) ion_temperature = IsoMapper3D(psi, ion_temperature_psi) electron_density_data = np.array(ppfget(PULSE_PLASMA, 'PRFL', 'NE')[2], dtype=np.float64)[mask] print("Ne between {} and {} m-3".format(electron_density_data.min(), electron_density_data.max())) electron_density_psi = Interpolate1DCubic(psi_coord, electron_density_data) electron_density = IsoMapper3D(psi, electron_density_psi) density_c6_data = np.array(ppfget(PULSE_PLASMA, 'PRFL', 'C6')[2], dtype=np.float64)[mask] density_c6_psi = Interpolate1DCubic(psi_coord, density_c6_data) density_c6 = IsoMapper3D(psi, density_c6_psi)
def get_maxwellian(self): return Maxwellian(lambda x, y, z: 1e20, lambda x, y, z: 1e3, lambda x, y, z: Vector3D(1e5, 0, 0), 4 * atomic_mass)
def _mayavi_source_from_raysect_object(self): # Calculate vertices and faces using the icosohedren method # We compute a regular icosohedren with 12 vertices and 20 faces. # Vertices given by all perturbations of: # (0, ±1, ±ϕ), (±1, ±ϕ, 0), (±ϕ, 0, ±1), where ϕ = golden ratio golden_ratio = 1.61803398875 radius = self._raysect_object.radius v1 = Vector3D(-1.0, golden_ratio, 0.0).normalise() * radius v2 = Vector3D(1.0, golden_ratio, 0.0).normalise() * radius v3 = Vector3D(-1.0, -golden_ratio, 0.0).normalise() * radius v4 = Vector3D(1.0, -golden_ratio, 0.0).normalise() * radius v5 = Vector3D(0.0, -1.0, golden_ratio).normalise() * radius v6 = Vector3D(0.0, 1.0, golden_ratio).normalise() * radius v7 = Vector3D(0.0, -1.0, -golden_ratio).normalise() * radius v8 = Vector3D(0.0, 1.0, -golden_ratio).normalise() * radius v9 = Vector3D(golden_ratio, 0.0, -1.0).normalise() * radius v10 = Vector3D(golden_ratio, 0.0, 1.0).normalise() * radius v11 = Vector3D(-golden_ratio, 0.0, -1.0).normalise() * radius v12 = Vector3D(-golden_ratio, 0.0, 1.0).normalise() * radius vertices = [ [v1.x, v1.y, v1.z], [v2.x, v2.y, v2.z], [v3.x, v3.y, v3.z], [v4.x, v4.y, v4.z], [v5.x, v5.y, v5.z], [v6.x, v6.y, v6.z], [v7.x, v7.y, v7.z], [v8.x, v8.y, v8.z], [v9.x, v9.y, v9.z], [v10.x, v10.y, v10.z], [v11.x, v11.y, v11.z], [v12.x, v12.y, v12.z], ] triangles = [[0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11], [1, 5, 9], [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8], [3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9], [4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1]] # Optional - subdivision of icosohedren to increase resolution num_vertices = 12 num_triangles = 20 for i in range(self._subdivision_count): for j in range(num_triangles): triangle = triangles[j] # extract current triangle vertices v0_id = triangle[0] v1_id = triangle[1] v2_id = triangle[2] v0 = Vector3D(vertices[v0_id][0], vertices[v0_id][1], vertices[v0_id][2]) v1 = Vector3D(vertices[v1_id][0], vertices[v1_id][1], vertices[v1_id][2]) v2 = Vector3D(vertices[v2_id][0], vertices[v2_id][1], vertices[v2_id][2]) # subdivide with three new vertices v3 = (v0 + v1).normalise() * radius v3_id = num_vertices v4 = (v1 + v2).normalise() * radius v4_id = num_vertices + 1 v5 = (v2 + v0).normalise() * radius v5_id = num_vertices + 2 vertices.append([v3.x, v3.y, v3.z]) vertices.append([v4.x, v4.y, v4.z]) vertices.append([v5.x, v5.y, v5.z]) # ... and three new faces triangles[j] = [v0_id, v3_id, v5_id] # replace the first face triangles.append([v3_id, v1_id, v4_id]) triangles.append([v4_id, v2_id, v5_id]) triangles.append([v3_id, v4_id, v5_id]) num_vertices += 3 num_triangles += 3 vertices = np.array(vertices) triangles = np.array(triangles) self._raysect_mesh = Mesh(vertices, triangles)
def raytraced_etendue(distance, detector_radius=0.001, ray_count=100000, batches=10): # generate the transform to the detector position and orientation detector_transform = translate(0, 0, distance) * rotate_basis( Vector3D(0, 0, -1), Vector3D(0, -1, 0)) # generate bounding sphere and convert to local coordinate system sphere = target.bounding_sphere() spheres = [(sphere.centre.transform(detector_transform), sphere.radius, 1.0)] # instance targetted pixel sampler targetted_sampler = TargettedHemisphereSampler(spheres) point_sampler = DiskSampler3D(detector_radius) detector_area = detector_radius**2 * np.pi solid_angle = 2 * np.pi etendue_sampled = solid_angle * detector_area etendues = [] for i in range(batches): # sample pixel origins origins = point_sampler(samples=ray_count) passed = 0.0 for origin in origins: # obtain targetted vector sample direction, pdf = targetted_sampler(origin, pdf=True) path_weight = R_2_PI * direction.z / pdf origin = origin.transform(detector_transform) direction = direction.transform(detector_transform) while True: # Find the next intersection point of the ray with the world intersection = world.hit(CoreRay(origin, direction)) if intersection is None: passed += 1 * path_weight break elif isinstance(intersection.primitive.material, NullMaterial): hit_point = intersection.hit_point.transform( intersection.primitive_to_world) origin = hit_point + direction * 1E-9 continue else: break if passed == 0: raise ValueError( "Something is wrong with the scene-graph, calculated etendue should not zero." ) etendue_fraction = passed / ray_count etendues.append(etendue_sampled * etendue_fraction) etendue = np.mean(etendues) etendue_error = np.std(etendues) return etendue, etendue_error
def basis_y(self): return Vector3D(0, 1, 0).transform(self.to_root())
def basis_x(self): return Vector3D(1, 0, 0).transform(self.to_root())
def normal_vector(self): return Vector3D(0, 0, 1).transform(self.to_root())
(-1, 1, 200)) plt.imshow(np.transpose(np.squeeze(t_samples)), extent=[-1, 2, -1, 1]) plt.colorbar() plt.axis('equal') plt.xlabel('x axis') plt.ylabel('z axis') plt.title("Neutral Density profile in x-z plane") ########################### # Inject beam into plasma # adas = OpenADAS(permit_extrapolation=True, missing_rates_return_null=True) integration_step = 0.0025 beam_transform = translate(-0.5, 0.0, 0) * rotate_basis( Vector3D(1, 0, 0), Vector3D(0, 0, 1)) beam_energy = 50000 # keV beam_full = Beam(parent=world, transform=beam_transform) beam_full.plasma = plasma beam_full.atomic_data = adas beam_full.energy = beam_energy beam_full.power = 3e6 beam_full.element = deuterium beam_full.sigma = 0.05 beam_full.divergence_x = 0.5 beam_full.divergence_y = 0.5 beam_full.length = 3.0 beam_full.attenuator = SingleRayAttenuator(clamp_to_zero=True) beam_full.models = [BeamCXLine(Line(carbon, 5, (8, 7)))]
def load_ks5_sightlines(pulse, spectrometer, parent=None, fibre_names=None, min_wavelength = 526, max_wavelength = 532, spectral_bins = 500): if not pulse >= 76666: raise ValueError("Only shots >= 76666 are supported at this time.") if spectrometer not in ["ks5c", "ks5d"]: raise ValueError("Only spectrometers ['ks5c', 'ks5d'] are supported at this time.") import idlbridge as idl idl.execute('searchpath = !PATH') global _idl_was_setup if not _idl_was_setup: _setup_idl() _idl_was_setup = True idl.execute("ret = get_ks5_alignment(pulse={}, spec='{}')".format(pulse, spectrometer)) # Pull out data cg_align = idl.get("ret") # Process fibres in the order that CXSfit uses. cxsfit_order = [i[0] for i in sorted(enumerate(cg_align['cxsfit_track']), key=lambda x:x[1], reverse=True)] sightline_group = LineOfSightGroup(parent=parent, name=spectrometer) for icg in cxsfit_order: fibre_name = str(cg_align['fibre_name'][icg]) # Some fibre names are blank, meaning ignore them. if not fibre_name.strip(): continue if fibre_names and fibre_name not in fibre_names: print('Skipped', fibre_name) continue # Extract the fibres origin and direction xi = cg_align['origin_cart']['x'][icg]/1000 yi = cg_align['origin_cart']['y'][icg]/1000 zi = cg_align['origin_cart']['z'][icg]/1000 pini6 = 5 xj = cg_align['pos_activevol_cart']['x'][pini6][icg]/1000 yj = cg_align['pos_activevol_cart']['y'][pini6][icg]/1000 zj = cg_align['pos_activevol_cart']['z'][pini6][icg]/1000 los_origin = Point3D(xi, yi, zi) los_vec = Vector3D(xj-xi, yj-yi, zj-zi).normalise() sight_line = SpectroscopicSightLine(los_origin, los_vec, name=fibre_name, parent=sightline_group) sight_line.min_wavelength = min_wavelength sight_line.max_wavelength = max_wavelength sight_line.spectral_bins = spectral_bins sightline_group.add_sight_line(sight_line) return sightline_group
def create_plasma(self, parent=None, transform=None, name=None): """ Make a CHERAB plasma object from this SOLEGE2D simulation. :param Node parent: The plasma's parent node in the scenegraph, e.g. a World object. :param AffineMatrix3D transform: Affine matrix describing the location and orientation of the plasma in the world. :param str name: User friendly name for this plasma (default = "SOLEDGE2D Plasma"). :rtype: Plasma """ mesh = self.mesh name = name or "SOLEDGE2D Plasma" plasma = Plasma(parent=parent, transform=transform, name=name) radius = mesh.mesh_extent['maxr'] height = mesh.mesh_extent['maxz'] - mesh.mesh_extent['minz'] plasma.geometry = Cylinder(radius, height) plasma.geometry_transform = translate(0, 0, mesh.mesh_extent['minz']) tri_index_lookup = self.mesh.triangle_index_lookup tri_to_grid = self.mesh.triangle_to_grid_map if isinstance(self._b_field_vectors, np.ndarray): plasma.b_field = SOLEDGE2DVectorFunction3D( tri_index_lookup, tri_to_grid, self._b_field_vectors_cartesian) else: print( 'Warning! No magnetic field data available for this simulation.' ) # Create electron species triangle_data = _map_data_onto_triangles(self._electron_temperature) electron_te_interp = Discrete2DMesh(mesh.vertex_coords, mesh.triangles, triangle_data, limit=False) electron_temp = AxisymmetricMapper(electron_te_interp) triangle_data = _map_data_onto_triangles(self._electron_density) electron_ne_interp = Discrete2DMesh.instance(electron_te_interp, triangle_data) electron_dens = AxisymmetricMapper(electron_ne_interp) electron_velocity = lambda x, y, z: Vector3D(0, 0, 0) plasma.electron_distribution = Maxwellian(electron_dens, electron_temp, electron_velocity, electron_mass) if not isinstance(self.velocities_cartesian, np.ndarray): print( 'Warning! No velocity field data available for this simulation.' ) b2_neutral_i = 0 # counter for B2 neutrals for k, sp in enumerate(self.species_list): # Identify the species based on its symbol symbol, charge = re.match(_SPECIES_REGEX, sp).groups() charge = int(charge) species_type = _species_symbol_map[symbol] # If neutral and B" atomic density available, use B2 density, otherwise use fluid species density. if isinstance(self.b2_neutral_densities, np.ndarray) and charge == 0: species_dens_data = self.b2_neutral_densities[:, :, b2_neutral_i] b2_neutral_i += 1 else: species_dens_data = self.species_density[:, :, k] triangle_data = _map_data_onto_triangles(species_dens_data) dens = AxisymmetricMapper( Discrete2DMesh.instance(electron_te_interp, triangle_data)) # dens = SOLPSFunction3D(tri_index_lookup, tri_to_grid, species_dens_data) # Create the velocity vector lookup function if isinstance(self.velocities_cartesian, np.ndarray): velocity = SOLEDGE2DVectorFunction3D( tri_index_lookup, tri_to_grid, self.velocities_cartesian[:, :, k, :]) else: velocity = lambda x, y, z: Vector3D(0, 0, 0) distribution = Maxwellian(dens, electron_temp, velocity, species_type.atomic_weight * atomic_mass) plasma.composition.add(Species(species_type, charge, distribution)) return plasma
inside_outside=plasma.inside_outside) d_delta_recom.add_emitter_to_world(world, plasma) d_epsilon_excit = ExcitationLine(d_epsilon, plasma.electron_distribution, d_atom_species, inside_outside=plasma.inside_outside) d_epsilon_excit.add_emitter_to_world(world, plasma) d_epsilon_recom = RecombinationLine(d_epsilon, plasma.electron_distribution, d_ion_species, inside_outside=plasma.inside_outside) d_epsilon_recom.add_emitter_to_world(world, plasma) start_point = Point3D(1.669, 0, -1.6502) forward_vector = Vector3D(1 - 1.669, 0, -2 + 1.6502).normalise() up_vector = Vector3D(0, 0, 1.0) spectra = SpectralPipeline0D() fibre = FibreOptic([spectra], acceptance_angle=1, radius=0.001, spectral_bins=8000, spectral_rays=1, pixel_samples=5, transform=translate(*start_point) * rotate_basis(forward_vector, up_vector), parent=world) fibre.min_wavelength = 350.0 fibre.max_wavelength = 700.0
def sphere_to_mesh(sphere, subdivision_count=2): if not isinstance(sphere, Sphere): raise TypeError("The _sphere_to_mesh() function takes a Raysect Box primitive as an argument, " "wrong type '{}' given.".format(type(sphere))) # Calculate vertices and faces using the icosohedren method # We compute a regular icosohedren with 12 vertices and 20 faces. # Vertices given by all perturbations of: # (0, ±1, ±ϕ), (±1, ±ϕ, 0), (±ϕ, 0, ±1), where ϕ = golden ratio golden_ratio = 1.61803398875 radius = sphere.radius v1 = Vector3D(-1.0, golden_ratio, 0.0).normalise() * radius v2 = Vector3D(1.0, golden_ratio, 0.0).normalise() * radius v3 = Vector3D(-1.0, -golden_ratio, 0.0).normalise() * radius v4 = Vector3D(1.0, -golden_ratio, 0.0).normalise() * radius v5 = Vector3D(0.0, -1.0, golden_ratio).normalise() * radius v6 = Vector3D(0.0, 1.0, golden_ratio).normalise() * radius v7 = Vector3D(0.0, -1.0, -golden_ratio).normalise() * radius v8 = Vector3D(0.0, 1.0, -golden_ratio).normalise() * radius v9 = Vector3D(golden_ratio, 0.0, -1.0).normalise() * radius v10 = Vector3D(golden_ratio, 0.0, 1.0).normalise() * radius v11 = Vector3D(-golden_ratio, 0.0, -1.0).normalise() * radius v12 = Vector3D(-golden_ratio, 0.0, 1.0).normalise() * radius vertices = [ [v1.x, v1.y, v1.z], [v2.x, v2.y, v2.z], [v3.x, v3.y, v3.z], [v4.x, v4.y, v4.z], [v5.x, v5.y, v5.z], [v6.x, v6.y, v6.z], [v7.x, v7.y, v7.z], [v8.x, v8.y, v8.z], [v9.x, v9.y, v9.z], [v10.x, v10.y, v10.z], [v11.x, v11.y, v11.z], [v12.x, v12.y, v12.z], ] triangles = [ [0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11], [1, 5, 9], [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8], [3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9], [4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1] ] # Optional - subdivision of icosohedren to increase resolution num_vertices = 12 num_triangles = 20 for i in range(subdivision_count): for j in range(num_triangles): triangle = triangles[j] # extract current triangle vertices v0_id = triangle[0] v1_id = triangle[1] v2_id = triangle[2] v0 = Vector3D(vertices[v0_id][0], vertices[v0_id][1], vertices[v0_id][2]) v1 = Vector3D(vertices[v1_id][0], vertices[v1_id][1], vertices[v1_id][2]) v2 = Vector3D(vertices[v2_id][0], vertices[v2_id][1], vertices[v2_id][2]) # subdivide with three new vertices v3 = (v0 + v1).normalise() * radius v3_id = num_vertices v4 = (v1 + v2).normalise() * radius v4_id = num_vertices + 1 v5 = (v2 + v0).normalise() * radius v5_id = num_vertices + 2 vertices.append([v3.x, v3.y, v3.z]) vertices.append([v4.x, v4.y, v4.z]) vertices.append([v5.x, v5.y, v5.z]) # ... and three new faces triangles[j] = [v0_id, v3_id, v5_id] # replace the first face triangles.append([v3_id, v1_id, v4_id]) triangles.append([v4_id, v2_id, v5_id]) triangles.append([v3_id, v4_id, v5_id]) num_vertices += 3 num_triangles += 3 vertices = np.array(vertices) triangles = np.array(triangles) if sphere.parent: to_world = sphere.to_root() else: to_world = sphere.transform # Convert vertices to positions in world coordinates for i in range(vertices.shape[0]): p = Point3D(vertices[i, 0], vertices[i, 1], vertices[i, 2]).transform(to_world) vertices[i, 0] = p.x vertices[i, 1] = p.y vertices[i, 2] = p.z return vertices, triangles
import numpy as np import matplotlib.pyplot as plt from mayavi import mlab from raysect.core import World, Point3D, Vector3D from cherab.core.math import ConstantVector3D from vita.modules.cherab import FieldlineTracer, Euler # the world scene-graph world = World() b_field = ConstantVector3D(Vector3D(0, 1.5, 0)) field_tracer = FieldlineTracer(b_field, method=Euler(step_size=0.001)) start_point = Point3D(0, 0, 0) end_point, trajectory = field_tracer.trace(world, start_point, save_trajectory=True, max_steps=1000) num_segments = len(trajectory) x = np.zeros(num_segments) y = np.zeros(num_segments) z = np.zeros(num_segments) for ith_position, position in enumerate(trajectory): x[ith_position] = position.x y[ith_position] = position.y z[ith_position] = position.z mlab.plot3d(x, y, z, tube_radius=0.0005, color=(1, 0, 0))
def load_kb1_camera(parent=None): camera_id = 'KB1' # Transforms, read from KB1 CAD model for INDIVIDUAL_BOLOMETER_ASSEMBLY # Note that the rotation angle is positive when Axis is the Z axis, and # negative when Axis is the -Z axis camera_transforms = [ translate(-1.73116, 2.59086, 3.31650) * rotate_z(123.75), translate(-3.05613, 0.60790, 3.31650) * rotate_z(168.75), translate(1.73116, -2.59086, 3.31650) * rotate_z(-56.25), translate(3.05613, -0.60790, 3.31650) * rotate_z(-11.25), ] # Transform for INDIVIDUAL_BOLOMETER_ASSEMBLY/SINGLE_BOLOMETER_ASSEMBLY/FOIL 1 # in CAD model foil_camera_transform = translate(0, 0, 18.70e-3) # Foils point downwards towards the plasma foil_orientation_transform = rotate_basis(Vector3D(0, 0, -1), Vector3D(0, 1, 0)) # Dimensions read from edge to edge (and adjacent vertices defining rounded corners) on # INDIVIDUAL_BOLOMETER_ASSEMBLY/SINGLE_BOLOMETER_ASSEMBLY/FOIL SUPPORT 1, # edges (and vertices) closest to the foil foil_width = 11e-3 foil_height = 11e-3 foil_curvature_radius = 1e-3 # KB1 does not really have a slit, per-se. The vessel functions as the # aperture. To ensure a sufficiently displaced bounding sphere for the # TargettedPixel, we'll put a dummy slit at the exit of the port through # which the camera views. Note that with the camera transform defined above, # the y axis is in the toroidal direction and the x axis in the inward # radial direction. # # The foil is not centred on the centre of the port. To measure the # displacement, the centre of the port was read from the CAD model for # KB1-1, then the vector from the foil centre to the centre of the port exit # for this channel was calculated in the foil's local coordinate system. foil_slit_transform = translate(-0.05025, 0, 1.38658) slit_width = 0.25 # slightly larger than widest point of port (~225 mm) slit_height = 0.09 # sligtly larger than length of port (~73.84 mm) num_slits = len(camera_transforms) num_foils = len(camera_transforms) bolometer_camera = BolometerCamera(name=camera_id, parent=parent) slit_objects = {} for i in range(num_slits): slit_id = '{}_Slit_#{}'.format(camera_id, i + 1) slit_transform = (camera_transforms[i] * foil_orientation_transform * foil_slit_transform * foil_camera_transform) centre_point = Point3D(0, 0, 0).transform(slit_transform) basis_x = Vector3D(1, 0, 0).transform(slit_transform) basis_y = Vector3D(0, 1, 0).transform(slit_transform) dx = slit_width dy = slit_height slit_objects[slit_id] = BolometerSlit(slit_id, centre_point, basis_x, dx, basis_y, dy, csg_aperture=True, parent=bolometer_camera) for i in range(num_foils): foil_id = '{}_CH{}_Foil'.format(camera_id, i + 1) slit_id = '{}_Slit_#{}'.format(camera_id, i + 1) foil_transform = (camera_transforms[i] * foil_orientation_transform * foil_camera_transform) centre_point = Point3D(0, 0, 0).transform(foil_transform) basis_x = Vector3D(1, 0, 0).transform(foil_transform) basis_y = Vector3D(0, 1, 0).transform(foil_transform) dx = foil_width dy = foil_height rc = foil_curvature_radius foil = BolometerFoil(foil_id, centre_point, basis_x, dx, basis_y, dy, slit_objects[slit_id], curvature_radius=rc, parent=bolometer_camera) bolometer_camera.add_foil_detector(foil) return bolometer_camera