Hierarchical Designs
Learn how to create and work with hierarchical designs using cell references.
Basic Cell Reference
Create a design with one cell referencing another:
use laykit::{GDSIIFile, GDSStructure, GDSTime, GDSElement, Boundary, StructRef}; fn main() -> Result<(), Box<dyn std::error::Error>> { let mut gds = GDSIIFile::new("HIERARCHICAL".to_string()); gds.units = (1e-6, 1e-9); // Create subcell let mut subcell = GDSStructure { name: "SUBCELL".to_string(), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; // Add a rectangle to subcell subcell.elements.push(GDSElement::Boundary(Boundary { layer: 1, datatype: 0, xy: vec![(0, 0), (1000, 0), (1000, 1000), (0, 1000), (0, 0)], properties: Vec::new(), })); // Create top cell let mut topcell = GDSStructure { name: "TOPCELL".to_string(), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; // Reference subcell topcell.elements.push(GDSElement::StructRef(StructRef { sname: "SUBCELL".to_string(), xy: (2000, 2000), strans: None, properties: Vec::new(), })); gds.structures.push(subcell); gds.structures.push(topcell); gds.write_to_file("hierarchical.gds")?; println!("✅ Created hierarchical design"); Ok(()) }
Multiple Instances
Place multiple instances of the same cell:
#![allow(unused)] fn main() { use laykit::{GDSIIFile, GDSStructure, GDSTime, GDSElement, Boundary, StructRef}; fn create_repeated_instances() -> Result<(), Box<dyn std::error::Error>> { let mut gds = GDSIIFile::new("MULTI_INSTANCE".to_string()); gds.units = (1e-6, 1e-9); // Create unit cell let mut unit = GDSStructure { name: "UNIT".to_string(), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; unit.elements.push(GDSElement::Boundary(Boundary { layer: 1, datatype: 0, xy: vec![(0, 0), (500, 0), (500, 500), (0, 500), (0, 0)], properties: Vec::new(), })); // Create top cell with multiple instances let mut top = GDSStructure { name: "TOP".to_string(), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; // Create a 3x3 grid manually for row in 0..3 { for col in 0..3 { top.elements.push(GDSElement::StructRef(StructRef { sname: "UNIT".to_string(), xy: (col * 1000, row * 1000), strans: None, properties: Vec::new(), })); } } gds.structures.push(unit); gds.structures.push(top); gds.write_to_file("multi_instance.gds")?; println!("✅ Created 3×3 array of instances"); Ok(()) } }
Array References
Use ArrayRef for efficient array representation:
#![allow(unused)] fn main() { use laykit::{GDSIIFile, GDSStructure, GDSTime, GDSElement, Boundary, ArrayRef}; fn create_array() -> Result<(), Box<dyn std::error::Error>> { let mut gds = GDSIIFile::new("ARRAY".to_string()); gds.units = (1e-6, 1e-9); // Create unit cell let mut unit = GDSStructure { name: "UNIT".to_string(), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; unit.elements.push(GDSElement::Boundary(Boundary { layer: 1, datatype: 0, xy: vec![(0, 0), (500, 0), (500, 500), (0, 500), (0, 0)], properties: Vec::new(), })); // Create top cell with array reference let mut top = GDSStructure { name: "TOP".to_string(), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; // 10×5 array with 1000nm spacing top.elements.push(GDSElement::ArrayRef(ArrayRef { sname: "UNIT".to_string(), columns: 10, rows: 5, xy: [ (0, 0), // Reference point (10000, 0), // Column extent (10 * 1000) (0, 5000), // Row extent (5 * 1000) ], strans: None, properties: Vec::new(), })); gds.structures.push(unit); gds.structures.push(top); gds.write_to_file("array.gds")?; println!("✅ Created 10×5 array"); Ok(()) } }
Transformations
Apply transformations to cell instances:
#![allow(unused)] fn main() { use laykit::{GDSIIFile, GDSStructure, GDSTime, GDSElement, Boundary, StructRef, STrans}; fn create_transformed_instances() -> Result<(), Box<dyn std::error::Error>> { let mut gds = GDSIIFile::new("TRANSFORMED".to_string()); gds.units = (1e-6, 1e-9); // Create base cell let mut base = GDSStructure { name: "BASE".to_string(), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; base.elements.push(GDSElement::Boundary(Boundary { layer: 1, datatype: 0, xy: vec![(0, 0), (1000, 0), (1000, 500), (0, 500), (0, 0)], properties: Vec::new(), })); let mut top = GDSStructure { name: "TOP".to_string(), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; // Original instance top.elements.push(GDSElement::StructRef(StructRef { sname: "BASE".to_string(), xy: (0, 0), strans: None, properties: Vec::new(), })); // Rotated 90 degrees top.elements.push(GDSElement::StructRef(StructRef { sname: "BASE".to_string(), xy: (3000, 0), strans: Some(STrans { reflect_x: false, absolute_mag: false, absolute_angle: false, magnification: None, angle: Some(90.0), }), properties: Vec::new(), })); // Mirrored top.elements.push(GDSElement::StructRef(StructRef { sname: "BASE".to_string(), xy: (6000, 0), strans: Some(STrans { reflect_x: true, absolute_mag: false, absolute_angle: false, magnification: None, angle: None, }), properties: Vec::new(), })); // Scaled 2x top.elements.push(GDSElement::StructRef(StructRef { sname: "BASE".to_string(), xy: (9000, 0), strans: Some(STrans { reflect_x: false, absolute_mag: false, absolute_angle: false, magnification: Some(2.0), angle: None, }), properties: Vec::new(), })); gds.structures.push(base); gds.structures.push(top); gds.write_to_file("transformed.gds")?; println!("✅ Created transformed instances"); Ok(()) } }
Multi-Level Hierarchy
Create a 3-level hierarchy:
#![allow(unused)] fn main() { use laykit::{GDSIIFile, GDSStructure, GDSTime, GDSElement, Boundary, StructRef}; fn create_multilevel() -> Result<(), Box<dyn std::error::Error>> { let mut gds = GDSIIFile::new("MULTILEVEL".to_string()); gds.units = (1e-6, 1e-9); // Level 1: Basic shape let mut l1_cell = GDSStructure { name: "L1_BASIC".to_string(), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; l1_cell.elements.push(GDSElement::Boundary(Boundary { layer: 1, datatype: 0, xy: vec![(0, 0), (100, 0), (100, 100), (0, 100), (0, 0)], properties: Vec::new(), })); // Level 2: Group of L1 cells let mut l2_cell = GDSStructure { name: "L2_GROUP".to_string(), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; for i in 0..4 { l2_cell.elements.push(GDSElement::StructRef(StructRef { sname: "L1_BASIC".to_string(), xy: (i * 200, 0), strans: None, properties: Vec::new(), })); } // Level 3: Top level with L2 cells let mut l3_cell = GDSStructure { name: "L3_TOP".to_string(), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; for i in 0..3 { l3_cell.elements.push(GDSElement::StructRef(StructRef { sname: "L2_GROUP".to_string(), xy: (0, i * 500), strans: None, properties: Vec::new(), })); } gds.structures.push(l1_cell); gds.structures.push(l2_cell); gds.structures.push(l3_cell); gds.write_to_file("multilevel.gds")?; println!("✅ Created 3-level hierarchy"); Ok(()) } }
Finding Cell Dependencies
Analyze cell reference relationships:
use laykit::{GDSIIFile, GDSElement}; use std::collections::{HashMap, HashSet}; fn analyze_hierarchy(gds: &GDSIIFile) { let mut references: HashMap<String, HashSet<String>> = HashMap::new(); // Build reference map for structure in &gds.structures { let mut refs = HashSet::new(); for element in &structure.elements { match element { GDSElement::StructRef(sref) => { refs.insert(sref.sname.clone()); } GDSElement::ArrayRef(aref) => { refs.insert(aref.sname.clone()); } _ => {} } } references.insert(structure.name.clone(), refs); } // Print hierarchy println!("Cell hierarchy:"); for (cell, refs) in &references { if !refs.is_empty() { println!(" {} references:", cell); for ref_name in refs { println!(" - {}", ref_name); } } } // Find top cells (not referenced by others) let all_refs: HashSet<_> = references.values() .flat_map(|refs| refs.iter().cloned()) .collect(); let top_cells: Vec<_> = references.keys() .filter(|name| !all_refs.contains(*name)) .collect(); println!("\nTop-level cells:"); for cell in top_cells { println!(" - {}", cell); } } fn main() -> Result<(), Box<dyn std::error::Error>> { let gds = GDSIIFile::read_from_file("design.gds")?; analyze_hierarchy(&gds); Ok(()) }
Flattening Hierarchy
Flatten a hierarchical design to a single level:
#![allow(unused)] fn main() { // Note: This is a simplified example // Full flattening requires resolving all transformations use laykit::{GDSIIFile, GDSStructure, GDSTime, GDSElement}; fn flatten_simple(gds: &GDSIIFile, top_cell_name: &str) -> Result<GDSStructure, Box<dyn std::error::Error>> { let mut flattened = GDSStructure { name: format!("{}_FLAT", top_cell_name), creation_time: GDSTime::now(), modification_time: GDSTime::now(), elements: Vec::new(), }; // Find top cell let top_cell = gds.structures.iter() .find(|s| s.name == top_cell_name) .ok_or("Top cell not found")?; // Copy non-reference elements for element in &top_cell.elements { match element { GDSElement::Boundary(_) | GDSElement::Path(_) | GDSElement::Text(_) => { flattened.elements.push(element.clone()); } _ => { // References would need transformation and recursion println!("Warning: Skipping reference (not implemented)"); } } } Ok(flattened) } }