Boundary Model
HSSSimulations.Boundary
— ModuleThe functions for calculating the ghost nodes needed to solve the boundary conditions. Basic boundary conditions are also included.
Parameters
HSSSimulations.Types.AbstractProblemParams
— Typeabstract type AbstractProblemParams <: HSSSimulations.Types.AbstractSimProperty
The Problem
struct contains a field called boundary that is of this type. The purpose of this field is to store any boundary condition inputs that changes over the course of a build, such as the air temperature in the machine. As this is an abstract type a new struct can be made, that is a subtype of AbstractProblemParams, to contain the required variables for a given simulation. An example of this is given in HSSParams
.
Any new subtype will need it's own boundaries written (the examples given are only meant to work with the HSSParams
example).
See the Problem Parameters Recipes for some insperation for implementing your own.
Load Sets
These are not actually memebers of the Boundary
module, but they are an integral part of how the boundary conditions are set up for a simulation so they are included here instead of in the Solver
documentation.
HSSSimulations.Types.AbstractLoadSet
— Typeabstract type AbstractLoadSet <: HSSSimulations.Types.AbstractSimProperty
This struct holds the information for a load set, primarily the list of loads to be solved, but also some extra bits. Subtypes should implement a method for the Solver.loadSetSolver!
function.
A simulation problem contains a list of load sets that are solved in order. Load sets of different types can be combined to create more complex build behavour. For example, the default HSS load sets include one fixed load set, for preheating; one layer load set, for all of the layers in the build; and another fixed load set, for cooldown after the build.
HSSSimulations.Solver.loadSetSolver!
— FunctionloadSetSolver!(
loadSet<:AbstractLoadSet
initResult::AbstractResult,
layerNum::Int,
prob::Problem{T,Gh,Mp,R,OR,B},
) where {T<:Any,Gh<:Any,Mp<:Any,R<:Any,OR<:Any,B<:Any}
This function is run once for each LoadSet
passed in to the problem, and dispatched on the type of that load set. For basic load sets, like FixedLoadSet
, it should basically just pass the arguments on to the innerLoadSetSolver!
function, the one exception being the list of loads being passed in instead of the AbstractLoadSet
struct. For more complicated load sets, like LayerLoadSet
, this should include the logic used for that load set. For LayerLoadSet
this is a loop that loops over the list of loads once for each layer, incrementing the layer number as it goes.
For examples see the source code for the two default implementations.
HSSSimulations.Solver.loadSetSolver!
— MethodloadSetSolver!(
loadSet::FixedLoadSet,
initResult::AbstractResult,
layerNum::Int64,
prob::Problem{T, Gh, Mp, R, OR, B}
)
HSSSimulations.Solver.loadSetSolver!
— MethodloadSetSolver!(
loadSet::LayerLoadSet,
initResult::AbstractResult,
initLayerNum::Int64,
prob::Problem{T, Gh, Mp, R, OR, B}
) -> Tuple{AbstractResult, Int64}
HSSSimulations.Solver.innerLoadSetSolver!
— FunctioninnerLoadSetSolver!(
loads::Vector{Load},
initResult::AbstractResult,
layerNum,
prob::Problem;
name,
isRecoatLoadSet,
prevLayerNum
)
Solves a load set of a problem, iterating over and solving the loads in the given load set in order. The results for a load is saved to the file after solving it.
If the list of loads passed given include powder deposition, the recoat
kwarg should be set to true, with the layerNum
set to the thickness of the powder bed in layers that is desired at the end of the loads. If this is more than one layer thicker than the previous value, then the previous values should also be passed in to prevLayerNum
.
It also helps to provide a unique name. This is the name that will be used to store the results from runnning the list of loads in the output file, and also will be used as the label in the progress meter. If no name is given then the current time is used instead. If the name given is not unique then you will get a run time error when it tries to save results to a place that is already taken.
Loads
HSSSimulations.Types.Load
— MethodTypes.Load(;
name="default", tₗ=1, skip=1,
x₁=SymetryBoundary, x₂=SymetryBoundary,
y₁=SymetryBoundary, y₂=SymetryBoundary,
z₁=SymetryBoundary, z₂=SymetryBoundary,
)
Default constructor for Types.Load
. All of the boundaries defaults to symetry boundaries (see Boundary.SymetryBoundary
). See Boundary.ConductionBoundary
and Boundary.ConvectionBoundary
, for some more built in loads. And HSS Boundary for more complicated loads.
The z₂
load is run before any others, so any calculations that need to be done first should be put there, such as recoating.
Examples
julia> Load(; name="test", tₗ=1, skip=1, x₁=ConductionBoundary, y₂=ConvectionBoundary)
x₁ : ConductionBoundary
x₂ : SymetryBoundary
y₁ : SymetryBoundary
y₂ : ConvectionBoundary
z₁ : SymetryBoundary
z₂ : SymetryBoundary
name : test
tₗ : 1.0
skip : 1
HSSSimulations.Types.Load
— Typestruct Load{X₁<:AbstractBoundary, X₂<:AbstractBoundary, Y₁<:AbstractBoundary, Y₂<:AbstractBoundary, Z₁<:AbstractBoundary, Z₂<:AbstractBoundary} <: HSSSimulations.Types.AbstractSimProperty
Fields
x₁::Type{X₁} where X₁<:AbstractBoundary
: The boundary condition for the start of the x axis ([1,:,:]
)x₂::Type{X₂} where X₂<:AbstractBoundary
: The boundary condition for the end of the x axis ([end,:,:]
)y₁::Type{Y₁} where Y₁<:AbstractBoundary
: The boundary condition for the start of the y axis ([:,1,:]
)y₂::Type{Y₂} where Y₂<:AbstractBoundary
: The boundary condition for the end of the y axis ([:,end,:]
)z₁::Type{Z₁} where Z₁<:AbstractBoundary
: The boundary condition for the start of the z axis ([:,:,1]
)z₂::Type{Z₂} where Z₂<:AbstractBoundary
: The boundary condition for the end of the z axis ([:,:,end]
)name::String
: Used to annotate the resultstₗ::Float64
: The duration of the load in secondsskip::Int64
: The distance between results to save (see Why We Skip Some Results)
Each of the boundaries given must satisfy Boundary isa Type{T} where T <: AbstractBoundary
. This is to say that they should be the type itself, not an instance of that type. Additionally it should be a subtype of AbstractBoundary
and meet all of the requirements outlined in the that documentation.
If the Load
is in a BuildLoadSet
then one of the boundaries in one of the loads in the set should call the recoating logic so that new powder is actuall being layed down. This should always be done in the z₂ boundary, as it is a special case that ensures the indicies are updated for all other boundaries.
Boundary
HSSSimulations.Types.boundaryHeatTransferRate
— FunctionboundaryHeatTransferRate(T, i, p<:AbstractBoundary) -> ϕ⃗::Float64
Used to calculate the heat flux denstity (ϕ⃗, in wm⁻²) into the model at a node on the relavant face for a given boundary condition. They are run per node, for each node on the face. The function should return a single numeric value that represents the heat flux density into the model.
Arguments
T
: The temperature of the node for which the ghost node is being calculatedi
: The cartesian index of the node for which the ghost node is being calculatedp
: The boundary struct for the current boundary (seeAbstractBoundary
)
See the implementations of this for examples, and Boundary Recipes for some additional examples along with the relevant boundary structs.
A positive heat flow equates to heat flowing into the model and negative out of the model.
As this is run for every node on the face every time step, try to avoid complex computations. Where possible, put them in the constructor of the AbstractBoundary
instead, as that is only run once per face per timestep.
HSSSimulations.Types.AbstractBoundary
— TypeSubtypes of this define a boundary for a single face of the model. An instance of the subtype is created per boundary each time step.
The fields of the subtype should contain all of the information needed to calculate the heatflow on that boundary (except for the information already passed in to Types.boundaryHeatTransferRate
).
Each subtype of this should have a constructor that satisfies the following signature:
<Boundary>(pts::AbstractResult, cts::AbstractResult, prob::Problem, ls::LoadStep)
Arguments
pts
: The results from the previous timestep. SeeResult
cts
: The results from the current timestep. SeeResult
prob
: The 'global' variables of the simulation. SeeProblem
ls
: The per load step variables. SeeTypes.LoadStep
For examples see the default subtypes, and their constructors. Boundary Recipes has examples of some of the extra things that can be done with new AbstractBoundary
s.
The constructor for z₂ is the only one that is run before the indices lists are set for that load. So any updating of the indices, such as that done by recoating!
, should be done in that boundary constructor.
cts.T
is unknown when this function is called, use the temperature from the previous time step insted (pts.T
).
Symmetry
HSSSimulations.Boundary.SymetryBoundary
— Typestruct SymetryBoundary <: AbstractBoundary
A boundary for a face that has no heatflow (such as a face on a symetrical boundary). Used as the default boundary in Types.Load
.
HSSSimulations.Types.boundaryHeatTransferRate
— MethodboundaryHeatTransferRate(
_,
_,
_::SymetryBoundary
) -> Float64
Conduction
HSSSimulations.Boundary.ConductionBoundary
— Typestruct ConductionBoundary <: AbstractBoundary
A boundary for a face that has a source of contact conductive heat flow.
Fields
temp::Float64
: Temperature of object being contactedcoef::Float64
: Coefficient of contact conduction
HSSSimulations.Types.boundaryHeatTransferRate
— MethodboundaryHeatTransferRate(T, _, p::ConductionBoundary) -> Any
HSSSimulations.Boundary.conductionFlow
— FunctionconductionFlow(T1, T2, h)
Calculates a heat flux density (ϕ⃗, in wm⁻²) for a given contact conduction coefficient, h, between points at temperature T1 (the node on the boundary) and T2 (the wall in contact with the node).
Convection
HSSSimulations.Boundary.ConvectionBoundary
— Typestruct ConvectionBoundary <: AbstractBoundary
A boundary for a face that has a source of convective heat flow.
Fields
temp::Float64
: Air temperaturecoef::Float64
: Convection coefficient
HSSSimulations.Types.boundaryHeatTransferRate
— MethodboundaryHeatTransferRate(T, _, p::ConvectionBoundary) -> Any
HSSSimulations.Boundary.convectionFlow
— FunctionconvectionFlow(T1, T∞, h)
Calculates a heat flux density (ϕ⃗, in wm⁻²) for a given convection coefficient, h, between a point at temperature T1 and a fluid with temperature T∞.
Radiation
HSSSimulations.Boundary.radiationFlow
— FunctionradiationFlow(T1, T∞, ε)
Calculates a heat flux density (ϕ⃗, in wm⁻²) for a given emmisivity, ε, between a point at temperature T1 and infinity at temperature T∞. The temperature arguments should both be in the units °C.
Utilities
HSSSimulations.Boundary.movingObjOverlap
— FunctionmovingObjOverlap(
geometry::Geometry,
movingObj,
objPos::Tuple{Int64, Int64}
) -> Vector
Makes a vector the length of the y-axis of the simulation. It represents the overlap between the simulation area an object that moves in the y-axis, whos left and right edge are given by objPos
. The overlap is filled with the value passed as movingObj
.
The difference between the first and second value of the objPos
Tuple multipled by the spacing in the y-axis (Δy) should match the width of the moving object.
Examples
julia> geometry = Geometry((0.015, 0.015, 0.010), 0.005, 0.010; offset=(0, 0.01));
julia> movingObjOverlap(geometry, true, (2, 4))
3-element Vector{Bool}:
1
1
0
movingObjOverlap(
geometry::Geometry,
movingObj::AbstractVector,
objPos::Int64
) -> Any
Makes a vector the length of the y-axis of the simulation. It represents the overlap between the simulation area an object that moves in the y-axis, whos right edge are given by objPos
. The overlap is filled with the matching values from the vector movingObj
.
The length of the vector multipled by the spacing in the y-axis (Δy) should match the width of the moving object.
Examples
julia> geometry = Geometry((0.015, 0.015, 0.010), 0.005, 0.010; offset=(0, 0.01));
julia> movingObjOverlap(geometry, [1, 2, 3, 4, 5], 6)
3-element Vector{Int64}:
2
3
4
julia> movingObjOverlap(geometry, [1, 2, 3, 4, 5], 8)
3-element Vector{Int64}:
0
1
2
movingObjOverlap(
geometry::Geometry,
movingObj::AbstractMatrix,
objPos::Int64
) -> Any
Makes a matrix with the same dimensions as the top surface of the simulation area. It represents the overlap between the simulation area an object that moves in the y-axis, whos right edge are given by objPos
. The overlap is filled with the matching values from the matrix movingObj
.
Also for Matrix inputs, the length of the first axis multipled by the spacing in the x-axis (Δx) should match the depth of the moving object and the length of the second axis multipled by the spacing in the y-axis (Δy) should match the width of the moving object.
Examples
julia> geometry = Geometry((0.015, 0.015, 0.010), 0.005, 0.010; offset=(0, 0.01));
julia> movingObjOverlap(geometry, [1 2 3; 4 5 6; 7 8 9], 4)
3×3 Matrix{Int64}:
2 3 0
5 6 0
8 9 0
HSSSimulations.Boundary.recoating!
— Functionrecoating!(
pts::AbstractResult,
cts::AbstractResult,
prob::Problem{T, Gh, Mp, R, OR, B},
ls::HSSSimulations.Types.LoadStep,
recoatDist,
temp
)
Powder recoating logic. This updates the indices and fills the new real indices with the initial results provided in the simulation's Problem
, except the temperature, which is set to temp
. This allows for the new powder temp to be set to the temperature of the powder in the hopper, or feed bed, at that time in the build. This always runs in the positive y axis, so new powder is always deposited starting from [:, 1, :]
and going to [:, end, :]
.
Internals
HSSSimulations.Boundary.calcInds
— FunctioncalcInds(
resultsArray::AbstractArray,
ghostArray::AbstractArray,
ΔH::Int64,
isRecoatLoadSet::Bool
) -> HSSSimulations.Types.Indices
Calculates the indices at the start of a load. For build load sets this includes 'imaginary' nodes that don't yet represent a volume with powder in it (but will after recoat). isRecoatLoadSet
should be set to true if the current load set includes powder recoating, and false if it does not. ΔH
is the same as that found in the Geometry
struct.
See Types.Indices
for more details on the struct returned by this function.
HSSSimulations.Boundary.padWithGhost!
— FunctionpadWithGhost!(
pts::AbstractResult,
cts::AbstractResult,
ls,
prob
)
Wraps the temperature array with ghost cells and updates the Tᵗ⁻¹ array in the Problem
with the new value. The ghost cells are calculated based on a the boundaries for each face provided in the current load. See Types.Load
for more details on these boundaries.
HSSSimulations.Boundary.updateInds!
— FunctionupdateInds!(
indStruct::HSSSimulations.Types.Indices,
resultsArray::AbstractArray,
ghostArray::AbstractArray,
recoatDist::Int64
)
Updates an indices struct during a load step. Used to update the real and imaginary nodes during recoat of the powder layer based on the recoatDist
(how far through the layer the powder has been deposited in number of nodes into the simulation area).
HSSSimulations.Boundary.ghostCalc!
— FunctionghostCalc!(Tᵗ⁻¹, T, params, indices, gdist, κ)
Fills in the ghost nodes for the boundary on the face defined by indices
using the boundaryHeatTransferRate
function for the type of the boundary parametre (params
) given.
HSSSimulations.Boundary.boundaryTemp
— FunctionboundaryTemp(ϕ⃗, T, κ, gdist) -> Any
Calculates a temperature for a ghost cell that will give the heat flux density (ϕ⃗, in wm⁻²) to a node with a temperature of T. gdist
is the distance between the ghost and real node in meters, eg. for a z boundary it would be Δz
.
HSSSimulations.Boundary.σ
— ConstantStefan-Boltzmann constant