Skip to content

Commit 1352c87

Browse files
committed
Add systematic single-channel tests
1 parent 96cd0de commit 1352c87

File tree

7 files changed

+118
-36
lines changed

7 files changed

+118
-36
lines changed

Project.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ version = "1.4.2"
44

55
[deps]
66
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
7-
BipartiteMatching = "79040ab4-24c8-4c92-950c-d48b5991a0f6"
87
Cairo = "159f3aea-2a34-519c-b102-8c37f9878175"
98
Clipper = "c8f6d549-b3ab-5508-a0d1-48fe138e8cc1"
109
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
1110
CoordinateTransformations = "150eb455-5306-5404-9cee-2592286d6298"
1211
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
13-
Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"
1412
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
1513
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
1614
Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb"
@@ -45,7 +43,6 @@ SchematicGraphMakieExt = "GraphMakie"
4543
[compat]
4644
Accessors = "0.1"
4745
Aqua = "0.8"
48-
BipartiteMatching = "0.1.1"
4946
Cairo = "1.0"
5047
Clipper = "0.6"
5148
ColorSchemes = "3"

src/paths/channels.jl

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,43 @@ function _route!(p::Path{T}, p1::Point, α1, rule::AbstractChannelRouting,
106106
push!(waypoints, p0(track_path_seg))
107107
else
108108
route!(p, p0(track_path_seg), α0(track_path_seg), next_entry_rule, sty; waypoints)
109-
push!(p, Node(track_path_seg, sty), reconcile=false) # already reconciled by construction
109+
push!(p, Node(track_path_seg, sty), reconcile=false) # p0, α0 reconciled by construction
110+
p[end-1].next = p[end]
111+
p[end].prev = p[end-1]
112+
# Note `auto_curvature` BSpline uses curvature from end of previous segment
113+
# and is not reconciled with the new node
114+
# But we can do this ourselves
115+
_reconcile_curvature!(p[end-1], next_entry_rule)
110116
empty!(waypoints)
111117
end
112118
end
113119
# Exit
114120
route!(p, p1, α1, exit_rule(rule), sty; waypoints)
121+
_reconcile_curvature!(p[end], exit_rule(rule))
115122
return
116123
end
117124

125+
function _reconcile_curvature!(n::Node{T}, rule::RouteRule) where {T} end
126+
function _reconcile_curvature!(n::Node{T}, rule::BSplineRouting) where {T}
127+
!rule.auto_curvature && return
128+
κ0 = if n.prev === n
129+
0.0 / oneunit(T)
130+
else
131+
signed_curvature(segment(n.prev), pathlength(segment(n.prev)))
132+
end
133+
κ1 = if n.next === n
134+
0.0 / oneunit(T)
135+
else
136+
signed_curvature(segment(n.next), zero(coordinatetype(n)))
137+
end
138+
_set_endpoints_curvature!(segment(n), κ0, κ1)
139+
if rule.auto_speed
140+
_optimize_bspline!(segment(n); endpoints_curvature=(κ0, κ1))
141+
else
142+
_update_interpolation!(segment(n))
143+
end
144+
end
145+
118146
struct SingleChannelRouting{T} <: AbstractChannelRouting
119147
channel::RouteChannel{T}
120148
transition_rules::Tuple{<:RouteRule,<:RouteRule}

src/paths/paths.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,6 @@ include("segments/bspline_optimization.jl")
697697
include("routes.jl")
698698

699699
include("channels.jl")
700-
include("channel_routers.jl")
701700

702701
function change_handedness!(seg::Union{Turn, Corner})
703702
return seg.α = -seg.α

src/paths/segments/bspline_optimization.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ function _optimize_bspline!(b::BSpline; endpoints_curvature=nothing)
1818
end
1919

2020
function _set_endpoints_curvature!(::BSpline, ::Nothing; add_points=false) end
21+
function _set_endpoints_curvature!(b::BSpline, κ0κ1; add_points=false)
22+
return _set_endpoints_curvature!(b, κ0κ1[1], κ0κ1[2]; add_points)
23+
end
2124

2225
function _set_endpoints_curvature!(
2326
b::BSpline{T},
24-
κ0=0.0 / oneunit(T),
27+
κ0::Union{Float64, DeviceLayout.InverseLength}=0.0 / oneunit(T),
2528
κ1=κ0;
2629
add_points=false
2730
) where {T}

test/coverage/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
[deps]
2+
DeviceLayout = "ebf59a4a-04ec-49d7-8cd4-c9382ceb8e85"
23
LocalCoverage = "5f6e1e16-694c-5876-87ef-16b5274f298e"
34
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ include("test_entity.jl")
3030
include("test_intersection.jl")
3131
include("test_shapes.jl")
3232
include("test_routes.jl")
33+
include("test_channels.jl")
3334
include("test_texts.jl")
3435
include("test_pointinpoly.jl")
3536
include("test_solidmodel.jl")

test/test_channels.jl

Lines changed: 83 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,52 @@
1-
using Revise
2-
using Graphs
3-
using FileIO
4-
import DeviceLayout.Paths: RouteChannel, ChannelRouter, assign_channels!, assign_tracks!, visualize_router_state
1+
import DeviceLayout.Paths: RouteChannel
52

6-
# @testset "Channels" begin
7-
### Unit tests
8-
9-
### Integration tests
3+
function test_single_channel_reversals(r, seg, sty)
4+
paths = test_single_channel(r, seg, sty;
5+
reverse_channel=false, reverse_paths=false)
6+
paths_revch = test_single_channel(r, seg, sty;
7+
reverse_channel=true, reverse_paths=false)
8+
paths_revp = test_single_channel(r, seg, sty;
9+
reverse_channel=false, reverse_paths=true)
10+
paths_rev_ch_p = test_single_channel(r, seg, sty;
11+
reverse_channel=true, reverse_paths=true)
12+
# Segments are approximately the same when channel is reversed
13+
for (pa1, pa2) in zip(paths, paths_revch)
14+
for (n1, n2) in zip(pa1, pa2)
15+
@test p0(n1.seg) p0(n2.seg) atol=1nm
16+
@test p1(n1.seg) p1(n2.seg) atol=1nm
17+
@test isapprox_angle(α0(n1.seg), α0(n2.seg), atol=1e-6)
18+
@test isapprox_angle(α1(n1.seg), α1(n2.seg), atol=1e-6)
19+
@test pathlength(n1.seg) pathlength(n2.seg) atol=1nm
20+
end
21+
end
22+
for (pa1, pa2) in zip(paths_revp, paths_rev_ch_p)
23+
for (n1, n2) in zip(pa1, pa2)
24+
@test p0(n1.seg) p0(n2.seg) atol=1nm
25+
@test p1(n1.seg) p1(n2.seg) atol=1nm
26+
@test isapprox_angle(α0(n1.seg), α0(n2.seg), atol=1e-6)
27+
@test isapprox_angle(α1(n1.seg), α1(n2.seg), atol=1e-6)
28+
@test pathlength(n1.seg) pathlength(n2.seg) atol=1nm
29+
end
30+
end
31+
# Segments are approximately reversed when paths are reversed
32+
for (pa1, pa2) in zip(paths, paths_revp)
33+
for (n1, n2) in zip(pa1, reverse(pa2.nodes))
34+
@test p0(n1.seg) p1(n2.seg) atol=1nm
35+
@test p1(n1.seg) p0(n2.seg) atol=1nm
36+
@test isapprox_angle(α0(n1.seg), α1(n2.seg) + 180°, atol=1e-6)
37+
@test isapprox_angle(α1(n1.seg), α0(n2.seg) + 180°, atol=1e-6)
38+
@test pathlength(n1.seg) pathlength(n2.seg) atol=1nm
39+
# Some reversed paths are visibly different with taper trace and auto_speed (1um length difference)
40+
# because the asymmetry causes speed optimization to find a different optimum
41+
# depending on which is t0 and which is t1. So we use manual speed
42+
# (also because it runs faster and we don't need to test auto further)
43+
end
44+
end
45+
return paths
46+
end
1047

11-
# end
12-
function test_single(transition_rule, channel_segment, channel_style; reverse_channel=false, reverse_paths=false)
48+
function test_single_channel(transition_rule, channel_segment, channel_style;
49+
reverse_channel=false, reverse_paths=false)
1350
channel = Path(0.0μm, 0.0μm)
1451
if channel_segment == Paths.Straight
1552
straight!(channel, 1mm, channel_style)
@@ -54,29 +91,45 @@ function test_single(transition_rule, channel_segment, channel_style; reverse_ch
5491

5592
paths = [Path(p, α0=α0) for (p, α0) in zip(p0s, α0s)]
5693
tracks = reverse_channel ? reverse(eachindex(paths)) : eachindex(paths)
94+
styles = [Paths.Trace(2μm); fill(Paths.CPW(2μm, 2μm), length(paths))]
5795

5896
rule = Paths.SingleChannelRouting(Paths.RouteChannel(channel), transition_rule, 50.0μm)
5997
setindex!.(Ref(rule.segment_tracks), tracks, paths)
60-
for (track, pa, p1, α1) in zip(tracks, paths, p1s, α1s)
98+
for (pa, p1, α1, sty) in zip(paths, p1s, α1s, styles)
6199
route!(pa, p1, α1, rule, Paths.CPW(2μm, 2μm))
62100
end
63-
64-
c = Cell("test", nm);
65-
render!.(c, paths, GDSMeta(), atol=1μm);
66-
render!(c, channel, GDSMeta(2));
67-
save("test.gds", c)
101+
return paths
68102
end
69-
transition_rules = [
70-
Paths.BSplineRouting(auto_speed=true, auto_curvature=true)
71-
Paths.StraightAnd90(min_bend_radius=25μm) # Can only be used with straight and trace if any paths enter from the sides, no curves or tapers
72-
]
73-
channel_segments = [
74-
Paths.Straight,
75-
Paths.Turn,
76-
Paths.BSpline,
77-
Paths.CompoundSegment
78-
]
79-
channel_styles = [
80-
Paths.Trace(100μm),
81-
Paths.TaperTrace(100μm, 50μm)
82-
]
103+
104+
@testset "Channels" begin
105+
### Single-channel integration tests
106+
## Geometry-level routing
107+
# StraightAnd90 only works with straight channel
108+
transition_rules = [
109+
Paths.StraightAnd90(min_bend_radius=25μm) # Can only be used with straight and trace if any paths enter from the sides, no curves or tapers
110+
Paths.BSplineRouting(endpoints_speed=150μm, auto_curvature=true)
111+
]
112+
channel_segments = [
113+
Paths.Straight,
114+
Paths.Turn,
115+
Paths.BSpline,
116+
Paths.CompoundSegment
117+
]
118+
channel_styles = [
119+
Paths.Trace(100μm),
120+
Paths.TaperTrace(100μm, 50μm)
121+
]
122+
@testset "Straight" begin
123+
rule = transition_rules[1]
124+
paths = test_single_channel_reversals(rule, channel_segments[1], channel_styles[1])
125+
@test isempty(Intersect.intersections(paths...))
126+
end
127+
rule = transition_rules[2] # BSpline rule for all-angle transitions
128+
for segtype in channel_segments[2:end]
129+
@testset "$segtype channel" begin
130+
for sty in channel_styles
131+
test_single_channel_reversals(rule, segtype, sty)
132+
end
133+
end
134+
end
135+
end

0 commit comments

Comments
 (0)