Solid Models

DeviceLayout.SolidModels.SolidModelType
struct SolidModel{T} where {T <: SolidModelKernel}
    name::String
    groups::NTuple{4,Dict{String,AbstractPhysicalGroup}}
end
SolidModel(name::String, kernel=OpenCascade; overwrite=false)

A 3D geometry model.

Geometry rendering, boolean operations, and export are provided by the specified kernel.

Physical groups can be accessed by name using indexing with a String or Symbol and a dimension: mymodel["mygroup", 3] will return the PhysicalGroup with dimension 3 called mygroup.

Physical groups can also be assigned as mymodel["mygroup"] = dimtags, where dimtags is a list of NTuple{2, Int32} of entities identified by (dim, tag) in mymodel. If dimtags includes entities of multiple dimensions, then a group is created for each dimension.

If the constructor is called with overwrite=false (the default), then an error will be thrown if a model with the same name already exists. If overwrite=true, then any existing model with the same name will be deleted and a new model will be created.

A SolidModel can be saved to a file with FileIO.save(filename, sm). Supported filetypes for OpenCASCADE geometries are .brep and .stp. Meshes can be exported as .msh2 (compatible with Palace) or .msh (most recent Gmsh format) files.

source

Physical groups

Rendering to a SolidModel

DeviceLayout.render!Method
render!(sm::SolidModel, cs::AbstractCoordinateSystem{T}; map_meta=layer, postrender_ops=[], zmap=(_) -> zero(T), kwargs...) where {T}

Render cs to sm.

Keywords

  • map_meta: Function (m::SemanticMeta) -> name of PhysicalGroup (as String or Symbol; may also return nothing to skip rendering m)
  • postrender_ops: Vector of Tuples (destination, op, args, op_kwargs...) specifying "postrendering" of PhysicalGroups executed after entities have been rendered to to sm. Each operation op creates a new PhysicalGroup defined as sm[destination] = op(sm, args...; op_kwargs...). That is, args are the arguments to op (following the first argument, which is always the model sm being rendered to). For most operations, these arguments include the names and dimensions of groups being operated on, and op_kwargs are the keyword arguments passed to op. For example, ("base", difference_geom!, ("writeable_area", "base_negative"), :remove_object => true, :remove_tool => true) defines a postrendering step that subtracts the PhysicalGroup named "base_negative" from "writeable_area" (by default using dimension 2 for each group) to define a new group called "base". The keyword pairs :remove_object=>true and :remove_tool=>true mean that the "object" (first argument) group "writeable_area" and the "tool" (second argument) group "base_negative" are both removed when "base" is created.
  • zmap: Function (m::SemanticMeta) -> z coordinate of corresponding elements. Default: Map all metadata to zero.
  • meshing_parameters: MeshingParameters allows customization of the top level meshing parameters when calling Gmsh.

Available postrendering operations are translate!, extrude_z!, revolve!, union_geom!, intersect_geom!, difference_geom!, fragment_geom!, and box_selection. (The geometric Boolean operations are only available for models using the OpenCASCADE kernel.)

Additional keyword arguments are passed to SolidModels.to_primitives (which falls back to to_polygons) and may be used for certain entity types to control how entities of cs are converted to primitives and added to sm.

source
DeviceLayout.SolidModels.to_primitivesFunction
to_primitives(::SolidModel, ent::GeometryEntity; kwargs...)

Return a GeometryEntity or a vector of entities equivalent to ent.

Called inside render! before adding entities to the SolidModel. Each resulting entity corresponds to a single entity in that SolidModel's geometry kernel.

If there is no special handling for ent in the kernel, then the result will be to_polygons(ent; kwargs...).

source

Postrendering operations

After adding the elements of a CoordinateSystem to a SolidModel ("rendering"), we can perform additional "postrendering" operations including boolean operations, extrusions, and selections, to create new PhysicalGroups.

The recommended pattern is to provide all postrendering operations to the postrender_ops keyword argument in render!, because render! performs important cleanup at the very end. Specifically, it removes duplicate elements using the fragment operation; because this can also change the labeling of elements, it then reassigns the resulting elements to their corresponding groups.

Boolean operations and infix equivalents:

DeviceLayout.SolidModels.difference_geom!Function
difference_geom!(
    object::Union{PhysicalGroup, AbstractArray{PhysicalGroup}},
    tool::Union{PhysicalGroup, AbstractArray{PhysicalGroup}};
    tag=-1,
    remove_object=false,
    remove_tool=false
)

Create the geometric difference of the groups object and tool.

Return the resulting entities as a vector of (dimension, entity_tag) Tuples.

If tag is positive, try to set the tag explicitly (only valid if the boolean operation results in a single entity). Remove the object if remove_object is set. Remove the tool if remove_tool is set.

The operator - can be used as a synonymous infix operator.

source
difference_geom!(sm::SolidModel, object::Union{String, Symbol}, tool::Union{String, Symbol}, d1=2, d2=2;
    tag=-1,
    remove_object=false,
    remove_tool=false)

Create the geometric difference of groups with Symbol or String names object, tool in sm.

Return the resulting entities as a vector of (dimension, entity_tag) Tuples.

The dimensions of the object and tool groups can be specified as d1 and d2, respectively. The dimension defaults to 2 (surfaces).

If the tag is positive, try to set the tag explicitly (only valid if the boolean operation results in a single entity). Remove the object if remove_object is set. Remove the tool if remove_tool is set.

If object is not a physical group in sm, will error and return an empty dimtag array. If tool is not a physical group in sm, will return dimtags of object.

source
difference_geom!(sm::SolidModel, object, tool, d1=2, d2=2; remove_tool=false,
remove_object=false, kwargs...)

Create the geometric difference of groups object and tool which can be collections of Union{String, Symbol}.

source
DeviceLayout.SolidModels.fragment_geom!Function
fragment_geom!(
    object::Union{PhysicalGroup, AbstractArray{PhysicalGroup}},
    tool::Union{PhysicalGroup, AbstractArray{PhysicalGroup}};
    tag=-1,
    remove_object=false,
    remove_tool=false
)

Create the Boolean fragments (general fuse) of the groups object and tool, making all interfaces conformal.

When applied to entities of different dimensions, the lower dimensional entities will be automatically embedded in the higher dimensional entities if they are not on their boundary.

Return the resulting entities as a vector of (dimension, entity_tag) Tuples.

If tag is positive, try to set the tag explicitly (only valid if the boolean operation results in a single entity). Remove the object if remove_object is set. Remove the tool if remove_tool is set.

source
fragment_geom!(sm::SolidModel, object::Union{String, Symbol}, tool::Union{String, Symbol}, d1=2, d2=2;
    tag=-1,
    remove_object=false,
    remove_tool=false)

Create the Boolean fragments (general fuse) of groups with Symbol or String names object, tool in sm, making all interfaces conformal.

When applied to entities of different dimensions, the lower dimensional entities will be automatically embedded in the higher dimensional entities if they are not on their boundary.

Return the resulting entities as a vector of (dimension, entity_tag) Tuples.

The dimensions of the object and tool groups can be specified as d1 and d2, respectively. The dimension defaults to 2 (surfaces).

If the tag is positive, try to set the tag explicitly (only valid if the boolean operation results in a single entity). Remove the object if remove_object is set. Remove the tool if remove_tool is set.

If only one of object or tool is a physical group in sm, will perform union of physical group with itself, if neither are present will return an empty array.

source
DeviceLayout.SolidModels.intersect_geom!Function
intersect_geom!(
    object::Union{PhysicalGroup, AbstractArray{PhysicalGroup}},
    tool::Union{PhysicalGroup, AbstractArray{PhysicalGroup}};
    tag=-1,
    remove_object=false,
    remove_tool=false
)

Create the geometric intersection (the common parts) of the groups object and tool.

Return the resulting entities as a vector of (dimension, entity_tag) Tuples.

If tag is positive, try to set the tag explicitly (only valid if the boolean operation results in a single entity). Remove the object if remove_object is set. Remove the tool if remove_tool is set.

The operators * and can be used as synonymous infix operators.

source
intersect_geom!(sm::SolidModel, object::Union{String, Symbol}, tool::Union{String, Symbol}, d1=2, d2=2;
    tag=-1,
    remove_object=false,
    remove_tool=false)

Create the geometric intersection (the common parts) of groups with Symbol or String names object, tool in sm.

Return the resulting entities as a vector of (dimension, entity_tag) Tuples.

The dimensions of the object and tool groups can be specified as d1 and d2, respectively. The dimension defaults to 2 (surfaces).

If the tag is positive, try to set the tag explicitly (only valid if the boolean operation results in a single entity). Remove the object if remove_object is set. Remove the tool if remove_tool is set.

If tool or object are not physical groups in sm, will error and return an empty dimtag array.

source
DeviceLayout.SolidModels.union_geom!Function
union_geom!(
    object::Union{PhysicalGroup, AbstractArray{PhysicalGroup}},
    tool::Union{PhysicalGroup, AbstractArray{PhysicalGroup}};
    tag=-1,
    remove_object=false,
    remove_tool=false
)

Create the geometric union (the fusion) of the groups object and tool.

Return the resulting entities as a vector of (dimension, entity_tag) Tuples.

If tag is positive, try to set the tag explicitly (only valid if the boolean operation results in a single entity). Remove the object if remove_object is set. Remove the tool if remove_tool is set.

The operators + and can be used as synonymous infix operators.

source
union_geom!(sm::SolidModel, object::Union{String, Symbol}, tool::Union{String, Symbol}, d1=2, d2=2;
    tag=-1,
    remove_object=false,
    remove_tool=false)

Create the geometric union of groups with Symbol or String names object, tool in sm.

Return the resulting entities as a vector of (dimension, entity_tag) Tuples.

The dimensions of the object and tool groups can be specified as d1 and d2, respectively. The dimension defaults to 2 (surfaces).

If tag is positive, try to set the tag explicitly (only valid if the boolean operation results in a single entity). Remove the object if remove_object is set. Remove the tool if remove_tool is set.

If only one of object or tool is a physical group in sm, will perform union of physical group with itself, if neither are present will return an empty array.

source
Base.:+Method
+(object::AbstractPhysicalGroup, tool::AbstractPhysicalGroup)

Equivalent to union_geom!(object, tool). Can be used as an infix (object + tool).

source
Base.:∪Method
∪(object::AbstractPhysicalGroup, tool::AbstractPhysicalGroup)

Equivalent to union_geom!(object, tool). Can be used as an infix (object ∪ tool).

source
Base.:-Method
-(object::AbstractPhysicalGroup, tool::AbstractPhysicalGroup)

Equivalent to difference_geom!(object, tool). Can be used as an infix (object - tool).

source
Base.:*Method
*(object::AbstractPhysicalGroup, tool::AbstractPhysicalGroup)

Equivalent to intersect_geom!(object, tool). Can be used as an infix (object * tool).

source
Base.:∩Method
∩(object::AbstractPhysicalGroup, tool::AbstractPhysicalGroup)

Equivalent to intersect_geom!(object, tool). Can be used as an infix (object ∩ tool).

source

Selections:

DeviceLayout.SolidModels.box_selectionFunction
box_selection(x1, y1, z1, x2, y2, z2; dim=-1, delta=zero(x1))
box_selection(::SolidModel, x1, y1, z1, x2, y2, z2; dim=-1, delta=zero(x1))

Get the model entities in the bounding box defined by the two points (xmin, ymin, zmin) and (xmax, ymax, zmax). If dim is >= 0, return only the entities of the specified dimension (e.g. points if dim == 0).

Return the selected entities as a vector of (dimension, entity_tag) Tuples.

source
DeviceLayout.SolidModels.get_boundaryFunction
get_boundary(group::AbstractPhysicalGroup; combined=true, oriented=true, recursive=false)
get_boundary(sm::SolidModel, groupname, dim=2; combined=true, oriented=true, recursive=false)

Get the boundary of the model entities in group, given as a vector of (dim, tag) tuples.

Return the boundary of the individual entities (if combined is false) or the boundary of the combined geometrical shape formed by all input entities (if combined is true).

Return tags multiplied by the sign of the boundary entity if oriented is true.

Apply the boundary operator recursively down to dimension 0 (i.e. to points) if recursive is true.

source

Other operations:

DeviceLayout.SolidModels.extrude_z!Function
extrude_z!(g::PhysicalGroup, dz; num_elements=[], heights=[], recombine=false)
extrude_z!(sm::SolidModel, groupname, dz, groupdim=2; num_elements=[], heights=[], recombine=false)

Extrude the entities in g in the z direction by dz.

If the numElements vector is not empty, also extrude the mesh: the entries in numElements give the number of elements in each layer. If the height vector is not empty, it provides the (cumulative) height of the different layers, normalized to 1. If recombine is set, recombine the mesh in the layers.

Return the resulting entities as a vector of (dimension, entity_tag) Tuples.

source
DeviceLayout.SolidModels.remove_group!Function
remove_group!(sm::SolidModel, group::Union{String, Symbol}, dim; recursive=true, remove_entities=false)
remove_group!(group::AbstractPhysicalGroup; recursive=true, remove_entities=false)

Remove entities in group from the model, unless they are boundaries of higher-dimensional entities.

If recursive is true, remove all entities on their boundaries, down to dimension zero (points).

Also removes the (now-empty) physical group.

source
DeviceLayout.SolidModels.restrict_to_volume!Function
restrict_to_volume(sm::SolidModel, volume)

Replace all entities and groups with their intersection with sm[volume, 3].

Embeds entities if they are on the boundary of higher-dimensional entities and removes duplicate entities.

Preserves the meaning of existing groups by assigning to them the (possibly new) entities corresponding to that group's intersection with the volume.

source
DeviceLayout.SolidModels.revolve!Function
revolve!(g::PhysicalGroup, x, y, z, ax, ay, az, θ; num_elements=[], heights=[], recombine=false)
revolve!(sm::SolidModel, groupname, groupdim, x, y, z, ax, ay, az, θ; num_elements=[], heights=[], recombine=false)

Extrude the entities in g using a rotation of θ radians around the axis of revolution through (x, y, z) in the direction (ax, ay, az).

If the numElements vector is not empty, also extrude the mesh: the entries in numElements give the number of elements in each layer. If the height vector is not empty, it provides the (cumulative) height of the different layers, normalized to 1. If recombine is set, recombine the mesh in the layers. When the mesh is extruded the angle should be strictly smaller than 2π.

Return the resulting entities as a vector of (dimension, entity_tag) Tuples.

source
DeviceLayout.SolidModels.translate!Function
translate!(group, dx, dy, dz; copy=true)
translate!(sm::SolidModel, groupname, dx, dy, dz, groupdim=2; copy=true)

Translate the entities in physical group group by (dx, dy, dz).

If copy=true, then a copy of the entities in group are translated instead.

Return the resulting entities as a vector of (dimension, entity_tag) Tuples.

source

Meshing

Entities can carry mesh sizing information with them when rendered to a SolidModel. Many entities will default to a maximal size to reasonably resolve the geometry. This is particularly useful together with adaptive mesh refinement to efficiently refine your mesh to minimize estimated error with as few elements as possible. You can also style entities with MeshSized to manually control mesh sizing, and provide MeshingParameters to render! using the meshing_parameters keyword argument.

DeviceLayout.SolidModels.MeshingParametersType
Base.@kwdef struct MeshingParameters
    mesh_scale::Float64 = 1.0
    mesh_order::Int = 1
    α_default::Float64 = 1.0
    apply_size_to_surfaces::Bool = false
    high_order_optimize::Int = 1
    surface_mesh_algorithm::Int = 6
    volume_mesh_algorithm::Int = 1
    options::Dict{String, Float64} = Dict{String, Float64}()
end

MeshingParameters contains high level parameters to specify mesh sizing fields throughout the domain.

  • mesh_scale applies multiplicatively to the smallest size specified by any size field function, comparing to the formula in the MeshSized style, this results in all mesh size fields being rescaled where hmesh_scale * h.
  • mesh_order specifies the order of polynomials to use in representing the geometry, this is important if curved geometric features are present, mesh_order == 1 will represent the geometry with linear polynomials, whilst mesh_order == 2 will represent it with quadratic polynomials, and mesh_order == 3 with cubic polynomials. Increasing the value of mesh_order results in greater geometric fidelity, whilst making meshing more difficult (and prone to errors).
  • α_default specifies the default value of α to use for MeshSized entities where α is set to less than 0, α_default ∈ (0, 1] is particularly used for the default grading of Path entities.
  • apply_size_to_surfaces=true will cause the mesh sizing field to specify the size within any sized entities, as opposed to only along the perimeter of the entity if apply_size_to_surfaces=false. Setting apply_size_to_surfaces=true will result in a larger number of elements.
  • high_order_optimize=0 flag to pass to gmsh if optimization of a higher order mesh is to be performed. (0: none, 1: optimization, 2: elastic+optimization, 3: elastic, 4: fast curving). Refer to the gmsh documentation for more details.
  • surface_mesh_algorithm specifies the algorithm gmsh should use when performing the surface mesh generation. Refer to the gmsh documentation for more details.
  • volume_mesh_algorithm specifies the algorithm gmsh should use when performing the volume mesh generation. Refer to the gmsh documentation for more details.
  • options used to specify any additional options provided to gmsh, which will be set with gmsh.options.set_number(key, value) for each key => value pair. Refer to the gmsh documentation for a list of available options. Will override any other options as is called last.
source

Example

Below, we create a 3D model of a meandered CPW on a chip, restricting the model to a small volume (for example, for the purposes of simulation).

using DeviceLayout
using FileIO

cs = CoordinateSystem("test", nm)

# Create a CPW meander
pa = Path(-0.5mm, 0nm)
straight!(pa, 900μm, Paths.SimpleCPW(10μm, 6μm))
turn!(pa, 180°, 50μm)
straight!(pa, 900μm)
turn!(pa, -180°, 50μm)
straight!(pa, 900μm)
turn!(pa, 180°, 50μm)
straight!(pa, 900μm)
terminate!(pa)
render!(cs, pa, SemanticMeta(:base_negative))

render!(cs, centered(Rectangle(10mm, 10mm)), SemanticMeta(:chip_outline))
render!(cs, centered(Rectangle(9mm, 9mm)), SemanticMeta(:writeable_area))
render!(cs, centered(Rectangle(2mm, 2mm)), SemanticMeta(:simulated_area))

# Define z heights and thickness (where nonzero)
layer_z = Dict(:chip_outline => -525μm, :simulated_area => -1mm)
layer_thickness = Dict(:chip_outline => 525μm, :simulated_area => 2mm)

# Define postrendering operations:
# Extrusions, geometric Boolean operations, and other transformations
postrender_ops = vcat(
    [   # Extrude layers with nonzero thickness
        (string(layer) * "_extrusion", SolidModels.extrude_z!, (layer, thickness)) for
        (layer, thickness) in pairs(layer_thickness)
    ],
    [   # sm["chip_sim"] = intersect_geom!(sm, "simulated_area_extrusion", ...)
        (   # Intersect chip volume with simulation volume
            "chip_sim", # New physical group name
            SolidModels.intersect_geom!, # Operation
            # Arguments: Object, tool, object dimension, tool dimension
            ("simulated_area_extrusion", "chip_outline_extrusion", 3, 3), # Vol ∩ Vol
            # Keyword arguments
            :remove_tool => true # Remove the "chip_outline_extrusion" group
        ),
        (   # Intersect writeable area with simulation volume
            "writeable_sim",
            SolidModels.intersect_geom!,
            ("simulated_area_extrusion", "writeable_area", 3, 2), # Volume ∩ Area
            :remove_tool => true # Remove "writeable_area" group
        ),
        (   # Create group for non-chip volumes (vacuum)
            "vac_sim",
            SolidModels.difference_geom!, # Subtract "tool" from "object"
            ("simulated_area_extrusion", "chip_sim", 3, 3),
            :remove_object => true # Remove "simulated_area_extrusion" group
        ),
        (   # Subtract negative from writeable area to get metal area
            "base_metal",
            SolidModels.difference_geom!,
            ("writeable_sim", "base_negative"), # Uses default dimension 2
            :remove_object => true # Remove "writeable_sim" group
        )
    ]
)

sm = SolidModel("model"; overwrite=true)
SolidModels.render!(
    sm,
    cs,
    zmap=(m) -> get(layer_z, layer(m), 0μm),
    postrender_ops=postrender_ops
)
SolidModels.gmsh.model.mesh.generate() # Generate default mesh (low quality)
save("model.msh2", sm) # Use older MSH v2 format (Gmsh format compatible with Palace)
# save("model.stp", sm) # Use standard STEP format
SolidModels.gmsh.finalize() # Finalize the Gmsh API when done using Gmsh