Boundary Model

HSSSimulations.BoundaryModule

The functions for calculating the ghost nodes needed to solve the boundary conditions. Basic boundary conditions are also included.

source

Parameters

HSSSimulations.Types.AbstractProblemParamsType
abstract 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.

source

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.AbstractLoadSetType
abstract 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.

source
HSSSimulations.Solver.loadSetSolver!Function
loadSetSolver!(
    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.

source
HSSSimulations.Solver.loadSetSolver!Method
loadSetSolver!(
    loadSet::LayerLoadSet,
    initResult::AbstractResult,
    initLayerNum::Int64,
    prob::Problem{T, Gh, Mp, R, OR, B}
) -> Tuple{AbstractResult, Int64}
source
HSSSimulations.Solver.innerLoadSetSolver!Function
innerLoadSetSolver!(
    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.

source

Loads

HSSSimulations.Types.LoadMethod
Types.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
source
HSSSimulations.Types.LoadType
struct 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 results

  • tₗ::Float64: The duration of the load in seconds

  • skip::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.

Note

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.

source

Boundary

HSSSimulations.Types.boundaryHeatTransferRateFunction
boundaryHeatTransferRate(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 calculated
  • i : The cartesian index of the node for which the ghost node is being calculated
  • p : The boundary struct for the current boundary (see AbstractBoundary)

See the implementations of this for examples, and Boundary Recipes for some additional examples along with the relevant boundary structs.

Note

A positive heat flow equates to heat flowing into the model and negative out of the model.

Warning

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.

source
HSSSimulations.Types.AbstractBoundaryType

Subtypes 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. See Result
  • cts : The results from the current timestep. See Result
  • prob : The 'global' variables of the simulation. See Problem
  • ls : The per load step variables. See Types.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 AbstractBoundarys.

Warning

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.

Warning

cts.T is unknown when this function is called, use the temperature from the previous time step insted (pts.T).

source

Symmetry

Conduction

HSSSimulations.Boundary.ConductionBoundaryType
struct ConductionBoundary <: AbstractBoundary

A boundary for a face that has a source of contact conductive heat flow.

Fields

  • temp::Float64: Temperature of object being contacted

  • coef::Float64: Coefficient of contact conduction

source
HSSSimulations.Boundary.conductionFlowFunction
conductionFlow(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).

source

Convection

HSSSimulations.Boundary.convectionFlowFunction
convectionFlow(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∞.

source

Radiation

HSSSimulations.Boundary.radiationFlowFunction
radiationFlow(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.

source

Utilities

HSSSimulations.Boundary.movingObjOverlapFunction
movingObjOverlap(
    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
source
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
source
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
source
HSSSimulations.Boundary.recoating!Function
recoating!(
    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, :].

source

Internals

HSSSimulations.Boundary.calcIndsFunction
calcInds(
    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.

source
HSSSimulations.Boundary.padWithGhost!Function
padWithGhost!(
    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.

source
HSSSimulations.Boundary.updateInds!Function
updateInds!(
    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).

source
HSSSimulations.Boundary.boundaryTempFunction
boundaryTemp(ϕ⃗, 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.

source