diff --git a/docs/usage_spherical_tokamak.rst b/docs/usage_spherical_tokamak.rst index dde8cfd8..3dfe7b46 100644 --- a/docs/usage_spherical_tokamak.rst +++ b/docs/usage_spherical_tokamak.rst @@ -323,6 +323,7 @@ Spherical tokamak with toroidal field coils vertical_mid_point = (600, 0), thickness = 50, distance = 40, + rotation_angle = 180, with_inner_leg = True, azimuthal_placement_angles = [0, 30, 60, 90, 120, 150, 180], ) @@ -349,6 +350,7 @@ Spherical tokamak with toroidal field coils r1=5, r2=610, azimuthal_placement_angles = [0, 30, 60, 90, 120, 150, 180], + rotation_angle = 180, thickness = 50, distance = 40 ) @@ -388,6 +390,7 @@ Spherical tokamak with toroidal field coils thickness = 50, distance = 40, with_inner_leg = True, + rotation_angle = 180, azimuthal_placement_angles = [0, 30, 60, 90, 120, 150, 180], ) @@ -415,6 +418,7 @@ Spherical tokamak with toroidal field coils r1=5, r2=610, azimuthal_placement_angles = [120, 150, 180], + rotation_angle = 180, thickness = 50, distance = 40 ) diff --git a/docs/usage_tokamak.rst b/docs/usage_tokamak.rst index c44987c2..8cab51e3 100644 --- a/docs/usage_tokamak.rst +++ b/docs/usage_tokamak.rst @@ -268,6 +268,7 @@ Tokamak with several customizations thickness = 50, distance = 40, with_inner_leg = True, + rotation_angle = 180, azimuthal_placement_angles = [0, 30, 60, 90, 120, 150, 180], ) @@ -346,6 +347,7 @@ Tokamak with several customizations thickness = 50, distance = 40, with_inner_leg = True, + rotation_angle = 180, azimuthal_placement_angles = [0, 30, 60, 90, 120, 150, 180], ) diff --git a/examples/spherical_tokamak_from_plasma_with_tf_magnets.py b/examples/spherical_tokamak_from_plasma_with_tf_magnets.py index fdc5e4fa..2dc7dc67 100644 --- a/examples/spherical_tokamak_from_plasma_with_tf_magnets.py +++ b/examples/spherical_tokamak_from_plasma_with_tf_magnets.py @@ -1,27 +1,49 @@ -from pathlib import Path - -from example_util_functions import transport_particles_on_h5m_geometry import paramak + +rotation_angle=90 tf_style_1 = paramak.toroidal_field_coil_rectangle( horizontal_start_point = (10, 520), vertical_mid_point = (600, 0), thickness = 50, distance = 40, with_inner_leg = True, - azimuthal_placement_angles = [0, 30, 60, 90], + azimuthal_placement_angles = [0, 30, 60, 90, 120, 150, 180], + rotation_angle=rotation_angle +) + +result1 = paramak.spherical_tokamak_from_plasma( + radial_build=[ + (paramak.LayerType.GAP, 70), + (paramak.LayerType.SOLID, 10), + (paramak.LayerType.SOLID, 10), + (paramak.LayerType.GAP, 50), + (paramak.LayerType.PLASMA, 300), + (paramak.LayerType.GAP, 60), + (paramak.LayerType.SOLID, 10), + (paramak.LayerType.SOLID, 60), + (paramak.LayerType.SOLID, 10), + ], + elongation=2.5, + rotation_angle=rotation_angle, + triangularity=0.55, + extra_cut_shapes=[tf_style_1] ) +result1.save("spherical_tokamak_from_plasma_with_rect_tf_coils.step") + + tf_style_2 = paramak.toroidal_field_coil_princeton_d( r1=5, r2=610, - azimuthal_placement_angles = [120, 150, 180], thickness = 50, - distance = 40 + distance = 40, + azimuthal_placement_angles = [0, 30, 60, 90, 120, 150, 180], + rotation_angle=rotation_angle ) -result = paramak.spherical_tokamak_from_plasma( +result2 = paramak.spherical_tokamak_from_plasma( radial_build=[ (paramak.LayerType.GAP, 70), (paramak.LayerType.SOLID, 10), @@ -34,13 +56,14 @@ (paramak.LayerType.SOLID, 10), ], elongation=2.5, - rotation_angle=180, + rotation_angle=rotation_angle, triangularity=0.55, - extra_cut_shapes=[tf_style_1, tf_style_2] + extra_cut_shapes=[tf_style_2] ) -result.save(f"spherical_tokamak_minimal.step") +result2.save("spherical_tokamak_from_plasma_with_prin_tf_coils.step") +# from example_util_functions import transport_particles_on_h5m_geometry # from cad_to_dagmc import CadToDagmc # my_model = CadToDagmc() # material_tags = ["mat1"] * 7 diff --git a/examples/tokamak_with_pf_tf_magnets_divertor.py b/examples/tokamak_with_pf_tf_magnets_divertor.py index 1f87440d..5097320e 100644 --- a/examples/tokamak_with_pf_tf_magnets_divertor.py +++ b/examples/tokamak_with_pf_tf_magnets_divertor.py @@ -2,12 +2,12 @@ from example_util_functions import transport_particles_on_h5m_geometry import paramak -from cadquery import vis, Workplane +import cadquery as cq # makes a rectangle that overlaps the lower blanket under the plasma # the intersection of this and the layers will form the lower divertor points = [(300, -700), (300, 0), (400, 0), (400, -700)] -divertor_lower = Workplane('XZ', origin=(0,0,0)).polyline(points).close().revolve(180) +divertor_lower = cq.Workplane('XZ', origin=(0,0,0)).polyline(points).close().revolve(180) # creates a toroidal tf = paramak.toroidal_field_coil_rectangle( @@ -15,6 +15,7 @@ vertical_mid_point = (860, 0), thickness = 50, distance = 40, + rotation_angle=180, with_inner_leg = True, azimuthal_placement_angles = [0, 30, 60, 90, 120, 150, 180], ) @@ -78,6 +79,8 @@ ) my_reactor.save(f"tokamak_with_divertor.step") print(f"Saved as tokamak_with_divertor.step") + +# from cadquery import vis # vis.show(my_reactor) # from cad_to_dagmc import CadToDagmc diff --git a/src/paramak/workplanes/cutting_wedge.py b/src/paramak/workplanes/cutting_wedge.py index 8a96c570..3c41527c 100644 --- a/src/paramak/workplanes/cutting_wedge.py +++ b/src/paramak/workplanes/cutting_wedge.py @@ -38,7 +38,13 @@ def cutting_wedge( wire = create_wire_workplane_from_points(points=points, plane=plane, origin=origin, obj=obj) - solid = wire.revolve(rotation_angle) + solid = wire.revolve(angleDegrees=rotation_angle,) + # The code can be changed to revolve it in the other direction + # solid = wire.revolve( + # angleDegrees=rotation_angle, + # axisStart=cq.Vector(0, 0), + # axisEnd=cq.Vector(0, -1) + # ) solid.name = name solid.color = cq.Color(*color) return solid diff --git a/src/paramak/workplanes/toroidal_field_coil_princeton_d.py b/src/paramak/workplanes/toroidal_field_coil_princeton_d.py index 28870aa5..8bfaa3d1 100644 --- a/src/paramak/workplanes/toroidal_field_coil_princeton_d.py +++ b/src/paramak/workplanes/toroidal_field_coil_princeton_d.py @@ -4,6 +4,8 @@ from scipy import integrate from scipy.optimize import minimize from typing import List, Tuple +from ..workplanes.cutting_wedge import cutting_wedge + def _compute_inner_points(R1, R2): """Computes the inner curve points @@ -152,6 +154,7 @@ def toroidal_field_coil_princeton_d( r2: float = 300, thickness: float = 30, distance: float = 20, + rotation_angle: float = 360.0, name: str = "toroidal_field_coil", with_inner_leg: bool = True, azimuthal_placement_angles: typing.Sequence[float] = [0], @@ -169,6 +172,7 @@ def toroidal_field_coil_princeton_d( r2 (float, optional): Outer radius of the coil. Defaults to 300. thickness (float, optional): Thickness of the coil. Defaults to 30. distance (float, optional): Distance to extrude the coil. Defaults to 20. + rotation_angle (float): angle of rotation in degrees, this cuts the resulting shape with a wedge. Useful for sector models. name (str, optional): Name of the coil. Defaults to "toroidal_field_coil". with_inner_leg (bool, optional): Whether to include the inner leg of the coil. Defaults to True. azimuthal_placement_angles (typing.Sequence[float], optional): Angles for azimuthal placement. Defaults to [0]. @@ -192,8 +196,6 @@ def toroidal_field_coil_princeton_d( inner_leg_connection_points=[(x,z,'straight') for x,z in inner_leg_connection_points] # need to get square end, it appears to miss the last point in the solid, TODO fix so this append is not needed inner_leg_connection_points.append(inner_leg_connection_points[-1]) - for i in inner_leg_connection_points: - print('inner_leg_connection_points' , i) inner_wire = create_wire_workplane_from_points( points=inner_leg_connection_points, plane=plane, origin=origin, obj=obj ) @@ -201,6 +203,13 @@ def toroidal_field_coil_princeton_d( inner_solid = rotate_solid(angles=azimuthal_placement_angles, solid=inner_solid) solid = solid.union(inner_solid) + if rotation_angle < 360.: + bb=solid.val().BoundingBox() + radius = max(bb.xmax, bb.ymax)*2.1 # larger than the bounding box to ensure clean cut + height = max(bb.zmax, bb.zmin)*2.1 # larger than the bounding box to ensure clean cut + cutting_shape = cutting_wedge(height=height, radius=radius, rotation_angle=rotation_angle) + solid = solid.intersect(cutting_shape) + solid.name = name solid.color = color return solid \ No newline at end of file diff --git a/src/paramak/workplanes/toroidal_field_coil_rectangle.py b/src/paramak/workplanes/toroidal_field_coil_rectangle.py index f8ad3c74..b19b748c 100644 --- a/src/paramak/workplanes/toroidal_field_coil_rectangle.py +++ b/src/paramak/workplanes/toroidal_field_coil_rectangle.py @@ -1,6 +1,7 @@ import typing from ..utils import create_wire_workplane_from_points, rotate_solid +from ..workplanes.cutting_wedge import cutting_wedge def toroidal_field_coil_rectangle( @@ -8,6 +9,7 @@ def toroidal_field_coil_rectangle( vertical_mid_point: typing.Tuple[float, float] = (350, 0), thickness: float = 30, distance: float = 20, + rotation_angle: float = 360.0, name: str = "toroidal_field_coil", with_inner_leg: bool = True, azimuthal_placement_angles: typing.Sequence[float] = [0], @@ -26,9 +28,7 @@ def toroidal_field_coil_rectangle( vertical section (cm). thickness: the thickness of the toroidal field coil. distance: the extrusion distance. - number_of_coils: the number of tf coils. This changes by the - azimuth_placement_angle dividing up 360 degrees by the number of - coils. + rotation_angle (float): angle of rotation in degrees, this cuts the resulting shape with a wedge. Useful for sector models. with_inner_leg: include the inner tf leg. Defaults to True. azimuth_start_angle: The azimuth angle to for the first TF coil which offsets the placement of coils around the azimuthal angle @@ -101,6 +101,13 @@ def toroidal_field_coil_rectangle( inner_solid = rotate_solid(angles=azimuthal_placement_angles, solid=inner_solid) solid = solid.union(inner_solid) + if rotation_angle < 360.: + bb=solid.val().BoundingBox() + radius = max(bb.xmax, bb.ymax)*1.1 # 10% larger than the bounding box to ensure clean cut + height = max(bb.zmax, bb.zmin)*2.1 # 10% larger than the bounding box to ensure clean cut + cutting_shape = cutting_wedge(height=height, radius=radius, rotation_angle=rotation_angle) + solid = solid.intersect(cutting_shape) + solid.name = name solid.color = color return solid diff --git a/tests/test_workplanes/test_toroidal_field_coil_princeton_d.py b/tests/test_workplanes/test_toroidal_field_coil_princeton_d.py index d1f6cf0d..45acb60a 100644 --- a/tests/test_workplanes/test_toroidal_field_coil_princeton_d.py +++ b/tests/test_workplanes/test_toroidal_field_coil_princeton_d.py @@ -5,4 +5,13 @@ def test_creation_of_inner_leg(): solid_without_inner_leg = paramak.toroidal_field_coil_princeton_d(with_inner_leg=False) solid_with_inner_leg = paramak.toroidal_field_coil_princeton_d(with_inner_leg=True) - assert solid_without_inner_leg.val().Volume() < solid_with_inner_leg.val().Volume() \ No newline at end of file + assert solid_without_inner_leg.val().Volume() < solid_with_inner_leg.val().Volume() + +def test_rotation_angle(): + solid_360_uncut = paramak.toroidal_field_coil_princeton_d(azimuthal_placement_angles=[0,180], rotation_angle=360) + solid_180_uncut = paramak.toroidal_field_coil_princeton_d(azimuthal_placement_angles=[0,180], rotation_angle=360) + solid_180_cut = paramak.toroidal_field_coil_princeton_d(azimuthal_placement_angles=[0,180], rotation_angle=180) + + assert solid_360_uncut.val().Volume() == solid_180_uncut.val().Volume() + # checks relative volume difference + assert abs(solid_180_cut.val().Volume() - 0.5 * solid_180_uncut.val().Volume()) / (0.5 * solid_180_uncut.val().Volume()) < 0.00001 diff --git a/tests/test_workplanes/test_toroidal_field_coil_rectangle.py b/tests/test_workplanes/test_toroidal_field_coil_rectangle.py index bf4a2958..f265c18d 100644 --- a/tests/test_workplanes/test_toroidal_field_coil_rectangle.py +++ b/tests/test_workplanes/test_toroidal_field_coil_rectangle.py @@ -22,3 +22,12 @@ def test_volume_rotation_angle(): assert math.isclose( half_toroidal_field_coil_rectangle.val().Volume(), toroidal_field_coil_rectangle.val().Volume() / 2 ) + +def test_rotation_angle(): + solid_360_uncut = paramak.toroidal_field_coil_rectangle(azimuthal_placement_angles=[0,180], rotation_angle=360) + solid_180_uncut = paramak.toroidal_field_coil_rectangle(azimuthal_placement_angles=[0,180], rotation_angle=360) + solid_180_cut = paramak.toroidal_field_coil_rectangle(azimuthal_placement_angles=[0,180], rotation_angle=180) + + assert solid_360_uncut.val().Volume() == solid_180_uncut.val().Volume() + # checks relative volume difference + assert abs(solid_180_cut.val().Volume() - 0.5 * solid_180_uncut.val().Volume()) / (0.5 * solid_180_uncut.val().Volume()) < 0.00001