Skip to content

Commit 88f4394

Browse files
checkpoint reached: YOU GOT A MESH, OH YES!
1 parent 0a43a03 commit 88f4394

13 files changed

Lines changed: 539 additions & 320581 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@ showcase
4444
!showcase/*.jl
4545
papers
4646
sprint
47+
*/fem_output

examples/fem_output/tutorial/tutorial.geo_unrolled

Lines changed: 0 additions & 52639 deletions
This file was deleted.

examples/fem_output/tutorial/tutorial.msh

Lines changed: 0 additions & 107535 deletions
This file was deleted.

examples/fem_output/tutorial3/tutorial3.geo_unrolled

Lines changed: 0 additions & 52639 deletions
This file was deleted.

examples/fem_output/tutorial3/tutorial3.msh

Lines changed: 0 additions & 107527 deletions
This file was deleted.

examples/tutorial3.jl

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,29 +36,31 @@ addto_linecablesystem!(
3636
display(cable_system)
3737

3838
# Create a FEMProblemDefinition with custom parameters
39+
domain_radius = 5.0
3940
problem = FEMProblemDefinition(
40-
domain_radius=5.0, # 5 m radius
41+
domain_radius=domain_radius,
4142
correct_twisting=true,
42-
elements_per_scale_length_conductor=3.0,
43-
elements_per_scale_length_insulator=2.0,
44-
elements_per_scale_length_semicon=4.0,
45-
elements_per_scale_length_interfaces=0.1,
43+
elements_per_length_conductor=1,
44+
elements_per_length_insulator=2,
45+
elements_per_length_semicon=1,
46+
elements_per_length_interfaces=5,
47+
points_per_circumference=8,
4648
analysis_type=[0, 1], # Electrostatic and magnetostatic
47-
mesh_size_min=1e-4,
48-
mesh_size_max=0.5,
49-
mesh_size_default=0.01,
50-
mesh_algorithm=6, # Frontal-Delaunay
49+
mesh_size_min=1e-6,
50+
mesh_size_max=domain_radius / 5,
51+
mesh_size_default=domain_radius / 10,
52+
mesh_algorithm=5,
5153
materials_db=materials_db
5254
)
5355

5456
# Create a FEMSolver with custom parameters
5557
solver = FEMSolver(
5658
force_remesh=true, # Force remeshing
5759
run_solver=false,
58-
preview_geo=true, # Preview geometry
59-
preview_mesh=false, # Preview the mesh
60+
preview_geo=false, # Preview geometry
61+
preview_mesh=true, # Preview the mesh
6062
base_path=joinpath(@__DIR__, "fem_output"),
61-
verbosity=2, # Verbose output
63+
verbosity=0, # Verbose output
6264
getdp_executable=joinpath("/home/amartins/Applications/onelab-Linux64", "getdp"), # Path to GetDP executable
6365
)
6466

src/FEMTools.jl

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -203,18 +203,18 @@ struct FEMProblemDefinition <: AbstractProblemDefinition
203203
domain_radius::Float64
204204
"Flag to correct for twisting effects \\[dimensionless\\]."
205205
correct_twisting::Bool
206-
"Elements per scale length for conductors \\[dimensionless\\]."
207-
elements_per_scale_length_conductor::Float64
208-
"Elements per scale length for insulators \\[dimensionless\\]."
209-
elements_per_scale_length_insulator::Float64
210-
"Elements per scale length for semiconductors \\[dimensionless\\]."
211-
elements_per_scale_length_semicon::Float64
212-
"Elements per scale length for interfaces \\[dimensionless\\]."
213-
elements_per_scale_length_interfaces::Float64
206+
"Elements per characteristic length for conductors \\[dimensionless\\]."
207+
elements_per_length_conductor::Int
208+
"Elements per characteristic length for insulators \\[dimensionless\\]."
209+
elements_per_length_insulator::Int
210+
"Elements per characteristic length for semiconductors \\[dimensionless\\]."
211+
elements_per_length_semicon::Int
212+
"Elements per characteristic length for interfaces \\[dimensionless\\]."
213+
elements_per_length_interfaces::Int
214+
"Points per circumference length (2π radians) \\[dimensionless\\]."
215+
points_per_circumference::Int
214216
"Analysis types to perform \\[dimensionless\\]."
215217
analysis_type::Vector{Int}
216-
"Materials database."
217-
materials_db::MaterialsLibrary
218218

219219
"Minimum mesh size \\[m\\]."
220220
mesh_size_min::Float64
@@ -225,6 +225,9 @@ struct FEMProblemDefinition <: AbstractProblemDefinition
225225
"Mesh algorithm to use \\[dimensionless\\]."
226226
mesh_algorithm::Int
227227

228+
"Materials database."
229+
materials_db::MaterialsLibrary
230+
228231
"""
229232
$(TYPEDSIGNATURES)
230233
@@ -234,10 +237,10 @@ struct FEMProblemDefinition <: AbstractProblemDefinition
234237
235238
- `domain_radius`: Domain radius for the simulation \\[m\\]. Default: 5.0.
236239
- `correct_twisting`: Flag to correct for twisting effects \\[dimensionless\\]. Default: true.
237-
- `elements_per_scale_length_conductor`: Elements per scale length for conductors \\[dimensionless\\]. Default: 3.0.
238-
- `elements_per_scale_length_insulator`: Elements per scale length for insulators \\[dimensionless\\]. Default: 2.0.
239-
- `elements_per_scale_length_semicon`: Elements per scale length for semiconductors \\[dimensionless\\]. Default: 4.0.
240-
- `elements_per_scale_length_interfaces`: Elements per scale length for interfaces \\[dimensionless\\]. Default: 0.1.
240+
- `elements_per_length_conductor`: Elements per scale length for conductors \\[dimensionless\\]. Default: 3.0.
241+
- `elements_per_length_insulator`: Elements per scale length for insulators \\[dimensionless\\]. Default: 2.0.
242+
- `elements_per_length_semicon`: Elements per scale length for semiconductors \\[dimensionless\\]. Default: 4.0.
243+
- `elements_per_length_interfaces`: Elements per scale length for interfaces \\[dimensionless\\]. Default: 0.1.
241244
- `analysis_type`: Analysis types to perform \\[dimensionless\\]. Default: [0, 1].
242245
- `mesh_size_min`: Minimum mesh size \\[m\\]. Default: 1e-4.
243246
- `mesh_size_max`: Maximum mesh size \\[m\\]. Default: 1.0.
@@ -258,18 +261,19 @@ struct FEMProblemDefinition <: AbstractProblemDefinition
258261
# Create a problem definition with custom parameters
259262
problem_def = $(FUNCTIONNAME)(
260263
domain_radius=10.0,
261-
elements_per_scale_length_conductor=5.0,
264+
elements_per_length_conductor=5.0,
262265
mesh_algorithm=2
263266
)
264267
```
265268
"""
266269
function FEMProblemDefinition(;
267270
domain_radius::Float64=5.0,
268271
correct_twisting::Bool=true,
269-
elements_per_scale_length_conductor::Float64=3.0,
270-
elements_per_scale_length_insulator::Float64=2.0,
271-
elements_per_scale_length_semicon::Float64=4.0,
272-
elements_per_scale_length_interfaces::Float64=0.1,
272+
elements_per_length_conductor::Int=3,
273+
elements_per_length_insulator::Int=2,
274+
elements_per_length_semicon::Int=4,
275+
elements_per_length_interfaces::Int=3,
276+
points_per_circumference::Int=16,
273277
analysis_type::Vector{Int}=[0, 1],
274278
mesh_size_min::Float64=1e-4,
275279
mesh_size_max::Float64=1.0,
@@ -279,10 +283,8 @@ struct FEMProblemDefinition <: AbstractProblemDefinition
279283
)
280284
return new(
281285
domain_radius, correct_twisting,
282-
elements_per_scale_length_conductor, elements_per_scale_length_insulator,
283-
elements_per_scale_length_semicon, elements_per_scale_length_interfaces,
284-
analysis_type, materials_db,
285-
mesh_size_min, mesh_size_max, mesh_size_default, mesh_algorithm
286+
elements_per_length_conductor, elements_per_length_insulator,
287+
elements_per_length_semicon, elements_per_length_interfaces, points_per_circumference, analysis_type, mesh_size_min, mesh_size_max, mesh_size_default, mesh_algorithm, materials_db
286288
)
287289
end
288290
end
@@ -572,8 +574,8 @@ function run_fem_model(cable_system::LineCableSystem,
572574
_assign_physical_groups(workspace)
573575

574576
# Mesh sizing
575-
# _log(workspace, 1, "Setting up physics-based mesh sizing...")
576-
# _config_mesh_sizes(workspace)
577+
_log(workspace, 1, "Setting up physics-based mesh sizing...")
578+
_config_mesh_sizes(workspace)
577579

578580
# Preview pre-meshing configuration if requested
579581
if solver.preview_geo

src/FEMTools/cable.jl

Lines changed: 92 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,6 @@ function _make_cablepart!(workspace::FEMWorkspace, part::AbstractCablePart,
130130
material_id # Material ID from registry
131131
)
132132

133-
# Calculate mesh size for this part
134-
mesh_size = calc_mesh_size(part, workspace)
135-
136133
# Create physical name
137134
part_type = lowercase(string(nameof(typeof(part))))
138135
elementary_name = create_cable_elementary_name(
@@ -148,15 +145,39 @@ function _make_cablepart!(workspace::FEMWorkspace, part::AbstractCablePart,
148145
radius_in = to_nominal(part.radius_in)
149146
radius_ext = to_nominal(part.radius_ext)
150147

148+
# Calculate mesh size for this part
149+
if part isa AbstractConductorPart
150+
num_elements = workspace.problem_def.elements_per_length_conductor
151+
elseif part isa Insulator
152+
num_elements = workspace.problem_def.elements_per_length_insulator
153+
elseif part isa Semicon
154+
num_elements = workspace.problem_def.elements_per_length_semicon
155+
end
156+
157+
mesh_size_current = calc_mesh_size(radius_in, radius_ext, part.material_props, num_elements, workspace)
158+
159+
# Calculate mesh size for the next part
160+
num_layers = length(cabledef.cable.components[comp_idx].conductor_group.layers)
161+
next_part = layer_idx < num_layers ? cabledef.cable.components[comp_idx].conductor_group.layers[layer_idx+1] : nothing
162+
163+
if !isnothing(next_part)
164+
next_radius_in = to_nominal(next_part.radius_in)
165+
next_radius_ext = to_nominal(next_part.radius_ext)
166+
mesh_size_next = calc_mesh_size(next_radius_in, next_radius_ext, next_part.material_props, num_elements, workspace)
167+
else
168+
mesh_size_next = mesh_size_current
169+
end
170+
171+
mesh_size = min(mesh_size_current, mesh_size_next)
172+
num_points_circumference = workspace.problem_def.points_per_circumference
173+
151174
# Create annular shape and assign marker
152175
if radius_in 0
153176
# Solid disk
154-
marker = _get_disk_marker(x_center, y_center)
155-
entity_tag = _draw_disk(x_center, y_center, radius_ext, mesh_size)
177+
_, _, marker = _draw_disk(x_center, y_center, radius_ext, mesh_size, num_points_circumference)
156178
else
157179
# Annular shape
158-
marker = _get_annular_marker(x_center, y_center, radius_in, radius_ext)
159-
entity_tag = _draw_annular(x_center, y_center, radius_in, radius_ext, mesh_size)
180+
_, _, marker = _draw_annular(x_center, y_center, radius_in, radius_ext, mesh_size, num_points_circumference)
160181
end
161182

162183
# Create entity data
@@ -219,34 +240,67 @@ function _make_cablepart!(workspace::FEMWorkspace, part::WireArray,
219240
material_id # Material ID from registry
220241
)
221242

222-
# Calculate mesh size for this part
223-
mesh_size = calc_mesh_size(part, workspace)
224-
225-
#
226-
# First handle the wires
227-
#
243+
# -------- First handle the wires
228244

229245
# Create physical name
230246
part_type = lowercase(string(nameof(typeof(part))))
231247

232248
# Extract parameters
233249
radius_in = to_nominal(part.radius_in)
250+
radius_ext = to_nominal(part.radius_ext)
234251

235-
radius_wire = to_nominal(part.radius_wire)
252+
TOL = 5e-6 # Shrink the radius to avoid overlapping boundaries, this must be greater than Gmsh geometry tolerance
253+
radius_wire = to_nominal(part.radius_wire) - TOL
236254
num_wires = part.num_wires
237-
lay_radius = num_wires == 1 ? 0 : to_nominal(part.radius_in)
255+
256+
257+
# Calculate mesh size for this part
258+
num_elements = workspace.problem_def.elements_per_length_conductor
259+
mesh_size_current = calc_mesh_size(radius_in, radius_ext, part.material_props, num_elements, workspace)
260+
261+
# Calculate mesh size for the next part
262+
num_layers = length(cabledef.cable.components[comp_idx].conductor_group.layers)
263+
next_part = layer_idx < num_layers ? cabledef.cable.components[comp_idx].conductor_group.layers[layer_idx+1] : nothing
264+
265+
if !isnothing(next_part)
266+
next_radius_in = to_nominal(next_part.radius_in)
267+
next_radius_ext = to_nominal(next_part.radius_ext)
268+
mesh_size_next = calc_mesh_size(next_radius_in, next_radius_ext, next_part.material_props, num_elements, workspace)
269+
else
270+
mesh_size_next = mesh_size_current
271+
end
272+
273+
mesh_size = min(mesh_size_current, mesh_size_next)
274+
num_points_circumference = workspace.problem_def.points_per_circumference
238275

239276
# Calculate wire positions
240-
wire_positions = calc_wirearray_coords(num_wires, radius_wire, lay_radius, C=(x_center, y_center))
277+
function _calc_wirearray_coords(
278+
num_wires::Number,
279+
# radius_wire::Number,
280+
radius_in::Number,
281+
radius_ext::Number;
282+
C=(0.0, 0.0),
283+
)
284+
wire_coords = [] # Global coordinates of all wires
285+
lay_radius = num_wires == 1 ? 0 : (radius_in + radius_ext) / 2
286+
287+
# Calculate the angle between each wire
288+
angle_step = 2 * π / num_wires
289+
for i in 0:num_wires-1
290+
angle = i * angle_step
291+
x = C[1] + lay_radius * cos(angle)
292+
y = C[2] + lay_radius * sin(angle)
293+
push!(wire_coords, (x, y)) # Add wire center
294+
end
295+
return wire_coords
296+
end
241297

298+
wire_positions = _calc_wirearray_coords(num_wires, radius_in, radius_ext, C=(x_center, y_center))
242299

243300
# Create wires
244301
for (wire_idx, (wx, wy)) in enumerate(wire_positions)
245-
# Create wire marker
246-
marker = _get_disk_marker(wx, wy)
247302

248-
# Create wire disk
249-
entity_tag = _draw_disk(wx, wy, radius_wire, mesh_size)
303+
_, _, marker = _draw_disk(wx, wy, radius_wire, mesh_size, num_points_circumference)
250304

251305
# Create wire name
252306
elementary_name = create_cable_elementary_name(
@@ -267,9 +321,24 @@ function _make_cablepart!(workspace::FEMWorkspace, part::WireArray,
267321
workspace.unassigned_entities[marker] = entity_data
268322
end
269323

270-
#
271-
# Then those nasty air gaps, the cause of this entire suffering
272-
#
324+
# Handle WireArray outermost boundary
325+
mesh_size = (radius_ext - radius_in)
326+
if !(next_part isa WireArray) && !isnothing(next_part)
327+
# step_angle = 2 * pi / num_wires
328+
_add_mesh_points(
329+
radius_in=radius_ext,
330+
radius_ext=radius_ext,
331+
theta_0=0,
332+
theta_1=2 * pi,
333+
mesh_size=mesh_size,
334+
num_points_ang=num_points_circumference,
335+
num_points_rad=0,
336+
C=(x_center, y_center),
337+
theta_offset=0 #step_angle / 2
338+
)
339+
end
340+
341+
# --------Then those nasty air gaps
273342

274343
# Air gaps will be determined from the boolean fragmentation operation and do not need to be drawn. Only the markers are needed.
275344
markers_air_gap = _get_air_gap_markers(num_wires, radius_wire, radius_in)
@@ -298,9 +367,6 @@ function _make_cablepart!(workspace::FEMWorkspace, part::WireArray,
298367
material_id # Material ID from registry
299368
)
300369

301-
# Calculate mesh size for this part
302-
mesh_size = calc_mesh_size(part, workspace)
303-
304370
for marker in markers_air_gap
305371
# elementary names are not assigned to the air gaps because they are not drawn and appear as a result of the boolean operation
306372
core_data = CoreEntityData(physical_group_tag_air_gap, "", mesh_size)

0 commit comments

Comments
 (0)