From 0ce14ba25ca58be6c08e38a5696bfc410ee41e36 Mon Sep 17 00:00:00 2001 From: Ojaswee Upadhyayy Date: Fri, 29 May 2026 14:24:13 +0530 Subject: [PATCH] feat: implement linear layout for decoupling capacitor partitions --- .../SingleInnerPartitionPackingSolver.ts | 408 ++++++++++-------- 1 file changed, 224 insertions(+), 184 deletions(-) diff --git a/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts b/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts index 88db103..40c876a 100644 --- a/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts +++ b/lib/solvers/PackInnerPartitionsSolver/SingleInnerPartitionPackingSolver.ts @@ -1,184 +1,224 @@ -/** - * Packs components within a single partition to create an optimal internal layout. - * Uses a packing algorithm to arrange chips and their connections within the partition. - */ - -import type { GraphicsObject } from "graphics-debug" -import { type PackInput, PackSolver2 } from "calculate-packing" -import { BaseSolver } from "../BaseSolver" -import type { OutputLayout, Placement } from "../../types/OutputLayout" -import type { - InputProblem, - PinId, - ChipId, - NetId, - ChipPin, - PartitionInputProblem, -} from "../../types/InputProblem" -import { visualizeInputProblem } from "../LayoutPipelineSolver/visualizeInputProblem" -import { createFilteredNetworkMapping } from "../../utils/networkFiltering" -import { getPadsBoundingBox } from "./getPadsBoundingBox" -import { doBasicInputProblemLayout } from "../LayoutPipelineSolver/doBasicInputProblemLayout" - -const PIN_SIZE = 0.1 - -export class SingleInnerPartitionPackingSolver extends BaseSolver { - partitionInputProblem: PartitionInputProblem - layout: OutputLayout | null = null - declare activeSubSolver: PackSolver2 | null - pinIdToStronglyConnectedPins: Record - - constructor(params: { - partitionInputProblem: PartitionInputProblem - pinIdToStronglyConnectedPins: Record - }) { - super() - this.partitionInputProblem = params.partitionInputProblem - this.pinIdToStronglyConnectedPins = params.pinIdToStronglyConnectedPins - } - - override _step() { - // Initialize PackSolver2 if not already created - if (!this.activeSubSolver) { - const packInput = this.createPackInput() - this.activeSubSolver = new PackSolver2(packInput) - this.activeSubSolver = this.activeSubSolver - } - - // Run one step of the PackSolver2 - this.activeSubSolver.step() - - if (this.activeSubSolver.failed) { - this.failed = true - this.error = `PackSolver2 failed: ${this.activeSubSolver.error}` - return - } - - if (this.activeSubSolver.solved) { - // Apply the packing result to create the layout - this.layout = this.createLayoutFromPackingResult( - this.activeSubSolver.packedComponents, - ) - this.solved = true - this.activeSubSolver = null - } - } - - private createPackInput(): PackInput { - // Fall back to filtered mapping (weak + strong) - const pinToNetworkMap = createFilteredNetworkMapping({ - inputProblem: this.partitionInputProblem, - pinIdToStronglyConnectedPins: this.pinIdToStronglyConnectedPins, - }).pinToNetworkMap - - // Create pack components for each chip - const packComponents = Object.entries( - this.partitionInputProblem.chipMap, - ).map(([chipId, chip]) => { - // Create pads for all pins of this chip - const pads: Array<{ - padId: string - networkId: string - type: "rect" - offset: { x: number; y: number } - size: { x: number; y: number } - }> = [] - - // Create a pad for each pin on this chip - for (const pinId of chip.pins) { - const pin = this.partitionInputProblem.chipPinMap[pinId] - if (!pin) continue - - // Find network for this pin from our connectivity map - const networkId = pinToNetworkMap.get(pinId) || `${pinId}_isolated` - - pads.push({ - padId: pinId, - networkId: networkId, - type: "rect" as const, - offset: { x: pin.offset.x, y: pin.offset.y }, - size: { x: PIN_SIZE, y: PIN_SIZE }, // Small size for pins - }) - } - - const padsBoundingBox = getPadsBoundingBox(pads) - const padsBoundingBoxSize = { - x: padsBoundingBox.maxX - padsBoundingBox.minX, - y: padsBoundingBox.maxY - padsBoundingBox.minY, - } - - // Add chip body pad (disconnected from any network) but make sure - // it fully envelopes the "pads" (pins) - - pads.push({ - padId: `${chipId}_body`, - networkId: `${chipId}_body_disconnected`, - type: "rect" as const, - offset: { x: 0, y: 0 }, - size: { - x: Math.max(padsBoundingBoxSize.x, chip.size.x), - y: Math.max(padsBoundingBoxSize.y, chip.size.y), - }, - }) - - return { - componentId: chipId, - pads, - availableRotationDegrees: chip.availableRotations || [0, 90, 180, 270], - } - }) - - let minGap = this.partitionInputProblem.chipGap - if (this.partitionInputProblem.partitionType === "decoupling_caps") { - minGap = this.partitionInputProblem.decouplingCapsGap ?? minGap - } - - return { - components: packComponents, - minGap, - packOrderStrategy: "largest_to_smallest", - packPlacementStrategy: "minimum_closest_sum_squared_distance", - } - } - - private createLayoutFromPackingResult( - packedComponents: PackSolver2["packedComponents"], - ): OutputLayout { - const chipPlacements: Record = {} - - for (const packedComponent of packedComponents) { - const chipId = packedComponent.componentId - - chipPlacements[chipId] = { - x: packedComponent.center.x, - y: packedComponent.center.y, - ccwRotationDegrees: - packedComponent.ccwRotationOffset || - packedComponent.ccwRotationDegrees || - 0, - } - } - - return { - chipPlacements, - groupPlacements: {}, - } - } - - override visualize(): GraphicsObject { - if (this.activeSubSolver && !this.solved) { - return this.activeSubSolver.visualize() - } - - if (!this.layout) { - const basicLayout = doBasicInputProblemLayout(this.partitionInputProblem) - return visualizeInputProblem(this.partitionInputProblem, basicLayout) - } - - return visualizeInputProblem(this.partitionInputProblem, this.layout) - } - - override getConstructorParams(): [InputProblem] { - return [this.partitionInputProblem] - } -} +/** + * Packs components within a single partition to create an optimal internal layout. + * Uses a packing algorithm to arrange chips and their connections within the partition. + */ + +import type { GraphicsObject } from "graphics-debug" +import { type PackInput, PackSolver2 } from "calculate-packing" +import { BaseSolver } from "../BaseSolver" +import type { OutputLayout, Placement } from "../../types/OutputLayout" +import type { + InputProblem, + PinId, + ChipId, + NetId, + ChipPin, + PartitionInputProblem, +} from "../../types/InputProblem" +import { visualizeInputProblem } from "../LayoutPipelineSolver/visualizeInputProblem" +import { createFilteredNetworkMapping } from "../../utils/networkFiltering" +import { getPadsBoundingBox } from "./getPadsBoundingBox" +import { doBasicInputProblemLayout } from "../LayoutPipelineSolver/doBasicInputProblemLayout" + +const PIN_SIZE = 0.1 + +export class SingleInnerPartitionPackingSolver extends BaseSolver { + partitionInputProblem: PartitionInputProblem + layout: OutputLayout | null = null + declare activeSubSolver: PackSolver2 | null + pinIdToStronglyConnectedPins: Record + + constructor(params: { + partitionInputProblem: PartitionInputProblem + pinIdToStronglyConnectedPins: Record + }) { + super() + this.partitionInputProblem = params.partitionInputProblem + this.pinIdToStronglyConnectedPins = params.pinIdToStronglyConnectedPins + } + + override _step() { + if (this.partitionInputProblem.partitionType === "decoupling_caps") { + this.layout = this.createLinearLayout() + this.solved = true + return + } + + // Initialize PackSolver2 if not already created + if (!this.activeSubSolver) { + const packInput = this.createPackInput() + this.activeSubSolver = new PackSolver2(packInput) + this.activeSubSolver = this.activeSubSolver + } + + // Run one step of the PackSolver2 + this.activeSubSolver.step() + + if (this.activeSubSolver.failed) { + this.failed = true + this.error = `PackSolver2 failed: ${this.activeSubSolver.error}` + return + } + + if (this.activeSubSolver.solved) { + // Apply the packing result to create the layout + this.layout = this.createLayoutFromPackingResult( + this.activeSubSolver.packedComponents, + ) + this.solved = true + this.activeSubSolver = null + } + } + + private createLinearLayout(): OutputLayout { + const chipPlacements: Record = {} + const minGap = + this.partitionInputProblem.decouplingCapsGap ?? + this.partitionInputProblem.chipGap + + const chipIds = Object.keys(this.partitionInputProblem.chipMap).sort() + + let currentY = 0 + for (const chipId of chipIds) { + const chip = this.partitionInputProblem.chipMap[chipId]! + + chipPlacements[chipId] = { + x: 0, + y: currentY + chip.size.y / 2, + ccwRotationDegrees: 0, + } + + currentY += chip.size.y + minGap + } + + const totalHeight = currentY > 0 ? currentY - minGap : 0 + const offsetY = -totalHeight / 2 + + for (const chipId of chipIds) { + chipPlacements[chipId]!.y += offsetY + } + + return { + chipPlacements, + groupPlacements: {}, + } + } + + private createPackInput(): PackInput { + // Fall back to filtered mapping (weak + strong) + const pinToNetworkMap = createFilteredNetworkMapping({ + inputProblem: this.partitionInputProblem, + pinIdToStronglyConnectedPins: this.pinIdToStronglyConnectedPins, + }).pinToNetworkMap + + // Create pack components for each chip + const packComponents = Object.entries( + this.partitionInputProblem.chipMap, + ).map(([chipId, chip]) => { + // Create pads for all pins of this chip + const pads: Array<{ + padId: string + networkId: string + type: "rect" + offset: { x: number; y: number } + size: { x: number; y: number } + }> = [] + + // Create a pad for each pin on this chip + for (const pinId of chip.pins) { + const pin = this.partitionInputProblem.chipPinMap[pinId] + if (!pin) continue + + // Find network for this pin from our connectivity map + const networkId = pinToNetworkMap.get(pinId) || `${pinId}_isolated` + + pads.push({ + padId: pinId, + networkId: networkId, + type: "rect" as const, + offset: { x: pin.offset.x, y: pin.offset.y }, + size: { x: PIN_SIZE, y: PIN_SIZE }, // Small size for pins + }) + } + + const padsBoundingBox = getPadsBoundingBox(pads) + const padsBoundingBoxSize = { + x: padsBoundingBox.maxX - padsBoundingBox.minX, + y: padsBoundingBox.maxY - padsBoundingBox.minY, + } + + // Add chip body pad (disconnected from any network) but make sure + // it fully envelopes the "pads" (pins) + + pads.push({ + padId: `${chipId}_body`, + networkId: `${chipId}_body_disconnected`, + type: "rect" as const, + offset: { x: 0, y: 0 }, + size: { + x: Math.max(padsBoundingBoxSize.x, chip.size.x), + y: Math.max(padsBoundingBoxSize.y, chip.size.y), + }, + }) + + return { + componentId: chipId, + pads, + availableRotationDegrees: chip.availableRotations || [0, 90, 180, 270], + } + }) + + let minGap = this.partitionInputProblem.chipGap + if (this.partitionInputProblem.partitionType === "decoupling_caps") { + minGap = this.partitionInputProblem.decouplingCapsGap ?? minGap + } + + return { + components: packComponents, + minGap, + packOrderStrategy: "largest_to_smallest", + packPlacementStrategy: "minimum_closest_sum_squared_distance", + } + } + + private createLayoutFromPackingResult( + packedComponents: PackSolver2["packedComponents"], + ): OutputLayout { + const chipPlacements: Record = {} + + for (const packedComponent of packedComponents) { + const chipId = packedComponent.componentId + + chipPlacements[chipId] = { + x: packedComponent.center.x, + y: packedComponent.center.y, + ccwRotationDegrees: + packedComponent.ccwRotationOffset || + packedComponent.ccwRotationDegrees || + 0, + } + } + + return { + chipPlacements, + groupPlacements: {}, + } + } + + override visualize(): GraphicsObject { + if (this.activeSubSolver && !this.solved) { + return this.activeSubSolver.visualize() + } + + if (!this.layout) { + const basicLayout = doBasicInputProblemLayout(this.partitionInputProblem) + return visualizeInputProblem(this.partitionInputProblem, basicLayout) + } + + return visualizeInputProblem(this.partitionInputProblem, this.layout) + } + + override getConstructorParams(): [InputProblem] { + return [this.partitionInputProblem] + } +}