-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
thermo-mechanical coupling #32
Comments
Hello @vavrines, I am very interested in implementing this into Would you also be interested in us collaborating on a publication about this topic? We have good HPC access and can run large-scale simulations once the model works with the package. |
@kaipartmann |
@vavrines, this sounds like a good plan! Using your example, I hacked something together. I am unsure if it works correctly - the code is probably full of bugs/errors. The structure of I am still in the process of reworking the internal structure of the package. I will consider how a coupled model could be easily implemented without changing many internal functions for the next version. However, you can probably try this out for your model development. Example simulation: heating of a barThe best thing is that you start from an empty new project and add the current
Then you create the following file: bb_thermal_coupling_model.jlusing Peridynamics
using Base.Threads
struct BBTMaterial <: Peridynamics.AbstractMaterial end
struct BBTPointParameters <: Peridynamics.AbstractPointParameters
δ::Float64
rho::Float64
E::Float64
nu::Float64
G::Float64
K::Float64
λ::Float64
μ::Float64
Gc::Float64
εc::Float64
bc::Float64
kc::Float64 # thermal conductivity
kp::Float64 # microconductivity
aph::Float64 # thermal expansion
cv::Float64 # specific heat capacity
end
@inline Peridynamics.point_param_type(::BBTMaterial) = BBTPointParameters
@inline function Peridynamics.allowed_material_kwargs(::BBTMaterial)
return (Peridynamics.DEFAULT_POINT_KWARGS..., :kc, :aph, :cv)
end
function Peridynamics.get_point_params(::BBTMaterial, p::Dict{Symbol,Any})
δ = Peridynamics.get_horizon(p)
rho = Peridynamics.get_density(p)
if haskey(p, :nu)
msg = "keyword `nu` is not allowed for BBTMaterial!\n"
msg *= "Bond-based peridynamics has a limitation on the possion ratio.\n"
msg *= "Therefore, when using BBTMaterial, `nu` is hardcoded as 1/4.\n"
throw(ArgumentError(msg))
else
p[:nu] = 0.25
end
E, nu, G, K, λ, μ = Peridynamics.get_elastic_params(p)
Gc, εc = Peridynamics.get_frac_params(p, δ, K)
bc = 18 * K / (π * δ^4) # bond constant
kc, kp, aph, cv = get_thermal_params(p, δ)
return BBTPointParameters(δ, rho, E, nu, G, K, λ, μ, Gc, εc, bc, kc, kp, aph, cv)
end
function get_thermal_params(p::Dict{Symbol,Any}, δ)
haskey(p, :kc) || throw(UndefKeywordError(:kc))
haskey(p, :aph) || throw(UndefKeywordError(:aph))
haskey(p, :cv) || throw(UndefKeywordError(:cv))
kc::Float64 = float(p[:kc])
kc ≤ 0 && throw(ArgumentError("`kc` should be larger than zero!\n"))
aph::Float64 = float(p[:aph])
aph ≤ 0 && throw(ArgumentError("`aph` should be larger than zero!\n"))
cv::Float64 = float(p[:cv])
cv ≤ 0 && throw(ArgumentError("`cv` should be larger than zero!\n"))
kp = 6 * kc / (π * δ^4)
return kc, kp, aph, cv
end
@inline Peridynamics.discretization_type(::BBTMaterial) = Peridynamics.BondDiscretization
@inline function Peridynamics.init_discretization(body::Body{BBTMaterial}, args...)
return Peridynamics.init_bond_discretization(body, args...)
end
struct BBTVerletStorage <: Peridynamics.AbstractStorage
position::Matrix{Float64}
displacement::Matrix{Float64}
velocity::Matrix{Float64}
velocity_half::Matrix{Float64}
acceleration::Matrix{Float64}
b_int::Matrix{Float64}
b_ext::Matrix{Float64}
damage::Vector{Float64}
bond_active::Vector{Bool}
n_active_bonds::Vector{Int}
temperature::Vector{Float64}
pflux::Vector{Float64}
end
const BBTStorage = Union{BBTVerletStorage}
@inline Peridynamics.storage_type(::BBTMaterial, ::VelocityVerlet) = BBTVerletStorage
function Peridynamics.init_storage(::Body{BBTMaterial}, ::VelocityVerlet,
bd::Peridynamics.BondDiscretization,
ch::Peridynamics.ChunkHandler)
n_loc_points = length(ch.loc_points)
n_points_with_halo = length(ch.point_ids)
position = copy(bd.position)
displacement = zeros(3, n_loc_points)
velocity = zeros(3, n_points_with_halo)
velocity_half = zeros(3, n_loc_points)
acceleration = zeros(3, n_loc_points)
b_int = zeros(3, n_loc_points)
b_ext = zeros(3, n_loc_points)
damage = zeros(n_loc_points)
bond_active = ones(Bool, length(bd.bonds))
n_active_bonds = copy(bd.n_neighbors)
temperature = zeros(n_points_with_halo)
pflux = zeros(n_loc_points)
return BBTVerletStorage(position, displacement, velocity, velocity_half, acceleration,
b_int, b_ext, damage, bond_active, n_active_bonds, temperature,
pflux)
end
Peridynamics.storage_field(s::BBTStorage, ::Val{:temperature}) = getfield(s, :temperature)
Peridynamics.storage_field(s::BBTStorage, ::Val{:pflux}) = getfield(s, :pflux)
@inline function Peridynamics.get_halo_read_fields(s::BBTStorage)
return (s.position, s.velocity, s.temperature)
end
@inline Peridynamics.get_halo_write_fields(::BBTStorage) = ()
const BBTBodyChunk = Union{Peridynamics.BodyChunk{BBTMaterial,P,D,S},
Peridynamics.MultiParamBodyChunk{BBTMaterial,P,D,S}} where {P,D,S}
function calc_pflux!(b::BBTBodyChunk)
b.store.pflux .= 0.0
for point_id in Peridynamics.each_point_idx(b)
pflux_point!(b.store, b.dscr, b.mat, get_param(b, point_id), point_id)
end
return nothing
end
function pflux_point!(s::BBTStorage, bd::Peridynamics.BondDiscretization, ::BBTMaterial,
param::BBTPointParameters, i::Int)
for bond_id in Peridynamics.each_bond_idx(bd, i)
bond = bd.bonds[bond_id]
j, L = bond.neighbor, bond.length
Δxijx = s.position[1, j] - s.position[1, i]
Δxijy = s.position[2, j] - s.position[2, i]
Δxijz = s.position[3, j] - s.position[3, i]
Δvijx = s.velocity[1, j] - s.velocity[1, i]
Δvijy = s.velocity[2, j] - s.velocity[2, i]
Δvijz = s.velocity[3, j] - s.velocity[3, i]
l = sqrt(Δxijx * Δxijx + Δxijy * Δxijy + Δxijz * Δxijz)
ev = (Δxijx * Δvijx + Δxijy * Δvijy + Δxijz * Δvijz) / l
Δtem = s.temperature[j] - s.temperature[i]
s.pflux[i] += s.bond_active[bond_id] * (param.kp * Δtem / L -
ev * 0.5 * param.bc * param.aph) * bd.volume[j]
end
return nothing
end
@inline function update_temperature!(b::Peridynamics.AbstractBodyChunk, Δt::Float64)
for point_id in Peridynamics.each_point_idx(b)
param = get_param(b, point_id)
k = Δt / (param.rho * param.cv)
_update_temperature!(b.store.temperature, b.store.pflux, k, point_id)
end
return nothing
end
@inline function _update_temperature!(temperature, pflux, k, i)
temperature[i] += pflux[i] * k
return nothing
end
function Peridynamics.force_density_point!(s::BBTStorage,
bd::Peridynamics.BondDiscretization,
::BBTMaterial, param::BBTPointParameters, i::Int)
for bond_id in Peridynamics.each_bond_idx(bd, i)
bond = bd.bonds[bond_id]
j, L = bond.neighbor, bond.length
# current bond length
Δxijx = s.position[1, j] - s.position[1, i]
Δxijy = s.position[2, j] - s.position[2, i]
Δxijz = s.position[3, j] - s.position[3, i]
l = sqrt(Δxijx * Δxijx + Δxijy * Δxijy + Δxijz * Δxijz)
# bond strain
εm = (l - L) / L
εt = 0.5 * (s.temperature[j] - s.temperature[i]) * param.aph
ε = εm + εt
# failure mechanism
if ε > param.εc && bond.fail_permit
s.bond_active[bond_id] = false
end
s.n_active_bonds[i] += s.bond_active[bond_id]
# update of force density
temp = s.bond_active[bond_id] * param.bc * ε / l * bd.volume[j]
s.b_int[1, i] += temp * Δxijx
s.b_int[2, i] += temp * Δxijy
s.b_int[3, i] += temp * Δxijz
end
return nothing
end
# You have to override the time stepping schemes
function Peridynamics.solve_timestep!(dh::Peridynamics.ThreadsDataHandler{BBTBodyChunk},
options::Peridynamics.ExportOptions, Δt::Float64,
Δt½::Float64, n::Int)
t = n * Δt
@threads :static for chunk_id in eachindex(dh.chunks)
chunk = dh.chunks[chunk_id]
Peridynamics.update_vel_half!(chunk, Δt½)
Peridynamics.apply_bcs!(chunk, t)
Peridynamics.update_disp_and_pos!(chunk, Δt)
end
@threads :static for chunk_id in eachindex(dh.chunks)
Peridynamics.exchange_read_fields!(dh, chunk_id)
chunk = dh.chunks[chunk_id]
calc_pflux!(chunk)
update_temperature!(chunk, Δt)
end
@threads :static for chunk_id in eachindex(dh.chunks)
Peridynamics.exchange_read_fields!(dh, chunk_id)
Peridynamics.calc_force_density!(dh.chunks[chunk_id])
end
@threads :static for chunk_id in eachindex(dh.chunks)
exchange_write_fields!(dh, chunk_id)
chunk = dh.chunks[chunk_id]
calc_damage!(chunk)
update_acc_and_vel!(chunk, Δt½)
export_results(dh, options, chunk_id, n, t)
end
return nothing
end
function Peridynamics.solve_timestep!(dh::Peridynamics.MPIDataHandler{BBTBodyChunk},
options::Peridynamics.ExportOptions, Δt::Float64,
Δt½::Float64, n::Int)
t = n * Δt
chunk = dh.chunk
Peridynamics.@timeit_debug Peridynamics.TO "update_vel_half!" Peridynamics.update_vel_half!(chunk, Δt½)
Peridynamics.@timeit_debug Peridynamics.TO "apply_bcs!" Peridynamics.apply_bcs!(chunk, t)
Peridynamics.@timeit_debug Peridynamics.TO "update_disp_and_pos!" Peridynamics.update_disp_and_pos!(chunk, Δt)
Peridynamics.@timeit_debug Peridynamics.TO "exchange_read_fields!" Peridynamics.exchange_read_fields!(dh)
Peridynamics.@timeit_debug Peridynamics.TO "calc_pflux!" calc_pflux!(chunk)
Peridynamics.@timeit_debug Peridynamics.TO "update_temperature!" update_temperature!(chunk, Δt)
Peridynamics.@timeit_debug Peridynamics.TO "exchange_read_fields!" Peridynamics.exchange_read_fields!(dh)
Peridynamics.@timeit_debug Peridynamics.TO "calc_force_density!" Peridynamics.calc_force_density!(chunk)
Peridynamics.@timeit_debug Peridynamics.TO "exchange_write_fields!" Peridynamics.exchange_write_fields!(dh)
Peridynamics.@timeit_debug Peridynamics.TO "calc_damage!" Peridynamics.calc_damage!(chunk)
Peridynamics.@timeit_debug Peridynamics.TO "update_acc_and_vel!" Peridynamics.update_acc_and_vel!(chunk, Δt½)
Peridynamics.@timeit_debug Peridynamics.TO "export_results" Peridynamics.export_results(dh, options, n, t)
return nothing
end
function temperature_bc!(f::F, b::Body, name::Symbol) where {F<:Function}
Peridynamics.check_if_set_is_defined(b.point_sets, name)
type = Peridynamics.check_condition_function(f)
if type === :sdbc
sdbc = Peridynamics.SingleDimBC(f, :temperature, name, 0x1)
Peridynamics._condition!(b.single_dim_bcs, sdbc)
elseif type === :pdsdbc
pdsdbc = Peridynamics.PosDepSingleDimBC(f, :temperature, name, 0x1)
Peridynamics._condition!(b.posdep_single_dim_bcs, pdsdbc)
end
return nothing
end
function temperature_ic!(b::Body, name::Symbol, value::Real)
Peridynamics.check_if_set_is_defined(b.point_sets, name)
sdic = Peridynamics.SingleDimIC(convert(Float64, value), :temperature, name, 0x1)
Peridynamics._condition!(b.single_dim_ics, sdic)
return nothing
end
@inline function Peridynamics.apply_sdbc!(field::Vector{Float64}, value::Float64, ::UInt8,
point_ids::Vector{Int})
@simd for i in point_ids
@inbounds field[i] = value
end
return nothing
end
@inline function Peridynamics.apply_pdsdbc!(field::Vector{Float64}, pos::Matrix{Float64},
bc::Peridynamics.PosDepSingleDimBC{F},
point_ids::Vector{Int}, t::Float64) where {F}
@simd for i in point_ids
value = bc(SVector{3}(pos[1,i], pos[2,i], pos[3,i]), t)
if !isnan(value)
field[i] = value
end
end
return nothing
end
function Peridynamics.apply_ic!(b::BBTBodyChunk, ic::Peridynamics.SingleDimIC)
for point_id in b.psets[ic.point_set]
_apply_ic!(Peridynamics.get_storage_field(b.store, ic.field), ic.value, ic.dim,
point_id)
end
return nothing
end
function _apply_ic!(field::Matrix{Float64}, value::Float64, dim::UInt8, point_id::Int)
setindex!(field, value, dim, point_id)
return nothing
end
function _apply_ic!(field::Vector{Float64}, value::Float64, ::UInt8, point_id::Int)
setindex!(field, value, point_id)
return nothing
end Then you can run a simulation with the new API of the package: include("bb_thermal_coupling_model.jl")
l, Δx, δ = 1.0, 1/100, 3.015/100
pos, vol = uniform_box(l, 0.1l, 0.1l, Δx)
body = Body(BBTMaterial(), pos, vol)
# add material parameters as in your code
material!(body; horizon=3.015Δx, E=2.1e5, rho=8e-6, Gc=2.7, kc=51.9, aph=11.5e-6, cv=472)
point_set!(x -> x > 0.0, body, :heating_area)
point_set!(x -> x < -l/2+3Δx, body, :fixed_area)
# temperature boundary condition: increase temperature by 1e5 K/s
temperature_bc!(t -> 1e5 * t, body, :heating_area)
velocity_bc!(t -> 0.0, body, :fixed_area, :x)
vv = VelocityVerlet(steps=2000)
job = Job(body, vv; path=joinpath(@__DIR__, "thermal_coupling"))
@time submit(job); How it worksI mainly defined a bond-based thermal material and added new internal methods for this material type. struct BBTMaterial <: Peridynamics.AbstractMaterial end The function pflux_point!(s::BBTStorage, bd::Peridynamics.BondDiscretization, ::BBTMaterial,
param::BBTPointParameters, i::Int)
for bond_id in Peridynamics.each_bond_idx(bd, i)
bond = bd.bonds[bond_id]
j, L = bond.neighbor, bond.length
Δxijx = s.position[1, j] - s.position[1, i]
Δxijy = s.position[2, j] - s.position[2, i]
Δxijz = s.position[3, j] - s.position[3, i]
Δvijx = s.velocity[1, j] - s.velocity[1, i]
Δvijy = s.velocity[2, j] - s.velocity[2, i]
Δvijz = s.velocity[3, j] - s.velocity[3, i]
l = sqrt(Δxijx * Δxijx + Δxijy * Δxijy + Δxijz * Δxijz)
ev = (Δxijx * Δvijx + Δxijy * Δvijy + Δxijz * Δvijz) / l
Δtem = s.temperature[j] - s.temperature[i]
s.pflux[i] += s.bond_active[bond_id] * (param.kp * Δtem / L -
ev * 0.5 * param.bc * param.aph) * bd.volume[j]
end
return nothing
end Then, the force density calculation of the plain function Peridynamics.force_density_point!(s::BBTStorage,
bd::Peridynamics.BondDiscretization,
::BBTMaterial, param::BBTPointParameters, i::Int)
for bond_id in Peridynamics.each_bond_idx(bd, i)
bond = bd.bonds[bond_id]
j, L = bond.neighbor, bond.length
# current bond length
Δxijx = s.position[1, j] - s.position[1, i]
Δxijy = s.position[2, j] - s.position[2, i]
Δxijz = s.position[3, j] - s.position[3, i]
l = sqrt(Δxijx * Δxijx + Δxijy * Δxijy + Δxijz * Δxijz)
# bond strain
εm = (l - L) / L
εt = 0.5 * (s.temperature[j] - s.temperature[i]) * param.aph
ε = εm + εt
# failure mechanism
if ε > param.εc && bond.fail_permit
s.bond_active[bond_id] = false
end
s.n_active_bonds[i] += s.bond_active[bond_id]
# update of force density
temp = s.bond_active[bond_id] * param.bc * ε / l * bd.volume[j]
s.b_int[1, i] += temp * Δxijx
s.b_int[2, i] += temp * Δxijy
s.b_int[3, i] += temp * Δxijz
end
return nothing
end The other methods are mostly internal functions. |
@vavrines I will introduce changes to the main branch in a few minutes that will break the provided code. So in the local environment just do (pd_thermal_coupling) pkg> rm Peridynamics
(pd_thermal_coupling) pkg> add Peridynamics#thermal_coupling |
Thank you for your assistance. I am a member of vavrines's team and am currently working on this project. I am benefiting greatly from reviewing your code. |
My apologies for the interruption, may I ask you a few questions?
|
Hello @ShiWeiHuH! I am happy that the example benefits you! :)
It seems that you're encountering an issue with the branch on GitHub. Please try in the package mode pkg> status
[4dc47793] Peridynamics v0.2.0 `https://github.com/kaipartmann/Peridynamics.jl.git#thermal_coupling` and check if it reads
We currently do not have volume or surface corrections implemented. If you need that, I can add it. I tried to implement it before but did not finish to debug the code.
This is very interesting, and I did not know that. I am very new to thermal models with peridynamics, so I am unsure if I correctly understand the actual consequences of this regarding the code. So, is it a problem if a point has some mechanical and thermal boundary conditions simultaneously? Can you highlight your implementation of these effects in the script you provided for me? |
Hi, @kaipartmann.Thank you very much for your response. |
Hi, @kaipartmann. We have updated the init.jl file. |
Hi @ShiWeiHuH, |
This part is newly added:
The modify.jl
My next step is to pass the correction coefficients into 'bb_thermal_coupling_model.jl'. |
Hi @ShiWeiHuH, Thank you very much for providing your code! First, I'm sorry about the bug with the I ran your code, and it should work fine. You are correct in bridging the parallel computation features with your newly defined I encountered the same issue and am working on it (#73). I'm now prioritizing the implementation of the surface correction (see #74), and your assistance is greatly appreciated. I already tried to do something similar to what you did, but my code still has some bugs. You can take a look at the Peridynamics.jl/src/physics/bond_based_corrected.jl Lines 205 to 243 in 1cb4317
You can proceed by adding your @inline function Peridynamics.init_discretization(body::Body{BBTMaterial}, args...)
return mbd_init(body, ...)
end But your Peridynamics.jl/src/discretizations/bond_discretization.jl Lines 15 to 28 in a71541f
However, please note that in the If I am ready with #73, I can provide a new script of the current state that includes your |
Hi @kaipartmann , Thanks for the tips! |
@kaipartmann |
@ShiWeiHuH thank you for taking the time to review the code. Your assistance is greatly appreciated! I wanted to do what you suggested but needed #73 solved for that. I will try to add the surface correction as part of the Unfortunately, the surface correction still has some bugs; I will explain this closer in #74 and ping you there. |
@kaipartmann
Function
|
@ShiWeiHuH @vavrines 0.6 * 1e-6 * mpm.E How did you derive these analytical strain energy density? |
Hi, @kaipartmann |
Hi @ShiWeiHuH, Thank you very much for your detailed description! I added an issue (#94) because the On the parallelism:The parallelism functionality is implemented at a very high level. With the Then, everything is done on the body chunk level. For multithreading, each thread works on its chunk: Peridynamics.jl/src/time_solvers/velocity_verlet.jl Lines 212 to 233 in 9bb65ae
For MPI, each process uses its chunk, and then the processes need to communicate to exchange the information of points at the chunk border (halo points): Peridynamics.jl/src/time_solvers/velocity_verlet.jl Lines 250 to 264 in 9bb65ae
If you have further questions regarding the parallelism, please do not hesitate to contact me. On volume correction:I did not include the volume correction because it needs a uniformly distributed point cloud to be correct, which is not guaranteed with the current approach of our package. If someone uses an arbitrary mesh with Abaqus and converts that into a point cloud, the volume correction will introduce errors, and the point spacing is unknown. However, from our observations, the correspondence formulation is also very precise with arbitrary meshes, and the volume error had only a very negligible effect on the simulation results, so we did not prioritize implementing it. Do you have observations on how much the volume error influences the simulation results in your thermo-mechanical coupled simulations? If you use the correspondence formulation instead of the bond-based formulation, all the corrections could theoretically be dismissed. However, I have some ideas about how to guarantee that a point cloud is uniformly distributed with a given point spacing. This would again break the current API but make it possible to use volume correction if a point cloud is guaranteed to be uniform and to ignore it otherwise. I will open an issue and ping you if my idea is clearer. |
Hi, @kaipartmann It's been a while! I noticed that PD has a new version with many great features. That's fantastic! I have a couple of questions: Currently, I'm using single-thread computation with one region. If I want to switch to using 64 threads, should I set the environment variable NUM=64 first? Will this automatically divide the region into 64 blocks? Can computation be resumed if it shuts down unexpectedly? |
Hi @ShiWeiHuH, I am very happy you like the new features and changes we made in the last weeks! ThreadsTo use multiple threads you can use a few different approaches, as also mentioned [here].(https://docs.julialang.org/en/v1/manual/multi-threading/#Starting-Julia-with-multiple-threads) Use Julia command line options julia -t 64
julia --threads 64 Specify the number of threads in the VSCode settings Setting an environment variable JULIA_NUM_THREADS=64 You can check how many threads you use with Threads.nthreads() The body is then divided in different blocks with Peridynamics.jl/src/discretization/decomposition.jl Lines 18 to 44 in bc81309
You can check the different regions with body = ...
n_chunks = 64
point_decomp = Peridynamics.PointDecomposition(body, n_chunks)
point_decomp.decomp # Vector with n_chunks elements and a range of the point ids for each region Resume simulationsFor now, it is not possible to resume simulations. The current approach is to capture as many bugs as possible before submitting the simulation job. If a simulation stops unexpectedly, all exported data up to this point is preserved. However, the main reason I have not considered it for now is performance. It would be necessary to export the current state of the data handler regularly, which would then take a significant fraction of the simulation time (I estimate up to 50% for some setups). I made an issue #144, so maybe there is a way to do it efficiently, and it will be included in a future version! |
Hi @kaipartmann
We're working on the thermo-mechanical coupling model. A script from scratch is here (https://github.com/vavrines/KitPD.jl/blob/main/couple/script.jl).
We're just about to clean and refactor the codes. The idea is to reuse the structs and methods here as much as possible.
I'd like to know if you're interested in implementing this functionality. If things go well, we should be able to contribute codes, but your assistance will likely be needed.
The text was updated successfully, but these errors were encountered: