diff --git a/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts b/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts index 8335b60..93a16a2 100644 --- a/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts +++ b/lib/solvers/ChipPartitionsSolver/ChipPartitionsSolver.ts @@ -51,6 +51,7 @@ export class ChipPartitionsSolver extends BaseSolver { const decapChipIdSet = new Set() const decapGroupPartitions: ChipId[][] = [] + // Use IdentifyDecouplingCapsSolver groups if available if (this.decouplingCapGroups && this.decouplingCapGroups.length > 0) { for (const group of this.decouplingCapGroups) { const capsOnly: ChipId[] = [] @@ -70,6 +71,20 @@ export class ChipPartitionsSolver extends BaseSolver { } } + // Fallback: detect decoupling-cap-like chips not identified by the solver + const fallbackGroups = this.findDecouplingCapFallbackGroups( + inputProblem, + decapChipIdSet, + ) + for (const capsOnly of fallbackGroups) { + if (capsOnly.length >= 2) { + decapGroupPartitions.push(capsOnly) + for (const capId of capsOnly) { + decapChipIdSet.add(capId) + } + } + } + // 2) Build adjacency graph for NON-decap chips based on strong pin connections const nonDecapChipIds = chipIds.filter((id) => !decapChipIdSet.has(id)) const adjacencyMap = new Map>() @@ -180,6 +195,68 @@ export class ChipPartitionsSolver extends BaseSolver { return partition } + /** + * Fallback detection for decoupling-cap-like chips when IdentifyDecouplingCapsSolver + * doesn't identify them (e.g. when availableRotations metadata is not set). + * + * Criteria: 2 pins, pins on y+/y- sides, strong connection to a chip with >2 pins + */ + private findDecouplingCapFallbackGroups( + inputProblem: InputProblem, + alreadyMarked: Set, + ): ChipId[][] { + const chipIds = Object.keys(inputProblem.chipMap).filter( + (id) => !alreadyMarked.has(id), + ) + + // Find cap candidates: 2 pins, y+/y- sides, connected to a complex chip + const capToMainChip = new Map() + for (const chipId of chipIds) { + const chip = inputProblem.chipMap[chipId] + if (!chip || chip.pins.length !== 2) continue + + // Check for y+/y- pin sides + const sides = chip.pins + .map((p) => inputProblem.chipPinMap[p]?.side) + .filter(Boolean) + if (!sides.includes("y+") || !sides.includes("y-")) continue + + // Find strong connections to complex chips (>2 pins) + for (const pinId of chip.pins) { + for (const [connKey, connected] of Object.entries( + inputProblem.pinStrongConnMap, + )) { + if (!connected) continue + const [a, b] = connKey.split("-") + const otherPinId = a === pinId ? b : b === pinId ? a : null + if (!otherPinId) continue + + const otherOwner = this.findPinOwner(otherPinId, inputProblem) + if ( + otherOwner && + otherOwner !== chipId && + !alreadyMarked.has(otherOwner) + ) { + const otherChip = inputProblem.chipMap[otherOwner] + if (otherChip && otherChip.pins.length > 2) { + capToMainChip.set(chipId, otherOwner) + } + } + } + } + } + + // Group caps by their main chip + const groups = new Map() + for (const [capId, mainChipId] of capToMainChip) { + const group = groups.get(mainChipId) || [] + group.push(capId) + groups.set(mainChipId, group) + } + + return Array.from(groups.values()) + } + /** * Creates a new InputProblem containing only the components in the given partition */ diff --git a/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts b/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts index 88db103..f6325f9 100644 --- a/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts +++ b/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts @@ -38,6 +38,13 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { } override _step() { + // Specialized layout for decoupling capacitors: clean horizontal row + if (this.partitionInputProblem.partitionType === "decoupling_caps") { + this.layout = this.createDecouplingCapsLayout() + this.solved = true + return + } + // Initialize PackSolver2 if not already created if (!this.activeSubSolver) { const packInput = this.createPackInput() @@ -64,6 +71,38 @@ export class SingleInnerPartitionPackingSolver extends BaseSolver { } } + private createDecouplingCapsLayout(): OutputLayout { + const chipPlacements: Record = {} + const chips = Object.entries(this.partitionInputProblem.chipMap) + const gap = + this.partitionInputProblem.decouplingCapsGap ?? + this.partitionInputProblem.chipGap ?? + 0.1 + + // Sort by chipId for deterministic ordering + chips.sort(([a], [b]) => a.localeCompare(b)) + + // Calculate total width for centering + let totalWidth = 0 + for (const [, chip] of chips) { + totalWidth += chip.size.x + } + totalWidth += (chips.length - 1) * gap + const startX = -totalWidth / 2 + + let currentX = startX + for (const [chipId, chip] of chips) { + chipPlacements[chipId] = { + x: currentX + chip.size.x / 2, + y: 0, + ccwRotationDegrees: 0, + } + currentX += chip.size.x + gap + } + + return { chipPlacements, groupPlacements: {} } + } + private createPackInput(): PackInput { // Fall back to filtered mapping (weak + strong) const pinToNetworkMap = createFilteredNetworkMapping({ diff --git a/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts b/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts index 149d617..8c9fdc7 100644 --- a/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts +++ b/lib/solvers/PartitionPackingSolver/PartitionPackingSolver.ts @@ -296,7 +296,7 @@ export class PartitionPackingSolver extends BaseSolver { components: packComponents, minGap: this.inputProblem.partitionGap, // Use partitionGap from input problem packOrderStrategy: "largest_to_smallest", - packPlacementStrategy: "minimum_sum_squared_distance_to_network", + packPlacementStrategy: "shortest_connection_along_outline", } }