def load_vessel_world(mesh_parts, shift_p5=False): """Load the world containing the vessel mesh parts. <mesh_parts> is a list of filenames containing mesh files in either RSM or OBJ format, which are to be loaded into the world. If shift_p5 is True, the mesh files representing the P5 coils will have a downward shift applied to them to account for the UEP sag. This is described in CD/MU/04783. Returns world, the root of the scenegraph. """ world = World() for path, _ in mesh_parts: print("importing {} ...".format(os.path.split(path)[1])) filename = os.path.split(path)[-1] name, ext = filename.split('.') if 'P5_' in path and shift_p5: p5_zshift = -0.00485 # From CD/MU/04783 transform = translate(0, 0, p5_zshift) else: transform = None if ext.lower() == 'rsm': Mesh.from_file(path, parent=world, material=AbsorbingSurface(), transform=transform, name=name) elif ext.lower() == 'obj': import_obj(path, parent=world, material=AbsorbingSurface(), name=name) else: raise ValueError("Only RSM and OBJ meshes are supported.") # Add a solid cylinder at R=0 to prevent rays finding their way through the # gaps in the centre column armour. This is only necessary for the MAST # meshes, but it does no harm to add it to MAST-U meshes too height = 6 radius = 0.1 Cylinder(radius=radius, height=height, parent=world, transform=translate(0, 0, -height / 2), material=AbsorbingSurface(), name='Blocking Cylinder') return world
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 __init__(self, slit_id, centre_point, basis_x, dx, basis_y, dy, dz=0.001, parent=None, csg_aperture=False, curvature_radius=0): # perform validation of input parameters if not isinstance(dx, (float, int)): raise TypeError("dx argument for BolometerSlit must be of type float/int.") if not dx > 0: raise ValueError("dx argument for BolometerSlit must be greater than zero.") if not isinstance(dy, (float, int)): raise TypeError("dy argument for BolometerSlit must be of type float/int.") if not dy > 0: raise ValueError("dy argument for BolometerSlit must be greater than zero.") if not isinstance(centre_point, Point3D): raise TypeError("centre_point argument for BolometerSlit must be of type Point3D.") if not isinstance(curvature_radius, (float, int)): raise TypeError("curvature_radius argument for BolometerSlit " "must be of type float/int.") if curvature_radius < 0: raise ValueError("curvature_radius argument for BolometerSlit " "must not be negative.") if not isinstance(basis_x, Vector3D): raise TypeError("The basis vectors of BolometerSlit must be of type Vector3D.") if not isinstance(basis_y, Vector3D): raise TypeError("The basis vectors of BolometerSlit must be of type Vector3D.") self._centre_point = centre_point self._basis_x = basis_x.normalise() self.dx = dx self._basis_y = basis_y.normalise() self.dy = dy self.dz = dz self._curvature_radius = curvature_radius # NOTE - target primitive and aperture surface cannot be co-incident otherwise numerics will cause Raysect # to be blind to one of the two surfaces. slit_normal = basis_x.cross(basis_y) transform = translate(centre_point.x, centre_point.y, centre_point.z) * rotate_basis(slit_normal, basis_y) super().__init__(parent=parent, transform=transform, name=slit_id) self.target = Box(lower=Point3D(-dx/2*1.01, -dy/2*1.01, -dz/2), upper=Point3D(dx/2*1.01, dy/2*1.01, dz/2), transform=None, material=NullMaterial(), parent=self, name=slit_id+' - target') self._csg_aperture = None self.csg_aperture = csg_aperture # round off the detector corners, if applicable if self._curvature_radius > 0: mask_corners(self)
def main(): camera = sys.argv[1] try: istart = int(sys.argv[2]) iend = int(sys.argv[3]) except (IndexError, ValueError): istart = None iend = None shift_p5 = False camera_transform = None if camera in ("Outer", "Upper"): MESH_PARTS = MASTU_FULL_MESH grid = "sxdl" elif camera in ("Poloidal", "Tangential"): MESH_PARTS = MASTU_FULL_MESH shift_p5 = True grid = "core" elif camera in ("PoloidalHighRes", "TangentialHighRes"): MESH_PARTS = MASTU_FULL_MESH shift_p5 = True grid = "core_high_res" # Trim off the HighRes suffix from the camera camera = camera[:-7] elif camera == "Poloidal-MAST": MESH_PARTS = MAST_FULL_MESH # MAST sensors were displaced by 100mm in the y direction compared with # the main chamber sensors at the beginning of MAST-U operation camera_transform = translate(0, 0.1, 0) grid = "core" else: raise ValueError("The following cameras are supported: " "'Outer', 'Upper', 'Poloidal', 'Tangential', " "'PoloidalHighRes', 'TangentialHighRes', " "'Poloidal-MAST'") calculate_and_save_sensitivities(grid, camera, MESH_PARTS, istart, iend, shift_p5, camera_transform)
def __init__(self, slit_id, centre_point, basis_x, dx, basis_y, dy, dz=0.001, parent=None, csg_aperture=False): self._centre_point = centre_point self._basis_x = basis_x.normalise() self.dx = dx self._basis_y = basis_y.normalise() self.dy = dy self.dz = dz # NOTE - target primitive and aperture surface cannot be co-incident otherwise numerics will cause Raysect # to be blind to one of the two surfaces. slit_normal = basis_x.cross(basis_y) transform = translate(centre_point.x, centre_point.y, centre_point.z) * rotate_basis( slit_normal, basis_y) super().__init__(parent=parent, transform=transform, name=slit_id) self.target = Box(lower=Point3D(-dx / 2 * 1.01, -dy / 2 * 1.01, -dz / 2), upper=Point3D(dx / 2 * 1.01, dy / 2 * 1.01, dz / 2), transform=None, material=NullMaterial(), parent=self, name=slit_id + ' - target') self._csg_aperture = None self.csg_aperture = csg_aperture
pini_8_1 = load_pini_from_ppf(PULSE, '8.1', plasma, adas, attenuation_instructions, beam_emission_instructions, world) pini_8_2 = load_pini_from_ppf(PULSE, '8.2', plasma, adas, attenuation_instructions, beam_emission_instructions, world) pini_8_5 = load_pini_from_ppf(PULSE, '8.5', plasma, adas, attenuation_instructions, beam_emission_instructions, world) pini_8_6 = load_pini_from_ppf(PULSE, '8.6', plasma, adas, attenuation_instructions, beam_emission_instructions, world) # ############################### OBSERVATION ############################### # print('Observation') los = Point3D(4.22950, -0.791368, 0.269430) direction = Vector3D(-0.760612, -0.648906, -0.0197396).normalise() los = los + direction * 0.9 up = Vector3D(0, 0, 1) camera = PinholeCamera( (512, 512), fov=45, parent=world, transform=translate(los.x, los.y, los.z) * rotate_basis(direction, up)) camera.pixel_samples = 50 camera.spectral_bins = 15 camera.observe()
export_vtk(mesh, basename + '.vtk', triangle_data=triangle_data) samples = 1000000 sphere_radius = 0.01 min_wl = 400 max_wl = 401 # set-up scenegraph world = World() emitter = Sphere(radius=sphere_radius, parent=world, transform=rotate_x(-90) * translate(-0.05409, -0.01264, 0.10064)) base_path = os.path.split(os.path.realpath(__file__))[0] mesh = import_obj(os.path.join(base_path, "../resources/stanford_bunny.obj"), material=AbsorbingSurface(), parent=world, flip_normals=True) power = PowerPipeline0D(accumulate=False) observer = MeshPixel(mesh, pipelines=[power], parent=world, min_wavelength=min_wl, max_wavelength=max_wl, spectral_bins=1, pixel_samples=samples,
c6_species = Species(carbon, 6, c6_distribution) #define plasma parameters - electron distribution, impurity composition and B field from EFIT plasma.electron_distribution = e_distribution plasma.composition = [d1_species, c6_species] plasma.b_field = VectorAxisymmetricMapper(equil_time_slice.b_field) sigma = 0.25 integration_step = 0.02 #define the plasma geometry plasma.integrator.step = integration_step plasma.integrator.min_samples = 1000 plasma.atomic_data = adas plasma.geometry = Cylinder(sigma * 2, sigma * 10.0) plasma.geometry_transform = translate(0, -sigma * 5.0, 0) * rotate(0, 90, 0) # # # ########################### NBI CONFIGURATION ############################# # #Geometry south_pos = Point3D(0.188819939, -6.88824321, 0.0) #Position of PINI grid center duct_pos = Point3D(0.539, -1.926, 0.00) #position of beam duct south_pos.vector_to(duct_pos) #beam vector beam_axis = south_pos.vector_to(duct_pos).normalise() up = Vector3D(0, 0, 1) beam_rotation = rotate_basis(beam_axis, up) beam_position = translate(south_pos.x, south_pos.y, south_pos.z)
from mayavi import mlab from raysect.core import translate, Point3D, rotate_basis, Vector3D, rotate from raysect.optical import World from raysect.primitive import Box, Sphere, Cylinder, Cone from raysect.primitive import Sphere, Mesh, Intersect, Subtract, Union from raysect_mayavi import visualise_scenegraph ######################################################################################################################## # Spheres s1 = Sphere(0.5, transform=translate(-0.25, 0, 0), name='s1') s2 = Sphere(0.5, transform=translate(0.25, 0, 0), name='s2') world = World() Union(s1, s2, parent=world) visualise_scenegraph(world) input('pause...') world = World() Intersect(s1, s2, parent=world) visualise_scenegraph(world) input('pause...') world = World() Subtract(s1, s2, parent=world) visualise_scenegraph(world) input('pause...') ########################################################################################################################
if x >= 0: return True return False def t2(self, x): if x <= 0: return True return False # OPERATION = Union() OPERATION = Intersection() # OPERATION = Difference() world = World() s1 = Sphere(0.5, transform=translate(-0.25, 0, 0), name='s1') s2 = Sphere(0.5, transform=translate(0.25, 0, 0), name='s2') s1_vertices, s1_triangles = to_mesh(s1) n_s1_vertices = s1_vertices.shape[0] n_s1_triangles = s1_triangles.shape[0] s1_mesh = Mesh(vertices=s1_vertices, triangles=s1_triangles, smoothing=False) print() print('n_s1_triangles', n_s1_triangles) s2_vertices, s2_triangles = to_mesh(s2) n_s2_vertices = s2_vertices.shape[0] n_s2_triangles = s2_triangles.shape[0] s2_mesh = Mesh(vertices=s2_vertices, triangles=s2_triangles, smoothing=False) print('n_s2_triangles', n_s2_triangles)
r, _, z, t_samples = sample3d(h0.distribution.density, (-1, 2, 200), (0, 0, 1), (-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)
from cherab.core.atomic import hydrogen from cherab.core.model import SingleRayAttenuator from cherab.tools.plasmas.slab import build_slab_plasma from cherab.openadas import OpenADAS # create atomic data source adas = OpenADAS(permit_extrapolation=True) world = World() # PLASMA ---------------------------------------------------------------------- plasma = build_slab_plasma(peak_density=5e19, world=world) integration_step = 0.0025 beam_transform = translate(-0.000001, 0.0, 0) * rotate_basis( Vector3D(1, 0, 0), Vector3D(0, 0, 1)) beam_full = Beam(parent=world, transform=beam_transform) beam_full.plasma = plasma beam_full.atomic_data = adas beam_full.energy = 100000 beam_full.power = 3e6 beam_full.element = hydrogen 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.integrator.step = integration_step beam_full.integrator.min_samples = 10
import matplotlib.pyplot as plt from raysect.core import Point3D, Vector3D, rotate_basis, translate, Ray as CoreRay from raysect.core.math.sampler import DiskSampler3D, RectangleSampler3D, TargettedHemisphereSampler from raysect.optical import World from raysect.primitive import Box, Cylinder, Subtract from raysect.optical.material import AbsorbingSurface, NullMaterial R_2_PI = 1 / (2 * np.pi) world = World() # Setup pinhole target_plane = Box(Point3D(-10, -10, -0.000001), Point3D(10, 10, 0.000001)) hole = Cylinder(0.001, 0.001, transform=translate(0, 0, -0.0005)) pinhole = Subtract(target_plane, hole, parent=world, material=AbsorbingSurface()) target = Cylinder(0.0012, 0.001, transform=translate(0, 0, -0.0011), parent=world, material=NullMaterial()) def analytic_etendue(area_det, area_slit, distance, alpha, gamma): return area_det * area_slit * np.cos(alpha/360 * (2*np.pi)) * np.cos(gamma/360 * (2*np.pi)) / distance**2 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))
max_wl = 401 # sanity check! if cube_size <= 2 * sphere_radius: raise ValueError("The cube dimensions must be larger that the sphere.") # set-up scenegraph world = World() emitter = Sphere(radius=sphere_radius, parent=world) # The observer plane covers 1 side of a cube - to work out total power, multiply result by 6 power = PowerPipeline0D(accumulate=False) observing_plane = Pixel([power], x_width=cube_size, y_width=cube_size, min_wavelength=min_wl, max_wavelength=max_wl, spectral_bins=1, pixel_samples=samples, parent=world, transform=rotate(0, 0, 0)*translate(0, 0, -cube_size / 2)) # Emitter is a sphere volume emitter located at the origin # Volume of the sphere is 4/3 * Pi * r^3, emission over 4 * pi # UnityVolumeEmitter emits 1W/str/m^3/ x nm, where x is the wavelength interval, integrated over length print("Starting observations with volume emitter...") calculated_volume_emission = 16 / 3 * pi**2 * sphere_radius**3 * (max_wl - min_wl) emitter.material = UnityVolumeEmitter() observing_plane.observe() measured_volume_emission = 6 * power.value.mean measured_volume_error = 6 * power.value.error() # Emitter is a sphere surface emitter located at the origin # Surface area of the sphere is 4 * Pi * r^2, lambert emitter
ExcitationLine(d_beta), ExcitationLine(d_gamma), ExcitationLine(d_delta), ExcitationLine(d_epsilon), RecombinationLine(d_alpha), RecombinationLine(d_beta), RecombinationLine(d_gamma), RecombinationLine(d_delta), RecombinationLine(d_epsilon) ] # ########################### NBI CONFIGURATION ############################# # # BEAM ------------------------------------------------------------------------ beam = Beam(parent=world, transform=translate(1.0, 0.0, 0) * rotate(90, 0, 0)) beam.plasma = plasma beam.atomic_data = adas beam.energy = 65000 beam.power = 3e6 beam.element = elements.deuterium beam.sigma = 0.025 beam.divergence_x = 0.5 beam.divergence_y = 0.5 beam.length = 3.0 beam.attenuator = SingleRayAttenuator(clamp_to_zero=True) beam.models = [ BeamCXLine(Line(elements.helium, 1, (4, 3))), BeamCXLine(Line(elements.helium, 1, (6, 4))), BeamCXLine(Line(elements.carbon, 5, (8, 7))), BeamCXLine(Line(elements.carbon, 5, (9, 8))),
emitting_sphere_radius = 0.5 collection_sphere_radius = 5 min_wl = 400 max_wl = 401 # sanity check! if collection_sphere_radius <= emitting_sphere_radius: raise ValueError("The collecting sphere radius must be larger that the emitting sphere.") power = PowerPipeline0D(accumulate=False) # set-up scenegraph world = World() emitter = Sphere(radius=emitting_sphere_radius, parent=world, transform=translate(4, 0, 0)) observer = ObservingSphere([power], radius=collection_sphere_radius, min_wavelength=min_wl, max_wavelength=max_wl, spectral_bins=1, pixel_samples=samples, parent=world) # Emitter is a sphere volume emitter located at the origin # Volume of the sphere is 4/3 * Pi * r^3, emission over 4 * pi # UnityVolumeEmitter emits 1W/str/m^3/ x nm, where x is the wavelength interval, integrated over length print("Starting observations with volume emitter...") calculated_volume_emission = (4 * pi) * (4/3 * pi * emitting_sphere_radius**3) * (max_wl - min_wl) emitter.material = UnityVolumeEmitter() # emitter.material = UniformVolumeEmitter(ConstantSF(1.0))
def make_cherab_image(self): """ run cherab to generate the synthetic spectral cube :return: """ if self.radiance is not NotImplemented: self.radiance.close() if self.spectral_radiance is not NotImplemented: self.spectral_radiance.close() import_mastu_mesh(self.world, ) # first, define camera, calculate view vectors and calculate ray lengths pipeline_spectral = SpectralPowerPipeline2D() pipeline_spectral_rad = SpectralRadiancePipeline2D() pipelines = [pipeline_spectral, pipeline_spectral_rad, ] camera = PinholeCamera(self.sensor_format_ds, fov=self.fov, pipelines=pipelines, parent=self.world) # orient and position the camera init_view_vector, init_up_vector = Vector3D(0, 0, 1), Vector3D(0, 1, 0) axle_1 = init_view_vector.cross(self.view_vector) angle = init_view_vector.angle(self.view_vector) t_1 = rotate_vector(angle, axle_1) final_up_vector = rotate_vector(-90, axle_1) * self.view_vector intermediate_up_vector = t_1 * init_up_vector angle_between = intermediate_up_vector.angle(final_up_vector) t_2 = rotate_vector(-angle_between, self.view_vector) camera.transform = translate(self.pupil_point[0], self.pupil_point[1], self.pupil_point[2], ) * t_2 * t_1 vector_xyz = np.arange(3) vector_xyz = xr.DataArray(vector_xyz, coords=(vector_xyz, ), dims=('vector_xyz',), name='vector_xyz', ) # calculating the pixel view directions view_vectors = xr.combine_nested( [xr.zeros_like(self.x_pixel_ds + self.y_pixel_ds) + self.view_vector[i] for i in [0, 1, 2, ]], concat_dim=(vector_xyz,), ) view_vectors = view_vectors.rename('view_vectors') def v3d2da(v3d): """ raysect Vector3D to xarray DataArray :param v3d: :return: """ da = np.array([v3d.x, v3d.y, v3d.z, ]) da = xr.DataArray(da, coords=(np.arange(3),), dims=('vector_xyz',), ) return da # basis unit vectors defining camera view -- v_z is forward and v_y is up v_y = final_up_vector.normalise() v_x = self.view_vector.cross(v_y).normalise() v_z = self.view_vector.normalise() v_x, v_y, v_z = [v3d2da(i) for i in [v_x, v_y, v_z, ]] # FOV defines the widest view, with pixels defined as square. sensor_aspect = self.sensor_format[1] / self.sensor_format[0] if sensor_aspect > 1: fov_v = self.fov fov_h = self.fov / sensor_aspect elif sensor_aspect == 1: fov_v = fov_h = self.fov elif sensor_aspect < 1: fov_h = self.fov fov_v = self.fov * sensor_aspect else: raise Exception() pixel_projection = 2 * np.tan(fov_h * np.pi / 360) / self.sensor_format[0] view_vectors = view_vectors + (v_x * (self.x_pixel_ds - self.sensor_format[0] / 2 + 0.5) * pixel_projection) + \ (v_y * (self.y_pixel_ds - self.sensor_format[1] / 2 + 0.5) * pixel_projection) if self.verbose: print('--status: calculating ray lengths') # TODO there has to be a better way of doing this?! ray_lengths = xr.DataArray(np.zeros(self.sensor_format_ds), dims=('x', 'y', ), coords=(self.x_ds, self.y_ds, )) for idx_x, x_pixel in enumerate(self.x_pixel_ds.values): if self.verbose and idx_x % 10 == 0: print('x =', str(x_pixel)) for idx_y, y_pixel in enumerate(self.y_pixel_ds.values): direction = Vector3D(*list(view_vectors.isel(x=idx_x, y=idx_y, ).values)) intersections = [] for p in self.world.primitives: intersection = p.hit(CoreRay(self.pupil_point, direction, )) if intersection is not None: intersections.append(intersection) # find the intersection corresponding to the shortest ray length no_intersections = len(intersections) if no_intersections == 0: ray_lengths.values[idx_x, idx_y] = 3 else: ray_lengths.values[idx_x, idx_y] = min([i.ray_distance for i in intersections if i.primitive.name != 'Plasma Geometry']) camera.spectral_bins = 40 camera.pixel_samples = 10 camera.min_wavelength = self.wl_min_nm camera.max_wavelength = self.wl_max_nm camera.quiet = not self.verbose camera.observe() # output to netCDF via xarray wl = pipeline_spectral.wavelengths wl = xr.DataArray(wl, coords=(wl, ), dims=('wavelength', )) * 1e-9 # ( m ) spec_power_ds = pipeline_spectral.frame.mean * 1e9 # converting units from (W/nm) --> (W/m) spec_radiance_ds = pipeline_spectral_rad.frame.mean * 1e9 coords = (self.x_ds, self.y_ds, wl, ) dims = ('x', 'y', 'wavelength', ) name = 'spec_power' attrs = {'units': 'W/m^2/str/m'} spec_power_ds = xr.DataArray(np.flip(spec_power_ds, axis=1), coords=coords, dims=dims, name=name, attrs=attrs, ) spec_radiance_ds = xr.DataArray(np.flip(spec_radiance_ds, axis=1, ), coords=coords, dims=dims, name=name, attrs=attrs, ) # calculate the centre-of-mass wavelength radiance_ds = spec_power_ds.integrate(dim='wavelength').assign_attrs({'units': 'W/m^2/str', }) ds_ds = xr.Dataset({'spectral_radiance_ds': spec_radiance_ds, 'radiance_ds': radiance_ds, 'view_vectors_ds': view_vectors, 'ray_lengths_ds': ray_lengths }) x_p_y = self.x + self.y spec_power = spec_power_ds.interp_like(x_p_y) / self.cherab_down_sample # to conserve power ds = xr.Dataset({'spectral_radiance': spec_power, }) ds_ds.to_netcdf(self.fpath_ds, mode='w', ) ds.to_netcdf(self.fpath, mode='w', )
start = time.time() for i, detector in enumerate(aug_wall_detectors): print() print("detector {}".format(i)) y_width = detector[2] centre_point = detector[3] normal_vector = detector[4] y_vector = detector[5] pixel_area = X_WIDTH * y_width power_data = PowerPipeline0D() pixel_transform = translate(centre_point.x, centre_point.y, centre_point.z) * rotate_basis( normal_vector, y_vector) pixel = Pixel([power_data], x_width=X_WIDTH, y_width=y_width, name='pixel-{}'.format(i), spectral_bins=1, transform=pixel_transform, parent=world, pixel_samples=500) pixel.observe() powers.append(power_data.value.mean / pixel_area) power_errors.append(power_data.value.error() / pixel_area) detector_numbers.append(i)
r, _, z, t_samples = sample3d(h0_dens, (-1, 2, 200), (0, 0, 1), (-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 # cVI_8_7 = Line(carbon, 5, (8, 7)) cVI_10_8 = Line(carbon, 5, (10, 8)) 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(cVI_8_7), BeamCXLine(cVI_10_8)]
def __init__(self, detector_id, centre_point, basis_x, dx, basis_y, dy, slit, parent=None, units="Power", accumulate=False, curvature_radius=0): # perform validation of input parameters if not isinstance(dx, (float, int)): raise TypeError("dx argument for BolometerFoil must be of type float/int.") if not dx > 0: raise ValueError("dx argument for BolometerFoil must be greater than zero.") if not isinstance(dy, (float, int)): raise TypeError("dy argument for BolometerFoil must be of type float/int.") if not dy > 0: raise ValueError("dy argument for BolometerFoil must be greater than zero.") if not isinstance(slit, BolometerSlit): raise TypeError("slit argument for BolometerFoil must be of type BolometerSlit.") if not isinstance(centre_point, Point3D): raise TypeError("centre_point argument for BolometerFoil must be of type Point3D.") if not isinstance(curvature_radius, (float, int)): raise TypeError("curvature_radius argument for BolometerFoil " "must be of type float/int.") if curvature_radius < 0: raise ValueError("curvature_radius argument for BolometerFoil " "must not be negative.") if not isinstance(basis_x, Vector3D): raise TypeError("The basis vectors of BolometerFoil must be of type Vector3D.") if not isinstance(basis_y, Vector3D): raise TypeError("The basis vectors of BolometerFoil must be of type Vector3D.") self._centre_point = centre_point self._basis_x = basis_x.normalise() self._basis_y = basis_y.normalise() self._normal_vec = self._basis_x.cross(self._basis_y) self._slit = slit self._foil_to_slit_vec = self._centre_point.vector_to(self._slit.centre_point).normalise() self._curvature_radius = curvature_radius self.units = units # setup root bolometer foil transform translation = translate(self._centre_point.x, self._centre_point.y, self._centre_point.z) rotation = rotate_basis(self._normal_vec, self._basis_y) if self.units == "Power": pipeline = PowerPipeline0D(accumulate=accumulate) elif self.units == "Radiance": pipeline = RadiancePipeline0D(accumulate=accumulate) else: raise ValueError("The units argument of BolometerFoil must be one of 'Power' or 'Radiance'.") super().__init__([slit.target], targetted_path_prob=1.0, pipelines=[pipeline], pixel_samples=1000, x_width=dx, y_width=dy, spectral_bins=1, quiet=True, parent=parent, transform=translation * rotation, name=detector_id) # round off the detector corners, if applicable if self._curvature_radius > 0: mask_corners(self)
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
import numpy as np import matplotlib.pyplot as plt from raysect.core import Point3D, Vector3D, rotate_basis, translate, Ray as CoreRay from raysect.core.math.sampler import DiskSampler3D, RectangleSampler3D, TargettedHemisphereSampler from raysect.optical import World from raysect.primitive import Box, Cylinder, Subtract from raysect.optical.material import AbsorbingSurface, NullMaterial R_2_PI = 1 / (2 * np.pi) world = World() # Setup pinhole target_plane = Box(Point3D(-10, -10, -0.000001), Point3D(10, 10, 0.000001)) hole = Cylinder(0.001, 0.001, transform=translate(0, 0, -0.0005)) pinhole = Subtract(target_plane, hole, parent=world, material=AbsorbingSurface()) target = Cylinder(0.0012, 0.001, transform=translate(0, 0, -0.0011), parent=world, material=NullMaterial()) def analytic_etendue(area_det, area_slit, distance, alpha, gamma): return area_det * area_slit * np.cos(alpha / 360 * (2 * np.pi)) * np.cos(
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
import numpy as np from mayavi import mlab from raysect.core import translate, Point3D from raysect.optical import World from raysect.primitive import Sphere, Mesh from raysect_mayavi.primitives import to_mesh from raysect_mayavi.primitives.triangle import triangle3d_intersects_triangle3d # OPERATION = 'UNION' # OPERATION = 'INTERSECTION' OPERATION = 'DIFFERENCE' world = World() s1 = Sphere(0.5, transform=translate(-0.25, 0, 0), name='s1') s2 = Sphere(0.5, transform=translate(0.25, 0, 0), name='s2') s1_vertices, s1_triangles = to_mesh(s1) n_s1_vertices = s1_vertices.shape[0] n_s1_triangles = s1_triangles.shape[0] s1_mesh = Mesh(vertices=s1_vertices, triangles=s1_triangles, smoothing=False) print() print('n_s1_triangles', n_s1_triangles) s2_vertices, s2_triangles = to_mesh(s2) n_s2_vertices = s2_vertices.shape[0] n_s2_triangles = s2_triangles.shape[0] s2_mesh = Mesh(vertices=s2_vertices, triangles=s2_triangles, smoothing=False) print('n_s2_triangles', n_s2_triangles)
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
triangle_data = {'PowerDensity': power_density, 'PowerDensityError': error} export_vtk(mesh, basename + '.vtk', triangle_data=triangle_data) samples = 1000000 sphere_radius = 0.01 min_wl = 400 max_wl = 401 # set-up scenegraph world = World() emitter = Sphere(radius=sphere_radius, parent=world, transform=rotate_x(-90)*translate(-0.05409, -0.01264, 0.10064)) base_path = os.path.split(os.path.realpath(__file__))[0] mesh = import_obj(os.path.join(base_path, "../resources/stanford_bunny.obj"), material=AbsorbingSurface(), parent=world, flip_normals=True) power = PowerPipeline0D(accumulate=False) observer = MeshPixel(mesh, pipelines=[power], parent=world, min_wavelength=min_wl, max_wavelength=max_wl, spectral_bins=1, pixel_samples=samples, surface_offset=1E-6) print("Starting observations with volume emitter...") calculated_volume_emission = 16 / 3 * pi**2 * sphere_radius**3 * (max_wl - min_wl)
if fibre_distance <= sphere_radius: raise ValueError("The fibre must be outside the sphere.") cone_radius_on_axis = np.tan(np.deg2rad(fibre_half_angle)) * fibre_distance if cone_radius_on_axis <= sphere_radius + fibre_radius: raise ValueError("The fibre's acceptance angle must be large enough to see all of the sphere's radiance.") # set-up scenegraph world = World() emitter = Sphere(radius=sphere_radius, parent=world, material=UnityVolumeEmitter()) # The observer plane covers 1 side of a cube - to work out total power, multiply result by 6 fibre_power = PowerPipeline0D(accumulate=False) optical_fibre = FibreOptic([fibre_power], acceptance_angle=fibre_half_angle, radius=fibre_radius, min_wavelength=min_wl, max_wavelength=max_wl, spectral_bins=1, pixel_samples=samples, parent=world, transform=rotate(0, 0, 0)*translate(0, 0, -fibre_distance)) approximate_fraction_of_sphere = np.pi*fibre_radius**2 / (4 * np.pi * fibre_distance**2) # Emitter is a sphere volume emitter located at the origin # Volume of the sphere is 4/3 * Pi * r^3, emission over 4 * pi # UnityVolumeEmitter emits 1W/str/m^3/ x nm, where x is the wavelength interval, integrated over length print("Starting observations with volume emitter...") calculated_volume_emission = 16 / 3 * pi**2 * sphere_radius**3 * (max_wl - min_wl) optical_fibre.observe() measured_volume_emission = fibre_power.value.mean / approximate_fraction_of_sphere
import vtk from raysect.core import translate from raysect_vtk.utility import convert_to_vtk_transform sphereSource1 = vtk.vtkSphereSource() sphereSource1.SetPhiResolution(50) sphereSource1.SetThetaResolution(50) sphereSource1.Update() transform_filter1 = vtk.vtkTransformPolyDataFilter() transform_filter1.SetInputConnection(sphereSource1.GetOutputPort()) transform_filter1.SetTransform(convert_to_vtk_transform(translate(0.25, 0, 0))) transform_filter1.Update() sphere1Tri = vtk.vtkTriangleFilter() sphere1Tri.SetInputData(transform_filter1.GetOutput()) sphereSource2 = vtk.vtkSphereSource() sphereSource2.SetPhiResolution(50) sphereSource2.SetThetaResolution(50) sphereSource2.Update() transform_filter2 = vtk.vtkTransformPolyDataFilter() transform_filter2.SetInputConnection(sphereSource2.GetOutputPort()) transform_filter2.SetTransform(convert_to_vtk_transform(translate(-0.25, 0, 0))) transform_filter2.Update() sphere2Tri = vtk.vtkTriangleFilter() sphere2Tri.SetInputData(transform_filter2.GetOutput()) booleanOperation1 = vtk.vtkBooleanOperationPolyDataFilter() booleanOperation1.SetOperationToIntersection()
def mask_corners(element): """ Support detectors with rounded corners, by producing a mask to cover the corners. The mask is produced by placing thin rectangles of side element.curvature_radius at each corner, and then cylinders of radius element.curvature_radius centred on the inner vertex of those rectangles. Then each corner of the mask is the part of the rectangle not covered by the cylinder. The curvature radius should be given in units of metres. """ # Make the mask very (but not infinitely) thin, so that raysect # can actually detect that it's there. We'll work in the local # coordinate system of the element, with dx=width, dy=height, # dz=depth. dz = 1e-6 rc = element.curvature_radius # Shorthand try: dx = element.x_width dy = element.y_width except AttributeError: dx = element.dx dy = element.dy # Create a box and a cylinder of the appropriate size. # Then position copies of these at each corner. box_template = Box(Point3D(0, 0, 0), Point3D(rc, rc, dz)) cylinder_template = Cylinder(rc, dz) top_left_box = box_template.instance( transform=translate(-dx / 2, dy / 2 - rc, 0), ) top_left_cylinder = cylinder_template.instance( transform=translate(-dx / 2 + rc, dy / 2 - rc, 0), ) top_left_mask = Subtract(top_left_box, Intersect(top_left_box, top_left_cylinder)) top_right_box = box_template.instance( transform=translate(dx / 2 - rc, dy / 2 - rc, 0), ) top_right_cylinder = cylinder_template.instance( transform=translate(dx / 2 - rc, dy / 2 - rc, 0), ) top_right_mask = Subtract(top_right_box, Intersect(top_right_box, top_right_cylinder)) bottom_right_box = box_template.instance( transform=translate(dx / 2 - rc, -dy / 2, 0), ) bottom_right_cylinder = cylinder_template.instance( transform=translate(dx / 2 - rc, -dy / 2 + rc, 0), ) bottom_right_mask = Subtract(bottom_right_box, Intersect(bottom_right_box, bottom_right_cylinder)) bottom_left_box = box_template.instance( transform=translate(-dx / 2, -dy / 2, 0), ) bottom_left_cylinder = cylinder_template.instance( transform=translate(-dx / 2 + rc, -dy / 2 + rc, 0), ) bottom_left_mask = Subtract(bottom_left_box, Intersect(bottom_left_box, bottom_left_cylinder)) # The foil mask is the sum of all 4 of these corner shapes mask = functools.reduce(Union, (top_left_mask, top_right_mask, bottom_right_mask, bottom_left_mask)) mask.material = AbsorbingSurface() mask.transform = translate(0, 0, dz) mask.name = element.name + ' - rounded edges mask' mask.parent = element
from raysect.optical import World from raysect.optical.observer import PinholeCamera, SightLine, PowerPipeline0D, SpectralPowerPipeline0D from cherab.core.atomic import lithium, Line from cherab.tools.plasmas.slab import build_slab_plasma from renate.cherab_models import RenateBeamEmissionLine, RenateBeam world = World() # PLASMA ---------------------------------------------------------------------- plasma = build_slab_plasma(peak_density=5e19, world=world) # BEAM SETUP ------------------------------------------------------------------ integration_step = 0.0025 beam_transform = translate(-0.5, 0.0, 0) * rotate_basis( Vector3D(1, 0, 0), Vector3D(0, 0, 1)) line = Line(lithium, 0, ('2p', '2s')) beam = RenateBeam(parent=world, transform=beam_transform) beam.plasma = plasma beam.energy = 60000 beam.power = 1e5 beam.element = lithium beam.temperature = 30 beam.sigma = 0.03 beam.divergence_x = 0.5 beam.divergence_y = 0.5 beam.length = 3.0 beam.models = [RenateBeamEmissionLine(line)] beam.integrator.step = integration_step
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
diameter = 1 center_thickness = 0.5 front_curvature = 2 back_curvature = 2 biconvex_lens = BiConvex(diameter, center_thickness, front_curvature, back_curvature, parent=world) biconcave_lens = BiConcave(diameter, center_thickness, front_curvature, back_curvature, parent=world, transform=translate(0, 0, 1)) planoconvex_lens = PlanoConvex(diameter, center_thickness, front_curvature, parent=world, transform=translate(0, 0, 2)) planoconcave_lens = PlanoConcave(diameter, center_thickness, front_curvature, parent=world, transform=translate(0, 0, 3)) meniscus_lens = Meniscus(diameter, center_thickness, front_curvature, back_curvature, parent=world,
# set-up scenegraph world = World() emitter = Sphere(radius=sphere_radius, parent=world) # The observer plane covers 1 side of a cube - to work out total power, multiply result by 6 power = PowerPipeline0D(accumulate=False) observing_plane = Pixel([power], x_width=cube_size, y_width=cube_size, min_wavelength=min_wl, max_wavelength=max_wl, spectral_bins=1, pixel_samples=samples, parent=world, transform=rotate(0, 0, 0) * translate(0, 0, -cube_size / 2)) # Emitter is a sphere volume emitter located at the origin # Volume of the sphere is 4/3 * Pi * r^3, emission over 4 * pi # UnityVolumeEmitter emits 1W/str/m^3/ x nm, where x is the wavelength interval, integrated over length print("Starting observations with volume emitter...") calculated_volume_emission = 16 / 3 * pi**2 * sphere_radius**3 * (max_wl - min_wl) emitter.material = UnityVolumeEmitter() observing_plane.observe() measured_volume_emission = 6 * power.value.mean measured_volume_error = 6 * power.value.error() # Emitter is a sphere surface emitter located at the origin
from cherab.tools.plasmas.slab import build_slab_plasma from renate.cherab_models import RenateBeamEmissionLine, RenateBeam world = World() # PLASMA ---------------------------------------------------------------------- plasma = build_slab_plasma(peak_density=5e19, world=world) plasma.b_field = ConstantVector3D(Vector3D(0, 0.6, 0)) # BEAM SETUP ------------------------------------------------------------------ integration_step = 0.0025 beam_transform = translate(-0.5, 0.0, 0) * rotate_basis(Vector3D(1, 0, 0), Vector3D(0, 0, 1)) line = Line(hydrogen, 0, (3, 2)) beam = RenateBeam(parent=world, transform=beam_transform) beam.plasma = plasma beam.energy = 100000 beam.power = 3e6 beam.element = hydrogen beam.temperature = 30 beam.sigma = 0.05 beam.divergence_x = 0. beam.divergence_y = 0. beam.length = 3.0 beam.models = [RenateBeamEmissionLine(line)] beam.integrator.step = integration_step beam.integrator.min_samples = 10