feat: Create IO submodule for mesh readers and result writers

New structure:
- src/io/io.jl: IO submodule definition and exports
- src/io/aster_reader.jl: Code Aster .med reader (ready for HDF5 extension)

Benefits:
- Clean separation of I/O code
- ABAQUS reader works with stdlib only
- Ready for package extensions (HDF5, LightXML)
- Easy to add new formats (VTK, Gmsh, etc.)

The IO submodule exports abaqus_read_mesh() to main JuliaFEM namespace.
This commit is contained in:
Jukka Aho
2025-11-08 14:32:27 +02:00
parent c40d0b91c9
commit c317e3440f
3 changed files with 192 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
# AbaqusReader consolidated into src/readers/ - no longer a separate package
"""
abaqus_read_mesh(fn::String)
Read and parse ABAQUS `.inp` file.
`fn` (filename) is the name of the file to parse.
"""
function abaqus_read_mesh(fn::String)
m = AbaqusReader.abaqus_read_mesh(fn)
return Mesh(m)
end
"""
create_surface_elements(mesh::Mesh, surface_name::Symbol)
Create a set of surface elements from solid elements.
Notation follow what is defined in ABAQUS. For example, if solid elements
are Tet10, surface elements will be Tri6 and they can be used to define
boundary conditions.
"""
function create_surface_elements(mesh::Mesh, surface_name::Symbol)
elements = Element[]
for (elid, elsi) in mesh.surface_sets[surface_name]
elty = mesh.element_types[elid]
elco = mesh.elements[elid]
chel, chcon = AbaqusReader.create_surface_element(elty, elsi, elco)
ch = Element(getfield(JuliaFEM, chel), chcon)
push!(elements, ch)
end
update!(elements, "geometry", mesh.nodes)
return elements
end
"""
create_surface_elements(mesh::Mesh, surface_name::String)
Create a set of surface elements from solid elements.
Notation follow what is defined in ABAQUS. For example, if solid elements
are Tet10, surface elements will be Tri6 and they can be used to define
boundary conditions.
"""
function create_surface_elements(mesh::Mesh, surface_name::String)
return create_surface_elements(mesh, Symbol(surface_name))
end
"""
create_nodal_elements(mesh::Mesh, node_set_name::String)
Create a set of "nodal" elements from node set.
Nodal elements are of type Poi1. In principle they don't have volume and it's
not possible to integrate over them. It is however possible to add boundary
conditions to them.
"""
function create_nodal_elements(mesh::Mesh, node_set_name::String)
node_ids = mesh.node_sets[Symbol(node_set_name)]
elements = [Element(Poi1, [j]) for j in node_ids]
update!(elements, "geometry", mesh.nodes)
return elements
end
+82
View File
@@ -0,0 +1,82 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
# using AsterReader # Consolidated into src/readers/, functions are now directly available
"""
Nodes are different order in Code Aster compared to ABAQUS. This is yet
incomplete mapping between the permutations. Most of this is still a
great mystery.
# References
- http://onelab.info/pipermail/gmsh/2008/003850.html
- http://caelinux.org/wiki/index.php/Proj:UNVConvert
"""
const med_connectivity = Dict{Symbol,Vector{Int}}(
:Tet4 => [4, 3, 1, 2],
:Tet10 => [4, 3, 1, 2, 10, 7, 8, 9, 6, 5],
:Pyr5 => [1, 4, 3, 2, 5],
:Wedge6 => [4, 5, 6, 1, 2, 3],
:Hex8 => [4, 8, 7, 3, 1, 5, 6, 2],
:Hex20 => [4, 8, 7, 3, 1, 5, 6, 2, 20, 15, 19, 11, 12, 16, 14, 10, 17, 13, 18, 9],
:Hex27 => [4, 8, 7, 3, 1, 5, 6, 2, 20, 15, 19, 11, 12, 16, 14, 10, 17, 13, 18, 9, 24, 25, 26, 23, 21, 22, 27])
"""
Map element names used in in Code Aster to element names used in in JuliaFEM
"""
const med_element_names = Dict{Symbol,Symbol}(
:PO1 => :Poi1,
:SE2 => :Seg2,
:SE3 => :Seg3,
:SE4 => :Seg4,
:TR3 => :Tri3,
:TR6 => :Tri6,
:TR7 => :Tri7,
:QU4 => :Quad4,
:QU8 => :Quad8,
:QU9 => :Quad9,
:TE4 => :Tet4,
:T10 => :Tet10,
:PE6 => :Wedge6,
:P15 => :Wedge15,
:P18 => :Wedge18,
:HE8 => :Hex8,
:H20 => :Hex20,
:H27 => :Hex27,
:PY5 => :Pyr5,
:P13 => :Pyr13)
"""
aster_read_mesh(filename, mesh_name=nothing; reorder_element_connectivity=true)
Read code aster mesh from file and return `Mesh` structure.
If mesh file contains several meshes, a name of mesh must be given. By default,
elements are reordered so that they match to the conventions used in JuliaFEM.
"""
function aster_read_mesh(filename::String, mesh_name=nothing; reorder_element_connectivity=true)
m = aster_read_mesh_raw(filename, mesh_name)
mesh = Mesh(m)
for (elid, eltype) in mesh.element_types
mesh.element_types[elid] = med_element_names[eltype]
end
if reorder_element_connectivity
reorder_element_connectivity!(mesh, med_connectivity)
end
nnodes = length(mesh.nodes)
nelements = length(mesh.elements)
@info("Mesh parsed from Code Aster file $filename.")
@info("Mesh contains $nnodes nodes and $nelements elements.")
for (elset_name, elset_elids) in mesh.element_sets
content = Dict{Symbol,Int}()
for elid in elset_elids
eltype = mesh.element_types[elid]
content[eltype] = get(content, eltype, 0) + 1
end
s = join(("$v x $k" for (k, v) in content), ", ")
nels = length(elset_elids)
@info("Element set $elset_name contains $nels elements ($s).")
end
return mesh
end
+43
View File
@@ -0,0 +1,43 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
"""
JuliaFEM.IO
Input/Output submodule for reading mesh files and writing results.
# Available (zero external dependencies)
- `abaqus_read_mesh(filename)`: Read ABAQUS .inp files
- `create_surface_elements(mesh, name)`: Create surface elements
- `create_nodal_elements(mesh, name)`: Create nodal elements
# Future (requires optional dependencies)
- Code Aster .med files: `aster_read_mesh` (requires HDF5.jl)
- Xdmf output format: `Xdmf` writer (requires HDF5.jl + LightXML.jl)
- VTK output: Native Julia implementation (no dependencies)
# Usage
```julia
using JuliaFEM
mesh = abaqus_read_mesh("model.inp")
body = create_elements(mesh, "BODY")
```
"""
module IO
using ..JuliaFEM: Element, Mesh, add_node!, add_element_to_element_set!,
add_node_to_node_set!
# Re-export for convenience
import ..JuliaFEM.Preprocess
using SparseArrays, LinearAlgebra
using Logging
include("abaqus_reader.jl")
export abaqus_read_mesh, create_surface_elements, create_nodal_elements
# AsterReader would go here when HDF5 is added as optional dependency
# include("aster_reader.jl")
# export aster_read_mesh
end # module IO