Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
9db1940
feat(sector): add Sector and SectorInsulator types with geometric pro…
MohamedNumair Aug 31, 2025
231f585
fix(sector): fix typo insulator group inclusion to SectorInsulator
MohamedNumair Aug 31, 2025
b767c69
feat(sector): add GeometryBasics and PolygonOps dependencies with ver…
MohamedNumair Aug 31, 2025
f6fefca
Merge remote-tracking branch 'upstream/main' into feature/sector-shap…
MohamedNumair Aug 31, 2025
237e517
feat(sector): add LinearAlgebra import
MohamedNumair Aug 31, 2025
251e0d0
feat(sectorinsulator): update _calculate_offset_polygon to use vertic…
MohamedNumair Aug 31, 2025
6c5b81e
feat(setor): update preview of insulator to be transparent (to be han…
MohamedNumair Aug 31, 2025
7c2f442
Merge remote-tracking branch 'upstream/main' into feature/sector-shap…
MohamedNumair Sep 1, 2025
4a0cc2c
feat(sector): update preview function to handle Sector layers with TO…
MohamedNumair Sep 1, 2025
05d99b7
feat(sector): adjust vertex calculations for Sector and SectorInsulat…
MohamedNumair Sep 1, 2025
08ce969
feat(sector-FEM): add functions to draw polygons (from points) and po…
MohamedNumair Sep 1, 2025
b75a9a4
feat(sector-FEM): add specialized `_make_cablepart!` methods for crea…
MohamedNumair Sep 1, 2025
15d3085
feat(sector-FEM): add GeometryBasics dependency for geometric operat…
MohamedNumair Sep 1, 2025
9492c87
feat(sector-FEM): update vertex translation to use GeometryBasics.Poi…
MohamedNumair Sep 1, 2025
036282c
feat(sector-FEM): enhance draw_polygon to calculate centroid for Poin…
MohamedNumair Sep 1, 2025
99528a4
fix(helper): use Base.error for GetDP executable error handling
MohamedNumair Sep 1, 2025
82ce0a9
Merge remote-tracking branch 'upstream/main' into feature/sector-shap…
MohamedNumair Sep 1, 2025
f67e07d
feat(sector-FEM): update logging in draw_polygon function for clarity
MohamedNumair Sep 2, 2025
7113f19
feat(sector-FEM): add draw_polygon function with max edge length para…
MohamedNumair Sep 2, 2025
9c4686c
feat(sector-FEM): enhance draw_polygon_with_hole to support max edge …
MohamedNumair Sep 2, 2025
a009dcf
feat(sector-FEM): refactor _densify_vertices for better meshing of se…
MohamedNumair Sep 3, 2025
3edbde8
Merge remote-tracking branch 'upstream/main' into feature/sector-shap…
MohamedNumair Sep 4, 2025
2c65b6e
chore(ci): remove main branch trigger from CI workflow to make it tes…
MohamedNumair Sep 4, 2025
1951c24
chore(ci): add gh-pages to branches-ignore in CI workflow but make it…
MohamedNumair Sep 4, 2025
a277a67
Merge remote-tracking branch 'upstream/main' into feature/sector-shap…
MohamedNumair Sep 9, 2025
78bf008
refactor(fem): add GeometryBasics dependency for enhanced geometric o…
MohamedNumair Sep 9, 2025
0728896
Merge remote-tracking branch 'upstream/main' into feature/sector-shap…
MohamedNumair Sep 9, 2025
9ef8c46
feat(sector): add insulation thickness and enhance geometry calculati…
MohamedNumair Sep 14, 2025
4675bc0
feat(sector): implement Shoelace formula for area calculations and en…
MohamedNumair Sep 14, 2025
5b684fa
refactor(sector): correct debug logging for cross-sectional area calc…
MohamedNumair Sep 19, 2025
0c1080f
Merge remote-tracking branch 'upstream/main' into feature/sector-shap…
MohamedNumair Sep 20, 2025
ad5724a
refactor(sector): remove GeometryBasics dependency and use Makie for …
MohamedNumair Sep 20, 2025
4717eda
feat(sector): add support for rendering Sector and SectorInsulator la…
MohamedNumair Sep 20, 2025
5415119
Merge remote-tracking branch 'upstream/main' into feature/sector-shap…
MohamedNumair Sep 20, 2025
7f73472
Merge remote-tracking branch 'upstream/main' into feature/sector-shap…
MohamedNumair Sep 21, 2025
66d5610
feat(sector): add comprehensive tutorial for sector-shaped cable desi…
MohamedNumair Sep 22, 2025
6d3c58c
Merge remote-tracking branch 'upstream/main' into feature/sector-shap…
MohamedNumair Sep 29, 2025
92d4911
tutorial(sector): added save capability
MohamedNumair Sep 30, 2025
c22ab4e
feat(sector): add centroid calculation for sector shape
MohamedNumair Sep 30, 2025
bfb5508
fix(sector-geometry): improve centroid calculation
MohamedNumair Sep 30, 2025
1411095
feat(sector-cableslibrary): enhance handling for Sector and SectorIns…
MohamedNumair Oct 3, 2025
4b52082
feat(Sector): export Sector and SectorInsulator in the data model
MohamedNumair Oct 3, 2025
c0eb865
fix(Sector): fix export of Sector Shape functions
MohamedNumair Feb 11, 2026
d736ca1
fix(Sector): correct include statement for SectorInsulator file
MohamedNumair Feb 11, 2026
a6d4a0e
feat(SectorParams): basic validation and constructor parameters for S…
MohamedNumair Feb 11, 2026
5293c2a
feat(SectorParams): ADDED Validation for sector geometry.
MohamedNumair Feb 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: CI
on:
push:
branches:
- main
branches-ignore:
- gh-pages
tags: ['*']
pull_request:
workflow_dispatch:
Expand Down
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7"
NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PolygonOps = "647866c9-e3ac-4575-94e7-e3d426903924"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Expand Down Expand Up @@ -68,6 +69,7 @@ Measurements = "2.11.0"
NLsolve = "4.5.1"
Pkg = "1.11.0"
Plots = "1.40.9"
PolygonOps = "0.1.2"
Printf = "1.11.0"
QuadGK = "2.11.2"
Reexport = "1.2.2"
Expand Down
172 changes: 172 additions & 0 deletions examples/tutorial2_sector.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#=
# Tutorial 2a - Building a sector-shaped cable design

This tutorial demonstrates how to model a typical low-voltage three-core power cable with sector-shaped conductors
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.
=#

#=
**Tutorial outline**
```@contents
Pages = [
"tutorial2_sector.md",
]
Depth = 2:3
```
=#

#=
## Introduction

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.

This tutorial covers:

1. Defining materials with corrected resistivity.
2. Creating sector-shaped conductors using [`SectorParams`](@ref) and [`Sector`](@ref).
3. Assembling a multi-core [`CableDesign`](@ref).
4. Modeling a concentric neutral wire array.
5. Previewing the final cable design.
=#

#=
## Getting started
=#

# Load the package and set up the environment:
using LineCableModels
using DataFrames
import LineCableModels.BackendHandler: renderfig #hide
fullfile(filename) = joinpath(@__DIR__, filename); #hide
set_verbosity!(0); #hide

#=
## Cable and Material Data

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.
=#

# Initialize materials library and add a PVC material
materials = MaterialsLibrary(add_defaults=true)
pvc = Material(Inf, 8.0, 1.0, 20.0, 0.1) # simple PVC
add!(materials, "pvc", pvc)
copper = get(materials, "copper")
aluminum = get(materials, "aluminum")


#=
## Sector-Shaped Core Conductors

The core of the cable consists of three sector-shaped aluminum conductors. We define the geometry using `SectorParams` based on datasheet or standard values.
=#

# === Sector (core) geometry (table data) ===
# Based on Urquhart's paper for a 3-core 95mm^2 cable
n_sectors = 3
r_back_mm = 10.24 # sector radius b
d_sector_mm = 9.14 # sector depth s
r_corner_mm = 1.02 # corner radius c
theta_cond_deg = 119.0 # sector angle φ
ins_thick = 1.1e-3 # core insulation thickness

sector_params = SectorParams(
n_sectors,
r_back_mm / 1000,
d_sector_mm / 1000,
r_corner_mm / 1000,
theta_cond_deg,
ins_thick
)

#=
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.
=#

rot_angles = (0.0, 120.0, 240.0)
sectors = [Sector(sector_params, ang, aluminum) for ang in rot_angles]
insulators = [SectorInsulator(sectors[i], ins_thick, pvc) for i in 1:3]

components = [
CableComponent("core1", ConductorGroup(sectors[1]), InsulatorGroup(insulators[1])),
CableComponent("core2", ConductorGroup(sectors[2]), InsulatorGroup(insulators[2])),
CableComponent("core3", ConductorGroup(sectors[3]), InsulatorGroup(insulators[3]))
]

#=
## Concentric Neutral and Outer Jacket

The cable includes a concentric neutral conductor made of copper wires and an outer PVC jacket.
=#

# === Concentric neutral (30 wires) ===
n_neutral = 30
r_strand = 0.79e-3
R_N = 14.36e-3 # radius to center of neutral wires
R_O = 17.25e-3 # outer radius of the cable

inner_radius_neutral = R_N - r_strand
outer_jacket_thickness = R_O - (R_N + r_strand)

neutral_wires = WireArray(
inner_radius_neutral,
Diameter(2*r_strand),
n_neutral,
0.0, # lay ratio
copper
)

neutral_jacket = Insulator(neutral_wires, Thickness(outer_jacket_thickness), pvc)
neutral_component = CableComponent("neutral", ConductorGroup(neutral_wires), InsulatorGroup(neutral_jacket))

#=
## Assembling the Cable Design

Now we assemble the complete `CableDesign` by adding all the components.
=#

design = CableDesign("NAYCWY_O_3x95_30x2_5", components[1])
add!(design, components[2])
add!(design, components[3])
add!(design, neutral_component)

#=
## Examining the Cable Design

We can now display a summary of the cable design and preview it graphically.
=#

println("Cable design summary:")
detailed_df = DataFrame(design, :detailed)
display(detailed_df)

println("Previewing cable design...")
plt, _ = preview(design)
plt #hide

#=
## Storing in a Library

Finally, we can store the cable design in a `CablesLibrary` for future reference.
=#

library = CablesLibrary()
add!(library, design)
library_df = DataFrame(library)

# Save to file for later use:
output_file = fullfile("cables_library.json")
save(library, file_name = output_file);

#=
## Conclusion

This tutorial has demonstrated how to model a three-core cable with sector-shaped conductors. Key takeaways include:

1. Creating custom materials with corrected properties.
2. Defining complex conductor shapes like sectors.
3. Assembling a multi-core cable design component by component.
4. Visualizing the final design for verification.

This detailed modeling capability allows for accurate analysis of various cable configurations.
=#

4 changes: 2 additions & 2 deletions src/LineCableModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export add!, set_verbosity!, set_backend!
export Material, MaterialsLibrary

# Data model (design + system):
export Thickness, Diameter, WireArray, Strip, Tubular, Semicon, Insulator
export Thickness, Diameter, WireArray, Strip, Tubular, Semicon, Insulator, Sector, SectorParams, SectorInsulator
export ConductorGroup, InsulatorGroup
export CableComponent, CableDesign, NominalData
export CablesLibrary
Expand Down Expand Up @@ -61,7 +61,7 @@ using .EarthProps: EarthModel

# Submodule `DataModel`
include("datamodel/DataModel.jl")
using .DataModel: Thickness, Diameter, WireArray, Strip, Tubular, Semicon, Insulator,
using .DataModel: Thickness, Diameter, WireArray, Strip, Tubular, Semicon, Insulator, Sector, SectorParams, SectorInsulator,
ConductorGroup, InsulatorGroup, CableComponent, CableDesign, NominalData, CablesLibrary,
CablePosition, LineCableSystem, trifoil_formation, flat_formation, preview, equivalent

Expand Down
9 changes: 6 additions & 3 deletions src/datamodel/DataModel.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ module DataModel

# Export public API
export Thickness, Diameter # Type definitions
export WireArray, Strip, Tubular # Conductor types
export Semicon, Insulator # Insulator types
export WireArray, Strip, Tubular, SectorParams, Sector # Conductor types
export Semicon, Insulator, SectorInsulator # Insulator types
export ConductorGroup, InsulatorGroup # Group types
export CableComponent, CableDesign # Cable design types
export CablePosition, LineCableSystem # System types
Expand Down Expand Up @@ -52,7 +52,8 @@ using DataFrames
using Colors
using Plots
using DisplayAs: DisplayAs

using LinearAlgebra
using Makie: Point, Point2f # otherwise will require adding GeometryBasics as a dependency
# Abstract types & interfaces
include("types.jl")
include("radii.jl")
Expand All @@ -70,11 +71,13 @@ include("wirearray.jl")
include("strip.jl")
include("tubular.jl")
include("conductorgroup.jl")
include("sector.jl")

# Insulators
include("insulator.jl")
include("semicon.jl")
include("insulatorgroup.jl")
include("sectorinsulator.jl")


# Groups
Expand Down
68 changes: 68 additions & 0 deletions src/datamodel/preview.jl
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,74 @@ function _plot_layer_makie!(ax, layer, label::String;
return plots
end

if layer isa Sector
vertices = layer.vertices
# Convert vertices to Makie.Point2f format with offset
makie_points = [Makie.Point2f(v[1] + x0, v[2] + y0) for v in vertices]
# Ensure polygon is closed by adding first point at the end if needed
if length(makie_points) > 0 && makie_points[1] != makie_points[end]
push!(makie_points, makie_points[1])
end

color = get_material_color_makie(layer.material_props)

poly = Makie.poly!(ax, makie_points;
color = color,
strokecolor = :black,
strokewidth = 0.5,
label = display_legend ? label : "")

if legend_sink !== nothing && display_legend
push!(legend_sink[1], poly)
push!(legend_sink[2], label)
if length(legend_sink) >= 3
push!(legend_sink[3], [poly])
end
if length(legend_sink) >= 4
push!(legend_sink[4], NaN) # not a wirearray
end
end
return (poly,)
end

if layer isa SectorInsulator
outer_vertices = [(v[1] + x0, v[2] + y0) for v in layer.outer_vertices]
# Convert to Makie.Point2f format
outer_points = [Makie.Point2f(v[1], v[2]) for v in outer_vertices]
# Ensure polygon is closed
if length(outer_points) > 0 && outer_points[1] != outer_points[end]
push!(outer_points, outer_points[1])
end

# (Not used for now) The inner boundary is the conductor's vertices. It must be reversed for the hole to be drawn correctly.
inner_vertices = [(v[1] + x0, v[2] + y0) for v in layer.inner_sector.vertices]
inner_points = [Makie.Point2f(v[1], v[2]) for v in inner_vertices]
# Ensure inner polygon is closed
if length(inner_points) > 0 && inner_points[1] != inner_points[end]
push!(inner_points, inner_points[1])
end
color = get_material_color_makie(layer.material_props)
# Create a shape with a hole by passing the outer boundary and holes as a vector of vectors
polygon_with_hole = Makie.Polygon(outer_points, [inner_points])
poly = Makie.poly!(ax, polygon_with_hole;
color = color,
strokecolor = :black,
strokewidth = 0.5,
label = display_legend ? label : "")

if legend_sink !== nothing && display_legend
push!(legend_sink[1], poly)
push!(legend_sink[2], label)
if length(legend_sink) >= 3
push!(legend_sink[3], [poly])
end
if length(legend_sink) >= 4
push!(legend_sink[4], NaN) # not a wirearray
end
end
return (poly,)
end

@warn "Unknown layer type $(typeof(layer)); skipping"
return ()
end
Expand Down
Loading