Files
reducers/backend/src/configurations/simple_2k_h_cascade/family.py
T

316 lines
11 KiB
Python

from __future__ import annotations
import math
from ...models import (
BoundaryCondition,
ConcreteNode,
ConcreteRelation,
ConcreteTopology,
ReducerParameters,
ReducerRequirement,
StageDerived,
StageParameters,
StageRequirement,
)
MOTION_MEMBERS = {"sun", "ring", "carrier"}
def stage_component_id(stage_id: str, local_role: str) -> str:
return f"{stage_id}_{local_role}"
def local_role(component_id: str) -> str:
return component_id.split("_", 1)[1] if "_" in component_id else component_id
def stage_boundary(stage: StageRequirement) -> BoundaryCondition:
fixed_members = (
[stage_component_id(stage.stage_id, stage.fixed)]
if stage.fixed is not None
else []
)
return BoundaryCondition(
input=stage_component_id(stage.stage_id, stage.input),
fixed=fixed_members[0] if len(fixed_members) == 1 else None,
output=stage_component_id(stage.stage_id, stage.output),
fixed_members=fixed_members,
)
def cascade_boundary(requirement: ReducerRequirement) -> BoundaryCondition:
if not requirement.stages or len(requirement.stages) != 2:
raise ValueError("simple_2k_h_cascade requires exactly two stages")
fixed_members = []
for stage in requirement.stages:
if stage.fixed is not None:
fixed_members.append(stage_component_id(stage.stage_id, stage.fixed))
return BoundaryCondition(
input=stage_component_id(requirement.stages[0].stage_id, requirement.stages[0].input),
fixed=fixed_members[0] if len(fixed_members) == 1 else None,
output=stage_component_id(requirement.stages[1].stage_id, requirement.stages[1].output),
fixed_members=fixed_members,
)
def stage_geometry(parameters: StageParameters) -> dict[str, float]:
sun_pitch_radius = parameters.module_mm * parameters.z_sun / 2.0
planet_pitch_radius = parameters.module_mm * parameters.z_planet / 2.0
ring_pitch_radius = parameters.module_mm * parameters.z_ring / 2.0
center_distance = parameters.module_mm * (parameters.z_sun + parameters.z_planet) / 2.0
planet_outer_diameter = parameters.module_mm * (
parameters.z_planet + 2.0 * parameters.addendum_coeff
)
neighbor_distance = 2.0 * center_distance * math.sin(math.pi / parameters.planet_count)
ring_outer_diameter = (
parameters.module_mm * parameters.z_ring
+ 2.0 * parameters.module_mm * (parameters.addendum_coeff + parameters.clearance_coeff)
+ 2.0 * parameters.ring_rim_thickness_mm
)
return {
"sun_pitch_radius_mm": sun_pitch_radius,
"planet_pitch_radius_mm": planet_pitch_radius,
"ring_pitch_radius_mm": ring_pitch_radius,
"sun_pitch_diameter_mm": sun_pitch_radius * 2.0,
"planet_pitch_diameter_mm": planet_pitch_radius * 2.0,
"ring_pitch_diameter_mm": ring_pitch_radius * 2.0,
"center_distance_mm": center_distance,
"planet_orbit_radius_mm": center_distance,
"ring_outer_diameter_mm": ring_outer_diameter,
"planet_outer_diameter_mm": planet_outer_diameter,
"planet_neighbor_distance_mm": neighbor_distance,
"planet_neighbor_clearance_mm": neighbor_distance - planet_outer_diameter,
}
def stage_derived(
*,
parameters: StageParameters,
ratio: float,
direction: str,
speed_mode: str,
z_offset_mm: float,
) -> StageDerived:
return StageDerived(
stage_id=parameters.stage_id,
ratio=abs(ratio),
direction=direction,
speed_mode=speed_mode,
z_offset_mm=z_offset_mm,
**stage_geometry(parameters),
)
def stage_parameter_by_id(parameters: ReducerParameters, stage_id: str) -> StageParameters:
if not parameters.stages:
raise ValueError("cascade parameters are missing stages")
for stage in parameters.stages:
if stage.stage_id == stage_id:
return stage
raise ValueError(f"missing cascade stage parameters: {stage_id}")
def _stage_nodes(stage: StageParameters) -> list[ConcreteNode]:
nodes = [
ConcreteNode(
component_id=stage_component_id(stage.stage_id, "sun"),
template_node_id=stage_component_id(stage.stage_id, "sun"),
kind="gear",
role="sun",
gear_type="external",
repeat_index=None,
stage_id=stage.stage_id,
local_role="sun",
),
ConcreteNode(
component_id=stage_component_id(stage.stage_id, "ring"),
template_node_id=stage_component_id(stage.stage_id, "ring"),
kind="gear",
role="ring",
gear_type="internal",
repeat_index=None,
stage_id=stage.stage_id,
local_role="ring",
),
ConcreteNode(
component_id=stage_component_id(stage.stage_id, "carrier"),
template_node_id=stage_component_id(stage.stage_id, "carrier"),
kind="carrier",
role="carrier",
gear_type=None,
repeat_index=None,
stage_id=stage.stage_id,
local_role="carrier",
),
]
for index in range(1, stage.planet_count + 1):
nodes.append(
ConcreteNode(
component_id=f"{stage.stage_id}_planet_{index}",
template_node_id=stage_component_id(stage.stage_id, "planet"),
kind="gear",
role="planet",
gear_type="external",
repeat_index=index,
stage_id=stage.stage_id,
local_role="planet",
)
)
return nodes
def _rel(
*,
relation_id: str,
relation_type: str,
source: str,
target: str,
source_template_node: str,
target_template_node: str,
stage_id: str | None = None,
carrier: str | None = None,
mesh_id: str | None = None,
) -> ConcreteRelation:
return ConcreteRelation(
relation_id=relation_id,
template_relation_id=relation_id,
relation_type=relation_type,
source_component_id=source,
target_component_id=target,
source_template_node=source_template_node,
target_template_node=target_template_node,
carrier_component_id=carrier,
mesh_id=mesh_id,
stage_id=stage_id,
)
def _stage_relations(stage: StageParameters, requirement_stage: StageRequirement) -> list[ConcreteRelation]:
sid = stage.stage_id
sun = stage_component_id(sid, "sun")
ring = stage_component_id(sid, "ring")
carrier = stage_component_id(sid, "carrier")
relations: list[ConcreteRelation] = []
for index in range(1, stage.planet_count + 1):
planet = f"{sid}_planet_{index}"
relations.extend(
[
_rel(
relation_id=f"{sid}_sun_to_planet_{index}_external_mesh",
relation_type="external_mesh",
source=sun,
target=planet,
source_template_node=sun,
target_template_node=stage_component_id(sid, "planet"),
carrier=carrier,
mesh_id=f"{sid}_sun_planet",
stage_id=sid,
),
_rel(
relation_id=f"{sid}_ring_to_planet_{index}_internal_mesh",
relation_type="internal_mesh",
source=ring,
target=planet,
source_template_node=ring,
target_template_node=stage_component_id(sid, "planet"),
carrier=carrier,
mesh_id=f"{sid}_ring_planet",
stage_id=sid,
),
_rel(
relation_id=f"{sid}_planet_{index}_pin_revolute",
relation_type="revolute_joint",
source=planet,
target=carrier,
source_template_node=stage_component_id(sid, "planet"),
target_template_node=carrier,
carrier=carrier,
stage_id=sid,
),
]
)
for local in ["sun", "ring", "carrier"]:
component = stage_component_id(sid, local)
relations.append(
_rel(
relation_id=f"{sid}_{local}_main_axis_coaxial",
relation_type="coaxial",
source=component,
target="main_axis",
source_template_node=component,
target_template_node="main_axis",
stage_id=sid,
)
)
if requirement_stage.fixed is not None:
fixed_component = stage_component_id(sid, requirement_stage.fixed)
relations.append(
_rel(
relation_id=f"{sid}_{requirement_stage.fixed}_housing_fixed",
relation_type="fixed",
source=fixed_component,
target="housing",
source_template_node=fixed_component,
target_template_node="housing",
stage_id=sid,
)
)
return relations
def build_cascade_topology(
requirement: ReducerRequirement,
parameters: ReducerParameters,
) -> ConcreteTopology:
if not requirement.stages or not parameters.stages:
raise ValueError("cascade topology requires requirement.stages and parameters.stages")
stage_requirements = {stage.stage_id: stage for stage in requirement.stages}
nodes = [
ConcreteNode(
component_id="housing",
template_node_id="housing",
kind="housing",
role="housing",
gear_type=None,
repeat_index=None,
local_role="housing",
),
ConcreteNode(
component_id="main_axis",
template_node_id="main_axis",
kind="axis",
role="main_axis",
gear_type=None,
repeat_index=None,
local_role="main_axis",
),
]
relations: list[ConcreteRelation] = []
for stage in parameters.stages:
nodes.extend(_stage_nodes(stage))
relations.extend(_stage_relations(stage, stage_requirements[stage.stage_id]))
first_requirement, second_requirement = requirement.stages
source = stage_component_id(first_requirement.stage_id, first_requirement.output)
target = stage_component_id(second_requirement.stage_id, second_requirement.input)
relations.append(
_rel(
relation_id=f"{source}_rigid_to_{target}",
relation_type="rigid",
source=source,
target=target,
source_template_node=source,
target_template_node=target,
stage_id="cascade",
)
)
return ConcreteTopology(
template_id="simple_2kh_cascade_runtime_v0_1",
family="simple_2k_h_cascade",
nodes=nodes,
relations=relations,
)