Source code for paramak.assemblies.tokamak

from typing import Optional, Sequence, Tuple, Union

import cadquery as cq

from ..utils import get_plasma_index, LayerType
from ..workplanes.blanket_from_plasma import blanket_from_plasma
from ..workplanes.center_column_shield_cylinder import center_column_shield_cylinder
from ..workplanes.plasma_simplified import plasma_simplified
from .spherical_tokamak import get_plasma_value, sum_up_to_plasma


def count_cylinder_layers(radial_build):
    before_plasma = 0
    after_plasma = 0
    found_plasma = False

    for item in radial_build:
        if item[0] == LayerType.PLASMA:
            found_plasma = True
        elif item[0] == LayerType.SOLID:
            if not found_plasma:
                before_plasma += 1
            else:
                after_plasma += 1

    return before_plasma - after_plasma


def create_center_column_shield_cylinders(radial_build, rotation_angle, center_column_shield_height):
    cylinders = []
    total_sum = 0
    layer_count = 0

    number_of_cylinder_layers = count_cylinder_layers(radial_build)

    for index, item in enumerate(radial_build):
        if item[0] == LayerType.PLASMA:
            break

        if item[0] == LayerType.GAP:
            total_sum += item[1]
            continue

        thickness = item[1]
        layer_count += 1

        if layer_count > number_of_cylinder_layers:
            break

        cylinder = center_column_shield_cylinder(
            inner_radius=total_sum,
            thickness=item[1],
            name=f"layer_{layer_count}",
            rotation_angle=rotation_angle,
            height=center_column_shield_height,
        )
        total_sum += thickness
        cylinders.append(cylinder)
    return cylinders


def distance_to_plasma(radial_build, index):
    distance = 0
    for item in radial_build[index + 1 :]:
        if item[0] == LayerType.PLASMA:
            break
        distance += item[1]
    return distance


def create_layers_from_plasma(
    radial_build, vertical_build, minor_radius, major_radius, triangularity, elongation, rotation_angle, center_column
):

    plasma_index_rb = get_plasma_index(radial_build)
    plasma_index_vb = get_plasma_index(vertical_build)
    indexes_from_plamsa_to_end = len(radial_build) - plasma_index_rb
    layers = []

    cumulative_thickness_orb = 0
    cumulative_thickness_irb = 0
    cumulative_thickness_uvb = 0
    cumulative_thickness_lvb = 0
    for index_delta in range(indexes_from_plamsa_to_end):

        if radial_build[plasma_index_rb + index_delta][0] == LayerType.PLASMA:
            continue
        outer_layer_thickness = radial_build[plasma_index_rb + index_delta][1]
        inner_layer_thickness = radial_build[plasma_index_rb - index_delta][1]
        upper_layer_thickness = vertical_build[plasma_index_vb - index_delta][1]
        lower_layer_thickness = vertical_build[plasma_index_vb + index_delta][1]

        if radial_build[plasma_index_rb + index_delta][0] == LayerType.GAP:
            cumulative_thickness_orb += outer_layer_thickness
            cumulative_thickness_irb += inner_layer_thickness
            cumulative_thickness_uvb += upper_layer_thickness
            cumulative_thickness_lvb += lower_layer_thickness
            continue

        # build outer layer
        if radial_build[plasma_index_rb + index_delta][0] == LayerType.SOLID:
            outer_layer = blanket_from_plasma(
                minor_radius=minor_radius,
                major_radius=major_radius,
                triangularity=triangularity,
                elongation=elongation,
                thickness=[
                    upper_layer_thickness,
                    outer_layer_thickness,
                    lower_layer_thickness
                ],
                offset_from_plasma=[
                    cumulative_thickness_uvb,
                    cumulative_thickness_orb,
                    cumulative_thickness_lvb
                ],
                start_angle=90,
                stop_angle=-90,
                rotation_angle=rotation_angle,
                color=(0.5, 0.5, 0.5),
                name=f"layer_{index_delta}",
                allow_overlapping_shape=True,
            )
            inner_layer = blanket_from_plasma(
                minor_radius=minor_radius,
                major_radius=major_radius,
                triangularity=triangularity,
                elongation=elongation,
                thickness=[
                    lower_layer_thickness,
                    inner_layer_thickness,
                    upper_layer_thickness,
                ],
                offset_from_plasma=[
                    cumulative_thickness_lvb,
                    cumulative_thickness_irb,
                    cumulative_thickness_uvb,
                ],
                start_angle=-90,
                stop_angle=-270,
                rotation_angle=rotation_angle,
                color=(0.5, 0.5, 0.5),
                name=f"layer_{index_delta}",
                allow_overlapping_shape=True,
            )
            layer = outer_layer.union(inner_layer)
            layers.append(layer)
            # layers.append(inner_layer)
        cumulative_thickness_orb += outer_layer_thickness
        cumulative_thickness_irb += inner_layer_thickness
        cumulative_thickness_uvb += upper_layer_thickness
        cumulative_thickness_lvb += lower_layer_thickness
        # build inner layer

        # union layers

    return layers

[docs] def tokamak_from_plasma( radial_build: Sequence[Tuple[LayerType, float]], elongation: float = 2.0, triangularity: float = 0.55, rotation_angle: float = 180.0, extra_cut_shapes: Sequence[cq.Workplane] = [], extra_intersect_shapes: Sequence[cq.Workplane] = [], ): inner_equatorial_point = sum_up_to_plasma(radial_build) plasma_radial_thickness = get_plasma_value(radial_build) outer_equatorial_point = inner_equatorial_point + plasma_radial_thickness # sets major radius and minor radius from equatorial_points to allow a # radial build. This helps avoid the plasma overlapping the center # column and other components major_radius = (outer_equatorial_point + inner_equatorial_point) / 2 minor_radius = major_radius - inner_equatorial_point # make vertical build from inner radial build pi = get_plasma_index(radial_build) rbi = len(radial_build)-1 - pi # number of unique entries in outer or inner radial build upper_vertical_build = radial_build[pi-rbi:pi][::-1] #get the inner radial build plasma_height = 2 * minor_radius * elongation # slice opperation reverses the list and removes the last value to avoid two plasmas vertical_build = upper_vertical_build[::-1] + [(LayerType.PLASMA, plasma_height)] + upper_vertical_build return tokamak( radial_build=radial_build, vertical_build=vertical_build, triangularity=triangularity, rotation_angle=rotation_angle, extra_cut_shapes=extra_cut_shapes, extra_intersect_shapes=extra_intersect_shapes, )
[docs] def tokamak( radial_build: Union[Sequence[Sequence[Tuple[str, float]]], Sequence[Tuple[str, float]]], vertical_build: Sequence[Tuple[str, float]], triangularity: float = 0.55, rotation_angle: float = 180.0, extra_cut_shapes: Sequence[cq.Workplane] = [], extra_intersect_shapes: Sequence[cq.Workplane] = [], ): """ Creates a tokamak fusion reactor from a radial build and plasma parameters. Args: radial_build: A list of tuples containing the radial build of the reactor. elongation: The elongation of the plasma. Defaults to 2.0. triangularity: The triangularity of the plasma. Defaults to 0.55. rotation_angle: The rotation angle of the plasma. Defaults to 180.0. extra_cut_shapes: A list of extra shapes to cut the reactor with. Defaults to []. extra_intersect_shapes: A list of extra shapes to intersect the reactor with. Defaults to []. Returns: CadQuery.Assembly: A CadQuery Assembly object representing the tokamak fusion reactor. """ inner_equatorial_point = sum_up_to_plasma(radial_build) plasma_radial_thickness = get_plasma_value(radial_build) plasma_vertical_thickness = get_plasma_value(vertical_build) outer_equatorial_point = inner_equatorial_point + plasma_radial_thickness major_radius = (outer_equatorial_point + inner_equatorial_point) / 2 minor_radius = major_radius - inner_equatorial_point elongation = (plasma_vertical_thickness / 2) / minor_radius blanket_rear_wall_end_height = sum([item[1] for item in vertical_build]) plasma = plasma_simplified( major_radius=major_radius, minor_radius=minor_radius, elongation=elongation, triangularity=triangularity, rotation_angle=rotation_angle, ) inner_radial_build = create_center_column_shield_cylinders( radial_build, rotation_angle, blanket_rear_wall_end_height ) blanket_layers = create_layers_from_plasma( radial_build=radial_build, vertical_build=vertical_build, minor_radius=minor_radius, major_radius=major_radius, triangularity=triangularity, elongation=elongation, rotation_angle=rotation_angle, center_column=inner_radial_build[0], # blanket_cutting_cylinder, ) my_assembly = cq.Assembly() for i, entry in enumerate(extra_cut_shapes): if isinstance(entry, cq.Workplane): my_assembly.add(entry, name=f"add_extra_cut_shape_{i+1}") else: raise ValueError(f"extra_cut_shapes should only contain cadquery Workplanes, not {type(entry)}") # builds up the intersect shapes intersect_shapes_to_cut = [] if len(extra_intersect_shapes)>0: all_shapes = [] for shape in inner_radial_build + blanket_layers: all_shapes.append(shape) # makes a union of the the radial build to use as a base for the intersect shapes reactor_compound=inner_radial_build[0] for i, entry in enumerate(inner_radial_build[1:] + blanket_layers): reactor_compound = reactor_compound.union(entry) # adds the extra intersect shapes to the assembly for i, entry in enumerate(extra_intersect_shapes): reactor_entry_intersection = entry.intersect(reactor_compound) intersect_shapes_to_cut.append(reactor_entry_intersection) my_assembly.add(reactor_entry_intersection, name=f"extra_intersect_shapes_{i+1}") # builds just the core if there are no extra parts if len(extra_cut_shapes) == 0 and len(intersect_shapes_to_cut) == 0: for i, entry in enumerate(inner_radial_build): my_assembly.add(entry, name=f"inboard_layer_{i+1})") for i, entry in enumerate(blanket_layers): my_assembly.add(entry, name=f"outboard_layer_{i+1})") else: shapes_and_components = [] for i, entry in enumerate(inner_radial_build + blanket_layers): for cutter in extra_cut_shapes + extra_intersect_shapes: entry = entry.cut(cutter) # TODO use something like this to return a list of material tags for the solids in order, as some solids get split into multiple # for subentry in entry.objects: # print(i, subentry) shapes_and_components.append(entry) for i, entry in enumerate(shapes_and_components): my_assembly.add(entry, name=f"layer_{i+1})") #TODO track the names of shapes, even when extra shapes are made due to splitting my_assembly.add(plasma, name="plasma") return my_assembly