diff --git a/README.md b/README.md
index 60cbebfd..0f65b7cb 100644
--- a/README.md
+++ b/README.md
@@ -17,4 +17,4 @@ The purpose of Paramak is to provide geometry for parameter studies and therefor
Paramak produces [CadQuery](https://github.com/CadQuery/CadQuery) objects which can easily be saved to CAD files in several formats (STEP, STL, BRep) and also readily convertable to DAGMC h5m neutronics geometry using tools such as [Cad-to_DAGMC](https://github.com/fusion-energy/cad_to_dagmc).
-:point_right: Please see the [Online documentation](https://fusion-energy.github.io/paramak) for installation, usage and API documentation.
+:point_right: Please see the [online documentation](https://fusion-energy.github.io/paramak) for installation, usage and API documentation.
diff --git a/docs/usage.rst b/docs/usage.rst
index d1c72ea3..0fc36347 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -17,30 +17,7 @@ These examples show how to make various reactors with and without different comp
.. toctree::
:maxdepth: 3
+ usage_vis
usage_tokamak
usage_spherical_tokamak
-
-Visualization options
-=====================
-
-The reactors (cadquery.Assembly objects) and components (cadquery.Workplane objects) can be visualized in a number of ways.
-First option is to export them to STEP, BREP or STL file then open the file with your favorite CAD software for example `FreeCAD `_.
-See the `CadQuery documentation on saving `_ for more information.
-Other options include the built in visualization tools in CadQuery
-
-.. code-block:: python
-
- from cadquery.vis import show
- show(result) # where result is the returned reactor or component object
-
-or the jupyter_cadquery package which allows for interactive 3D visualization in a web browser.
-or the jupyter_cadquery package which allows for interactive 3D visualization in a web browser.
-
-.. code-block:: python
-
- # pip install jupyter_cadquery
- # might needed to downgrade pip with ... python -m pip install pip==24.0
- from jupyter_cadquery import show
- view = show(result) # where result is the returned reactor or component object
- view.export_html("3d.html")
diff --git a/docs/usage_spherical_tokamak.rst b/docs/usage_spherical_tokamak.rst
index 90c68d2b..dde8cfd8 100644
--- a/docs/usage_spherical_tokamak.rst
+++ b/docs/usage_spherical_tokamak.rst
@@ -1,61 +1,7 @@
+Spherical Tokamak
+=================
-Spherical tokamak from plasma
------------------------------
-
-- The spherical_tokamak_from_plasma function provides a parametric tokamak shaped reactor.
-- This is characterized by a blanket that only goes around the outboard sides of the plasma.
-- This reactor requires few arguments to create as it keeps the vertical build of the blanket layers the same thickness as the radial build.
-
-
-.. cadquery::
- :gridsize: 0
- :select: result
- :color: #00cd00
- :width: 100%
- :height: 600px
-
- import paramak
- result = paramak.spherical_tokamak_from_plasma(
- radial_build=[
- (paramak.LayerType.GAP, 10),
- (paramak.LayerType.SOLID, 60),
- (paramak.LayerType.SOLID, 20),
- (paramak.LayerType.GAP, 60),
- (paramak.LayerType.PLASMA, 300),
- (paramak.LayerType.GAP, 60),
- (paramak.LayerType.SOLID, 20),
- (paramak.LayerType.SOLID, 120),
- (paramak.LayerType.SOLID, 10),
- ],
- elongation=2,
- triangularity=0.55,
- rotation_angle=90,
- ).toCompound()
-
-
-.. code-block:: python
-
- import paramak
- result = paramak.spherical_tokamak_from_plasma(
- radial_build=[
- (paramak.LayerType.GAP, 10),
- (paramak.LayerType.SOLID, 60),
- (paramak.LayerType.SOLID, 20),
- (paramak.LayerType.GAP, 60),
- (paramak.LayerType.PLASMA, 300),
- (paramak.LayerType.GAP, 60),
- (paramak.LayerType.SOLID, 20),
- (paramak.LayerType.SOLID, 120),
- (paramak.LayerType.SOLID, 10),
- ],
- elongation=2,
- triangularity=0.55,
- rotation_angle=90,
- )
- result.save('reactor.step')
-
-
-
+- This is characterized by a blanket that only goes around the outboard sides of the plasma and the center column has no inboard breeding.
Spherical tokamak
-----------------
@@ -133,8 +79,64 @@ Spherical tokamak
result.save(f"spherical_tokamak_minimal.step")
-Reactor with divertor(s)
-------------------------
+
+Spherical tokamak from plasma
+-----------------------------
+
+- The spherical_tokamak_from_plasma function provides a parametric tokamak shaped reactor.
+- This reactor requires minimal arguments to create as it keeps the vertical build of the blanket layers the same thickness as the radial build.
+
+
+.. cadquery::
+ :gridsize: 0
+ :select: result
+ :color: #00cd00
+ :width: 100%
+ :height: 600px
+
+ import paramak
+ result = paramak.spherical_tokamak_from_plasma(
+ radial_build=[
+ (paramak.LayerType.GAP, 10),
+ (paramak.LayerType.SOLID, 60),
+ (paramak.LayerType.SOLID, 20),
+ (paramak.LayerType.GAP, 60),
+ (paramak.LayerType.PLASMA, 300),
+ (paramak.LayerType.GAP, 60),
+ (paramak.LayerType.SOLID, 20),
+ (paramak.LayerType.SOLID, 120),
+ (paramak.LayerType.SOLID, 10),
+ ],
+ elongation=2,
+ triangularity=0.55,
+ rotation_angle=90,
+ ).toCompound()
+
+
+.. code-block:: python
+
+ import paramak
+ result = paramak.spherical_tokamak_from_plasma(
+ radial_build=[
+ (paramak.LayerType.GAP, 10),
+ (paramak.LayerType.SOLID, 60),
+ (paramak.LayerType.SOLID, 20),
+ (paramak.LayerType.GAP, 60),
+ (paramak.LayerType.PLASMA, 300),
+ (paramak.LayerType.GAP, 60),
+ (paramak.LayerType.SOLID, 20),
+ (paramak.LayerType.SOLID, 120),
+ (paramak.LayerType.SOLID, 10),
+ ],
+ elongation=2,
+ triangularity=0.55,
+ rotation_angle=90,
+ )
+ result.save('reactor.step')
+
+
+Spherical tokamak with divertor(s)
+----------------------------------
- ll reactors support adding additional radial builds for the lower_divertor and or the upper_divertor.
- This example adds two divertors to a spherical_tokamak_from_plasma reactor but and other reactor would also work.
@@ -198,8 +200,8 @@ Reactor with divertor(s)
)
result.save('reactor.step')
-Reactor with poloidal field coils
----------------------------------
+Spherical tokamak with poloidal field coils
+-------------------------------------------
- All reactors support adding a sequence of CadQuery shapes (e.g. workplanes) to the reactor using the extra_cut_shapes argument
- This example adds PF coils to a spherical_tokamak_from_plasma reactor but and other reactor would also work.
@@ -298,8 +300,8 @@ Reactor with poloidal field coils
result.save(f"spherical_tokamak_from_plasma_with_pf_magnets.step")
-Reactor with toroidal field coils
----------------------------------
+Spherical tokamak with toroidal field coils
+-------------------------------------------
- In a similar way to adding poloidal field coils one can also add toroidal field coils by making use of the extra_cut_shapes argument.
- All reactors support adding a sequence of CadQuery shapes (e.g. workplanes) to the reactor using the extra_cut_shapes argument
@@ -316,7 +318,7 @@ Reactor with toroidal field coils
import paramak
- tf = paramak.toroidal_field_coil_rectangle(
+ tf_style_1 = paramak.toroidal_field_coil_rectangle(
horizontal_start_point = (10, 520),
vertical_mid_point = (600, 0),
thickness = 50,
@@ -325,7 +327,7 @@ Reactor with toroidal field coils
azimuthal_placement_angles = [0, 30, 60, 90, 120, 150, 180],
)
- result = paramak.spherical_tokamak_from_plasma(
+ result1 = paramak.spherical_tokamak_from_plasma(
radial_build=[
(paramak.LayerType.GAP, 70),
(paramak.LayerType.SOLID, 10),
@@ -340,14 +342,47 @@ Reactor with toroidal field coils
elongation=2.5,
rotation_angle=180,
triangularity=0.55,
- extra_cut_shapes=[tf]
- ).toCompound()
+ extra_cut_shapes=[tf_style_1]
+ ).toCompound().translate((700, 0, 0))
+
+ tf_style_2 = paramak.toroidal_field_coil_princeton_d(
+ r1=5,
+ r2=610,
+ azimuthal_placement_angles = [0, 30, 60, 90, 120, 150, 180],
+ thickness = 50,
+ distance = 40
+ )
+
+ result2 = 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=180,
+ triangularity=0.55,
+ extra_cut_shapes=[tf_style_2]
+ ).toCompound().translate((-700, 0, 0))
+
+ import cadquery as cq
+ result = cq.Assembly()
+ result.add(result1)
+ result.add(result2)
+ result = result.toCompound()
+
.. code-block:: python
import paramak
- tf = paramak.toroidal_field_coil_rectangle(
+ tf_style_1 = paramak.toroidal_field_coil_rectangle(
horizontal_start_point = (10, 520),
vertical_mid_point = (600, 0),
thickness = 50,
@@ -357,6 +392,34 @@ Reactor with toroidal field coils
)
result = 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=180,
+ triangularity=0.55,
+ extra_cut_shapes=[tf_style_1]
+ )
+
+ result.save(f"spherical_tokamak_with_rectangular_tf.step")
+
+ tf_style_2 = paramak.toroidal_field_coil_princeton_d(
+ r1=5,
+ r2=610,
+ azimuthal_placement_angles = [120, 150, 180],
+ thickness = 50,
+ distance = 40
+ )
+
+ result2 = paramak.spherical_tokamak_from_plasma(
radial_build=[
(paramak.LayerType.GAP, 70),
(paramak.LayerType.SOLID, 10),
@@ -374,7 +437,7 @@ Reactor with toroidal field coils
extra_cut_shapes=[tf]
)
- result.save(f"spherical_tokamak_minimal.step")
+ result2.save(f"spherical_tokamak_with_princeton_tf.step")
Spherical tokamak with negative triangularity
diff --git a/docs/usage_tokamak.rst b/docs/usage_tokamak.rst
index 6e05aa9b..c44987c2 100644
--- a/docs/usage_tokamak.rst
+++ b/docs/usage_tokamak.rst
@@ -1,10 +1,15 @@
-Tokamak from plasma
--------------------
+Tokamak
+=======
-- The tokamak_from_plasma function provides a parametric tokamak shaped reactor.
- This is characterized by a continuous blanket that goes around the inboard and outboard sides of the plasma.
-- This reactor requires few arguments to create as it keeps the vertical build of the blanket layers the same thickness as the radial build.
+
+Tokamak
+-------
+
+- The spherical_tokamak function provides a parametric tokamak shaped reactor.
+- This is characterized by a blanket that only goes around the outboard sides of the plasma.
+- This reactor allows for a separate vertical and radial build which allows different thickness layers in the blanket.
.. cadquery::
:gridsize: 0
@@ -14,7 +19,8 @@ Tokamak from plasma
:height: 600px
import paramak
- result = paramak.tokamak_from_plasma(
+
+ result = paramak.tokamak(
radial_build=[
(paramak.LayerType.GAP, 10),
(paramak.LayerType.SOLID, 30),
@@ -29,16 +35,26 @@ Tokamak from plasma
(paramak.LayerType.SOLID, 120),
(paramak.LayerType.SOLID, 10),
],
- elongation=2,
+ vertical_build=[
+ (paramak.LayerType.SOLID, 15),
+ (paramak.LayerType.SOLID, 80),
+ (paramak.LayerType.SOLID, 10),
+ (paramak.LayerType.GAP, 50),
+ (paramak.LayerType.PLASMA, 700),
+ (paramak.LayerType.GAP, 60),
+ (paramak.LayerType.SOLID, 10),
+ (paramak.LayerType.SOLID, 40),
+ (paramak.LayerType.SOLID, 15),
+ ],
triangularity=0.55,
- rotation_angle=90,
+ rotation_angle=180,
).toCompound()
-
.. code-block:: python
import paramak
- result = paramak.tokamak_from_plasma(
+
+ result = paramak.tokamak(
radial_build=[
(paramak.LayerType.GAP, 10),
(paramak.LayerType.SOLID, 30),
@@ -53,18 +69,29 @@ Tokamak from plasma
(paramak.LayerType.SOLID, 120),
(paramak.LayerType.SOLID, 10),
],
- elongation=2,
+ vertical_build=[
+ (paramak.LayerType.SOLID, 15),
+ (paramak.LayerType.SOLID, 80),
+ (paramak.LayerType.SOLID, 10),
+ (paramak.LayerType.GAP, 50),
+ (paramak.LayerType.PLASMA, 700),
+ (paramak.LayerType.GAP, 60),
+ (paramak.LayerType.SOLID, 10),
+ (paramak.LayerType.SOLID, 40),
+ (paramak.LayerType.SOLID, 15),
+ ],
triangularity=0.55,
- rotation_angle=90,
+ rotation_angle=180,
)
+ result.save(f"tokamak_minimal.step")
-Tokamak
--------
-- The spherical_tokamak function provides a parametric tokamak shaped reactor.
-- This is characterized by a blanket that only goes around the outboard sides of the plasma.
-- This reactor allows for a separate vertical and radial build which allows different thickness layers in the blanket.
+Tokamak from plasma
+-------------------
+
+- The tokamak_from_plasma function provides a parametric tokamak shaped reactor.
+- This reactor requires few arguments to create as it keeps the vertical build of the blanket layers the same thickness as the radial build.
.. cadquery::
:gridsize: 0
@@ -74,8 +101,7 @@ Tokamak
:height: 600px
import paramak
-
- result = paramak.tokamak(
+ result = paramak.tokamak_from_plasma(
radial_build=[
(paramak.LayerType.GAP, 10),
(paramak.LayerType.SOLID, 30),
@@ -90,26 +116,16 @@ Tokamak
(paramak.LayerType.SOLID, 120),
(paramak.LayerType.SOLID, 10),
],
- vertical_build=[
- (paramak.LayerType.SOLID, 15),
- (paramak.LayerType.SOLID, 80),
- (paramak.LayerType.SOLID, 10),
- (paramak.LayerType.GAP, 50),
- (paramak.LayerType.PLASMA, 700),
- (paramak.LayerType.GAP, 60),
- (paramak.LayerType.SOLID, 10),
- (paramak.LayerType.SOLID, 40),
- (paramak.LayerType.SOLID, 15),
- ],
+ elongation=2,
triangularity=0.55,
- rotation_angle=180,
+ rotation_angle=90,
).toCompound()
+
.. code-block:: python
import paramak
-
- result = paramak.tokamak(
+ result = paramak.tokamak_from_plasma(
radial_build=[
(paramak.LayerType.GAP, 10),
(paramak.LayerType.SOLID, 30),
@@ -124,23 +140,22 @@ Tokamak
(paramak.LayerType.SOLID, 120),
(paramak.LayerType.SOLID, 10),
],
- vertical_build=[
- (paramak.LayerType.SOLID, 15),
- (paramak.LayerType.SOLID, 80),
- (paramak.LayerType.SOLID, 10),
- (paramak.LayerType.GAP, 50),
- (paramak.LayerType.PLASMA, 700),
- (paramak.LayerType.GAP, 60),
- (paramak.LayerType.SOLID, 10),
- (paramak.LayerType.SOLID, 40),
- (paramak.LayerType.SOLID, 15),
- ],
+ elongation=2,
triangularity=0.55,
- rotation_angle=180,
+ rotation_angle=90,
)
- result.save(f"tokamak_minimal.step")
+Tokamak with divertor(s)
+------------------------
+TODO
+
+Tokamak with poloidal field coils
+---------------------------------
+TODO
+Tokamak with toroidal field coils
+---------------------------------
+TODO
Tokamak with negative triangularity
diff --git a/docs/usage_vis.rst b/docs/usage_vis.rst
new file mode 100644
index 00000000..c80d7373
--- /dev/null
+++ b/docs/usage_vis.rst
@@ -0,0 +1,24 @@
+
+Visualization options
+=====================
+
+The reactors (cadquery.Assembly objects) and components (cadquery.Workplane objects) can be visualized in a number of ways.
+First option is to export them to STEP, BREP or STL file then open the file with your favorite CAD software for example `FreeCAD `_.
+See the `CadQuery documentation on saving `_ for more information.
+Other options include the built in visualization tools in CadQuery
+
+.. code-block:: python
+
+ from cadquery.vis import show
+ show(result) # where result is the returned reactor or component object
+
+Jupyter_cadquery package can be installed (pip install jupyter-cadquery) which allows for interactive 3D visualization in a web browser.
+This package can also be used to export portable html files with the 3D visualization.
+
+.. code-block:: python
+
+ # pip install jupyter_cadquery
+ # might needed to downgrade pip with ... python -m pip install pip==24.0
+ from jupyter_cadquery import show
+ view = show(result) # where result is the returned reactor or component object
+ view.export_html("3d.html")
diff --git a/examples/spherical_tokamak_from_plasma_with_tf_magnets.py b/examples/spherical_tokamak_from_plasma_with_tf_magnets.py
index d458fa83..fdc5e4fa 100644
--- a/examples/spherical_tokamak_from_plasma_with_tf_magnets.py
+++ b/examples/spherical_tokamak_from_plasma_with_tf_magnets.py
@@ -4,13 +4,21 @@
import paramak
-tf = paramak.toroidal_field_coil_rectangle(
+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, 120, 150, 180],
+ azimuthal_placement_angles = [0, 30, 60, 90],
+)
+
+tf_style_2 = paramak.toroidal_field_coil_princeton_d(
+ r1=5,
+ r2=610,
+ azimuthal_placement_angles = [120, 150, 180],
+ thickness = 50,
+ distance = 40
)
result = paramak.spherical_tokamak_from_plasma(
@@ -28,7 +36,7 @@
elongation=2.5,
rotation_angle=180,
triangularity=0.55,
- extra_cut_shapes=[tf]
+ extra_cut_shapes=[tf_style_1, tf_style_2]
)
result.save(f"spherical_tokamak_minimal.step")
diff --git a/src/paramak/__init__.py b/src/paramak/__init__.py
index f793fab8..376795d8 100644
--- a/src/paramak/__init__.py
+++ b/src/paramak/__init__.py
@@ -14,6 +14,7 @@
from .workplanes.poloidal_field_coil_case import poloidal_field_coil_case
from .workplanes.toroidal_field_coil_rectangle import toroidal_field_coil_rectangle
from .workplanes.u_shaped_dome import u_shaped_dome
+from .workplanes.toroidal_field_coil_princeton_d import toroidal_field_coil_princeton_d
from .utils import LayerType
diff --git a/src/paramak/workplanes/toroidal_field_coil_princeton_d.py b/src/paramak/workplanes/toroidal_field_coil_princeton_d.py
new file mode 100644
index 00000000..28870aa5
--- /dev/null
+++ b/src/paramak/workplanes/toroidal_field_coil_princeton_d.py
@@ -0,0 +1,206 @@
+import typing
+import numpy as np
+from ..utils import create_wire_workplane_from_points, rotate_solid
+from scipy import integrate
+from scipy.optimize import minimize
+from typing import List, Tuple
+
+def _compute_inner_points(R1, R2):
+ """Computes the inner curve points
+
+ Args:
+ R1 (float): smallest radius (cm)
+ R2 (float): largest radius (cm)
+
+ Returns:
+ (list, list, list): R, Z and derivative lists for outer curve
+ points
+ """
+
+ def error(z_0, R0, R2):
+ segment = get_segment(R0, R2, z_0)
+ return abs(segment[1][-1])
+
+ def get_segment(a, b, z_0,num=5):
+ a_R = np.linspace(a, b, num=num, endpoint=True)
+ asol = integrate.odeint(solvr, [z_0[0], 0], a_R)
+ return a_R, asol[:, 0], asol[:, 1]
+
+ def solvr(Y, R):
+ return [Y[1], -1 / (k * R) * (1 + Y[1] ** 2) ** (3 / 2)]
+
+ R0 = (R1 * R2) ** 0.5
+ k = 0.5 * np.log(R2 / R1)
+
+ # computing of z_0
+ # z_0 is computed by ensuring outer segment end is zero
+ z_0 = 10 # initial guess for z_0
+ res = minimize(error, z_0, args=(R0, R2))
+ z_0 = res.x
+
+ # compute inner and outer segments
+ segment1 = get_segment(R0, R1, z_0)
+ segment2 = get_segment(R0, R2, z_0)
+
+ r_values = np.concatenate(
+ [
+ np.flip(segment1[0]),
+ segment2[0][1:],
+ np.flip(segment2[0])[1:],
+ segment1[0][1:],
+ ]
+ )
+ z_values = np.concatenate(
+ [
+ np.flip(segment1[1]),
+ segment2[1][1:],
+ -np.flip(segment2[1])[1:],
+ -segment1[1][1:],
+ ]
+ )
+ return r_values, z_values
+
+
+def add_thickness(x: List[float], y: List[float], thickness: float, dy_dx: List[float] = None) -> Tuple[list, list]:
+ """Computes outer curve points based on thickness
+
+ Args:
+ x (list): list of floats containing x values
+ y (list): list of floats containing y values
+ thickness (float): thickness of the magnet
+ dy_dx (list): list of floats containing the first order
+ derivatives
+
+ Returns:
+ R and Z lists for outer curve points
+ """
+
+ if dy_dx is None:
+ dy_dx = np.diff(y) / np.diff(x)
+
+ x_outer, y_outer = [], []
+ for i in range(len(dy_dx)):
+ if dy_dx[i] == float("-inf"):
+ nx, ny = -1, 0
+ elif dy_dx[i] == float("inf"):
+ nx, ny = 1, 0
+ else:
+ nx = -dy_dx[i]
+ ny = 1
+ if i != len(dy_dx) - 1:
+ if x[i] < x[i + 1]:
+ convex = False
+ else:
+ convex = True
+
+ if convex:
+ nx *= -1
+ ny *= -1
+ # normalize normal vector
+ normal_vector_norm = (nx**2 + ny**2) ** 0.5
+ nx /= normal_vector_norm
+ ny /= normal_vector_norm
+ # calculate outer points
+ val_x_outer = x[i] + thickness * nx
+ val_y_outer = y[i] + thickness * ny
+ x_outer.append(val_x_outer)
+ y_outer.append(val_y_outer)
+
+ return x_outer, y_outer
+
+
+def find_points(R1, R2, thickness,vertical_displacement):
+ """Finds the XZ points joined by connections that describe the 2D
+ profile of the toroidal field coil shape."""
+ # compute inner points
+ r_inner, z_inner = _compute_inner_points(R1 + thickness, R2)
+
+ # compute outer points
+ dz_dr = np.diff(z_inner) / np.diff(r_inner)
+ dz_dr[0] = float("-inf")
+ dz_dr = np.append(dz_dr, float("inf"))
+ r_outer, z_outer = add_thickness(r_inner, z_inner, thickness, dy_dx=dz_dr)
+ r_outer, z_outer = np.flip(r_outer), np.flip(z_outer)
+
+ # add vertical displacement
+ z_outer += vertical_displacement
+ z_inner += vertical_displacement
+
+ # extract helping points for inner leg
+ inner_leg_connection_points = [
+ (r_inner[0], z_inner[0]),
+ (r_inner[-1], z_inner[-1]),
+ (r_outer[0], z_outer[0]),
+ (r_outer[-1], z_outer[-1]),
+ ]
+
+ # add connections
+ inner_points = [[r, z, "spline"] for r, z in zip(r_inner, z_inner)]
+ outer_points = [[r, z, "spline"] for r, z in zip(r_outer, z_outer)]
+
+ inner_points[-1][2] = "straight"
+ outer_points[-1][2] = "straight"
+
+ points = inner_points + outer_points
+ outer_points = np.vstack((r_outer, z_outer)).T
+ inner_points = np.vstack((r_inner, z_inner)).T
+
+ return points, inner_leg_connection_points, inner_points, outer_points
+
+def toroidal_field_coil_princeton_d(
+ r1: float = 100,
+ r2: float = 300,
+ thickness: float = 30,
+ distance: float = 20,
+ name: str = "toroidal_field_coil",
+ with_inner_leg: bool = True,
+ azimuthal_placement_angles: typing.Sequence[float] = [0],
+ vertical_displacement: float = 0.0,
+ color: typing.Tuple[float, float, float, typing.Optional[float]] = (0.0, 0.0, 1.0),
+ plane: str = "XZ",
+ origin: typing.Tuple[float, float, float] = (0.0, 0.0, 0.0),
+ obj=None,
+):
+ """
+ Creates a toroidal field coil with a Princeton-D shape.
+
+ Args:
+ r1 (float, optional): Inner radius of the coil. Defaults to 100.
+ 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.
+ 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].
+ vertical_displacement (float, optional): Vertical displacement of the coil. Defaults to 0.0.
+ color (typing.Tuple[float, float, float, typing.Optional[float]], optional): Color of the coil. Defaults to (0.0, 0.0, 1.0).
+ plane (str, optional): Plane in which to create the coil. Defaults to "XZ".
+ origin (typing.Tuple[float, float, float], optional): Origin point for the coil. Defaults to (0.0, 0.0, 0.0).
+ obj (optional): Existing object to modify. Defaults to None.
+
+ Returns:
+ solid: The created toroidal field coil solid.
+ """
+ points, inner_leg_connection_points, inner_points, outer_points = find_points(r1, r2, thickness, vertical_displacement)
+ # need to get square end, it appears to miss the last point in the solid, TODO fix so this append is not needed
+ points.append(points[-1])
+ wire = create_wire_workplane_from_points(points=points, plane=plane, origin=origin, obj=obj)
+ solid = wire.extrude(until=distance / 2, both=True)
+ solid = rotate_solid(angles=azimuthal_placement_angles, solid=solid)
+
+ if with_inner_leg:
+ 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
+ )
+ inner_solid = inner_wire.extrude(until=distance / 2, both=True)
+ inner_solid = rotate_solid(angles=azimuthal_placement_angles, solid=inner_solid)
+ solid = solid.union(inner_solid)
+
+ 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 61c46977..f8ad3c74 100644
--- a/src/paramak/workplanes/toroidal_field_coil_rectangle.py
+++ b/src/paramak/workplanes/toroidal_field_coil_rectangle.py
@@ -10,7 +10,7 @@ def toroidal_field_coil_rectangle(
distance: float = 20,
name: str = "toroidal_field_coil",
with_inner_leg: bool = True,
- azimuthal_placement_angles: typing.Sequence[float] = [0, 90, 180],
+ azimuthal_placement_angles: typing.Sequence[float] = [0],
vertical_displacement: float = 0.0,
color: typing.Tuple[float, float, float, typing.Optional[float]] = (0.0, 0.0, 1.0),
plane: str = "XZ",
diff --git a/tests/test_workplanes/test_toroidal_field_coil_princeton_d.py b/tests/test_workplanes/test_toroidal_field_coil_princeton_d.py
new file mode 100644
index 00000000..d1f6cf0d
--- /dev/null
+++ b/tests/test_workplanes/test_toroidal_field_coil_princeton_d.py
@@ -0,0 +1,8 @@
+
+import paramak
+
+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