diff --git a/src/doc/doc/about/drc_ref_global.xml b/src/doc/doc/about/drc_ref_global.xml index 27f0d6a35..da588bf12 100644 --- a/src/doc/doc/about/drc_ref_global.xml +++ b/src/doc/doc/about/drc_ref_global.xml @@ -722,6 +722,15 @@ actual edges from the first input (see separation for

Similar to log, but the message is printed formatted as an error

+

"evaluate_nets" - Evaluates expressions on nets

+ +

Usage:

+ +

+See Netter#evaluate_nets for a description of that function. +

"extent" - Creates a new layer with the bounding box of the default source or cell bounding boxes

Usage:

diff --git a/src/doc/doc/about/drc_ref_layer.xml b/src/doc/doc/about/drc_ref_layer.xml index b7d8cc9aa..361c7c8ab 100644 --- a/src/doc/doc/about/drc_ref_layer.xml +++ b/src/doc/doc/about/drc_ref_layer.xml @@ -967,6 +967,104 @@ The following images show the effect of the method:

+

"evaluate" - Evaluates expressions on the shapes of the layer

+ +

Usage:

+ +

+Evaluates the given expression on the shapes of the layer. +The expression needs to be written in the KLayout expressions +notation. +

+The expressions can place properties on the shapes using the +"put" function. As input, the expressions will receive the +(merged) shapes in micrometer units (e.g. DPolygon type) +by calling the "shape" function. +

+By default, input shapes are copied to the output with the properties +attached to them by "put". You can skip shapes by calling "skip" with +a 'true' value. In that case, the shape is not copied. This allows +implementing filters. +

+Available functions for the expressions are: +

+

+

+Properties with well-formed names (e.g. "VALUE") are available as +variables in the expressions as a shortcut. +

+'variables' is a hash of arbitrary names and values. Each of these values +becomes available as a variable in the expression. This eliminates the need +to build expression strings with pasted value strings for passing external values. +

+If 'keep_properties' is true, the existing properties of the shape will be kept. +Otherwise (the default), existing properties will be removed before adding new +ones with 'put'. +

+The following example computes the area of the shapes and puts them +into a property 'area': +

+

+layer.evaluate(scales("put('area', shape.area)"))
+
+

+NOTE: GDS does not support properties with string names, so +either save to OASIS or use integer numbers for the property names. +

+The expressions require a hint +whether they make use of anisotropic or scale-dependent properties. +For example, the height of a box is an anisotropic property. If a check is made +inside a rotated cell, the height transforms to a width and the check renders +different results for the same cell, if the cell is placed rotated and non-rotated. +The solution is cell variant formation which happens automatically when the +"aniso" hint is present. +

+Similarly, if a check is made against physical dimensions, the check will have +different results for cells placed with different magnifications. Such a check +is not scale-invariant and needs to get a "scaled" hint. +

+By default it is assumed that the expressions are isotropic and scale invariant. +You can mark an expression as anisotropic and/or scale dependent using the following +expression modifiers: +

+

+# isotropic and scale invariant
+layer.evaluate("put('holes', shape.holes)")
+
+# anisotropic, but scale invariant
+layer.evaluate(aniso("put('aspect_ratio', shape.bbox.height/shape.bbox.width)"))
+
+# isotropic, but not scale invariant
+layer.evaluate(scales("put('area', shape.area)"))
+
+# anisotropic and not scale invariant
+layer.evaluate(aniso_and_scales("put('width', shape.bbox.width)"))
+
+

+If you forget to specify this hint, the expression will use the local +shape properties and fail to correctly produce the results in the presence +of deep mode and rotated or magnified cell instances. +

+The "evaluate" method modifies the input layer. A version that returns +a new layer without modifying the input is evaluated. +

+

"evaluated" - Evaluates expressions on the shapes of the layer and returns a new layer

+ +

Usage:

+ +

+This method is the out-of-place version of evaluate. It takes the same +arguments. +

"extended" - Returns polygons describing an area along the edges of the input

Usage:

@@ -2780,6 +2878,87 @@ is
covering.

This method is available for polygons only.

+

"select_if" - Selects shapes of a layer based on the evaluation of an expression

+ +

Usage:

+ +

+Evaluates the given expression on the shapes of the layer. +If the evaluation gives a 'true' value, the shape is selected. Otherwise +it is discarded. +

+The expression is written in KLayout expression notation. +

+As input, the expressions will receive the +(merged) shapes in micrometer units (e.g. DPolygon type) +by calling the "shape" function. +

+Available functions are: +

+

+

+Properties with well-formed names (e.g. "VALUE") are available as +variables in the expressions as a shortcut. +

+The expressions require a hint +whether they make use of anisotropic or scale-dependent properties. +For example, the height of a box is an anisotropic property. If a check is made +inside a rotated cell, the height transforms to a width and the check renders +different results for the same cell, if the cell is placed rotated and non-rotated. +The solution is cell variant formation which happens automatically when the +"aniso" hint is present. +

+Similarly, if a check is made against physical dimensions, the check will have +different results for cells placed with different magnifications. Such a check +is not scale-invariant and needs to get a "scaled" hint. +

+By default it is assumed that the expressions are isotropic and scale invariant. +You can mark an expression as anisotropic and/or scale dependent using the following +expression modifiers: +

+

+# isotropic and scale invariant
+layer.select_if("shape.holes > 0")
+
+# anisotropic, but scale invariant
+layer.select_if(aniso("shape.bbox.height/shape.bbox.width > 2"))
+
+# isotropic, but not scale invariant
+layer.select_if(scales("shape.area > 10.0"))
+
+# anisotropic and not scale invariant
+layer.select_if(aniso_and_scales("shape.bbox.width > 10.0"))
+
+

+If you forget to specify this hint, the expression will use the local +shape properties and fail to correctly produce the results in the presence +of deep mode and rotated or magnified cell instances. +

+'variables' is a hash of arbitrary names and values. Each of these values +becomes available as a variable in the expression. This eliminates the need +to build expression strings with pasted value strings for passing external values. +

+The following example selects all shapes on the layer with an area +less than 10 square micrometers: +

+

+layer.select(scales("shape.area < 10.0"))
+
+

+Written with a variable as a threshold this becomes: +

+

+layer.select(scales("shape.area < thr"), { "thr" => 10.0 })
+
+

+This version modifies the input layer. A version that returns +a new layer with the selected shapes is selected_if. +

"select_inside" - Selects edges, edge pairs or polygons of self which are inside edges or polygons from the other layer

Usage:

@@ -2960,6 +3139,15 @@ properties. See
DRCSource#input map_props is a way to change property keys and remove_props will entirely remove all user properties.

+

"selected_if" - Selects shapes based on the evaluation of an expression

+ +

Usage:

+ +

+This method is the out-of-place version of select_if. It takes the same arguments. +

"sep" - An alias for "separation"

Usage:

@@ -3331,6 +3519,23 @@ one layer and all others in a second layer. This method is equivalent to calling

The options of this method are the same than covering.

+

"split_if" - Selects shapes based on the evaluation of an expression and returns selected and unselected shapes in different layers

+ +

Usage:

+ +

+This method, like the other 'split_...' methods returns two layers: +one with the result of selected_if, and a second with all other shapes. +

+The following example splits a layer into two: one with the shapes that have an area +less than 10 square micrometers and another one with the shapes that have a bigger area: +

+

+(smaller, bigger) = layer.split_if(scales("shape.area < 10.0"))
+
+

"split_inside" - Returns the results of inside and not_inside at the same time

Usage:

diff --git a/src/doc/doc/about/drc_ref_netter.xml b/src/doc/doc/about/drc_ref_netter.xml index 9cfe03f3f..0157af89f 100644 --- a/src/doc/doc/about/drc_ref_netter.xml +++ b/src/doc/doc/about/drc_ref_netter.xml @@ -342,6 +342,82 @@ Specifying a factor of 2 will make all devices being extracted as if the geometries were two times larger. This feature is useful when the drawn layout does not correspond to the physical dimensions.

+

"evaluate_nets" - Evaluates net properties and annotates shapes from a given layer on the nets

+ +

Usage:

+
    +
  • evaluate_nets(primary_layer, secondary_layers, expression [, variables])
  • +
+

+The function takes a primary layer and a number of secondary layers, each of +them given a variable name. +It visits each net and evaluates the given expression on the net. +The expression needs to be written in KLayout expression notations. +

+The default action is to copy the shapes of the primary layer to the +output. This action can be modified in some ways: skip shapes of +certain nets or attach properties to the shapes during the evaluation. +

+Using the "put" function inside the expression, properties can be +attached to the output shapes. The properties can be computed using +a number of net attributes - area and perimeter for example. +

+Also the Net object representing the net is available through the +'net' function. This allows implementing a more elaborate +antenna check for example. +

+Also, the expression can choose to drop shapes and not copy them to +the output by calling the "skip" function with a "true" argument. +

+Arbitrary values can be passed as variables, which removes the need +to encode variable values into the expression. For this, use the +'variables' argument and pass a hash with names and values. Each of +those values becomes available as a variable with the given name +inside the expression. +

+The following functions are available inside the expressions: +

+

    +
  • "net" - the Net object of the current net
  • +
  • "skip(flag)" - if called with a 'true' argument, the primary layer's shapes are not copied for this net
  • +
  • "put(name, value)" - places the value as a property with name 'name' (this must be a string) on the output shapes
  • +
  • "area" - the combined area of the primary layer's shapes on the net in square micrometer units
  • +
  • "area(symbol)" - the combined area of the secondary layer's shapes on the net in square micrometer units
  • +
  • "perimeter" - the perimeter of the primary layer's shapes on the net in micrometer units
  • +
  • "perimeter(symbol)" - the perimeter of the secondary layer's shapes on the net in micrometer units
  • +
+

+Here, 'symbol' is the name given to the secondary layer in the secondary layer +dictionary. +

+The following example emulates an antenna check. It computes the area ratio of metal vs. gate area and +attaches the value as a property with name 'AR' to the shapes, copied from the 'gate' layer: +

+

+gate = ...   # a layer with the gate shapes
+metal = ...  # a layer with metal shapes
+
+# NOTE: 'MET' is the name we are going to use in the expression
+antenna_errors = evaluate_nets(gate, { "MET" => metal }, "put('AR',area(MET)/area)")
+
+

+This other example also computes the area ratio of metal vs. gate area, and +outputs the gate shapes of all nets whose metal to gate area ratio is bigger than +500. The area ratio is output to a property with name 'AR': +

+

+gate = ...   # a layer with the gate shapes
+metal = ...  # a layer with metal shapes
+
+variables = { "thr" => 500.0 }
+expression = "var ar=area(MET)/area; put('AR',ar); skip(ar<thr)"
+
+antenna_errors = evaluate_nets(gate, { "MET" => metal }, expression, variables)
+
+

+NOTE: GDS does not support properties with string names, so +either save to OASIS, or use integer numbers for the property names. +

"extract_devices" - Extracts devices based on the given extractor class, name and device layer selection

Usage:

diff --git a/src/drc/drc/built-in-macros/_drc_layer.rb b/src/drc/drc/built-in-macros/_drc_layer.rb index acfbba82f..ff13742ae 100644 --- a/src/drc/drc/built-in-macros/_drc_layer.rb +++ b/src/drc/drc/built-in-macros/_drc_layer.rb @@ -5175,7 +5175,7 @@ CODE # %DRC% # @name evaluate - # @brief Evaluate expressions on the shapes of the layer + # @brief Evaluates expressions on the shapes of the layer # @synopsis layer.evaluate(expression [, variables [, keep_properties ]]) # # Evaluates the given expression on the shapes of the layer. @@ -5185,39 +5185,54 @@ CODE # The expressions can place properties on the shapes using the # "put" function. As input, the expressions will receive the # (merged) shapes in micrometer units (e.g. RBA::DPolygon type) - # by calling the "shape" function. You can also call 'skip' with - # a 'true' value to drop the shape from the output entirely. + # by calling the "shape" function. + # + # By default, input shapes are copied to the output with the properties + # attached to them by "put". You can skip shapes by calling "skip" with + # a 'true' value. In that case, the shape is not copied. This allows + # implementing filters. # - # Available functions are: + # Available functions for the expressions are: # # @ul # @li "put(name, value)": creates a property with the given name and value @/li # @li "skip(flag)": if called with a 'true' value, the shape will be dropped from the output @/li # @li "shape": the current shape in micrometer units @/li # @li "value(name)": the value of a property with name 'name' or nil if the current shape does not have a property with this name @/li - # @ul + # @/ul # # Properties with well-formed names (e.g. "VALUE") are available as # variables in the expressions as a shortcut. # - # 'variables' can be a hash of arbitrary names and values. Each of these values - # becomes available as variables in the expression. This eliminates the need - # to build expression strings to pass variable values. + # 'variables' is a hash of arbitrary names and values. Each of these values + # becomes available as a variable in the expression. This eliminates the need + # to build expression strings with pasted value strings for passing external values. # # If 'keep_properties' is true, the existing properties of the shape will be kept. # Otherwise (the default), existing properties will be removed before adding new # ones with 'put'. # + # The following example computes the area of the shapes and puts them + # into a property 'area': + # + # @code + # layer.evaluate(scales("put('area', shape.area)")) + # @/code + # + # NOTE: GDS does not support properties with string names, so + # either save to OASIS or use integer numbers for the property names. + # # The expressions require a hint - # whether the they make use of anisotropic or scale-dependent properties. + # whether they make use of anisotropic or scale-dependent properties. # For example, the height of a box is an anisotropic property. If a check is made # inside a rotated cell, the height transforms to a width and the check renders - # different results for the same cell if the cell is placed rotated and non-rotated. - # The solution is cell variant formation. + # different results for the same cell, if the cell is placed rotated and non-rotated. + # The solution is cell variant formation which happens automatically when the + # "aniso" hint is present. # # Similarly, if a check is made against physical dimensions, the check will have # different results for cells placed with different magnifications. Such a check - # is not scale-invariant. + # is not scale-invariant and needs to get a "scaled" hint. # # By default it is assumed that the expressions are isotropic and scale invariant. # You can mark an expression as anisotropic and/or scale dependent using the following @@ -5241,25 +5256,16 @@ CODE # shape properties and fail to correctly produce the results in the presence # of deep mode and rotated or magnified cell instances. # - # The following example computes the area of the shapes and puts them - # into a property 'area': - # - # @code - # layer.evaluate(scales("put('area', shape.area)")) - # @/code - # - # NOTE: GDS does not support properties with string names, so - # either save to OASIS or use integer numbers for the property names. - # - # This version modifies the input layer. A version that returns - # a new layer is \evaluated. + # The "evaluate" method modifies the input layer. A version that returns + # a new layer without modifying the input is \evaluated. # %DRC% # @name evaluated - # @brief Evaluate expressions on the shapes of the layer and returns a new layer + # @brief Evaluates expressions on the shapes of the layer and returns a new layer # @synopsis layer.evaluated(expression [, variables [, keep_properties]]) # - # This method is the out-of-place version of \evaluate. + # This method is the out-of-place version of \evaluate. It takes the same + # arguments. def _make_proc(expression, variables, keep_properties) @@ -5324,21 +5330,22 @@ CODE # @ul # @li "shape": the current shape in micrometer units @/li # @li "value(name)": the value of a property with name 'name' or nil if the current shape does not have a property with this name @/li - # @ul + # @/ul # # Properties with well-formed names (e.g. "VALUE") are available as # variables in the expressions as a shortcut. # # The expressions require a hint - # whether the they make use of anisotropic or scale-dependent properties. + # whether they make use of anisotropic or scale-dependent properties. # For example, the height of a box is an anisotropic property. If a check is made # inside a rotated cell, the height transforms to a width and the check renders - # different results for the same cell if the cell is placed rotated and non-rotated. - # The solution is cell variant formation. + # different results for the same cell, if the cell is placed rotated and non-rotated. + # The solution is cell variant formation which happens automatically when the + # "aniso" hint is present. # # Similarly, if a check is made against physical dimensions, the check will have # different results for cells placed with different magnifications. Such a check - # is not scale-invariant. + # is not scale-invariant and needs to get a "scaled" hint. # # By default it is assumed that the expressions are isotropic and scale invariant. # You can mark an expression as anisotropic and/or scale dependent using the following @@ -5362,9 +5369,9 @@ CODE # shape properties and fail to correctly produce the results in the presence # of deep mode and rotated or magnified cell instances. # - # 'variables' can be a hash of arbitrary names and values. Each of these values - # becomes available as variables in the expression. This eliminates the need - # to build expression strings to pass variable values. + # 'variables' is a hash of arbitrary names and values. Each of these values + # becomes available as a variable in the expression. This eliminates the need + # to build expression strings with pasted value strings for passing external values. # # The following example selects all shapes on the layer with an area # less than 10 square micrometers: @@ -5373,6 +5380,12 @@ CODE # layer.select(scales("shape.area < 10.0")) # @/code # + # Written with a variable as a threshold this becomes: + # + # @code + # layer.select(scales("shape.area < thr"), { "thr" => 10.0 }) + # @/code + # # This version modifies the input layer. A version that returns # a new layer with the selected shapes is \selected_if. @@ -5381,7 +5394,7 @@ CODE # @brief Selects shapes based on the evaluation of an expression # @synopsis layer.selected_if(expression [, variables]) # - # This method is the out-of-place version of \select_if. + # This method is the out-of-place version of \select_if. It takes the same arguments. # %DRC% # @name split_if @@ -5389,10 +5402,10 @@ CODE # @synopsis layer.selected_if(expression [, variables]) # # This method, like the other 'split_...' methods returns two layers: - # one with the result of \\selected_if, and a second with all other shapes. + # one with the result of \selected_if, and a second with all other shapes. # - # The following example splits a layer into two: one with the shapes with an area - # less than 10 square micrometers and one with the shapes with a bigger area: + # The following example splits a layer into two: one with the shapes that have an area + # less than 10 square micrometers and another one with the shapes that have a bigger area: # # @code # (smaller, bigger) = layer.split_if(scales("shape.area < 10.0")) diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 8c009b57c..20fc3d03b 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -779,38 +779,44 @@ module DRC # It visits each net and evaluates the given expression on the net. # The expression needs to be written in KLayout expression notations. # - # By default, the shapes of the primary layer are copied to the output. + # The default action is to copy the shapes of the primary layer to the + # output. This action can be modified in some ways: skip shapes of + # certain nets or attach properties to the shapes during the evaluation. # # Using the "put" function inside the expression, properties can be # attached to the output shapes. The properties can be computed using - # a number of net attributes - area and perimeter, but also the RBA::Net - # object representing the net. This allows implementing a more elaborate + # a number of net attributes - area and perimeter for example. + # + # Also the RBA::Net object representing the net is available through the + # 'net' function. This allows implementing a more elaborate # antenna check for example. # - # Also, the expression can choose to drop shapes and not copy then to + # Also, the expression can choose to drop shapes and not copy them to # the output by calling the "skip" function with a "true" argument. # # Arbitrary values can be passed as variables, which removes the need - # to encode variable values into the expression. + # to encode variable values into the expression. For this, use the + # 'variables' argument and pass a hash with names and values. Each of + # those values becomes available as a variable with the given name + # inside the expression. # - # The following functions are available + # The following functions are available inside the expressions: # # @ul - # @li "net" - the RBA::Net object of the current net - # @li "skip(flag)" - if called with a 'true' argument, the primary layer's shapes are not copied for this net - # @li "put(name, value)" - places the value as a property with name 'name' (this must be a string) on the output shapes - # @li "area" - the combined area of the primary layer's shapes on the net in square micrometer units - # @li "area(symbol)" - the combined area of the secondary layer's shapes on the net in square micrometer units - # @li "perimeter" - the perimeter of the primary layer's shapes on the net in micrometer units - # @li "perimeter(symbol)" - the perimeter of the secondary layer's shapes on the net in micrometer units + # @li "net" - the RBA::Net object of the current net @/li + # @li "skip(flag)" - if called with a 'true' argument, the primary layer's shapes are not copied for this net @/li + # @li "put(name, value)" - places the value as a property with name 'name' (this must be a string) on the output shapes @/li + # @li "area" - the combined area of the primary layer's shapes on the net in square micrometer units @/li + # @li "area(symbol)" - the combined area of the secondary layer's shapes on the net in square micrometer units @/li + # @li "perimeter" - the perimeter of the primary layer's shapes on the net in micrometer units @/li + # @li "perimeter(symbol)" - the perimeter of the secondary layer's shapes on the net in micrometer units @/li # @/ul # # Here, 'symbol' is the name given to the secondary layer in the secondary layer # dictionary. # - # The following example computes the area ratio of metal vs. gate area and - # attaches the value on a property with name 'AR' to the shapes of a copy of the - # 'gate' layer: + # The following example emulates an antenna check. It computes the area ratio of metal vs. gate area and + # attaches the value as a property with name 'AR' to the shapes, copied from the 'gate' layer: # # @code # gate = ... # a layer with the gate shapes @@ -820,7 +826,7 @@ module DRC # antenna_errors = evaluate_nets(gate, { "MET" => metal }, "put('AR',area(MET)/area)") # @/code # - # The following example computes the area ratio of metal vs. gate area and + # This other example also computes the area ratio of metal vs. gate area, and # outputs the gate shapes of all nets whose metal to gate area ratio is bigger than # 500. The area ratio is output to a property with name 'AR': # @@ -835,7 +841,7 @@ module DRC # @/code # # NOTE: GDS does not support properties with string names, so - # either save to OASIS or use integer numbers for the property names. + # either save to OASIS, or use integer numbers for the property names. def evaluate_nets(primary, secondary, expression, variables = {})