# how to read DRC min. value?

Hi sir ,
in our team , we not only need to know if the case is out of rule , but we also need what the min. value of out rule issue is.
below is what my code , as you can see , I make a DRC check in first , then let it export result and re-load it into programming to sort and get min value.
But it is not smare way due to we have to run this code 2 times.
in first time, DRC function export the text data.
in second time , using this code to read the text data and get min. value.
do you have another way or smart function to get the same result?

``````#############################################
def dis(a1x,a1y,b1x,b1y)
distance=(((a1x-b1x)**2 + (a1y-b1y)**2)**0.5).round(3)
return distance
end
############################################
def disMin(a1x,a1y,a2x,a2y,c1x,c1y)
distanceR=((c1x-a1x)*(a2x-a1x) + (c1y-a1y)*(a2y-a1y)) / ((a1x-a2x)**2 + (a1y-a2y)**2)
if distanceR <= 0 then
return dis(a1x,a1y,c1x,c1y)
else
return dis(a2x,a2y,c1x,c1y)
end
end
###############rule for width check#################
gap=0.05
dataR= Array.new()
dataC=Array.new()
realdata=Array.new()
IO.foreach(filename) {
|block|
if block.split(":")[0]=="    <value>edge-pair" then
realdata << (block.split(":")[1].split("<")[0]).gsub('(',"").gsub(')',"").gsub('/','|')
dataZ = (block.split(":")[1].split("<")[0]).gsub('(',"").gsub(')',"").gsub('/','|')
dataZ_E1=dataZ.split("|")[0]
dataZ_E2=dataZ.split("|")[1]
dataZ_E1P1=dataZ_E1.split(";")[0]
dataZ_E1P2=dataZ_E1.split(";")[1]
dataZ_E2P1=dataZ_E2.split(";")[0]
dataZ_E2P2=dataZ_E2.split(";")[1]
dataZ_E1P1x=dataZ_E1P1.split(",")[0].to_f
dataZ_E1P1y=dataZ_E1P1.split(",")[1].to_f
dataZ_E1P2x=dataZ_E1P2.split(",")[0].to_f
dataZ_E1P2y=dataZ_E1P2.split(",")[1].to_f
dataZ_E2P1x=dataZ_E2P1.split(",")[0].to_f
dataZ_E2P1y=dataZ_E2P1.split(",")[1].to_f
dataZ_E2P2x=dataZ_E2P2.split(",")[0].to_f
dataZ_E2P2y=dataZ_E2P2.split(",")[1].to_f
value1=disMin(dataZ_E1P1x,dataZ_E1P1y,dataZ_E1P2x,dataZ_E1P2y,dataZ_E2P1x,dataZ_E2P1y)
value2=disMin(dataZ_E1P1x,dataZ_E1P1y,dataZ_E1P2x,dataZ_E1P2y,dataZ_E2P2x,dataZ_E2P2y)
if value1 > gap then
dataC << value1
end

if value2 > gap then
dataC << value2
end

end
#############################################################
}
min_value_dataC=dataC.min
puts "min. value is #{min_value_dataC}"
end

##################################################################################
layoutlibrary = RBA::Application::instance.main_window.current_view.active_cellview.name
rulefile="D:\\rulefile.txt"
ubm=input(94,0)
ruleE=26 ##Check space
report("rulechecker",rulefile)
ubm.space(ruleE.micron,projection).output("RuleE")
if File.exist?(rulefile) then
end
``````

• edited December 2023

Hi jiunnweiyeh

Because you request to dump DRC results to a file so the file will be occupied by DRC engine until the script is completed finished, which blocks the `readFile_getdis` from accessing the contents.

The way to avoid this is actually simple, since the space check DRC already provides a `EdgePairs` object, contains all the data required for this distiance calculation so we can drop the text parsing completely.

The `readFile_getdis` and `disMin` can be combined into this:

``````ubm    = input(94,0)
ruleE  = 26
ubm_sp = ubm.space(ruleE.micron,projection)

def disMin(eps)
dist = Float::INFINITY
eps.each{|ep|
dist1 = ep.first.distance_abs(ep.second.p1)
dist2 = ep.first.distance_abs(ep.second.p2)
dist  = [dist, dist1, dist2].min
}
puts "min. value is #{dist}"
end

disMin(ubm_sp)
report("rulechecker")
ubm_sp.output("RuleE")
``````

• Hi @RawrRanger,

thanks for bringing this up. I noticed that on C++ level, the edge pairs have a distance property, but not on Python or Ruby level. I will add this implementation to the API.

The C++ implementation computes the distance in a slightly different way, but in normal cases, the results should be same.

Best regards,

Matthias

• Hi @RawrRanger
Thanks , it is workable.

• Here is my code for reference .

``````ubm    = input(94,0)
ruleE  = 20
ubm_sp = ubm.space(ruleE.micron,projection)

def disMin(eps)
dist = Float::INFINITY
eps.each{|ep|
dist1 = ep.first.distance_abs(ep.second.p1)
dist2 = ep.first.distance_abs(ep.second.p2)
dist  = [dist, dist1, dist2].min
}
return "#{dist.round(3)}"
end
runing_time=0
check_result=disMin(ubm_sp)
while check_result.match("Infinity") and runing_time <10
ruleE  = ruleE+5
ubm_sp = ubm.space(ruleE.micron,projection)
check_result=disMin(ubm_sp)
runing_time+=1
end
puts check_result
RBA::MessageBox.info("result","min. value is #{check_result}", RBA::MessageBox.b_ok)
report("rulechecker")
ubm_sp.output("RuleE")
``````
• Hi @RawrRanger
May I know what the function for "distance_abs"?
Yes , I knew that wording should be check what the distance between first / sencond.
But when I usnig code as below to double confirm the distance .
dist1 , using distnace_abs function ,
distance_fp1sp1 , using (((X1-X2)^2+(Y1-Y2)^2))^0.5 to do the cacaulator.
I got difference answer...

``````ubm    = input(94,0)
ruleE  = 24.8
ubm_sp = ubm.space(ruleE.micron)

def disMin(eps)
dist = Float::INFINITY
dist_cacu = Float::INFINITY
eps.each{|ep|
dist1 = ep.first.distance_abs(ep.second.p1).round(3)
dist2 = ep.first.distance_abs(ep.second.p2).round(3)
dist  = [dist, dist1, dist2].min
fp1x=ep.first.p1.x.to_f
fp1y=ep.first.p1.y.to_f
fp2x=ep.first.p2.x.to_f
fp2y=ep.first.p2.x.to_f
sp1x=ep.second.p1.x.to_f
sp1y=ep.second.p1.y.to_f
sp2x=ep.second.p2.x.to_f
sp2y=ep.second.p2.x.to_f
distance_fp1sp1=(((fp1x-sp1x)**2 + (fp1y-sp1y)**2)**0.5).round(3)
distance_fp1sp2=(((fp1x-sp2x)**2 + (fp1y-sp2y)**2)**0.5).round(3)
distance_fp2sp1=(((fp2x-sp1x)**2 + (fp2y-sp1y)**2)**0.5).round(3)
distance_fp2sp2=(((fp2x-sp2x)**2 + (fp2y-sp2y)**2)**0.5).round(3)
distance_min=[distance_fp1sp1,distance_fp1sp2,distance_fp2sp1,distance_fp2sp2].min
dist_cacu  = [dist_cacu, distance_min].min
}
puts "dist result : #{dist}"
puts "dist_cacu result : #{dist_cacu}"
return "#{dist_cacu}"

end
runing_time=0
check_result=disMin(ubm_sp)
while check_result.match("Infinity") and runing_time <10
ruleE  = ruleE+5
ubm_sp = ubm.space(ruleE.micron)
check_result=disMin(ubm_sp)
runing_time+=1
end
RBA::MessageBox.info("result","min. value is #{check_result}", RBA::MessageBox.b_ok)
#report("rulechecker")
ubm_sp.output("RuleE")
``````

• by the way , I am using Klayout 0.28.12

• edited December 2023

sorry for code typo , I update it as below.
But , same issue I got.. the distance_abs result didn't match with real data.

• hi sir, sorry , the code should be update as below.
But same issue,
dist1 , using distnace_abs function ,
distance_fp1sp1 , using (((X1-X2)^2+(Y1-Y2)^2))^0.5 to do the cacaulator.
I got difference answer.

``````ubm    = input(94,0)
ruleE  = 24.8
ubm_sp = ubm.space(ruleE.micron)

def disMin(eps)
dist = Float::INFINITY
dist_cacu = Float::INFINITY
eps.each{|ep|
dist1 = ep.first.distance_abs(ep.second.p1).round(3)
dist2 = ep.first.distance_abs(ep.second.p2).round(3)
dist  = [dist, dist1, dist2].min
fp1x=ep.first.p1.x.to_f
fp1y=ep.first.p1.y.to_f
fp2x=ep.first.p2.x.to_f
fp2y=ep.first.p2.y.to_f
sp1x=ep.second.p1.x.to_f
sp1y=ep.second.p1.y.to_f
sp2x=ep.second.p2.x.to_f
sp2y=ep.second.p2.y.to_f
distance_fp1sp1=(((fp1x-sp1x)**2 + (fp1y-sp1y)**2)**0.5).round(3)
distance_fp1sp2=(((fp1x-sp2x)**2 + (fp1y-sp2y)**2)**0.5).round(3)
distance_fp2sp1=(((fp2x-sp1x)**2 + (fp2y-sp1y)**2)**0.5).round(3)
distance_fp2sp2=(((fp2x-sp2x)**2 + (fp2y-sp2y)**2)**0.5).round(3)
distance_min=[distance_fp1sp1,distance_fp1sp2,distance_fp2sp1,distance_fp2sp2].min
dist_cacu  = [dist_cacu, distance_min].min
}
puts "dist result : #{dist}"
puts "dist_cacu result : #{dist_cacu}"
return "#{dist_cacu}"

end
runing_time=0
check_result=disMin(ubm_sp)
while check_result.match("Infinity") and runing_time <10
ruleE  = ruleE+5
ubm_sp = ubm.space(ruleE.micron)
check_result=disMin(ubm_sp)
runing_time+=1
end
RBA::MessageBox.info("result","min. value is #{check_result}", RBA::MessageBox.b_ok)
#report("rulechecker")
ubm_sp.output("RuleE")
``````
• Hi jiunnweiyeh

The `abs_distance` provided from edge returns a minimum distance between point and given line which extends to infinity. Dependes on use case, you will need to add some conditions check to get what you want.

Here's an example of running result of different `min` dist extraction method

Method-1 : use `edge.abs_distance` only, which does not always get correct results.
Method-2 : use 4 vertiex from edge pair to find distance, which only works for certain cases.
Method-3 : combine the concept of both case, dilivers the correct results.

Both method-1 and 2 failed on case-2 for different reasons. Case-2 returns 2 error `EdgePairs` EP1 and EP2
The checked distance is marked in black double arrow.

Method-1
Check `Edge` from `EdgePair` to points on another sides, the issue of this method is the `abs_distance` returns distance of a `Point` to `Edge` EXTENDED line. additional condition check is required to get good results.

Method-2
Check distance of four corner and not actual `Edge` to `point` distance.

Method-3
Check `Point` to `Edge` relation, if point is in direct projection range of the `Edge`, use `abs_distance`, otherwise returns `Point` to closest `Edge.Point` distance.

Example as below, this compares three method with 5um spacing check test case, test case gds is as attached.

``````def vectorAngle(v1, v2)
vp, sp = v1.vprod(v2), v1.sprod(v2)
return(Math.atan2(vp, sp) * 180.0 / Math::PI)
end

def edgePointDist(edge, point)
ev1,  ev2  = edge.p2 - edge.p1, edge.p1 - edge.p2
pv1,  pv2  = point   - edge.p1, point   - edge.p2
aep1, aep2 = vectorAngle(ev1, pv1).abs, vectorAngle(ev2, pv2).abs
return [
(aep1 <= 90 && aep2 <= 90) ? edge.distance_abs(point) : Float::INFINITY,
edge.p1.distance(point),
edge.p2.distance(point),
].min
end

def disMin(eps)
dist_method1 = Float::INFINITY
dist_method2 = Float::INFINITY
dist_method3 = Float::INFINITY
eps.each{|ep|

dist_method1 = [
dist_method1,
ep.second.distance_abs(ep.first.p1),
ep.second.distance_abs(ep.first.p2),
].min

dist_method2 = [
dist_method2,
ep.first.p1.distance(ep.second.p1),
ep.first.p1.distance(ep.second.p2),
ep.first.p2.distance(ep.second.p1),
ep.first.p2.distance(ep.second.p2),
].min

dist_method3 = [
dist_method3,
edgePointDist(ep.first,  ep.second.p1),
edgePointDist(ep.first,  ep.second.p2),
edgePointDist(ep.second, ep.first.p1),
edgePointDist(ep.second, ep.first.p2),
].min
}
puts "dist method1 : #{dist_method1.round(3)}"
puts "dist method2 : #{dist_method2.round(3)}"
puts "dist method3 : #{dist_method3.round(3)}"
end

ubm    = input(94,0)
ruleE  = 5
ubm_sp = ubm.space(ruleE.micron)
disMin(ubm_sp)
ubm_sp.output("RuleE")
``````
• edited December 2023

Hi @RawrRanger
Thanks very much for your help , look like it is what I need.
But in my case , the method 1/3 will get 0..
I need to check what is better way in my side.
Thanks your hard work.

• Here I have some modify , just a little difference..
hope this will be helpful for everyone.

``````def vectorAngle(v1, v2)
vp, sp = v1.vprod(v2), v1.sprod(v2)
return(Math.atan2(vp, sp) * 180.0 / Math::PI)
end

def edgePointDist(edge, point)
ev1,  ev2  = edge.p2 - edge.p1, edge.p1 - edge.p2
pv1,  pv2  = point   - edge.p1, point   - edge.p2
aep1, aep2 = vectorAngle(ev1, pv1).abs, vectorAngle(ev2, pv2).abs
return [
(aep1 <= 90 && aep2 <= 90) ? edge.distance_abs(point) : Float::INFINITY,
edge.p1.distance(point),
edge.p2.distance(point),
].min
end

def disMin(eps)
small_value=0.001
dist_method1 = Float::INFINITY
dist_method2 = Float::INFINITY
dist_method3 = Float::INFINITY
dist_sort=Array.new()
eps.each{|ep|

dist_method1 = [
dist_method1,
ep.second.distance_abs(ep.first.p1),
ep.second.distance_abs(ep.first.p2),
].min

dist_method2 = [
dist_method2,
ep.first.p1.distance(ep.second.p1),
ep.first.p1.distance(ep.second.p2),
ep.first.p2.distance(ep.second.p1),
ep.first.p2.distance(ep.second.p2),
].min

dist_method3 = [
dist_method3,
edgePointDist(ep.first,  ep.second.p1),
edgePointDist(ep.first,  ep.second.p2),
edgePointDist(ep.second, ep.first.p1),
edgePointDist(ep.second, ep.first.p2),
].min
}
if dist_method1.round(3) > small_value then
dist_sort << dist_method1.round(3)
end

if dist_method2.round(3) > small_value then
dist_sort << dist_method2.round(3)
end

if dist_method3.round(3) > small_value then
dist_sort << dist_method3.round(3)
end
return dist_sort.min
end

ubm    = input(94,0)
ruleE  = 51.04
ubm_sp = ubm.space(ruleE.micron)
min_distance=disMin(ubm_sp) + 0.1
ubm_sp = ubm.space(min_distance.micron)
ubm_sp.output("RuleE")
RBA::MessageBox.info("Answer","Finally , it is answer  #{min_distance}", RBA::MessageBox.b_ok)
``````
• edited December 2023

I have just released version 0.28.14 where EdgePair features a method "distance". It will return the minimum distance between every two points on edge 1 and 2.

It is based on the definition of an euclidian distance of a point from an edge:

The minimum distance is either the distance of one of the points of edge 1 against edge 2 or vice versa.

The C++ implementation is this:

``````Edge:

distance_type euclidian_distance (const db::point<C> &p)
{
if (db::sprod_sign (p - p1 (), d ()) < 0) {
return p1 ().distance (p);
} else if (db::sprod_sign (p - p2 (), d ()) > 0) {
return p2 ().distance (p);
} else {
return std::abs (distance (p));
}
}

Edge Pair:

distance_type distance () const
{
db::edge<C> e1 = first (), e2 = second ();
if (! e1.intersect (e2)) {
distance_type d12 = std::min (e2.euclidian_distance (e1.p1 ()), e2.euclidian_distance (e1.p2 ()));
distance_type d21 = std::min (e1.euclidian_distance (e2.p1 ()), e1.euclidian_distance (e2.p2 ()));
return std::min (d12, d21);
} else {
return 0;
}
}
``````

Matthias

• Hi sir,
1 issue need your help for DRC function.

In bumping house , we usually using "circle" or "oval(track)" pattern for UBM layer (Under Bump metal )
that always not a regular shape like suqare or octagon.
Yes, that is similar to PCB...not only using a track shape , we also rotate it.....

as what the programming /code in space .
sometimes that can't find the min. space between edge to edge in this case.
Look like that programming wants to checking where the out of rule in pattern edge (shift to on grid )
to another edge(shift to on grid).

As below ( this is a Hyperbole picture , just to shown what I means)
so that will cause the DRC result have little shift (compare with what the real space)around 0.05um.
(in fact , I didn't know what the gap is , I just base on current pattern to guess that)
Is any possible way to avoid this gap or make it as shrink /little /reduce as possible?

• edited December 2023

Hi jiunnweiyeh,

I've adjust the Min distance code based on Matthias suggestion, the only difference I've made is this section in `epDistance`.

``````    if edge1.length == 0 or edge2.length == 0
return edge1.p1.distance(edge2.p1)
``````

this length check exist is to avoid the zero distance issue you've mensioned in previous post, which I found is triggered by `Edge` in `EdgePair` having length that is 0, in this case the edge to edge calculation is no longer suitable and returns point to point distance instead.

Example code as below, test cased as attached, return result : 49.729um
(without checking length, result will be 0um)

``````def epDistance (edge, point)
if (point - edge.p1).sprod_sign(edge.d) < 0
return edge.p1.distance(point)

elsif (point - edge.p2).sprod_sign(edge.d) > 0
return edge.p2.distance(point)

else
return edge.distance(point).abs

end
end

def eeDistance(edge1, edge2)
if edge1.length == 0 or edge2.length == 0
return edge1.p1.distance(edge2.p1)

elsif !(edge1.intersect(edge2))
return [
epDistance(edge2, edge1.p1),
epDistance(edge2, edge1.p2),
epDistance(edge1, edge2.p1),
epDistance(edge1, edge2.p2),
].min
end
return 0
end

def disMin(eps)
dist = []
eps.each{|ep| dist << eeDistance(ep.first, ep.second)}
puts "dist : #{dist.min.round(3)}"
end

ubm    = input(94,0)
ruleE  = 50
ubm_sp = ubm.space(ruleE.micron)
disMin(ubm_sp)
ubm_sp.output("RuleE")
``````

Regarding to the DRC display accuracy issue you've mentioned, this only affects the final display showed on screen
following example runs a space check on a 4um space object, the result is accurate, but the display result have an offset of 1 dbu on each side.

DRC EdgePair printed out by function : `(6.5,-5;6.5,5) (2.5,2.5;2.5,-2.5)`
DRC result from property window `(6.501, -5.001) (2.499, -2.501) (2.499, 2.501) (6.501, 5.001)`

This I think this offset might be implemented on purpose to make display result of small/slim object have better visibility.

The way to avoid this is actually simple:

``````ubm_sp.output("RuleE")
ubm_sp.polygons.output("RuleE_polygon")  #use this can avoid small offset
``````
• Hi @RawrRanger
Thanks very much for your help , I will base on that code to check and building mind.
Thanks.

• Version 0.28.14 now has the `EdgePair#distance` attribute. Maybe you would like to try this.

Best regards,

Matthias

• @Matthias,
OK, got it , I will try 0.28.14 version and the function of EdgePair .
Thanks.