Electrode Assemblies¶
Electrode assemblies combine layups into a three-dimensional structure. Two families are supported:
| Type | Description | Cell Format |
|---|---|---|
WoundJellyRoll |
Round wound jelly roll | Cylindrical |
FlatWoundJellyRoll |
Flat (racetrack) wound jelly roll | Prismatic |
PunchedStack |
Punched and stacked electrodes | Prismatic, Pouch, Flex-frame |
ZFoldStack |
Z-fold separator with stacked electrodes | Prismatic, Pouch |
Jelly Rolls¶
Wound electrode assemblies for cylindrical and flat-wound prismatic cells.
JellyRolls
¶
Jelly roll electrode assemblies wound around cylindrical or flat mandrels.
Classes¶
TapeDriver
¶
Bases: Enum
Enumeration for tape length driver modes.
JELLY_ROLL_DRIVEN: Tape length is calculated from additional_tape_wraps TAPE_DRIVEN: Additional_tape_wraps is calculated from tape.length
WoundJellyRoll
¶
WoundJellyRoll(laminate: Laminate, mandrel: RoundMandrel, tape: Optional[Tape] = None, additional_tape_wraps: float = 0, collector_tab_crumple_factor: float = 50.0, name: str = 'Wound Jelly Roll')
Bases: _JellyRoll
Wound jelly roll electrode assembly for cylindrical cells.
A wound jelly roll consists of electrodes and separators wound around a round mandrel in a circular spiral pattern. This configuration is commonly used in cylindrical lithium-ion batteries.
Parameters¶
laminate : Laminate The layup structure containing electrode and separator layers mandrel : RoundMandrel Round mandrel for cylindrical winding
Attributes¶
radius : float Outer radius of the wound roll in mm diameter : float Outer diameter of the wound roll in mm
Examples¶
from steer_opencell_design import Laminate, RoundMandrel mandrel = RoundMandrel(radius=0.001) # 1mm radius layup = Laminate(...) # Define layup structure jelly_roll = WoundJellyRoll(laminate=layup, mandrel=mandrel) print(f"Outer diameter: {jelly_roll.diameter} mm")
Initialize wound jelly roll electrode assembly.
Parameters¶
laminate : Laminate The layup structure to be wound mandrel : RoundMandrel Round mandrel for cylindrical winding
Raises¶
TypeError If mandrel is not RoundMandrel or laminate is not Laminate
Attributes¶
radius
property
writable
¶
Return the outer radius of the wound jelly roll in mm.
Returns¶
float Outer radius in millimeters, rounded to 2 decimal places
radius_hard_range
property
¶
Return the hard radius range (min, max) of the wound jelly roll in mm.
diameter
property
writable
¶
Return the outer diameter of the wound jelly roll in mm.
diameter_range
property
¶
Return the diameter range (min, max) of the wound jelly roll in mm.
diameter_hard_range
property
¶
Return the hard diameter range (min, max) of the wound jelly roll in mm.
Methods:¶
from_flat_jelly_roll
classmethod
¶
from_flat_jelly_roll(flat_jelly_roll: FlatWoundJellyRoll) -> WoundJellyRoll
Create a WoundJellyRoll from an existing FlatWoundJellyRoll.
This method takes a flat wound jelly roll and creates a cylindrical wound jelly roll using the same layup structure. A round mandrel is automatically created based on the flat mandrel's properties.
Parameters¶
flat_jelly_roll : FlatWoundJellyRoll The flat wound jelly roll to convert from
Returns¶
WoundJellyRoll A new wound jelly roll with the same layup as the flat jelly roll
Raises¶
TypeError If flat_jelly_roll is not a FlatWoundJellyRoll instance
Examples¶
flat_roll = FlatWoundJellyRoll(layup, flat_mandrel) wound_roll = WoundJellyRoll.from_flat_jelly_roll(flat_roll)
FlatWoundJellyRoll
¶
FlatWoundJellyRoll(laminate: Laminate, mandrel: FlatMandrel, tape: Optional[Tape] = None, additional_tape_wraps: float = 0, collector_tab_crumple_factor: float = 50.0, name: str = 'Flat Wound Jelly Roll', cathode_notch_alignment_position: Optional[float] = None, anode_notch_alignment_position: Optional[float] = None)
Bases: _JellyRoll
Flat wound jelly roll electrode assembly for prismatic cells.
A flat wound jelly roll consists of electrodes and separators wound around a flat mandrel in a racetrack spiral pattern. This configuration is commonly used in prismatic and pouch lithium-ion batteries and includes hot-pressing simulation.
Parameters¶
laminate : Laminate The layup structure containing electrode and separator layers mandrel : FlatMandrel Flat mandrel for racetrack winding
Attributes¶
thickness : float Overall thickness of the flat wound roll in mm width : float Overall width of the flat wound roll in mm pressed_radius : float Mandrel radius after hot pressing in mm pressed_straight_length : float Mandrel straight length after hot pressing in mm
Examples¶
from steer_opencell_design import Laminate, FlatMandrel mandrel = FlatMandrel(radius=0.002, straight_length=0.050) # 2mm radius, 50mm straight layup = Laminate(...) # Define layup structure jelly_roll = FlatWoundJellyRoll(laminate=layup, mandrel=mandrel) print(f"Dimensions: {jelly_roll.width} x {jelly_roll.thickness} mm")
Initialize flat wound jelly roll electrode assembly.
Parameters¶
laminate : Laminate
The layup structure to be wound
mandrel : FlatMandrel
Flat mandrel for racetrack winding
cathode_notch_alignment_position : float, optional
Cathode notch-center position in mm from the left edge of the
unrotated pressed mandrel's straight racetrack section. The
complete tab must lie on that straight section.
anode_notch_alignment_position : float, optional
Anode notch-center position in mm from the same straight-section edge.
Cathode/anode transverse or longitudinal arrangement is controlled
by laminate.electrode_orientation.
Raises¶
TypeError If mandrel is not FlatMandrel or laminate is not Laminate
Attributes¶
pressed_radius
property
¶
Return the pressed mandrel radius in mm.
Returns¶
float Pressed mandrel radius in millimeters, rounded to 2 decimal places
pressed_straight_length
property
¶
Return the pressed mandrel straight length in mm.
cathode_notch_alignment_position
property
writable
¶
Return the cathode notch center from the straight section's left edge.
anode_notch_alignment_position
property
writable
¶
Return the anode notch center from the straight section's left edge.
notches_aligned
property
¶
Return whether BOTH electrodes' notches are aligned.
A cell that welds one tab stack per electrode -- a pouch -- needs both,
so this is the whole-roll predicate rather than a per-electrode one.
Distinct from :meth:can_align_notches, which asks whether an electrode
could be aligned; this asks whether it currently is.
footprint_datum_xy
property
¶
Return the (x, y) the footprint is centered on, in meters.
foil_extent_y
property
¶
Return the wound roll's y extent, in meters.
The base implementation measures the collectors' own foil outlines, which for a roll describe the UNWOUND sheet: its tabs stand at full cut height in a frame the wound cell never occupies. Sizing an enclosure from that inflates it (a 4 mm clipped tab grew the pouch by 3 mm over the roll it holds). The roll's own height is the answer, and it already accounts for the crumpled tabs.
cathode_notch_alignment_position_range
property
¶
Return the valid cathode notch alignment position range in mm.
anode_notch_alignment_position_range
property
¶
Return the valid anode notch alignment position range in mm.
thickness_aware_notch_data
property
¶
Return configured notch centers, spacings, and gaps for each electrode.
thickness
property
writable
¶
Return the overall jelly roll thickness in millimeters.
thickness_range
property
¶
Return the thickness range (min, max) of the flat wound jelly roll in mm.
width_range
property
¶
Return the width range (min, max) of the flat wound jelly roll in mm.
Methods:¶
from_round_jelly_roll
classmethod
¶
from_round_jelly_roll(round_jelly_roll: WoundJellyRoll, width: float = None) -> FlatWoundJellyRoll
Create a FlatWoundJellyRoll from an existing WoundJellyRoll.
This method takes a wound jelly roll and creates a flat wound jelly roll using the same layup structure. A flat mandrel is automatically created based on the round mandrel's properties.
Parameters¶
round_jelly_roll : WoundJellyRoll The wound jelly roll to convert from width : float, optional The width of the flat mandrel in mm. If not provided, defaults to diameter * 11 (creating a flat mandrel with a straight section of 10x the diameter).
Returns¶
FlatWoundJellyRoll A new flat wound jelly roll with the same layup as the wound jelly roll
Raises¶
TypeError If round_jelly_roll is not a WoundJellyRoll instance
Examples¶
wound_roll = WoundJellyRoll(layup, round_mandrel) flat_roll = FlatWoundJellyRoll.from_round_jelly_roll(wound_roll) flat_roll_custom = FlatWoundJellyRoll.from_round_jelly_roll(wound_roll, width=50)
convert_to_aligned_notches
¶
convert_to_aligned_notches(position: Optional[float] = None, tab_width: Optional[float] = None, electrodes: Union[str, Iterable[str]] = ('cathode', 'anode')) -> Dict[str, Dict[str, Any]]
Give the named electrodes notched collectors with aligned notches.
Replaces each electrode's current collector with the equivalent
NotchedCurrentCollector (via the matching from_* classmethod)
and then aligns its notches, in a single recalculation. Electrodes that
already carry a notched collector keep it, so repeated calls are safe.
Parameters¶
position : float, optional
Notch-center position in mm from the left edge of the pressed
mandrel's straight racetrack section. Defaults to the center of
that section, which is the one position that is valid whenever the
tab fits at all. A cell knows where its terminal sits and should
pass that instead when the physical location matters.
tab_width : float, optional
Tab width in mm applied to every converted collector. Defaults to
whatever the collector already carries -- note that
NotchedCurrentCollector.from_tabless starts at 50 mm, which a
short straight section may not accommodate.
electrodes : str or iterable of str, default ("cathode", "anode")
Which electrodes to convert. Pass a single name to convert one.
Returns¶
Dict[str, Dict[str, Any]]
thickness_aware_notch_data for the resulting configuration.
Raises¶
ValueError
If the requested (or default) tab width cannot be aligned anywhere
on this roll's straight section, or if an explicit position
falls outside the valid range. Nothing is mutated in either case.
can_align_notches
¶
Return whether an electrode's collector can carry aligned notches.
Alignment needs discrete tabs to place, so it is available only where
the electrode carries a NotchedCurrentCollector that is not a
TablessCurrentCollector. Callers deciding whether to offer
alignment should use this rather than whether a position happens to be
set, which is a different question.
Parameters¶
electrode : str
'cathode' or 'anode'.
default_notch_alignment_position
¶
Return the position in mm to align an electrode's notches at.
The midpoint of the straight racetrack section: the one position that is valid whenever the tab fits at all, and the furthest from both tangents.
Raises¶
TypeError If the electrode's collector cannot carry aligned notches.
notch_alignment_center_x
¶
Return the x of an electrode's aligned notch stack, in mm.
The physical coordinate in the roll's own frame -- which is what a cell
needs to put a terminal on the tab stack. Not to be confused with
<electrode>_notch_alignment_position, which is measured along the
straight racetrack section, nor with the collector's
tab_center_positions, which are distances along the UNWOUND foil and
have no meaning as a position in the wound cell.
Raises¶
TypeError If the electrode's collector cannot carry aligned notches. ValueError If the electrode's notches are not currently aligned.
tab_stack_x
¶
Return the x of an electrode's tab stack, in meters, in the roll's frame.
tab_extends_positive_y
¶
Return whether an electrode's tab leaves the footprint toward +y.
Read off the built geometry rather than a cathode-up convention: a Laminate expresses electrode orientation by flipping collector coordinates, so the coordinates are the only reliable answer.
tab_tip_y
¶
Return the y a notch stack actually reaches, in meters.
Winding crumples an integral tab, so the stack stands
1 - collector_tab_crumple_factor of its cut height off the foil
edge -- the same height _calculate_notch_stack_top_down_coords
draws it at. A cell welding a terminal to this stack has to meet it
where it physically ends, not where an uncrumpled tab of that height
would have.
plot_notch_alignment
¶
Plot the flat-wound cross-section with aligned notch-center markers.
Markers should form one constant-x stack for each configured electrode.
Stacks¶
Stacked electrode assemblies for prismatic, pouch, and flex-frame cells.
Stacks
¶
Stacked electrode assembly configurations (Z-fold and punched).
Classes¶
ZFoldStack
¶
ZFoldStack(layup: ZFoldMonoLayer, n_layers: int, additional_separator_wraps: float = 1, name: str = 'ZFoldStack')
Bases: _Stack
Z-Fold stack electrode assembly.
Accepts a ZFoldMonoLayer layup representing stacked sheets in a Z-fold configuration.
Additional separator wraps provide extra insulation around the stack.
Parameters¶
layup : ZFoldMonoLayer The Z-fold layup configuration n_layers : int Number of electrode layers in the stack additional_separator_wraps : float, optional Number of additional separator wraps around the stack, by default 1
PunchedStack
¶
PunchedStack(layup: MonoLayer, n_layers: int, name: str = 'PunchedStack')
Bases: _Stack
Punched mono-layer stack electrode assembly.
Accepts a MonoLayer layup representing stacked sheets in a mono-layer configuration.
This configuration is typically used with punched current collectors.
Parameters¶
layup : MonoLayer The mono-layer layup configuration n_layers : int Number of electrode layers in the stack
Tape¶
Termination tape used to secure wound assemblies.
Tape
¶
Termination tape for wound electrode assemblies.
Classes¶
Tape
¶
Tape(material: TapeMaterial, thickness: float, width: float = None, length: float = None, datum: Tuple[float, float, float] = (0.0, 0.0, 0.0), name: str = 'Tape')
Bases: CoordinateMixin, DatumMixin, ValidationMixin, DunderMixin, PlotterMixin, PropagationMixin, SerializerMixin
Adhesive tape used to secure the outer wraps of wound jelly roll assemblies. Manages tape geometry (width, thickness) and material properties.
Initialize an object that represents insulating tape
Parameters¶
material : TapeMaterial Material of the tape. thickness : float Thickness of the tape in um. width : float, optional Width of the tape in mm. If None, bulk properties won't be calculated until set. length : float, optional Length of the tape in mm. If None, bulk properties won't be calculated until set. datum : Tuple[float, float, float], optional Reference point (x, y, z) in mm. Defaults to (0.0, 0.0, 0.0). name : str, optional Name of the tape. Defaults to 'Tape'.
Winding Equipment¶
Mandrel geometry for jelly roll winding.
WindingEquipment
¶
Winding mandrel definitions for jelly roll electrode assemblies.
Classes¶
RoundMandrel
¶
RoundMandrel(diameter: float, length: float, datum: Tuple[float, float, float] = (0, 0, 0), material=None, name='Mandrel')
Bases: _Mandrel
Cylindrical mandrel for winding round jelly rolls. Defined by diameter and length.
Initialize a round (cylindrical) mandrel.
Parameters¶
diameter : float Mandrel outer diameter in mm. length : float Mandrel length in mm. datum : Tuple[float, float, float], optional Reference position (x, y, z) in mm. material : CurrentCollectorMaterial, optional Mandrel material. name : str, optional Display name.
FlatMandrel
¶
FlatMandrel(length: float, width: float, height: float, datum: Tuple[float, float, float] = (0, 0, 0), material: CurrentCollectorMaterial = None, name: str = 'Flat Mandrel')
Bases: _Mandrel
Flat (racetrack) mandrel for winding flat jelly rolls. Defined by width, height, and length.
Initialize a flat (racetrack) mandrel.
Parameters¶
length : float Mandrel length in mm (along the winding axis). width : float Mandrel width in mm (total racetrack width). height : float Mandrel height in mm (total racetrack height, defines end radius). datum : Tuple[float, float, float], optional Reference position (x, y, z) in mm. material : CurrentCollectorMaterial, optional Mandrel material. name : str, optional Display name.
Spiral Utilities¶
Low-level functions for spiral geometry calculations.
SpiralUtils
¶
Static utility methods for spiral and racetrack geometry calculations used in jelly roll winding.
Classes¶
SpiralCalculator
¶
Utility class providing static methods for spiral and racetrack geometry calculations. Computes variable-thickness spirals, builds component-level spiral coordinates, and handles racetrack (flat-wound) geometry for FlatWoundJellyRoll assemblies.
Methods:¶
aligned_positions_at_x
staticmethod
¶
Return two unwrapped centers per complete turn at a fixed x-coordinate.
Length inputs and outputs use meters. target_x is expressed in the
supplied spiral's unrotated racetrack coordinate system. The turn
column, which is derived from winding phase, separates successive
turns. A complete turn crosses target_x once on each straight
branch, producing one notch on either side of the racetrack's z-axis.
Partial turns without both crossings are omitted.
Keeping target_x far enough inside a straight racetrack section for
the whole tab is the caller's responsibility (see
FlatWoundJellyRoll._notch_alignment_target_x). On a straight branch
z is constant, so unwrapped distance and x-distance are identical there
and no separate endpoint projection is needed.
calculate_variable_thickness_spiral
staticmethod
¶
calculate_variable_thickness_spiral(laminate: Laminate, start_radius: float, dtheta: Optional[float] = None, **kwargs) -> ndarray
Integrate a variable-thickness Archimedean-like spiral (clockwise) with adaptive RK4.
Governing relations (parametrized by θ, clockwise so θ decreases from π/2): dr/dθ = t(x) / (2π) ds/dθ = sqrt(r² + (dr/dθ)²) (local arc length rate) dx_unwrapped/dθ = ds/dθ (treat unwrapped length x as accumulated arc length)
With spatially varying thickness t(x) provided by layup.get_thickness_at_x(x).
Improvements over prior implementation
- 4th order Runge–Kutta integration for coupled (r, x) system.
- Adaptive step sizing via local Richardson error estimate (1 step h vs 2 half-steps).
- Automatic fallback to analytic uniform-thickness solution when thickness variation is negligible.
- Gradient-aware minimum step to avoid under-resolving rapid thickness changes.
- Early termination and endpoint interpolation to land exactly on total unwrapped length.
- Conservative iteration / memory limits to avoid runaway loops.
Parameters¶
dtheta : float, optional Nominal (maximum) angular step magnitude (radians). Smaller steps are chosen adaptively as needed. If None, will look for 'dtheta' in kwargs, defaults to 0.5. **kwargs Additional keyword arguments. Can include 'dtheta' as alternative parameter specification.
Returns¶
np.ndarray Columns: [theta, x_unwrapped, r, x, z, turns]
Raises¶
ValueError If laminate total length is invalid or spiral calculation fails
build_component_spirals
staticmethod
¶
build_component_spirals(base_spiral: ndarray, layup: Laminate, mandrel_radius: float) -> dict
Build component spirals by mapping flattened center lines onto the wound spiral.
Maps individual electrode and separator components from the layup onto the base spiral geometry, accounting for their specific thickness and positioning.
Parameters¶
base_spiral : np.ndarray Base spiral coordinates [theta, x_unwrapped, r, x, z, turns] layup : Laminate The layup structure containing component center lines mandrel_radius : float Radius of the mandrel in meters
Returns¶
dict Component spirals keyed by component name
Raises¶
ValueError If component mapping fails or invalid geometry is encountered
build_extruded_component_spirals
staticmethod
¶
Build extruded component spirals by radially thickening center line spirals.
For each component, creates a filled shape by: 1. Taking the center line spiral from component_spirals 2. Creating outer spiral by adding thickness/2 to radius 3. Creating inner spiral by subtracting thickness/2 from radius 4. Reversing inner spiral direction for proper winding 5. Combining outer + inner spirals into a closed filled shape
Parameters¶
component_spirals : dict Component spirals from build_component_spirals component_thicknesses : dict Thicknesses for each component
Returns¶
dict Extruded component spirals for 2D visualization
Raises¶
ValueError If extrusion calculation fails or invalid thickness values
calculate_simple_spiral
staticmethod
¶
calculate_simple_spiral(n_turns: Optional[float] = None, start_radius: Optional[float] = None, thickness: Optional[float] = None, points_per_turn: int = 100, target_length: Optional[float] = None) -> ndarray
Calculate a simple uniform-thickness Archimedean spiral.
This function generates a basic spiral with constant thickness increment per turn, useful for simple geometric calculations or as a baseline comparison for more complex variable-thickness spirals.
Parameters¶
n_turns : float, optional Number of turns to generate. Either this or target_length must be provided, but not both. start_radius : float Starting radius in meters thickness : float Constant thickness per turn in meters points_per_turn : int, optional Number of points per complete turn, by default 100 target_length : float, optional Target unwrapped length in meters. Either this or n_turns must be provided, but not both.
Returns¶
np.ndarray Spiral coordinates with columns: [theta, x_unwrapped, r, x, z, turns] Same format as calculate_variable_thickness_spiral()
Raises¶
ValueError If both n_turns and target_length are provided, or if neither are provided, or if required parameters are missing.
calculate_variable_thickness_racetrack
staticmethod
¶
calculate_variable_thickness_racetrack(laminate: Laminate, mandrel_radius: float, straight_length: float, ds_target: Optional[float] = None, **kwargs) -> ndarray
Calculate spiral path for given mandrel geometry parameters (clockwise direction).
The racetrack consists of: - Two semicircular ends (radius = height/2) - Two straight sections (length = width - height)
Calculates spiral in clockwise direction, consistent with WoundJellyRoll.
Parameters¶
laminate : Laminate The layup structure containing thickness information mandrel_radius : float Radius of semicircular ends (height/2) in meters straight_length : float Length of straight sections (width - height) in meters ds_target : float, optional Target arc length step size in meters. If None, will look for 'ds_target' in kwargs, defaults to 0.5e-3. **kwargs Additional keyword arguments. Can include 'ds_target' as alternative parameter specification.
Returns¶
np.ndarray Columns: [theta, x_unwrapped, r, x, z, turns]
calculate_simple_racetrack
staticmethod
¶
calculate_simple_racetrack(n_turns: Optional[float] = None, start_radius: Optional[float] = None, straight_length: Optional[float] = None, thickness: Optional[float] = None, points_per_turn: int = 300, target_length: Optional[float] = None) -> ndarray
Calculate a simple uniform-thickness racetrack spiral.
This function generates a basic racetrack spiral with constant thickness increment per turn, useful for simple geometric calculations or as a baseline comparison for more complex variable-thickness racetracks.
Parameters¶
n_turns : float, optional Number of turns to generate. Either this or target_length must be provided, but not both. start_radius : float Starting radius of semicircular ends in meters straight_length : float Length of straight sections in meters thickness : float Constant thickness per turn in meters points_per_turn : int, optional Number of points per complete turn, by default 100 target_length : float, optional Target unwrapped length in meters. Either this or n_turns must be provided, but not both.
Returns¶
np.ndarray Racetrack coordinates with columns: [theta, x_unwrapped, r, x, z, turns] Same format as calculate_variable_thickness_racetrack()
Raises¶
ValueError If both n_turns and target_length are provided, or if neither are provided, or if required parameters are missing.
racetrack_position
staticmethod
¶
Calculate x,z position on racetrack at given parametric angle (clockwise direction).
For clockwise motion starting at top-right: - theta=2π: top of right semicircle - theta=3π/2: right side - theta=π: bottom of right semicircle - theta=π/2: bottom of left semicircle - theta=0: top of left semicircle
Parameters¶
theta : float Parametric angle (0 to 2π), decreases clockwise from 2π radius : float Current layer radius straight_length : float Length of straight sections
Returns¶
tuple (x_position, z_position) in meters
racetrack_curvature
staticmethod
¶
build_extruded_component_racetracks
staticmethod
¶
build_extruded_component_racetracks(component_spirals: dict, component_thicknesses: dict, mandrel_radius: float, straight_length: float) -> dict
Build extruded component spirals for a given component spirals dictionary and mandrel geometry.
This is a generalized version of build_extruded_component_spirals that can work with any component spirals dictionary and mandrel geometry parameters, used for hot-pressed spirals.
Parameters¶
component_spirals : dict Component spirals dictionary to extrude component_thicknesses : dict Thicknesses for each component mandrel_radius : float Radius of semicircular ends (height/2) in meters straight_length : float Length of straight sections (width - height) in meters
Returns¶
dict Extruded component spirals dictionary
create_flat_mandrel_thickness_spirals
staticmethod
¶
create_flat_mandrel_thickness_spirals(center_spiral: ndarray, half_thickness: float, mandrel_radius: float, straight_length: float) -> tuple
Create outer and inner spirals by applying thickness in correct directions.
Parameters¶
center_spiral : np.ndarray Center line spiral coordinates half_thickness : float Half of the component thickness mandrel_radius : float Radius of semicircular ends straight_length : float Length of straight sections
Returns¶
tuple (outer_spiral, inner_spiral) as numpy arrays
get_coordinate_based_adjustment_direction
staticmethod
¶
get_coordinate_based_adjustment_direction(x: float, z: float, mandrel_radius: float, straight_length: float) -> ndarray
Get the direction vector for height adjustment based on actual coordinates.
This method determines the adjustment direction by analyzing the actual position rather than using parametric angles, which avoids bunching issues on curved sections.
Parameters¶
x : float Current x coordinate z : float Current z coordinate mandrel_radius : float Radius of semicircular ends straight_length : float Length of straight sections
Returns¶
np.ndarray Unit vector [dx, dz] indicating direction for height adjustment
get_thickness_of_racetrack
staticmethod
¶
get_width_of_racetrack
staticmethod
¶
format_np_spiral_for_df
staticmethod
¶
Format numpy spiral array into pandas DataFrame with proper units and column names.
Parameters¶
np_array : np.ndarray Spiral array with columns [theta, x_unwrapped, r, x, z, turns]
Returns¶
pd.DataFrame Formatted DataFrame with columns: theta (degrees), x_unwrapped (mm), r (mm), x (mm), z (mm), turns
Raises¶
ValueError If input array has incorrect shape or invalid data
build_component_racetracks
staticmethod
¶
build_component_racetracks(base_spiral, layup, mandrel_radius, straight_length, original_mandrel_radius)
Build component spirals for racetrack geometry based on a base spiral.
This is a generalized version that can work with any mandrel geometry parameters, used for hot-pressed spirals in flat wound jelly rolls.
Parameters¶
base_spiral : np.ndarray Base spiral to map components onto layup : Laminate Layup object containing component names and flattened center lines mandrel_radius : float Radius of semicircular ends (height/2) in meters (pressed radius) straight_length : float Length of straight sections (width - height) in meters original_mandrel_radius : float Original mandrel radius before pressing in meters
Returns¶
dict Component spirals dictionary mapping component names to their spiral arrays
apply_flat_mandrel_height_adjustments
staticmethod
¶
Apply height adjustments to spiral points based on their position on the racetrack.
Parameters¶
spiral : np.ndarray Component spiral array (modified in-place) height_adjustments : np.ndarray Height adjustments to apply at each point mandrel_radius : float Radius of the semicircular ends straight_length : float Length of straight sections
Returns¶
np.ndarray Modified spiral array
min_thickness_orientation
staticmethod
¶
Exact minimal-thickness orientation via convex hull + rotating calipers.
The thickness after rotating by θ equals the extent of the point set
along the direction v(θ) = (sin θ, cos θ), and the minimum width
of a convex polygon is always attained perpendicular to one of its
edges. So the global optimum is found by scanning hull-edge normals —
no iterative search, no local minima.
Dense clouds are first reduced to their outer rim (max-radius points per angular bin around the centroid), which preserves the hull vertices of the star-shaped jelly-roll cross-sections to sub-µm accuracy while capping the qhull input size.
Parameters¶
valid_points : np.ndarray (N, 2) array of finite x-z points.
Returns¶
tuple[float, float, float]
(angle, thickness, width) where angle is the rotation (in
radians, mod π) that minimizes the z-extent, thickness is that
minimal z-extent, and width is the x-extent at that rotation.
Raises¶
Exception Degenerate inputs (e.g. all-collinear points) propagate qhull errors; callers fall back to the scalar search.
rotate_spiral_to_minimize_thickness
staticmethod
¶
rotate_spiral_to_minimize_thickness(spiral_data, x_col: int = X_COORD_COL, z_col: int = Z_COORD_COL, initial_angle: Optional[float] = None, bracket_width: float = pi / 30.0)
Rotate spiral data in x-z plane to minimize overall thickness.
Finds the rotation angle that minimizes the vertical extent (thickness = max(z) - min(z)) and applies it in-place to the spiral data. The angle is computed exactly with rotating calipers on the convex hull; a Brent scalar search over the raw points remains as a fallback for degenerate geometry that qhull rejects.
Parameters¶
spiral_data : Union[np.ndarray, Dict[str, np.ndarray]] Either a single spiral array or a dictionary of spiral arrays. Each array should have columns including x and z coordinates. x_col : int, optional Column index for x coordinates (default: X_COORD_COL) z_col : int, optional Column index for z coordinates (default: Z_COORD_COL) initial_angle : float, optional Legacy warm-start seed for the Brent fallback path. The rotating calipers path is non-iterative and ignores it. bracket_width : float, optional Half-width of the warm-start bracket used by the Brent fallback.
Returns¶
Tuple[Union[np.ndarray, Dict[str, np.ndarray]], float] (rotated_spiral_data, optimal_angle) where optimal_angle is in radians
Notes¶
- Rotation is performed about the centroid of all points.
- Optimization searches over [0, π) since thickness is symmetric about π.
- Modifies spiral_data in-place and returns it for convenience.
translate_spirals_xz
staticmethod
¶
translate_spirals_xz(spiral_data, x_shift: float, z_shift: float, x_col: int = X_COORD_COL, z_col: int = Z_COORD_COL)
Translate spiral coordinates by specified amounts in x and z directions.
This function applies a rigid body translation to spiral geometries. Commonly used for centering spirals or aligning them to a coordinate system.
Parameters¶
spiral_data : Union[np.ndarray, Dict[str, np.ndarray]] Either a single spiral array or a dictionary of spiral arrays. Each array should have columns including x and z coordinates. x_shift : float Translation distance in x-direction (meters) z_shift : float Translation distance in z-direction (meters) x_col : int, optional Column index for x coordinates (default: X_COORD_COL) z_col : int, optional Column index for z coordinates (default: Z_COORD_COL)
Returns¶
Union[np.ndarray, Dict[str, np.ndarray]] Translated spiral data (modified in-place and returned)
Notes¶
- Modifies spiral_data in-place and returns it for convenience
- Works with both single arrays and dictionaries of arrays