Skip to content

Commit 1cebbc8

Browse files
Feature/sector shaped support (#23)
* feat(sector): add Sector and SectorInsulator types with geometric properties * fix(sector): fix typo insulator group inclusion to SectorInsulator * feat(sector): add GeometryBasics and PolygonOps dependencies with version updates * feat(sector): add LinearAlgebra import * feat(sectorinsulator): update _calculate_offset_polygon to use vertices and improve offset calculation * feat(setor): update preview of insulator to be transparent (to be handled later) * feat(sector): update preview function to handle Sector layers with TODOs for nominal length and offset adjustments * feat(sector): adjust vertex calculations for Sector and SectorInsulator layers to take the offset into account * feat(sector-FEM): add functions to draw polygons (from points) and polygons with holes in Gmsh * feat(sector-FEM): add specialized `_make_cablepart!` methods for creating Sector and SectorInsulator geometries * feat(sector-FEM): add GeometryBasics dependency for geometric operations (using Point) * feat(sector-FEM): update vertex translation to use GeometryBasics.Point for Sector and SectorInsulator * feat(sector-FEM): enhance draw_polygon to calculate centroid for Point vertices manually * fix(helper): use Base.error for GetDP executable error handling * feat(sector-FEM): update logging in draw_polygon function for clarity * feat(sector-FEM): add draw_polygon function with max edge length parameter * feat(sector-FEM): enhance draw_polygon_with_hole to support max edge length parameter TODO: it is using thickness to divide! * feat(sector-FEM): refactor _densify_vertices for better meshing of sector shaped conductor and insulator * chore(ci): remove main branch trigger from CI workflow to make it test on my feature branch too * chore(ci): add gh-pages to branches-ignore in CI workflow but make it run CI on anything else * refactor(fem): add GeometryBasics dependency for enhanced geometric operations - needed for sector shaped cable * feat(sector): add insulation thickness and enhance geometry calculations with debug logging * feat(sector): implement Shoelace formula for area calculations and ensure polygon closure in sector geometry * refactor(sector): correct debug logging for cross-sectional area calculation and increase arc points for improved accuracy * refactor(sector): remove GeometryBasics dependency and use Makie for point definitions * feat(sector): add support for rendering Sector and SectorInsulator layers with Makie * feat(sector): add comprehensive tutorial for sector-shaped cable design along with tests for CI * tutorial(sector): added save capability * feat(sector): add centroid calculation for sector shape * fix(sector-geometry): improve centroid calculation * feat(sector-cableslibrary): enhance handling for Sector and SectorInsulator layer types * feat(Sector): export Sector and SectorInsulator in the data model * fix(Sector): fix export of Sector Shape functions * fix(Sector): correct include statement for SectorInsulator file * feat(SectorParams): basic validation and constructor parameters for SectorParams struct. just testing validation. * feat(SectorParams): ADDED Validation for sector geometry. I followed the rules so the sectors play nice with other cable parts, In hope of being accepted into the main branch. * fix(fem/cable): correct botched merge conflict * fix(sectorinsulator): fix missing export * fix(fix): fix * fix(fix): now vibe fixing * fixfixfixfixfixfix --------- Co-authored-by: Amauri Martins <[email protected]> Co-authored-by: Amauri Martins <[email protected]>
1 parent 03142c6 commit 1cebbc8

13 files changed

Lines changed: 1192 additions & 18 deletions

File tree

.github/workflows/CI.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
name: CI
22
on:
33
push:
4-
branches:
5-
- main
4+
branches-ignore:
5+
- gh-pages
66
tags: ['*']
77
pull_request:
88
workflow_dispatch:

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7"
2929
NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56"
3030
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
3131
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
32+
PolygonOps = "647866c9-e3ac-4575-94e7-e3d426903924"
3233
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
3334
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
3435
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
@@ -74,6 +75,7 @@ Measurements = "2.11.0"
7475
NLsolve = "4.5.1"
7576
Pkg = "1.11.0"
7677
Plots = "1.40.9"
78+
PolygonOps = "0.1.2"
7779
Printf = "1.11.0"
7880
QuadGK = "2.11.2"
7981
Random = "1.11.0"

examples/tutorial2_sector.jl

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#=
2+
# Tutorial 2a - Building a sector-shaped cable design
3+
4+
This tutorial demonstrates how to model a typical low-voltage three-core power cable with sector-shaped conductors
5+
using the [`LineCableModels.jl`](@ref) package. The objective is to build a complete representation of a three-core 1 kV cable with 95 mm² aluminum sector-shaped conductors and a concentric copper neutral.
6+
=#
7+
8+
#=
9+
**Tutorial outline**
10+
```@contents
11+
Pages = [
12+
"tutorial2_sector.md",
13+
]
14+
Depth = 2:3
15+
```
16+
=#
17+
18+
#=
19+
## Introduction
20+
21+
Three-core power cables with sector-shaped conductors are common in low-voltage distribution networks. Their compact design allows for efficient use of space. This tutorial will guide you through creating a detailed [`CableDesign`](@ref) for such a cable.
22+
23+
This tutorial covers:
24+
25+
1. Defining materials with corrected resistivity.
26+
2. Creating sector-shaped conductors using [`SectorParams`](@ref) and [`Sector`](@ref).
27+
3. Assembling a multi-core [`CableDesign`](@ref).
28+
4. Modeling a concentric neutral wire array.
29+
5. Previewing the final cable design.
30+
=#
31+
32+
#=
33+
## Getting started
34+
=#
35+
36+
# Load the package and set up the environment:
37+
using LineCableModels
38+
using DataFrames
39+
import LineCableModels.BackendHandler: renderfig #hide
40+
fullfile(filename) = joinpath(@__DIR__, filename); #hide
41+
set_verbosity!(0); #hide
42+
43+
#=
44+
## Cable and Material Data
45+
46+
We start by defining the materials. We will create a custom aluminum material with a resistivity corrected based on its nominal DC resistance, and a PVC material for insulation.
47+
=#
48+
49+
# Initialize materials library and add a PVC material
50+
materials = MaterialsLibrary(add_defaults=true)
51+
pvc = Material(Inf, 8.0, 1.0, 20.0, 0.1) # simple PVC
52+
add!(materials, "pvc", pvc)
53+
copper = get(materials, "copper")
54+
aluminum = get(materials, "aluminum")
55+
56+
57+
#=
58+
## Sector-Shaped Core Conductors
59+
60+
The core of the cable consists of three sector-shaped aluminum conductors. We define the geometry using `SectorParams` based on datasheet or standard values.
61+
=#
62+
63+
# === Sector (core) geometry (table data) ===
64+
# Based on Urquhart's paper for a 3-core 95mm^2 cable
65+
n_sectors = 3
66+
r_back_mm = 10.24 # sector radius b
67+
d_sector_mm = 9.14 # sector depth s
68+
r_corner_mm = 1.02 # corner radius c
69+
theta_cond_deg = 119.0 # sector angle φ
70+
ins_thick = 1.1e-3 # core insulation thickness
71+
72+
sector_params = SectorParams(
73+
n_sectors,
74+
r_back_mm / 1000,
75+
d_sector_mm / 1000,
76+
r_corner_mm / 1000,
77+
theta_cond_deg,
78+
ins_thick
79+
)
80+
81+
#=
82+
With the sector parameters defined, we can create the individual `Sector` conductors and their insulation. Each sector is rotated to form the 3-core bundle.
83+
=#
84+
85+
rot_angles = (0.0, 120.0, 240.0)
86+
sectors = [Sector(sector_params, ang, aluminum) for ang in rot_angles]
87+
insulators = [SectorInsulator(sectors[i], ins_thick, pvc) for i in 1:3]
88+
89+
components = [
90+
CableComponent("core1", ConductorGroup(sectors[1]), InsulatorGroup(insulators[1])),
91+
CableComponent("core2", ConductorGroup(sectors[2]), InsulatorGroup(insulators[2])),
92+
CableComponent("core3", ConductorGroup(sectors[3]), InsulatorGroup(insulators[3]))
93+
]
94+
95+
#=
96+
## Concentric Neutral and Outer Jacket
97+
98+
The cable includes a concentric neutral conductor made of copper wires and an outer PVC jacket.
99+
=#
100+
101+
# === Concentric neutral (30 wires) ===
102+
n_neutral = 30
103+
r_strand = 0.79e-3
104+
R_N = 14.36e-3 # radius to center of neutral wires
105+
R_O = 17.25e-3 # outer radius of the cable
106+
107+
inner_radius_neutral = R_N - r_strand
108+
outer_jacket_thickness = R_O - (R_N + r_strand)
109+
110+
neutral_wires = WireArray(
111+
inner_radius_neutral,
112+
Diameter(2*r_strand),
113+
n_neutral,
114+
0.0, # lay ratio
115+
copper
116+
)
117+
118+
neutral_jacket = Insulator(neutral_wires, Thickness(outer_jacket_thickness), pvc)
119+
neutral_component = CableComponent("neutral", ConductorGroup(neutral_wires), InsulatorGroup(neutral_jacket))
120+
121+
#=
122+
## Assembling the Cable Design
123+
124+
Now we assemble the complete `CableDesign` by adding all the components.
125+
=#
126+
127+
design = CableDesign("NAYCWY_O_3x95_30x2_5", components[1])
128+
add!(design, components[2])
129+
add!(design, components[3])
130+
add!(design, neutral_component)
131+
132+
#=
133+
## Examining the Cable Design
134+
135+
We can now display a summary of the cable design and preview it graphically.
136+
=#
137+
138+
println("Cable design summary:")
139+
detailed_df = DataFrame(design, :detailed)
140+
display(detailed_df)
141+
142+
println("Previewing cable design...")
143+
plt, _ = preview(design)
144+
plt #hide
145+
146+
#=
147+
## Storing in a Library
148+
149+
Finally, we can store the cable design in a `CablesLibrary` for future reference.
150+
=#
151+
152+
library = CablesLibrary()
153+
add!(library, design)
154+
library_df = DataFrame(library)
155+
156+
# Save to file for later use:
157+
output_file = fullfile("cables_library.json")
158+
save(library, file_name = output_file);
159+
160+
#=
161+
## Conclusion
162+
163+
This tutorial has demonstrated how to model a three-core cable with sector-shaped conductors. Key takeaways include:
164+
165+
1. Creating custom materials with corrected properties.
166+
2. Defining complex conductor shapes like sectors.
167+
3. Assembling a multi-core cable design component by component.
168+
4. Visualizing the final design for verification.
169+
170+
This detailed modeling capability allows for accurate analysis of various cable configurations.
171+
=#
172+

src/LineCableModels.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export add!, set_verbosity!, set_backend!
99
export Material, MaterialsLibrary
1010

1111
# Data model (design + system):
12-
export Thickness, Diameter, CircStrands, RectStrands, Strip, Tubular, Semicon, Insulator
12+
export Thickness, Diameter, WireArray, Strip, Tubular, Semicon, Insulator, Sector, SectorParams, SectorInsulator
1313
export ConductorGroup, InsulatorGroup
1414
export CableComponent, CableDesign, NominalData
1515
export CablesLibrary
@@ -67,7 +67,7 @@ include("datamodel/DataModel.jl")
6767
using .DataModel: Thickness, Diameter, CircStrands, RectStrands, Strip, Tubular, Semicon,
6868
Insulator, ConductorGroup, InsulatorGroup, CableComponent, CableDesign, NominalData,
6969
CablesLibrary, CablePosition, LineCableSystem, trifoil_formation, flat_formation,
70-
preview, equivalent, MaxFill
70+
preview, equivalent, MaxFill, Sector, SectorParams, SectorInsulator
7171

7272
# Submodule `Engine`
7373
include("engine/Engine.jl")

src/datamodel/DataModel.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ module DataModel
2323

2424
# Export public API
2525
export Thickness, Diameter # Type definitions
26-
export CircStrands, RectStrands, Strip, Tubular # Conductor types
27-
export Semicon, Insulator # Insulator types
26+
export CircStrands, RectStrands, Strip, Tubular, SectorParams, Sector # Conductor types
27+
export Semicon, Insulator, SectorInsulator # Insulator types
2828
export ConductorGroup, InsulatorGroup # Group types
2929
export CableComponent, CableDesign # Cable design types
3030
export CablePosition, LineCableSystem # System types
@@ -52,7 +52,8 @@ using DataFrames
5252
using Colors
5353
using Plots
5454
using DisplayAs: DisplayAs
55-
55+
using LinearAlgebra
56+
using Makie: Point, Point2f # otherwise will require adding GeometryBasics as a dependency
5657
# Abstract types & interfaces
5758
include("types.jl")
5859
include("radii.jl")
@@ -72,11 +73,13 @@ include("rectstrands.jl")
7273
include("strip.jl")
7374
include("tubular.jl")
7475
include("conductorgroup.jl")
76+
include("sector.jl")
7577

7678
# Insulators
7779
include("insulator.jl")
7880
include("semicon.jl")
7981
include("insulatorgroup.jl")
82+
include("sectorinsulator.jl")
8083

8184

8285
# Groups

src/datamodel/preview.jl

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,74 @@ function _plot_layer_makie!(ax, layer, label::String;
498498
return plots
499499
end
500500

501+
if layer isa Sector
502+
vertices = layer.vertices
503+
# Convert vertices to Makie.Point2f format with offset
504+
makie_points = [Makie.Point2f(v[1] + x0, v[2] + y0) for v in vertices]
505+
# Ensure polygon is closed by adding first point at the end if needed
506+
if length(makie_points) > 0 && makie_points[1] != makie_points[end]
507+
push!(makie_points, makie_points[1])
508+
end
509+
510+
color = get_material_color_makie(layer.material_props)
511+
512+
poly = Makie.poly!(ax, makie_points;
513+
color = color,
514+
strokecolor = :black,
515+
strokewidth = 0.5,
516+
label = display_legend ? label : "")
517+
518+
if legend_sink !== nothing && display_legend
519+
push!(legend_sink[1], poly)
520+
push!(legend_sink[2], label)
521+
if length(legend_sink) >= 3
522+
push!(legend_sink[3], [poly])
523+
end
524+
if length(legend_sink) >= 4
525+
push!(legend_sink[4], NaN) # not a wirearray
526+
end
527+
end
528+
return (poly,)
529+
end
530+
531+
if layer isa SectorInsulator
532+
outer_vertices = [(v[1] + x0, v[2] + y0) for v in layer.outer_vertices]
533+
# Convert to Makie.Point2f format
534+
outer_points = [Makie.Point2f(v[1], v[2]) for v in outer_vertices]
535+
# Ensure polygon is closed
536+
if length(outer_points) > 0 && outer_points[1] != outer_points[end]
537+
push!(outer_points, outer_points[1])
538+
end
539+
540+
# (Not used for now) The inner boundary is the conductor's vertices. It must be reversed for the hole to be drawn correctly.
541+
inner_vertices = [(v[1] + x0, v[2] + y0) for v in layer.inner_sector.vertices]
542+
inner_points = [Makie.Point2f(v[1], v[2]) for v in inner_vertices]
543+
# Ensure inner polygon is closed
544+
if length(inner_points) > 0 && inner_points[1] != inner_points[end]
545+
push!(inner_points, inner_points[1])
546+
end
547+
color = get_material_color_makie(layer.material_props)
548+
# Create a shape with a hole by passing the outer boundary and holes as a vector of vectors
549+
polygon_with_hole = Makie.Polygon(outer_points, [inner_points])
550+
poly = Makie.poly!(ax, polygon_with_hole;
551+
color = color,
552+
strokecolor = :black,
553+
strokewidth = 0.5,
554+
label = display_legend ? label : "")
555+
556+
if legend_sink !== nothing && display_legend
557+
push!(legend_sink[1], poly)
558+
push!(legend_sink[2], label)
559+
if length(legend_sink) >= 3
560+
push!(legend_sink[3], [poly])
561+
end
562+
if length(legend_sink) >= 4
563+
push!(legend_sink[4], NaN) # not a wirearray
564+
end
565+
end
566+
return (poly,)
567+
end
568+
501569
@warn "Unknown layer type $(typeof(layer)); skipping"
502570
return ()
503571
end

0 commit comments

Comments
 (0)