Beispiel #1
0
def generate_annulus_mesh_segments(lower_corner, upper_corner, number_segments, world, material=None):
    """
    Generates an annulus from many smaller mesh segments.

    Used for calculating sensitivity matrices for poloidal inversion grids.

    :param Point2D lower_corner: the lower corner of the poloidal 2D cell.
    :param Point2D upper_corner: the upper corner of the poloidal 2D cell.
    :param int number_segments: The number of angular mesh segments used to build the annulus.
    :param World world: The scene-graph to which the annulus will be attached.
    :return: Node holding all the annulus segment primitives.
    :rtype: Node
    """

    material = material or UnityVolumeEmitter()

    annulus_node = Node(parent=world)

    theta_adjusted = 360 / number_segments

    # Set of points in x-z plane
    p1a = Point3D(lower_corner.x, 0, lower_corner.y)  # corresponds to lower corner is x-z plane
    p2a = Point3D(lower_corner.x, 0, upper_corner.y)
    p3a = Point3D(upper_corner.x, 0, upper_corner.y)  # corresponds to upper corner in x-z plane
    p4a = Point3D(upper_corner.x, 0, lower_corner.y)

    # Set of points rotated away from x-z plane
    p1b = p1a.transform(rotate_z(theta_adjusted))
    p2b = p2a.transform(rotate_z(theta_adjusted))
    p3b = p3a.transform(rotate_z(theta_adjusted))
    p4b = p4a.transform(rotate_z(theta_adjusted))

    vertices = [[p1a.x, p1a.y, p1a.z], [p2a.x, p2a.y, p2a.z],
                [p3a.x, p3a.y, p3a.z], [p4a.x, p4a.y, p4a.z],
                [p1b.x, p1b.y, p1b.z], [p2b.x, p2b.y, p2b.z],
                [p3b.x, p3b.y, p3b.z], [p4b.x, p4b.y, p4b.z]]

    triangles = [[1, 0, 3], [1, 3, 2],  # front face (x-z)
                 [7, 4, 5], [7, 5, 6],  # rear face (rotated out of x-z plane)
                 [5, 1, 2], [5, 2, 6],  # top face (x-y plane)
                 [3, 0, 4], [3, 4, 7],  # bottom face (x-y plane)
                 [4, 0, 5], [1, 5, 0],  # inner face (y-z plane)
                 [2, 3, 7], [2, 7, 6]]  # outer face (y-z plane)

    base_segment = Mesh(vertices=vertices, triangles=triangles, smoothing=False)

    # Construct annulus by duplicating and rotating base segment.
    for i in range(number_segments):
        theta_rotation = theta_adjusted * i
        segment = Mesh(instance=base_segment, transform=rotate_z(theta_rotation),
                       material=material, parent=annulus_node)

    return annulus_node
def generate_annulus_mesh_segments(lower_corner, upper_corner, theta_width,
                                   theta_offset, world):

    material = UnityVolumeEmitter()

    # Set of points in x-z plane
    p1a = Point3D(lower_corner.x, 0,
                  lower_corner.y)  # corresponds to lower corner is x-z plane
    p2a = Point3D(lower_corner.x, 0, upper_corner.y)
    p3a = Point3D(upper_corner.x, 0,
                  upper_corner.y)  # corresponds to upper corner in x-z plane
    p4a = Point3D(upper_corner.x, 0, lower_corner.y)

    # Set of points rotated away from x-z plane
    p1b = p1a.transform(rotate_z(theta_width))
    p2b = p2a.transform(rotate_z(theta_width))
    p3b = p3a.transform(rotate_z(theta_width))
    p4b = p4a.transform(rotate_z(theta_width))

    vertices = [[p1a.x, p1a.y, p1a.z], [p2a.x, p2a.y, p2a.z],
                [p3a.x, p3a.y, p3a.z], [p4a.x, p4a.y, p4a.z],
                [p1b.x, p1b.y, p1b.z], [p2b.x, p2b.y, p2b.z],
                [p3b.x, p3b.y, p3b.z], [p4b.x, p4b.y, p4b.z]]

    triangles = [
        [1, 0, 3],
        [1, 3, 2],  # front face (x-z)
        [7, 4, 5],
        [7, 5, 6],  # rear face (rotated out of x-z plane)
        [5, 1, 2],
        [5, 2, 6],  # top face (x-y plane)
        [3, 0, 4],
        [3, 4, 7],  # bottom face (x-y plane)
        [4, 0, 5],
        [1, 5, 0],  # inner face (y-z plane)
        [2, 3, 7],
        [2, 7, 6]
    ]  # outer face (y-z plane)

    return Mesh(vertices=vertices,
                triangles=triangles,
                smoothing=False,
                transform=rotate_z(theta_offset),
                material=material,
                parent=world)
Beispiel #3
0
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
Beispiel #4
0
centre_column = get_resource("ST40-IVC2", "mesh", "centre_column")
centre_column = import_ply(centre_column,
                           scaling=0.001,
                           parent=world,
                           name="centre_column")
meshes["centre_column"] = centre_column

poloidal_coil_lower_45 = get_resource("ST40-IVC2", "mesh",
                                      "poloidal_coil_lower_45")
poloidal_coil_lower_45 = import_ply(poloidal_coil_lower_45,
                                    scaling=0.001,
                                    name="poloidal_coil_lower_45")
meshes["poloidal_coil_lower_45"] = poloidal_coil_lower_45
for i in range(8):
    poloidal_coil_lower_45.instance(parent=world,
                                    transform=rotate_z(i * 45),
                                    name="poloidal_coil_lower_45")

poloidal_coil_upper_45 = get_resource("ST40-IVC2", "mesh",
                                      "poloidal_coil_upper_45")
poloidal_coil_upper_45 = import_ply(poloidal_coil_upper_45,
                                    scaling=0.001,
                                    name="poloidal_coil_upper_45")
meshes["poloidal_coil_upper_45"] = poloidal_coil_upper_45
for i in range(8):
    poloidal_coil_upper_45.instance(parent=world,
                                    transform=rotate_z(i * 45),
                                    name="poloidal_coil_upper_45")

limiter_lower_45 = get_resource("ST40-IVC2", "mesh", "limiter_lower_45")
limiter_lower_45 = import_ply(limiter_lower_45,
Beispiel #5
0
    def map_power(self,
                  power,
                  angle_period,
                  field_tracer,
                  world,
                  num_of_fieldlines=50000,
                  max_tracing_length=15,
                  phi_offset=0,
                  debug_output=False,
                  debug_count=10000,
                  write_output=True):
        """
        Map the power from this surface onto the wall tiles.

        :param float power: The total power that will be mapped onto the tiles using the
          specified distribution.
        :param float angle_period: the spatial period for the interface surface in degrees,
          e.g. 45 degrees. Exploiting cylindrical symmetry will enable a significant speed up
          of the calculations.
        :param FieldlineTracer field_tracer: a pre-configured field line tracer object.
        :param World world; A world scenegraph to be mapped. This scenegraph must contain all the
          desired meshes for power tracking calculations.
        :param int num_of_fieldlines: the number of fieldlines to launch in the mapping process.
          Defaults to 50000 fieldlines.
        :param float max_tracing_length: the maximum length for tracing fieldlines.
        :param float phi_offset: the angular range offset for collision point mapping.
          Important for cases where the periodic divertor tiles straddle phi=0.
        :param bool debug_output: toggle to print extra debug information output, such as the meshes
          collided with and the amount of lost power.
        """

        if not (isinstance(power, (float, int)) and power > 0):
            raise TypeError("The interface power must be a float/int > 0.")

        if not (isinstance(angle_period,
                           (float, int)) and 0 < angle_period <= 360):
            raise TypeError(
                "The angle period must be a float/int between (0, 360].")

        if 360 % angle_period:
            raise ValueError(
                "The angle period must be divisible into 360 degrees an integer number of times."
            )

        if not (isinstance(num_of_fieldlines, int) and num_of_fieldlines > 0):
            raise TypeError(
                "The number of fieldlines to trace must be an integer > 0.")

        # reduce power when exploiting  cylindrical symmetry
        reduction_factor = 360 / angle_period
        total_power = power / reduction_factor
        power_per_fieldline = total_power / num_of_fieldlines

        meshes = {}
        mesh_powers = {}
        mesh_hitpoints = {}
        mesh_seedpoints = {}
        null_intersections = 0
        lost_power = 0

        t_start = time.time()
        for i in range(num_of_fieldlines):

            seed_point_2d = self._generate_sample_point()
            seed_angle = random() * angle_period
            seed_point = Point3D(
                seed_point_2d.x * np.cos(np.deg2rad(seed_angle)),
                seed_point_2d.x * np.sin(np.deg2rad(seed_angle)),
                seed_point_2d.y)

            end_point, intersection, _ = field_tracer.trace(
                world, seed_point, max_length=max_tracing_length)

            # log the collision information for power tallies
            if intersection is not None:

                # catch primitive for later if we haven't encountered it before
                try:
                    meshes[intersection.primitive.name]
                except KeyError:
                    meshes[
                        intersection.primitive.name] = intersection.primitive

                # extract power array for intersected mesh and save results
                try:
                    powers = mesh_powers[intersection.primitive.name]
                except KeyError:
                    mesh = intersection.primitive
                    powers = np.zeros((mesh.data.triangles.shape[0]))
                    mesh_powers[intersection.primitive.name] = powers

                tri_id = intersection.primitive_coords[0]
                powers[tri_id] += power_per_fieldline

                # save hit points for saving to a separate vtk file
                try:
                    hitpoints = mesh_hitpoints[intersection.primitive.name]
                except KeyError:
                    hitpoints = []
                    mesh_hitpoints[intersection.primitive.name] = hitpoints

                # save seed points for separate analysis
                try:
                    seedpoints = mesh_seedpoints[intersection.primitive.name]
                except KeyError:
                    seedpoints = []
                    mesh_seedpoints[intersection.primitive.name] = seedpoints

                # map the hit point back to the starting sector (angular period)
                hit_point = intersection.hit_point.transform(
                    intersection.primitive_to_world)
                phi = np.rad2deg(atan2(hit_point.y, hit_point.x)) - phi_offset
                phase_phi = phi - phi % angle_period
                mapped_point = hit_point.transform(rotate_z(-phase_phi))
                hitpoints.append(
                    (mapped_point.x, mapped_point.y, mapped_point.z))

                seedpoints.append(seed_point)

            else:
                null_intersections += 1
                lost_power += power_per_fieldline

            if not i % debug_count and debug_output:
                print("Tracing fieldline {}.".format(i))

        t_end = time.time()

        if debug_output:
            print("Meshes collided with:")
            for mesh_name, mesh_values in mesh_powers.items():
                power_fraction = mesh_values.sum() / total_power * 100
                print('{} - {:.4G}%'.format(mesh_name, power_fraction))
            print("Fraction of lost power - {:.4G}%".format(lost_power /
                                                            total_power))

            print()
            print("execution time: {}".format(t_end - t_start))
            print()

        if write_output:

            for mesh_name in mesh_powers.keys():

                mesh_primitive = meshes[mesh_name]
                powers = mesh_powers[mesh_name]
                hitpoints = np.array(mesh_hitpoints[mesh_name])

                output_filename = mesh_name + ".vtk"
                self._write_mesh_power_vtk(output_filename, powers,
                                           mesh_primitive)

                point_filename = mesh_name + ".vtp"
                self._write_mesh_points_vtk(point_filename, hitpoints,
                                            power_per_fieldline)

        return mesh_powers, mesh_hitpoints, mesh_seedpoints
Beispiel #6
0
def load_wall_configuration(config_file, parent):

    with open(config_file, 'r') as fh:
        vita_input = json.load(fh)

    machine_id = vita_input["machine-settings"]["machine-id"]

    try:
        wall_components = vita_input["machine-settings"]["wall-components"]
    except KeyError:
        raise IOError(
            "Invalid vita config JSON - no 'wall-components' specification.")

    if not isinstance(wall_components, list):
        raise IOError(
            "Invalid vita config JSON - expected a list of wall component specifications."
        )

    for component in wall_components:

        if not isinstance(component, dict):
            raise IOError(
                "Invalid vita config JSON - expected a dictionary specifying the component."
            )

        resource_id = component["mesh-resource-id"]

        try:
            scaling = component["scaling"]
        except KeyError:
            scaling = None

        try:
            period = component["period"]
            if 360 % period:
                raise IOError(
                    "Invalid vita config JSON - the angle period must be divisible into 360 "
                    "degrees an integer number of times.")
        except KeyError:
            period = 360

        try:
            rotation_offset = component["rotation_offset"]
        except KeyError:
            rotation_offset = 0

        # TODO - add proper material handling
        material = AbsorbingSurface()

        mesh_instances = int(360 / period)

        mesh_file = get_resource(machine_id, 'mesh', resource_id)
        path, filename = os.path.split(mesh_file)
        name, ext = os.path.splitext(filename)

        importer = _import_functions[ext]

        mesh = importer(mesh_file,
                        scaling=scaling,
                        name=name,
                        transform=rotate_z(rotation_offset))
        for i in range(mesh_instances):
            mesh.instance(material=material,
                          parent=parent,
                          transform=rotate_z(i * period),
                          name=name)
Beispiel #7
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
Beispiel #8
0
cylinder_source1.Update()
transform_filter = vtk.vtkTransformPolyDataFilter()
transform_filter.SetInputConnection(cylinder_source1.GetOutputPort())
transform_filter.SetTransform(convert_to_vtk_transform(rotate_x(90)))
transform_filter.Update()
tris1 = vtk.vtkTriangleFilter()
tris1.SetInputData(transform_filter.GetOutput())

cylinder_source2 = vtk.vtkCylinderSource()
cylinder_source2.SetRadius(1)
cylinder_source2.SetHeight(4.2)
cylinder_source2.SetResolution(50)
cylinder_source2.Update()
transform_filter = vtk.vtkTransformPolyDataFilter()
transform_filter.SetInputConnection(cylinder_source2.GetOutputPort())
transform_filter.SetTransform(convert_to_vtk_transform(rotate_z(90)))
transform_filter.Update()
tris2 = vtk.vtkTriangleFilter()
tris2.SetInputData(transform_filter.GetOutput())

booleanOperation1 = vtk.vtkBooleanOperationPolyDataFilter()
booleanOperation1.SetOperationToUnion()
booleanOperation1.SetInputConnection(0, tris1.GetOutputPort())
booleanOperation1.SetInputConnection(1, tris2.GetOutputPort())
booleanOperation1.Update()
bool_tris = vtk.vtkTriangleFilter()
bool_tris.SetInputData(booleanOperation1.GetOutput())

cylinder_source3 = vtk.vtkCylinderSource()
cylinder_source3.SetRadius(1)
cylinder_source3.SetHeight(4.2)