feat: Consolidate HeatTransfer.jl (partial - API needs update)

- Added 118 lines of heat transfer code to src/problems_heat.jl
- Problem types: Heat (3D), PlaneHeat (2D)
- Fields: thermal conductivity, heat source, heat flux, convection
- Fixed Element type signatures (Element{M,B})
- NOTE: Tests currently failing due to element_info! API mismatch
- Will fix after more consolidations (old FEMBase 0.x API)

Result: 9 vendor packages consolidated (~6620 lines total)
Tests: 5 passing baseline maintained (heat tests need API fix)
This commit is contained in:
Jukka Aho
2025-11-08 12:11:37 +02:00
parent a76146cdaf
commit 2cee2222e1
5 changed files with 230 additions and 53 deletions
+3 -1
View File
@@ -177,7 +177,9 @@ export @timeit, print_timer
# using AbaqusReader # Consolidated into src/readers.jl
# using AsterReader # Consolidated into src/readers.jl
# @reexport using HeatTransfer
# Problem types
include("problems_heat.jl")
export Heat, PlaneHeat
include("problems_elasticity.jl")
export Elasticity
include("materials_plasticity.jl")
+8 -8
View File
@@ -5,11 +5,11 @@
# Consolidated from GraphOrdering.jl
struct GraphOrderingResult
perm :: Vector{Int}
invperm :: Vector{Int}
degrees :: Vector{Int}
edge :: Vector{Int}
dist :: Vector{Int}
perm::Vector{Int}
invperm::Vector{Int}
degrees::Vector{Int}
edge::Vector{Int}
dist::Vector{Int}
end
"""
@@ -22,10 +22,10 @@ function bandwidth(G)
bw = -1
for (v, adj) in G
for w in adj
bw = max(bw, abs(v-w))
bw = max(bw, abs(v - w))
end
end
return 2*bw + 1
return 2 * bw + 1
end
"""
@@ -50,7 +50,7 @@ function symrcm(G, v)
wrk = zeros(Int, nwrk)
idx = 2
for i=1:n
for i = 1:n
v = permutation[i]
adj = G[v]
+58
View File
@@ -0,0 +1,58 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/Gmsh.jl/blob/master/LICENSE
#
# Gmsh wrapper - consolidated from Gmsh.jl
# Provides convenient access to Gmsh API via gmsh_jll
import gmsh_jll
include(gmsh_jll.gmsh_api)
import .gmsh
"""
gmsh_initialize(argv=String[]; finalize_atexit=true)
Wrapper around `gmsh.initialize` which make sure to only call it if `gmsh` is not already
initialized. Return `true` if `gmsh.initialize` was called, and `false` if `gmsh` was
already initialized.
The argument vector `argv` is passed to `gmsh.initialize`. `argv` can be used to pass
command line options to Gmsh, see [Gmsh documentation for more
details](https://gmsh.info/doc/texinfo/gmsh.html#index-Command_002dline-options). Note that
this wrapper prepends the program name to `argv` since Gmsh expects that to be the first
entry.
If `finalize_atexit` is `true` a Julia exit hook is added, which calls `finalize()`.
**Example**
```julia
Gmsh.initialize(["-v", "0"]) # initialize with decreased verbosity
```
"""
function gmsh_initialize(argv=String[]; finalize_atexit=true)
if Bool(gmsh.isInitialized())
return false
end
# Prepend a dummy program name in case argv only contains options
# see https://gitlab.onelab.info/gmsh/gmsh/-/issues/2112
if length(argv) > 0 && startswith(first(argv), "-")
argv = pushfirst!(copy(argv), "gmsh")
end
gmsh.initialize(argv)
if finalize_atexit
atexit(finalize)
end
return true
"""
Gmsh.finalize()
Wrapper around `gmsh.finalize` which make sure to only call it if `gmsh` is initialized.
"""
function gmsh_finalize()
if Bool(gmsh.isInitialized())
gmsh.finalize()
end
+117
View File
@@ -0,0 +1,117 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/HeatTransfer.jl/blob/master/LICENSE
#
# Heat transfer problem types - consolidated from HeatTransfer.jl
"""
Heat
3D heat transfer analysis for JuliaFEM.
# Fields used in formulation
- `thermal conductivity`
- `heat source`
- `heat flux`
- `external temperature`
- `heat transfer coefficient`
# References
- https://en.wikipedia.org/wiki/Heat_equation
- https://en.wikipedia.org/wiki/Heat_capacity
- https://en.wikipedia.org/wiki/Heat_flux
- https://en.wikipedia.org/wiki/Thermal_conduction
- https://en.wikipedia.org/wiki/Thermal_conductivity
- https://en.wikipedia.org/wiki/Thermal_diffusivity
- https://en.wikipedia.org/wiki/Volumetric_heat_capacity
"""
struct PlaneHeat <: FieldProblem end
struct Heat <: FieldProblem end
get_unknown_field_name(::PlaneHeat) = "temperature"
get_unknown_field_name(::Heat) = "temperature"
function assemble_elements!(problem::Problem{P}, assembly::Assembly,
elements::Vector{Element{M,B}}, time::Float64) where
{M,B,P<:Union{PlaneHeat,Heat}}
bi = BasisInfo(B)
ndofs = length(bi)
Ke = zeros(ndofs, ndofs)
fe = zeros(ndofs)
for element in elements
fill!(Ke, 0.0)
fill!(fe, 0.0)
for ip in get_integration_points(element)
J, detJ, N, dN = element_info!(bi, element, ip, time)
s = ip.weight * detJ
k = element("thermal conductivity", ip, time)
Ke += s * k * dN' * dN
if haskey(element, "heat source")
f = element("heat source", ip, time)
fe += s * N' * f
end
end
if haskey(element, "temperature")
T = [element("temperature", time)...]
fe -= Ke * T
end
gdofs = get_gdofs(problem, element)
add!(assembly.K, gdofs, gdofs, Ke)
add!(assembly.f, gdofs, fe)
end
end
function assemble_elements!(problem::Problem{PlaneHeat}, assembly::Assembly,
elements::Vector{Element{M,B}}, time::Float64) where
{M,B<:Union{Seg2,Seg3}}
return assemble_boundary_elements!(problem, assembly, elements, time)
end
function assemble_elements!(problem::Problem{Heat}, assembly::Assembly,
elements::Vector{Element{M,B}}, time::Float64) where
{M,B<:Union{Tri3,Quad4,Tri6,Quad8,Quad9}}
return assemble_boundary_elements!(problem, assembly, elements, time)
end
function assemble_boundary_elements!(problem::Problem, assembly::Assembly,
elements::Vector{Element{M,B}}, time::Float64) where {M,B}
bi = BasisInfo(B)
ndofs = length(bi)
Ke = zeros(ndofs, ndofs)
fe = zeros(ndofs)
for element in elements
fill!(fe, 0.0)
fill!(Ke, 0.0)
for ip in get_integration_points(element, 2)
J, detJ, N, dN = element_info!(bi, element, ip, time)
s = ip.weight * detJ
if haskey(element, "heat flux")
g = element("heat flux", ip, time)
fe += s * N' * g
end
if haskey(element, "heat transfer coefficient") && haskey(element, "external temperature")
h = element("heat transfer coefficient", ip, time)
Tu = element("external temperature", ip, time)
Ke += s * h * N' * N
fe += s * N' * h * Tu
end
end
if haskey(element, "temperature")
T = [element("temperature", time)...]
fe -= Ke * T
end
gdofs = get_gdofs(problem, element)
add!(assembly.K, gdofs, gdofs, Ke)
add!(assembly.f, gdofs, fe)
end
end
export Heat, PlaneHeat
+44 -44
View File
@@ -14,78 +14,78 @@ abstract type AbstractBoundaryCondition end
abstract type AbstractOutputRequest end
mutable struct Mesh
nodes :: Dict{Int, Vector{Float64}}
node_sets :: Dict{String, Vector{Int}}
elements :: Dict{Int, Vector{Int}}
element_types :: Dict{Int, Symbol}
element_sets :: Dict{String, Vector{Int}}
surface_sets :: Dict{String, Vector{Tuple{Int, Symbol}}}
surface_types :: Dict{String, Symbol}
nodes::Dict{Int,Vector{Float64}}
node_sets::Dict{String,Vector{Int}}
elements::Dict{Int,Vector{Int}}
element_types::Dict{Int,Symbol}
element_sets::Dict{String,Vector{Int}}
surface_sets::Dict{String,Vector{Tuple{Int,Symbol}}}
surface_types::Dict{String,Symbol}
end
function Mesh(d::Dict{String, Dict})
function Mesh(d::Dict{String,Dict})
return Mesh(d["nodes"], d["node_sets"], d["elements"],
d["element_types"], d["element_sets"],
d["surface_sets"], d["surface_types"])
d["element_types"], d["element_sets"],
d["surface_sets"], d["surface_types"])
end
mutable struct Model
path :: String
name :: String
mesh :: Mesh
materials :: Dict{Symbol, AbstractMaterial}
properties :: Vector{AbstractProperty}
boundary_conditions :: Vector{AbstractBoundaryCondition}
steps :: Vector{AbstractStep}
path::String
name::String
mesh::Mesh
materials::Dict{Symbol,AbstractMaterial}
properties::Vector{AbstractProperty}
boundary_conditions::Vector{AbstractBoundaryCondition}
steps::Vector{AbstractStep}
end
mutable struct SolidSection <: AbstractProperty
element_set :: Symbol
material_name :: Symbol
element_set::Symbol
material_name::Symbol
end
mutable struct Material <: AbstractMaterial
name :: Symbol
properties :: Vector{AbstractMaterialProperty}
name::Symbol
properties::Vector{AbstractMaterialProperty}
end
mutable struct Elastic <: AbstractMaterialProperty
E :: Float64
nu :: Float64
E::Float64
nu::Float64
end
mutable struct Step <: AbstractStep
kind :: Union{Symbol, Nothing} # STATIC, ... (was Nullable{Symbol} in Julia 0.x)
boundary_conditions :: Vector{AbstractBoundaryCondition}
output_requests :: Vector{AbstractOutputRequest}
kind::Union{Symbol,Nothing} # STATIC, ... (was Nullable{Symbol} in Julia 0.x)
boundary_conditions::Vector{AbstractBoundaryCondition}
output_requests::Vector{AbstractOutputRequest}
end
mutable struct BoundaryCondition <: AbstractBoundaryCondition
kind :: Symbol # BOUNDARY, CLOAD, DLOAD, DSLOAD, ...
data :: Vector
options :: Dict
kind::Symbol # BOUNDARY, CLOAD, DLOAD, DSLOAD, ...
data::Vector
options::Dict
end
mutable struct OutputRequest <: AbstractOutputRequest
kind :: Symbol # NODE, EL, SECTION, ...
data :: Vector
options :: Dict
target :: Symbol # PRINT, FILE
kind::Symbol # NODE, EL, SECTION, ...
data::Vector
options::Dict
target::Symbol # PRINT, FILE
end
### Utility functions to parse ABAQUS .inp file to data model
mutable struct Keyword
name :: String
options :: Vector{Union{String, Pair}}
name::String
options::Vector{Union{String,Pair}}
end
mutable struct AbaqusReaderState
section :: Union{Keyword, Nothing} # was Nullable{Keyword}
material :: Union{AbstractMaterial, Nothing} # was Nullable
property :: Union{AbstractProperty, Nothing} # was Nullable
step :: Union{AbstractStep, Nothing} # was Nullable
data :: Vector{String}
section::Union{Keyword,Nothing} # was Nullable{Keyword}
material::Union{AbstractMaterial,Nothing} # was Nullable
property::Union{AbstractProperty,Nothing} # was Nullable
step::Union{AbstractStep,Nothing} # was Nullable
data::Vector{String}
end
function get_data(state::AbaqusReaderState)
@@ -161,7 +161,7 @@ function maybe_close_section!(model, state)
isnull(state.section) && return
section_name = state.section.name
@debug("Close section: $section_name")
args = Tuple{Model, AbaqusReaderState, Type{Val{Symbol(section_name)}}}
args = Tuple{Model,AbaqusReaderState,Type{Val{Symbol(section_name)}}}
if hasmethod(close_section!, args)
close_section!(model, state, Val{Symbol(section_name)})
else
@@ -175,7 +175,7 @@ function maybe_open_section!(model, state)
section_name = state.section.name
section_options = state.section.options
@debug("New section: $section_name with options $section_options")
args = Tuple{Model, AbaqusReaderState, Type{Val{Symbol(section_name)}}}
args = Tuple{Model,AbaqusReaderState,Type{Val{Symbol(section_name)}}}
if hasmethod(open_section!, args)
open_section!(model, state, Val{Symbol(section_name)})
else
@@ -259,12 +259,12 @@ BOUNDARY = register_abaqus_keyword("BOUNDARY")
CLOAD = register_abaqus_keyword("CLOAD")
DLOAD = register_abaqus_keyword("DLOAD")
DSLOAD = register_abaqus_keyword("DSLOAD")
const BOUNDARY_CONDITIONS = Union{BOUNDARY, CLOAD, DLOAD, DSLOAD}
const BOUNDARY_CONDITIONS = Union{BOUNDARY,CLOAD,DLOAD,DSLOAD}
NODE_PRINT = register_abaqus_keyword("NODE PRINT")
EL_PRINT = register_abaqus_keyword("EL PRINT")
SECTION_PRINT = register_abaqus_keyword("SECTION PRINT")
const OUTPUT_REQUESTS = Union{NODE_PRINT, EL_PRINT, SECTION_PRINT}
const OUTPUT_REQUESTS = Union{NODE_PRINT,EL_PRINT,SECTION_PRINT}
## Properties