173 lines
7.0 KiB
Python
173 lines
7.0 KiB
Python
from __future__ import annotations
|
|
|
|
import math
|
|
from dataclasses import dataclass
|
|
|
|
from .models import ReducerInstance, StageDerived
|
|
|
|
|
|
CASCADE_INTERSTAGE_COMPONENT_IDS = {
|
|
"cascade_input_shaft",
|
|
"cascade_output_shaft",
|
|
"interstage_shaft",
|
|
"interstage_coupler",
|
|
"interstage_bearing_1",
|
|
"interstage_bearing_2",
|
|
"interstage_support_1",
|
|
"interstage_support_2",
|
|
}
|
|
|
|
|
|
def stage_carrier_plate_thickness_mm(stage_gap_mm: float) -> float:
|
|
return max(0.8, min(1.2, max(0.8, stage_gap_mm * 0.45)))
|
|
|
|
|
|
def sun_bore_radius_from_derived(derived: StageDerived) -> float:
|
|
return max(0.8, min(2.2, derived.sun_pitch_radius_mm * 0.35))
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CascadeInterstageDesign:
|
|
source_component_id: str
|
|
target_component_id: str
|
|
shaft_diameter_mm: float
|
|
shaft_bottom_z_mm: float
|
|
shaft_top_z_mm: float
|
|
s1_output_axis_z_mm: float
|
|
s2_input_axis_z_mm: float
|
|
coupler_inner_diameter_mm: float
|
|
coupler_outer_diameter_mm: float
|
|
coupler_width_mm: float
|
|
coupler_center_z_mm: float
|
|
bearing_inner_diameter_mm: float
|
|
bearing_outer_diameter_mm: float
|
|
bearing_width_mm: float
|
|
bearing_ball_diameter_mm: float
|
|
bearing_ball_count: int
|
|
bearing_centers_z_mm: dict[str, float]
|
|
support_inner_diameter_mm: float
|
|
support_outer_diameter_mm: float
|
|
support_width_mm: float
|
|
support_centers_z_mm: dict[str, float]
|
|
housing_inner_diameter_mm: float
|
|
housing_outer_diameter_mm: float
|
|
s1_carrier_top_z_mm: float
|
|
s2_sun_bottom_z_mm: float
|
|
s2_carrier_top_z_mm: float
|
|
s2_sun_bore_diameter_mm: float
|
|
|
|
@property
|
|
def shaft_span_mm(self) -> float:
|
|
return self.shaft_top_z_mm - self.shaft_bottom_z_mm
|
|
|
|
|
|
def _stage_by_id(instance: ReducerInstance) -> dict[str, StageDerived]:
|
|
return {stage.stage_id: stage for stage in (instance.derived.stages or [])}
|
|
|
|
|
|
def compute_cascade_interstage_design(instance: ReducerInstance) -> CascadeInterstageDesign:
|
|
if instance.topology_family != "simple_2k_h_cascade":
|
|
raise ValueError("cascade interstage design requires simple_2k_h_cascade")
|
|
if not instance.parameters.stages or len(instance.parameters.stages) != 2:
|
|
raise ValueError("cascade interstage design requires exactly two stage parameter sets")
|
|
if not instance.requirement.stages or len(instance.requirement.stages) != 2:
|
|
raise ValueError("cascade interstage design requires exactly two stage requirements")
|
|
|
|
stage_derived = _stage_by_id(instance)
|
|
s1 = instance.parameters.stages[0]
|
|
s2 = instance.parameters.stages[1]
|
|
s1d = stage_derived[s1.stage_id]
|
|
s2d = stage_derived[s2.stage_id]
|
|
stage_gap = float((instance.derived.axial_stack or {}).get("stage_gap_mm", 2.0))
|
|
s1_plate_thickness = stage_carrier_plate_thickness_mm(stage_gap)
|
|
s2_plate_thickness = stage_carrier_plate_thickness_mm(stage_gap)
|
|
s1_carrier_top = s1d.z_offset_mm + s1.face_width_mm + 0.25 + s1_plate_thickness
|
|
s2_sun_bottom = s2d.z_offset_mm
|
|
available_gap = max(0.35, s2_sun_bottom - s1_carrier_top)
|
|
|
|
s2_sun_bore_radius = sun_bore_radius_from_derived(s2d)
|
|
shaft_radius = min(
|
|
max(0.45, s2_sun_bore_radius * 0.72),
|
|
max(0.35, s2_sun_bore_radius - 0.20),
|
|
)
|
|
bearing_inner_radius = shaft_radius + max(0.08, s2.module_mm * 0.18)
|
|
bearing_outer_radius = bearing_inner_radius + max(0.35, s2.module_mm * 0.90)
|
|
bearing_width = max(0.28, min(0.60, available_gap * 0.45))
|
|
bearing_ball_diameter = max(
|
|
0.12,
|
|
min(
|
|
(bearing_outer_radius - bearing_inner_radius) * 0.62,
|
|
bearing_width * 0.42,
|
|
),
|
|
)
|
|
ball_pitch_radius = (bearing_inner_radius + bearing_outer_radius) / 2.0
|
|
estimated_ball_count = int((2.0 * math.pi * ball_pitch_radius) / (bearing_ball_diameter * 1.5))
|
|
ball_count = max(8, min(18, estimated_ball_count))
|
|
|
|
bearing_1_center = s1_carrier_top + available_gap * 0.35
|
|
s2_carrier_top = s2d.z_offset_mm + s2.face_width_mm + 0.25 + s2_plate_thickness
|
|
bearing_2_center = s2_carrier_top + 0.35 + bearing_width / 2.0
|
|
coupler_width = max(0.20, min(0.40, available_gap * 0.28))
|
|
coupler_center = s2_sun_bottom - coupler_width / 2.0 - 0.05
|
|
|
|
support_inner_radius = bearing_outer_radius + max(0.08, s2.module_mm * 0.20)
|
|
max_ring_outer_radius = max(stage.ring_outer_diameter_mm / 2.0 for stage in stage_derived.values())
|
|
housing_inner_radius = max_ring_outer_radius + 2.5
|
|
housing_outer_radius = max_ring_outer_radius + 5.0
|
|
support_outer_radius = housing_inner_radius - 0.25
|
|
coupler_inner_radius = shaft_radius + max(0.04, s2.module_mm * 0.10)
|
|
coupler_outer_radius = min(
|
|
support_inner_radius - max(0.08, s2.module_mm * 0.16),
|
|
s2_sun_bore_radius + max(0.25, s2.module_mm * 0.75),
|
|
)
|
|
coupler_outer_radius = max(coupler_outer_radius, coupler_inner_radius + max(0.20, s2.module_mm * 0.40))
|
|
|
|
shaft_bottom = max(s1d.z_offset_mm, s1.face_width_mm - 0.50)
|
|
shaft_top = bearing_2_center + bearing_width / 2.0 + 0.40
|
|
|
|
first_requirement, second_requirement = instance.requirement.stages
|
|
source_component_id = f"{first_requirement.stage_id}_{first_requirement.output}"
|
|
target_component_id = f"{second_requirement.stage_id}_{second_requirement.input}"
|
|
|
|
return CascadeInterstageDesign(
|
|
source_component_id=source_component_id,
|
|
target_component_id=target_component_id,
|
|
shaft_diameter_mm=2.0 * shaft_radius,
|
|
shaft_bottom_z_mm=shaft_bottom,
|
|
shaft_top_z_mm=shaft_top,
|
|
s1_output_axis_z_mm=s2d.z_offset_mm + s2.face_width_mm / 2.0,
|
|
s2_input_axis_z_mm=s2d.z_offset_mm + s2.face_width_mm / 2.0,
|
|
coupler_inner_diameter_mm=2.0 * coupler_inner_radius,
|
|
coupler_outer_diameter_mm=2.0 * coupler_outer_radius,
|
|
coupler_width_mm=coupler_width,
|
|
coupler_center_z_mm=coupler_center,
|
|
bearing_inner_diameter_mm=2.0 * bearing_inner_radius,
|
|
bearing_outer_diameter_mm=2.0 * bearing_outer_radius,
|
|
bearing_width_mm=bearing_width,
|
|
bearing_ball_diameter_mm=bearing_ball_diameter,
|
|
bearing_ball_count=ball_count,
|
|
bearing_centers_z_mm={
|
|
"interstage_bearing_1": bearing_1_center,
|
|
"interstage_bearing_2": bearing_2_center,
|
|
},
|
|
support_inner_diameter_mm=2.0 * support_inner_radius,
|
|
support_outer_diameter_mm=2.0 * support_outer_radius,
|
|
support_width_mm=bearing_width,
|
|
support_centers_z_mm={
|
|
"interstage_support_1": bearing_1_center,
|
|
"interstage_support_2": bearing_2_center,
|
|
},
|
|
housing_inner_diameter_mm=2.0 * housing_inner_radius,
|
|
housing_outer_diameter_mm=2.0 * housing_outer_radius,
|
|
s1_carrier_top_z_mm=s1_carrier_top,
|
|
s2_sun_bottom_z_mm=s2_sun_bottom,
|
|
s2_carrier_top_z_mm=s2_carrier_top,
|
|
s2_sun_bore_diameter_mm=2.0 * s2_sun_bore_radius,
|
|
)
|
|
|
|
|
|
def cascade_interstage_component_ids(instance: ReducerInstance) -> set[str]:
|
|
if instance.topology_family != "simple_2k_h_cascade":
|
|
return set()
|
|
return set(CASCADE_INTERSTAGE_COMPONENT_IDS)
|