Skip to content

Commit

Permalink
Add face_forces field to SingleTPMResult struct to store thermal …
Browse files Browse the repository at this point in the history
…force on every facet (#117)

* Update TPM.jl

Add `face_forces` into struct `SingleTPMResult` to store the thermal force on every facet of the shape model.

* Update TPM.jl

* Update TPM.jl

Just a cleanup.

* Add a progress meter option in `find_visiblefacets!`

* Update src/TPM.jl

Co-authored-by: Yuto Horikawa <[email protected]>

* Update TPM.jl

---------

Co-authored-by: Yuto Horikawa <[email protected]>
  • Loading branch information
MasanoriKanamaru and hyrodium authored Jan 27, 2024
1 parent 8ae1852 commit 7bad773
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 25 deletions.
63 changes: 46 additions & 17 deletions src/TPM.jl
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ function SingleTPM(shape, thermo_params; SELF_SHADOWING, SELF_HEATING, SOLVER, B
flux = zeros(Ns, 3)
temperature = zeros(Nz, Ns)

face_forces = [zero(SVector{3, Float64}) for _ in shape.faces]
face_forces = zeros(SVector{3, Float64}, Ns)
force = zero(MVector{3, Float64})
torque = zero(MVector{3, Float64})

Expand Down Expand Up @@ -236,14 +236,17 @@ Output data format for `SingleTPM`
- `torque` : Thermal torque on the asteroid [N ⋅ m]
## Saved only at the time steps desired by the user
- `times_to_save` : Timesteps to save temperature [s]
- `depth_nodes` : Depths of the calculation nodes for 1-D heat conduction [m], a vector of size `Nz`
- `surface_temperature` : Surface temperature [K], a matrix in size of `(Ns, Nt)`.
- `times_to_save` : Timesteps to save temperature and thermal force on every face [s]
- `depth_nodes` : Depths of the calculation nodes for 1-D heat conduction [m], a vector of size `Nz`
- `surface_temperature` : Surface temperature [K], a matrix in size of `(Ns, Nt)`.
- `Ns` : Number of faces
- `Nt` : Number of time steps to save surface temperature
- `subsurface_temperature` : Temperature [K] as a function of depth [m] and time [s], `Dict` with face ID as key and a matrix `(Nz, Nt)` as an entry.
- `subsurface_temperature` : Temperature [K] as a function of depth [m] and time [s], `Dict` with face ID as key and a matrix `(Nz, Nt)` as an entry.
- `Nz` : The number of the depth nodes
- `Nt` : The number of time steps to save temperature
- `face_forces` : Thermal force on every face of the shape model [N], a matrix in size of `(Ns, Nt)`.
- `Ns` : Number of faces
- `Nt` : Number of time steps to save surface temperature
"""
struct SingleTPMResult
times ::Vector{Float64}
Expand All @@ -253,10 +256,11 @@ struct SingleTPMResult
force ::Vector{SVector{3, Float64}}
torque ::Vector{SVector{3, Float64}}

times_to_save ::Vector{Float64}
depth_nodes ::Vector{Float64}
surface_temperature ::Matrix{Float64}
subsurface_temperature ::Dict{Int, Matrix{Float64}}
times_to_save ::Vector{Float64}
depth_nodes ::Vector{Float64}
surface_temperature ::Matrix{Float64}
subsurface_temperature ::Dict{Int, Matrix{Float64}}
face_forces ::Matrix{SVector{3, Float64}}
end


Expand All @@ -270,8 +274,9 @@ Outer constructor of `SingleTPMResult`
- `face_ID` : Face indices to save subsurface temperature
"""
function SingleTPMResult(stpm::SingleTPM, ephem, times_to_save::Vector{Float64}, face_ID::Vector{Int})
nsteps = length(ephem.time)
nsteps_to_save = length(times_to_save)
nsteps = length(ephem.time) # Number of time steps
nsteps_to_save = length(times_to_save) # Number of time steps to save temperature
nfaces = length(stpm.shape.faces) # Number of faces of the shape model

E_in = zeros(nsteps)
E_out = zeros(nsteps)
Expand All @@ -280,10 +285,11 @@ function SingleTPMResult(stpm::SingleTPM, ephem, times_to_save::Vector{Float64},
torque = zeros(SVector{3, Float64}, nsteps)

depth_nodes = stpm.thermo_params.Δz * (0:stpm.thermo_params.Nz-1)
surface_temperature = zeros(length(stpm.shape.faces), nsteps_to_save)
surface_temperature = zeros(nfaces, nsteps_to_save)
subsurface_temperature = Dict{Int,Matrix{Float64}}(
nₛ => zeros(stpm.thermo_params.Nz, nsteps_to_save) for nₛ in face_ID
)
face_forces = zeros(SVector{3, Float64}, nfaces, nsteps_to_save)

return SingleTPMResult(
ephem.time,
Expand All @@ -296,6 +302,7 @@ function SingleTPMResult(stpm::SingleTPM, ephem, times_to_save::Vector{Float64},
depth_nodes,
surface_temperature,
subsurface_temperature,
face_forces,
)
end

Expand Down Expand Up @@ -370,6 +377,8 @@ function update_TPM_result!(result::SingleTPMResult, stpm::SingleTPM, nₜ::Inte
for (nₛ, temperature) in result.subsurface_temperature
temperature[:, nₜ_save] .= stpm.temperature[:, nₛ]
end

result.face_forces[:, nₜ_save] .= stpm.face_forces
end
end

Expand Down Expand Up @@ -400,7 +409,8 @@ The output files are saved in the following directory structure:
dirpath
├── physical_quantities.csv
├── subsurface_temperature.csv
└── surface_temperature.csv
├── surface_temperature.csv
└── thermal_force.csv
# Arguments
- `dirpath` : Path to the directory to save CSV files.
Expand Down Expand Up @@ -454,6 +464,23 @@ function export_TPM_results(dirpath, result::SingleTPMResult)
df = df[:, ["time", "depth", keys_sorted...]]

CSV.write(filepath, df)

##= Thermal force on every face of the shape model =##
filepath = joinpath(dirpath, "thermal_force.csv")

n_faces = size(result.face_forces, 1) # Number of faces of the shape model
n_steps = size(result.face_forces, 2) # Number of time steps to save temperature
nrows = n_faces * n_steps

df = DataFrame(
time = reshape([t for _ in 1:n_faces, t in result.times_to_save], nrows),
face = reshape([i for i in 1:n_faces, _ in result.times_to_save], nrows),
)
df.x = reshape([f[1] for f in result.face_forces], nrows) # x-component of the thermal force
df.y = reshape([f[2] for f in result.face_forces], nrows) # y-component of the thermal force
df.z = reshape([f[3] for f in result.face_forces], nrows) # z-component of the thermal force

CSV.write(filepath, df)
end


Expand All @@ -465,13 +492,15 @@ The output files are saved in the following directory structure:
dirpath
├── pri
│   ├── physical_quantities.csv
│   ├── subsurface_temperature.csv
│   └── surface_temperature.csv
│ ├── physical_quantities.csv
│ ├── subsurface_temperature.csv
│ ├── surface_temperature.csv
│ └── thermal_force.csv
└── sec
├── physical_quantities.csv
├── subsurface_temperature.csv
└── surface_temperature.csv
├── surface_temperature.csv
└── thermal_force.csv
# Arguments
- `dirpath` : Path to the directory to save CSV files.
Expand Down
29 changes: 23 additions & 6 deletions src/facet.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,23 +81,31 @@ raycast(A::StaticVector{3}, B::StaticVector{3}, C::StaticVector{3}, R::StaticVec


"""
find_visiblefacets!(obs::Facet, facets)
find_visiblefacets!(shape::ShapeModel; show_progress=true)
Find facets that is visible from the facet where the observer is located.
# Parameters
- `obs` : Facet where the observer stands
- `facets` : Array of `Facet`
# Arguments
- `shape` : Shape model of an asteroid
# Keyword arguments
- `show_progress` : Switch to show a progress meter
"""
function find_visiblefacets!(shape::ShapeModel)
function find_visiblefacets!(shape::ShapeModel; show_progress=true)
nodes = shape.nodes
faces = shape.faces
face_centers = shape.face_centers
face_normals = shape.face_normals
face_areas = shape.face_areas
visiblefacets = shape.visiblefacets

@showprogress 1 "Searching for visible faces..." for i in eachindex(faces)
## `ProgressMeter` setting
if show_progress
p = Progress(length(faces); dt=1, desc="Searching for visible faces...", showspeed=true)
ProgressMeter.ijulia_behavior(:clear)
end

for i in eachindex(faces)
cᵢ = face_centers[i]
n̂ᵢ = face_normals[i]
aᵢ = face_areas[i]
Expand Down Expand Up @@ -141,6 +149,15 @@ function find_visiblefacets!(shape::ShapeModel)
push!(visiblefacets[i], VisibleFacet(j, view_factor(cᵢ, cⱼ, n̂ᵢ, n̂ⱼ, aⱼ)...)) # i -> j
push!(visiblefacets[j], VisibleFacet(i, view_factor(cⱼ, cᵢ, n̂ⱼ, n̂ᵢ, aᵢ)...)) # j -> i
end

## Update the progress meter
if show_progress
showvalues = [
("Face ID ", i),
("Visible faces ", length(visiblefacets[i])),
]
ProgressMeter.next!(p; showvalues)
end
end
end

Expand Down
17 changes: 15 additions & 2 deletions src/shape.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,20 @@ function Base.show(io::IO, shape::ShapeModel)
print(io, msg)
end

function load_shape_obj(shapepath; scale = 1.0, find_visible_facets = false)
"""
load_shape_obj(shapepath; scale=1.0, find_visible_facets=false; show_progress=true)
Load a shape model from a Wavefront OBJ file.
# Arguments
- `shapepath` : Path to a Wavefront OBJ file
# Keyword arguments
- `scale` : Scale factor of the shape model
- `find_visible_facets` : Switch to find visible facets
- `show_progress` : Switch to show a progress meter
"""
function load_shape_obj(shapepath; scale=1.0, find_visible_facets=false, show_progress=true)
# TODO: use MeshIO.jl
nodes, faces = loadobj(shapepath; scale = scale, message = false)

Expand All @@ -66,7 +79,7 @@ function load_shape_obj(shapepath; scale = 1.0, find_visible_facets = false)
visiblefacets = [VisibleFacet[] for _ in faces]

shape = ShapeModel(nodes, faces, face_centers, face_normals, face_areas, visiblefacets)
find_visible_facets && find_visiblefacets!(shape)
find_visible_facets && find_visiblefacets!(shape; show_progress)

return shape
end
Expand Down

0 comments on commit 7bad773

Please sign in to comment.