From b3ec468b56931186044b585dec85f88de90cbf35 Mon Sep 17 00:00:00 2001 From: mohamed82008 Date: Thu, 18 Nov 2021 01:04:04 +0000 Subject: [PATCH] Format .jl files --- deps/build.jl | 1 + docs/generate.jl | 4 +- docs/make.jl | 27 +- docs/src/literate/beso.jl | 2 +- docs/src/literate/csimp.jl | 39 +- docs/src/literate/geso.jl | 2 +- docs/src/literate/global_stress.jl | 27 +- docs/src/literate/local_stress.jl | 16 +- docs/src/literate/simp.jl | 22 +- src/Algorithms/Algorithms.jl | 22 +- src/Algorithms/SIMP/basic_simp.jl | 22 +- src/Algorithms/SIMP/continuation_schemes.jl | 98 ++- src/Algorithms/SIMP/continuation_simp.jl | 82 +-- src/Algorithms/SIMP/mma_optimizer.jl | 41 +- src/Algorithms/beso.jl | 51 +- src/Algorithms/geso.jl | 158 +++-- src/CheqFilters/CheqFilters.jl | 30 +- src/CheqFilters/density_filter.jl | 42 +- src/CheqFilters/gpu_cheqfilter.jl | 46 +- src/CheqFilters/sens_filter.jl | 98 ++- src/FEA/FEA.jl | 29 +- src/FEA/assembly_cg_displacement_solvers.jl | 85 ++- src/FEA/convergence_criteria.jl | 6 +- src/FEA/direct_displacement_solver.jl | 57 +- src/FEA/gpu_solvers.jl | 162 +++-- src/FEA/grid_utils.jl | 1 + src/FEA/matrix_free_apply_bcs.jl | 85 ++- .../matrix_free_cg_displacement_solvers.jl | 108 +++- src/FEA/matrix_free_operator.jl | 56 +- src/FEA/simulate.jl | 21 +- src/FEA/solvers_api.jl | 9 +- src/Functions/Functions.jl | 42 +- src/Functions/apply_boundary.jl | 15 +- src/Functions/assemble_K.jl | 15 +- src/Functions/block_compliance.jl | 56 +- src/Functions/compliance.jl | 114 +++- src/Functions/displacement.jl | 5 +- src/Functions/element_k.jl | 17 +- src/Functions/element_ksigma.jl | 38 +- src/Functions/function_utils.jl | 5 +- src/Functions/gpu_support.jl | 166 ++++- src/Functions/mean_compliance.jl | 161 +++-- src/Functions/stress.jl | 207 ++++--- src/Functions/trace.jl | 71 ++- src/Functions/truss_stress.jl | 15 +- src/Functions/volume.jl | 43 +- src/GPUUtils/GPUUtils.jl | 116 ++-- src/TopOpt.jl | 78 +-- src/TopOptProblems/IO/INP/INP.jl | 12 +- .../Parser/FeatureExtractors/extract_cells.jl | 13 +- .../Parser/FeatureExtractors/extract_cload.jl | 6 +- .../Parser/FeatureExtractors/extract_dbcs.jl | 10 +- .../Parser/FeatureExtractors/extract_dload.jl | 10 +- .../FeatureExtractors/extract_material.jl | 2 +- .../Parser/FeatureExtractors/extract_nodes.jl | 16 +- .../Parser/FeatureExtractors/extract_set.jl | 11 +- src/TopOptProblems/IO/INP/Parser/Parser.jl | 39 +- .../IO/INP/Parser/inp_to_ferrite.jl | 20 +- src/TopOptProblems/IO/INP/inpstiffness.jl | 18 +- src/TopOptProblems/IO/IO.jl | 3 +- src/TopOptProblems/IO/VTK.jl | 8 +- src/TopOptProblems/TopOptProblems.jl | 21 +- .../Visualization/Visualization.jl | 4 +- src/TopOptProblems/Visualization/makie.jl | 113 ++-- .../Visualization/mesh_types.jl | 54 +- src/TopOptProblems/assemble.jl | 84 ++- src/TopOptProblems/buckling.jl | 34 +- src/TopOptProblems/elementinfo.jl | 51 +- src/TopOptProblems/elementmatrix.jl | 69 ++- src/TopOptProblems/gpu_support.jl | 65 +- src/TopOptProblems/gpu_utils.jl | 37 +- src/TopOptProblems/grids.jl | 571 ++++++++++++------ src/TopOptProblems/matrices_and_vectors.jl | 134 ++-- src/TopOptProblems/metadata.jl | 40 +- src/TopOptProblems/multiload.jl | 50 +- src/TopOptProblems/problem_types.jl | 242 +++++--- src/TrussTopOptProblems/TrussIO/TrussIO.jl | 1 - src/TrussTopOptProblems/TrussIO/parse_geo.jl | 18 +- src/TrussTopOptProblems/TrussIO/parse_json.jl | 16 +- .../TrussVisualization/TrussVisualization.jl | 4 +- .../TrussVisualization/makie.jl | 118 ++-- src/TrussTopOptProblems/elementinfo.jl | 56 +- src/TrussTopOptProblems/grids.jl | 33 +- .../matrices_and_vectors.jl | 135 +++-- src/TrussTopOptProblems/problem_types.jl | 34 +- src/Utilities/Utilities.jl | 46 +- src/Utilities/gpu_utilities.jl | 8 +- src/Utilities/penalties.jl | 8 +- src/Utilities/traces.jl | 29 +- src/Utilities/utils.jl | 34 +- test/Functions/test_buckling_fns.jl | 92 +-- test/Functions/test_common_fns.jl | 58 +- test/examples/csimp.jl | 29 +- test/examples/global_stress.jl | 31 +- test/examples/local_stress.jl | 26 +- test/examples/test_examples.jl | 30 +- test/fea/solvers.jl | 4 +- test/inp_parser/parser.jl | 2 +- test/runtests.jl | 2 +- .../element_stiffness_matrix.jl | 34 +- test/topopt_problems/metadata.jl | 65 +- test/topopt_problems/problems.jl | 71 ++- test/truss_topopt_problems/test_buckling.jl | 4 +- .../test_buckling_optimize.jl | 19 +- test/truss_topopt_problems/test_fea.jl | 31 +- test/truss_topopt_problems/test_problem.jl | 36 +- test/truss_topopt_problems/utils.jl | 29 +- 107 files changed, 3500 insertions(+), 1953 deletions(-) diff --git a/deps/build.jl b/deps/build.jl index e69de29b..8b137891 100644 --- a/deps/build.jl +++ b/deps/build.jl @@ -0,0 +1 @@ + diff --git a/docs/generate.jl b/docs/generate.jl index 112a1c62..a4b7e1ba 100644 --- a/docs/generate.jl +++ b/docs/generate.jl @@ -13,7 +13,7 @@ for example in readdir(EXAMPLE_DIR) Literate.markdown(input, GENERATED_DIR, postprocess = mdpost) Literate.notebook(input, GENERATED_DIR, execute = true) elseif any(endswith.(example, [".png", ".jpg", ".gif"])) - cp(joinpath(EXAMPLE_DIR, example), joinpath(GENERATED_DIR, example); force=true) + cp(joinpath(EXAMPLE_DIR, example), joinpath(GENERATED_DIR, example); force = true) else @warn "ignoring $example" end @@ -22,4 +22,4 @@ end # remove any .vtu files in the generated dir (should not be deployed) cd(GENERATED_DIR) do foreach(file -> endswith(file, ".vtu") && rm(file), readdir()) -end \ No newline at end of file +end diff --git a/docs/make.jl b/docs/make.jl index 1ca10a55..2d004d42 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -7,30 +7,22 @@ using DocumenterCitations # Generate examples include("generate.jl") -GENERATED_EXAMPLES = [joinpath("examples", f) for f in ( - "simp.md", - "beso.md", - "geso.md", - "csimp.md", - "global_stress.md", - )] +GENERATED_EXAMPLES = [ + joinpath("examples", f) for + f in ("simp.md", "beso.md", "geso.md", "csimp.md", "global_stress.md") +] bib = CitationBibliography(joinpath(@__DIR__, "biblio", "ref.bib")) makedocs( bib, sitename = "TopOpt.jl", - format = Documenter.HTML( - prettyurls = get(ENV, "CI", nothing) == "true" - ), + format = Documenter.HTML(prettyurls = get(ENV, "CI", nothing) == "true"), # doctest = false, pages = [ "Home" => "index.md", "Examples" => GENERATED_EXAMPLES, - "API Reference" => [ - "reference/TopOptProblems.md", - "reference/Algorithms.md", - ], - "Bibliography" => "bibliography.md" + "API Reference" => ["reference/TopOptProblems.md", "reference/Algorithms.md"], + "Bibliography" => "bibliography.md", ], ) @@ -40,8 +32,5 @@ makedocs( # end if get(ENV, "CI", nothing) == "true" - deploydocs( - repo = "github.com/JuliaTopOpt/TopOpt.jl.git", - push_preview=true, - ) + deploydocs(repo = "github.com/JuliaTopOpt/TopOpt.jl.git", push_preview = true) end diff --git a/docs/src/literate/beso.jl b/docs/src/literate/beso.jl index 703882df..6beb5f89 100644 --- a/docs/src/literate/beso.jl +++ b/docs/src/literate/beso.jl @@ -47,4 +47,4 @@ result = beso(x0) #md # #md # ```julia #md # @__CODE__ -#md # ``` \ No newline at end of file +#md # ``` diff --git a/docs/src/literate/csimp.jl b/docs/src/literate/csimp.jl index cfd50802..3e88c70e 100644 --- a/docs/src/literate/csimp.jl +++ b/docs/src/literate/csimp.jl @@ -17,19 +17,14 @@ v = 0.3 # Poisson’s ratio f = 1.0 # downward force problems = Any[ - PointLoadCantilever(Val{:Linear}, (60, 20, 20), (1.0, 1.0, 1.0), E, v, f), - PointLoadCantilever(Val{:Linear}, (160, 40), (1.0, 1.0), E, v, f), - HalfMBB(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), - LBeam(Val{:Linear}, Float64, force = f), - TieBeam(Val{:Quadratic}, Float64) - ] -problem_names = [ - "3d cantilever beam", - "cantilever beam", - "half MBB beam", - "L-beam", - "tie-beam", - ] + PointLoadCantilever(Val{:Linear}, (60, 20, 20), (1.0, 1.0, 1.0), E, v, f), + PointLoadCantilever(Val{:Linear}, (160, 40), (1.0, 1.0), E, v, f), + HalfMBB(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), + LBeam(Val{:Linear}, Float64, force = f), + TieBeam(Val{:Quadratic}, Float64), +] +problem_names = + ["3d cantilever beam", "cantilever beam", "half MBB beam", "L-beam", "tie-beam"] i = 2 println(problem_names[i]) @@ -47,7 +42,7 @@ penalty = TopOpt.PowerPenalty(1.0) pcont = Continuation(penalty, steps = steps, xmin = xmin, pmax = 5.0) # NOTE: non-convexity + computational error lead to different solutions that satisfy the KKT tolerance -mma_options = options = MMAOptions(maxiter=1000) +mma_options = options = MMAOptions(maxiter = 1000) maxtol = 0.01 # maximum tolerance mintol = 0.0001 # minimum tolerance b = log(mintol / maxtol) / steps @@ -58,9 +53,9 @@ mma_options_gen = TopOpt.MMAOptionsGen( ftol_gen = ExponentialContinuation(a, b, 0.0, steps + 1, mintol), ) csimp_options = TopOpt.CSIMPOptions( - steps = steps, - options_gen = mma_options_gen, - p_gen = pcont, + steps = steps, + options_gen = mma_options_gen, + p_gen = pcont, reuse = reuse, ) @@ -82,14 +77,12 @@ constr = x -> volfrac(filter(x)) - V # ### Define subproblem optimizer x0 = fill(V, length(solver.vars)) -optimizer = Optimizer( - obj, constr, x0, MMA87(), - options = mma_options, convcriteria = convcriteria, -) +optimizer = + Optimizer(obj, constr, x0, MMA87(), options = mma_options, convcriteria = convcriteria) # ### Define continuation SIMP optimizer simp = SIMP(optimizer, solver, penalty.p) -cont_simp = ContinuationSIMP(simp, steps, csimp_options) +cont_simp = ContinuationSIMP(simp, steps, csimp_options) # ### Solve result = cont_simp(x0) @@ -116,4 +109,4 @@ result = cont_simp(x0) #md # #md # ```julia #md # @__CODE__ -#md # ``` \ No newline at end of file +#md # ``` diff --git a/docs/src/literate/geso.jl b/docs/src/literate/geso.jl index 8c433701..9791a5c9 100644 --- a/docs/src/literate/geso.jl +++ b/docs/src/literate/geso.jl @@ -47,4 +47,4 @@ result = geso(x0) #md # #md # ```julia #md # @__CODE__ -#md # ``` \ No newline at end of file +#md # ``` diff --git a/docs/src/literate/global_stress.jl b/docs/src/literate/global_stress.jl index 98a7e9ef..18b8aa7b 100644 --- a/docs/src/literate/global_stress.jl +++ b/docs/src/literate/global_stress.jl @@ -18,15 +18,10 @@ f = 1.0 # downward force rmin = 3.0 # filter radius problems = Any[ - PointLoadCantilever(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), - HalfMBB(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), -] -problem_names = [ - "Cantilever beam", - "Half MBB beam", - "L-beam", - "Tie-beam", + PointLoadCantilever(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), + HalfMBB(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), ] +problem_names = ["Cantilever beam", "Half MBB beam", "L-beam", "Tie-beam"] i = 1 println(problem_names[i]) @@ -40,9 +35,7 @@ convcriteria = Nonconvex.KKTCriteria() penalty = TopOpt.PowerPenalty(1.0) # ### Define a finite element solver -solver = FEASolver( - Direct, problem, xmin = xmin, penalty = penalty, -) +solver = FEASolver(Direct, problem, xmin = xmin, penalty = penalty) # ### Define **stress** objective # Notice that gradient is derived automatically by automatic differentiation (Zygote.jl)! @@ -56,16 +49,12 @@ volfrac = TopOpt.Volume(problem, solver) obj = x -> volfrac(filter(x)) constr = x -> norm(stress(filter(x)), 5) - 1.0 -options = MMAOptions( - maxiter=2000, tol = Nonconvex.Tolerance(kkt = 1e-4), -) +options = MMAOptions(maxiter = 2000, tol = Nonconvex.Tolerance(kkt = 1e-4)) # ### Define subproblem optimizer x0 = fill(1.0, length(solver.vars)) -optimizer = Optimizer( - obj, constr, x0, MMA87(), - options = options, convcriteria = convcriteria, -) +optimizer = + Optimizer(obj, constr, x0, MMA87(), options = options, convcriteria = convcriteria) # ### Define continuation SIMP optimizer simp = SIMP(optimizer, solver, 3.0) @@ -92,4 +81,4 @@ result = simp(x0) #md # #md # ```julia #md # @__CODE__ -#md # ``` \ No newline at end of file +#md # ``` diff --git a/docs/src/literate/local_stress.jl b/docs/src/literate/local_stress.jl index ac1a716d..bb61a313 100644 --- a/docs/src/literate/local_stress.jl +++ b/docs/src/literate/local_stress.jl @@ -15,14 +15,12 @@ xmin = 0.0001 # minimum density steps = 40 # maximum number of penalty steps, delta_p0 = 0.1 # ### Continuation SIMP -x0 = fill(1.0, 160*40) # initial design +x0 = fill(1.0, 160 * 40) # initial design x = copy(x0) for p in [1.0, 2.0, 3.0] global penalty, stress, filter, result, stress, x penalty = TopOpt.PowerPenalty(p) - solver = FEASolver( - Direct, problem, xmin = xmin, penalty = penalty, - ) + solver = FEASolver(Direct, problem, xmin = xmin, penalty = penalty) stress = TopOpt.MicroVonMisesStress(solver) filter = DensityFilter(solver, rmin = rmin) volfrac = TopOpt.Volume(problem, solver) @@ -31,17 +29,11 @@ for p in [1.0, 2.0, 3.0] thr = 10 # stress threshold constr = x -> begin s = stress(filter(x)) - vcat( - (s .- thr) / 100, - logsumexp(s) - log(length(s)) - thr, - ) + vcat((s .- thr) / 100, logsumexp(s) - log(length(s)) - thr) end alg = PercivalAlg() options = PercivalOptions() - optimizer = Optimizer( - obj, constr, x, alg, - options = options, - ) + optimizer = Optimizer(obj, constr, x, alg, options = options) simp = SIMP(optimizer, solver, p) result = simp(x) x = result.topology diff --git a/docs/src/literate/simp.jl b/docs/src/literate/simp.jl index 48557e65..313d5777 100644 --- a/docs/src/literate/simp.jl +++ b/docs/src/literate/simp.jl @@ -16,7 +16,7 @@ E = 1.0 # Young’s modulus v = 0.3 # Poisson’s ratio f = 1.0; # downward force -nels = (30, 10, 10) +nels = (30, 10, 10) problem = PointLoadCantilever(Val{:Linear}, nels, (1.0, 1.0, 1.0), E, v, f); # See also the detailed API of `PointLoadCantilever`: @@ -31,9 +31,7 @@ rmin = 2.0; # density filter radius # ### Define a finite element solver penalty = TopOpt.PowerPenalty(3.0) -solver = FEASolver( - Direct, problem, xmin = xmin, penalty = penalty, -) +solver = FEASolver(Direct, problem, xmin = xmin, penalty = penalty) # ### Define compliance objective comp = TopOpt.Compliance(problem, solver) @@ -47,15 +45,15 @@ constr = x -> volfrac(filter(x)) - V # You can enable the iteration printouts with `Nonconvex.show_residuals[] = true` # ### Define subproblem optimizer -mma_options = options = MMAOptions( - maxiter = 3000, tol = Nonconvex.Tolerance(x = 1e-3, f = 1e-3, kkt = 0.001), -) +mma_options = + options = MMAOptions( + maxiter = 3000, + tol = Nonconvex.Tolerance(x = 1e-3, f = 1e-3, kkt = 0.001), + ) convcriteria = Nonconvex.KKTCriteria() x0 = fill(V, length(solver.vars)) -optimizer = Optimizer( - obj, constr, x0, MMA87(), - options = mma_options, convcriteria = convcriteria, -) +optimizer = + Optimizer(obj, constr, x0, MMA87(), options = mma_options, convcriteria = convcriteria) # ### Define SIMP optimizer simp = SIMP(optimizer, solver, penalty.p); @@ -93,4 +91,4 @@ result = simp(x0); #md # #md # ```julia #md # @__CODE__ -#md # ``` \ No newline at end of file +#md # ``` diff --git a/src/Algorithms/Algorithms.jl b/src/Algorithms/Algorithms.jl index e75bd897..c3c262b9 100644 --- a/src/Algorithms/Algorithms.jl +++ b/src/Algorithms/Algorithms.jl @@ -8,17 +8,17 @@ using Parameters: @unpack, @pack! using ..Utilities, Ferrite using LinearAlgebra, Zygote -export Optimizer, - SIMP, - ExponentialContinuation, - ContinuationSIMP, - AdaptiveSIMP, - MMAOptionsGen, - CSIMPOptions, - BESO, - GESO, - Continuation, - PowerContinuation +export Optimizer, + SIMP, + ExponentialContinuation, + ContinuationSIMP, + AdaptiveSIMP, + MMAOptionsGen, + CSIMPOptions, + BESO, + GESO, + Continuation, + PowerContinuation const to = TimerOutput() diff --git a/src/Algorithms/SIMP/basic_simp.jl b/src/Algorithms/SIMP/basic_simp.jl index 7f69791f..ebdc057e 100644 --- a/src/Algorithms/SIMP/basic_simp.jl +++ b/src/Algorithms/SIMP/basic_simp.jl @@ -1,7 +1,7 @@ @params mutable struct SIMPResult{T} topology::AbstractVector{T} objval::T - convstate + convstate::Any nsubproblems::Int end Base.show(::IO, ::MIME{Symbol("text/plain")}, ::SIMPResult) = println("TopOpt SIMP result") @@ -14,16 +14,16 @@ end The vanilla SIMP algorithm, see [Bendsoe1989](@cite). """ @params mutable struct SIMP{T} <: AbstractSIMP - optimizer - penalty - prev_penalty - solver + optimizer::Any + penalty::Any + prev_penalty::Any + solver::Any result::SIMPResult{T} tracing::Bool end Base.show(::IO, ::MIME{Symbol("text/plain")}, ::SIMP) = println("TopOpt SIMP algorithm") -function SIMP(optimizer, solver, p::T; tracing=true) where T +function SIMP(optimizer, solver, p::T; tracing = true) where {T} penalty = getpenalty(solver) prev_penalty = deepcopy(penalty) setpenalty!(penalty, p) @@ -41,14 +41,14 @@ function Utilities.setpenalty!(s::AbstractSIMP, p::Number) return s end -function (s::SIMP{T, TO})(x0 = s.solver.vars) where {T, TO <: Optimizer} +function (s::SIMP{T,TO})(x0 = s.solver.vars) where {T,TO<:Optimizer} setpenalty!(s.solver, s.penalty.p) mma_results = s.optimizer(x0) update_result!(s, mma_results) return s.result end -function (s::SIMP{T, TO})(workspace::Nonconvex.Workspace) where {T, TO <: Optimizer} +function (s::SIMP{T,TO})(workspace::Nonconvex.Workspace) where {T,TO<:Optimizer} mma_results = s.optimizer(workspace) update_result!(s, mma_results) return s.result @@ -60,14 +60,14 @@ function get_topologies(problem, trace::TopOptTrace) nel = length(black) topologies = Vector{Float64}[] topology = zeros(T, nel) - for i in 1:length(x_hist) + for i = 1:length(x_hist) update_topology!(topology, black, white, x_hist[i], varind) push!(topologies, copy(topology)) end return topologies end -function update_result!(s::SIMP{T}, mma_results) where T +function update_result!(s::SIMP{T}, mma_results) where {T} # Postprocessing @unpack result, optimizer = s @unpack problem = s.solver @@ -83,7 +83,7 @@ function update_result!(s::SIMP{T}, mma_results) where T end function update_topology!(topology, black, white, x, varind) - for i in 1:length(black) + for i = 1:length(black) if black[i] topology[i] = 1 elseif white[i] diff --git a/src/Algorithms/SIMP/continuation_schemes.jl b/src/Algorithms/SIMP/continuation_schemes.jl index 380ce39c..15019bec 100644 --- a/src/Algorithms/SIMP/continuation_schemes.jl +++ b/src/Algorithms/SIMP/continuation_schemes.jl @@ -10,9 +10,9 @@ function Base.getproperty(c::RationalContinuation, f::Symbol) return getfield(c, f) end function RationalContinuation(xmin, steps::Int) - pmax = (1 - xmin)/xmin + pmax = (1 - xmin) / xmin kmax = steps - b = 1/steps * (1 - sqrt(xmin)) / (1 + sqrt(xmin)) + b = 1 / steps * (1 - sqrt(xmin)) / (1 + sqrt(xmin)) return RationalContinuation(pmax, kmax, b) end function Base.iterate(s::RationalContinuation, k = 1) @@ -21,22 +21,22 @@ function Base.iterate(s::RationalContinuation, k = 1) return s(k), k + 1 end Base.length(s::RationalContinuation) = s.kmax + 1 -function (s::RationalContinuation{T})(k) where T +function (s::RationalContinuation{T})(k) where {T} k = clamp(k, 1, s.kmax + 1) b = s.b - return 4*b*(k-1) / (1 - b*(k-1))^2 -end + return 4 * b * (k - 1) / (1 - b * (k - 1))^2 +end struct FixedContinuation{T} <: AbstractContinuation param::T length::Int end -function Base.iterate(s::FixedContinuation, x=1) +function Base.iterate(s::FixedContinuation, x = 1) (x > s.length) && return nothing - s.param, x+1 + s.param, x + 1 end Base.length(s::FixedContinuation) = s.length -(s::FixedContinuation{T})(x...) where T = s.param +(s::FixedContinuation{T})(x...) where {T} = s.param """ p(x) = 1/(a + b*ℯ^(-c*x)) @@ -49,17 +49,23 @@ struct SigmoidContinuation{T} <: AbstractContinuation min::T end SigmoidContinuation(; kwargs...) = SigmoidContinuation{Float64}(; kwargs...) -function SigmoidContinuation{T}(; c::T = T(0.1), start::T = T(1), finish::T = T(5), steps::Int = 30, min::T = -Inf) where T - a = 1 - T(finish-start)/finish/(ℯ^(-c) - ℯ^(-steps*c)) * ℯ^(-c) - b = T(finish-start)/finish/(ℯ^(-c) - ℯ^(-steps*c)) +function SigmoidContinuation{T}(; + c::T = T(0.1), + start::T = T(1), + finish::T = T(5), + steps::Int = 30, + min::T = -Inf, +) where {T} + a = 1 - T(finish - start) / finish / (ℯ^(-c) - ℯ^(-steps * c)) * ℯ^(-c) + b = T(finish - start) / finish / (ℯ^(-c) - ℯ^(-steps * c)) SigmoidContinuation(a, b, c, steps, min) end -function Base.iterate(s::SigmoidContinuation, x=1) +function Base.iterate(s::SigmoidContinuation, x = 1) (x > s.length) && return nothing - max(1 / (s.a + s.b*ℯ^(-s.c*x)), s.min), x+1 + max(1 / (s.a + s.b * ℯ^(-s.c * x)), s.min), x + 1 end Base.length(s::SigmoidContinuation) = s.length -(s::SigmoidContinuation{T})(x) where T = max(1 / (s.a + s.b * ℯ^(-s.c * T(x))), s.min) +(s::SigmoidContinuation{T})(x) where {T} = max(1 / (s.a + s.b * ℯ^(-s.c * T(x))), s.min) """ p(x) = a(x-b*steps)^3 + c @@ -72,18 +78,25 @@ struct CubicSplineContinuation{T} <: AbstractContinuation min::T end CubicSplineContinuation(; kwargs...) = CubicSplineContinuation{Float64}(; kwargs...) -function CubicSplineContinuation{T}(;b::T=T(0.5), start::T=T(1), finish::T=T(5), steps::Int=30, min::T=-Inf) where T +function CubicSplineContinuation{T}(; + b::T = T(0.5), + start::T = T(1), + finish::T = T(5), + steps::Int = 30, + min::T = -Inf, +) where {T} a = (finish - start) / (steps^3 * (1 - b)^3 - (1 - b * steps)^3) c = start - a * (1 - b * steps)^3 CubicSplineContinuation(a, b, c, steps, min) end -function Base.iterate(s::CubicSplineContinuation, x=1) +function Base.iterate(s::CubicSplineContinuation, x = 1) x > s.length && return nothing - max(s.a*(x-s.b*s.length)^3 + s.c, s.min), x+1 + max(s.a * (x - s.b * s.length)^3 + s.c, s.min), x + 1 end Base.length(s::CubicSplineContinuation) = s.length -(s::CubicSplineContinuation{T})(x) where T = max(s.a*(T(x)-s.b*s.length)^3 + s.c, s.min) +(s::CubicSplineContinuation{T})(x) where {T} = + max(s.a * (T(x) - s.b * s.length)^3 + s.c, s.min) """ p(x) = a*x^b + c @@ -96,17 +109,23 @@ struct PowerContinuation{T} <: AbstractContinuation min::T end PowerContinuation(; kwargs...) = PowerContinuation{Float64}(; kwargs...) -function PowerContinuation{T}(; b=T(2), start=T(1), finish=T(5), steps::Int=30, min = -T(Inf)) where T +function PowerContinuation{T}(; + b = T(2), + start = T(1), + finish = T(5), + steps::Int = 30, + min = -T(Inf), +) where {T} a = (finish - start) / max(T(1), steps^b - 1) c = start - a PowerContinuation(a, b, c, steps, min) end -function Base.iterate(s::PowerContinuation, x=1) +function Base.iterate(s::PowerContinuation, x = 1) x > s.length && return nothing - max(s.a * x^s.b + s.c, s.min), x+1 + max(s.a * x^s.b + s.c, s.min), x + 1 end Base.length(s::PowerContinuation) = s.length -(s::PowerContinuation{T})(x) where T = max(s.a * T(x)^s.b + s.c, s.min) +(s::PowerContinuation{T})(x) where {T} = max(s.a * T(x)^s.b + s.c, s.min) """ p(x) = a*ℯ^(b*x) + c @@ -119,17 +138,23 @@ struct ExponentialContinuation{T} <: AbstractContinuation min::T end ExponentialContinuation(; kwargs...) = ExponentialContinuation{Float64}(; kwargs...) -function ExponentialContinuation{T}(; b::T = T(0.1), start::T = T(1), finish::T = T(5), steps::Int = 30, min::T = -Inf) where T +function ExponentialContinuation{T}(; + b::T = T(0.1), + start::T = T(1), + finish::T = T(5), + steps::Int = 30, + min::T = -Inf, +) where {T} a = (finish - start) / max(T(1), ℯ^(b * steps) - ℯ^(b)) c = start - a * ℯ^(b) ExponentialContinuation(a, b, c, steps, min) end -function Base.iterate(s::ExponentialContinuation, x=1) +function Base.iterate(s::ExponentialContinuation, x = 1) x > s.length && return nothing - max(s.a*ℯ^(s.b*x) + s.c, s.min), x+1 + max(s.a * ℯ^(s.b * x) + s.c, s.min), x + 1 end Base.length(s::ExponentialContinuation) = s.length -(s::ExponentialContinuation{T})(x) where T = max(s.a*ℯ^(s.b*T(x)) + s.c, s.min) +(s::ExponentialContinuation{T})(x) where {T} = max(s.a * ℯ^(s.b * T(x)) + s.c, s.min) """ p(x) = a*log(b*x) + c @@ -142,22 +167,33 @@ struct LogarithmicContinuation{T} <: AbstractContinuation min::T end LogarithmicContinuation(; kwargs...) = LogarithmicContinuation{Float64}(; kwargs...) -function LogarithmicContinuation{T}(; b::T = T(1), start::T = T(1), finish::T = T(5), steps::Int = 30, min::T = -Inf) where T +function LogarithmicContinuation{T}(; + b::T = T(1), + start::T = T(1), + finish::T = T(5), + steps::Int = 30, + min::T = -Inf, +) where {T} a = (finish - start) / max(T(1), log(b * steps) - log(b)) c = start - a * log(b) LogarithmicContinuation(a, b, c, steps, min) end -function Base.iterate(s::LogarithmicContinuation, x=1) +function Base.iterate(s::LogarithmicContinuation, x = 1) x > s.length && return nothing - max(s.a*log(s.b*x) + s.c, s.min), x+1 + max(s.a * log(s.b * x) + s.c, s.min), x + 1 end Base.length(s::LogarithmicContinuation) = s.length -(s::LogarithmicContinuation{T})(x) where T = max(s.a * log(s.b * T(x)) + s.c, s.min) +(s::LogarithmicContinuation{T})(x) where {T} = max(s.a * log(s.b * T(x)) + s.c, s.min) Continuation(::PowerPenalty; kwargs...) = Continuation(PowerPenalty; kwargs...) Continuation(::RationalPenalty; kwargs...) = Continuation(RationalPenalty; kwargs...) function Continuation(::Type{<:PowerPenalty}; steps, pmax = 5.0, kwargs...) - return PowerContinuation{Float64}(b = 1.0, start = 1.0, steps = steps + 1, finish = pmax) + return PowerContinuation{Float64}( + b = 1.0, + start = 1.0, + steps = steps + 1, + finish = pmax, + ) end function Continuation(::Type{<:RationalPenalty}; steps, xmin = 0.001, kwargs...) return RationalContinuation(xmin, steps) diff --git a/src/Algorithms/SIMP/continuation_simp.jl b/src/Algorithms/SIMP/continuation_simp.jl index b0feb472..e678630a 100644 --- a/src/Algorithms/SIMP/continuation_simp.jl +++ b/src/Algorithms/SIMP/continuation_simp.jl @@ -2,37 +2,41 @@ Continuous SIMP algorithm, see [TarekRay2020](@cite). """ @params mutable struct ContinuationSIMP <: AbstractSIMP - simp - result - options - callback + simp::Any + result::Any + options::Any + callback::Any end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::ContinuationSIMP) = println("TopOpt continuation SIMP algorithm") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::ContinuationSIMP) = + println("TopOpt continuation SIMP algorithm") @params struct CSIMPOptions - p_cont - option_cont + p_cont::Any + option_cont::Any reuse::Bool steps::Integer end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::CSIMPOptions) = println("TopOpt continuation SIMP options") - -function CSIMPOptions(::Type{T} = Float64; - steps = 40, - p_gen = nothing, - initial_options = MMAOptions(), - pstart = T(1), - pfinish = T(5), - reuse = false, - options_gen = nothing - ) where {T} +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::CSIMPOptions) = + println("TopOpt continuation SIMP options") + +function CSIMPOptions( + ::Type{T} = Float64; + steps = 40, + p_gen = nothing, + initial_options = MMAOptions(), + pstart = T(1), + pfinish = T(5), + reuse = false, + options_gen = nothing, +) where {T} if p_gen == nothing - p_cont = PowerContinuation{T}( ; b = T(1), - start = pstart, - steps = steps + 1, - finish = pfinish - ) + p_cont = PowerContinuation{T}(; + b = T(1), + start = pstart, + steps = steps + 1, + finish = pfinish, + ) else @assert steps == p_gen.length - 1 p_cont = p_gen @@ -49,14 +53,14 @@ end function ContinuationSIMP( simp::SIMP{T}, - steps::Int = 40, + steps::Int = 40, options::CSIMPOptions = CSIMPOptions( T, steps = steps, initial_options = deepcopy(simp.optimizer.options), ), callback = (i) -> (), -) where T +) where {T} @assert steps == options.steps ncells = getncells(simp.solver.problem) result = NewSIMPResult(T, simp.optimizer, ncells) @@ -71,9 +75,9 @@ function default_grtol(solver) end function MMAOptionsGen(; - steps::Int = 40, - initial_options = MMAOptions(), - ftol_gen = nothing, + steps::Int = 40, + initial_options = MMAOptions(), + ftol_gen = nothing, xtol_gen = nothing, grtol_gen = nothing, kkttol_gen = nothing, @@ -85,7 +89,7 @@ function MMAOptionsGen(; store_trace_gen = nothing, show_trace_gen = nothing, auto_scale_gen = nothing, - dual_options_gen = nothing + dual_options_gen = nothing, ) if maxiter_gen == nothing maxiter_cont = FixedContinuation(initial_options.maxiter, steps + 1) @@ -173,24 +177,22 @@ function MMAOptionsGen(; end return MMAOptionsGen( - maxiter_cont, + maxiter_cont, outer_maxiter_cont, - tol_cont, - s_init_cont, + tol_cont, + s_init_cont, s_incr_cont, s_decr_cont, store_trace_cont, show_trace_cont, auto_scale_cont, - dual_options_cont + dual_options_cont, ) end -function (c_simp::ContinuationSIMP)( - x0 = copy(c_simp.simp.solver.vars), -) +function (c_simp::ContinuationSIMP)(x0 = copy(c_simp.simp.solver.vars)) @unpack optimizer = c_simp.simp - @unpack workspace = optimizer + @unpack workspace = optimizer @unpack options = workspace update!(c_simp, 1) @@ -203,9 +205,9 @@ function (c_simp::ContinuationSIMP)( g_previous = copy(c_simp.simp.optimizer.workspace.solution.g) original_maxiter = options.maxiter - for i in 1:c_simp.options.steps + for i = 1:c_simp.options.steps c_simp.callback(i) - update!(c_simp, i+1) + update!(c_simp, i + 1) Nonconvex.NonconvexCore.reset!(workspace) maxiter = 1 @@ -234,7 +236,7 @@ function update!(c_simp::ContinuationSIMP, i) c_simp.simp.optimizer.workspace.options = options return c_simp end -frac(x) = 2*min(abs(x), abs(x - 1)) +frac(x) = 2 * min(abs(x), abs(x - 1)) function undo_values!(workspace, f_x_previous, g_previous) workspace.solution.f = workspace.solution.prevf diff --git a/src/Algorithms/SIMP/mma_optimizer.jl b/src/Algorithms/SIMP/mma_optimizer.jl index 4117ba0f..77d26858 100644 --- a/src/Algorithms/SIMP/mma_optimizer.jl +++ b/src/Algorithms/SIMP/mma_optimizer.jl @@ -4,8 +4,8 @@ abstract type AbstractOptimizer end @params mutable struct Optimizer{Tdev} <: AbstractOptimizer model::AbstractModel - alg - workspace + alg::Any + workspace::Any dev::Tdev end Base.show(::IO, ::MIME{Symbol("text/plain")}, ::Optimizer) = println("TopOpt optimizer") @@ -24,7 +24,7 @@ function Optimizer( device::Tdev = CPU(); options = MMAOptions(), convcriteria = KKTCriteria(), -) where {Tdev <: AbstractDevice} +) where {Tdev<:AbstractDevice} T = eltype(vars) nvars = length(vars) @@ -32,7 +32,13 @@ function Optimizer( model = Nonconvex.Model(obj) addvar!(model, zeros(T, nvars), ones(T, nvars)) add_ineq_constraint!(model, Nonconvex.FunctionWrapper(constr, length(constr(x0)))) - workspace = NonconvexMMA.Workspace(NonconvexCore.tovecmodel(model)[1], opt, x0; options = options, convcriteria = convcriteria) + workspace = NonconvexMMA.Workspace( + NonconvexCore.tovecmodel(model)[1], + opt, + x0; + options = options, + convcriteria = convcriteria, + ) return Optimizer(model, opt, workspace, device) end @@ -62,18 +68,19 @@ function (o::Optimizer)(workspace::Nonconvex.Workspace) end @params struct MMAOptionsGen - maxiter - outer_maxiter - tol - s_init - s_incr - s_decr - store_trace - show_trace - auto_scale - dual_options + maxiter::Any + outer_maxiter::Any + tol::Any + s_init::Any + s_incr::Any + s_decr::Any + store_trace::Any + show_trace::Any + auto_scale::Any + dual_options::Any end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::MMAOptionsGen) = println("TopOpt MMA options generator") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::MMAOptionsGen) = + println("TopOpt MMA options generator") function (g::MMAOptionsGen)(i) MMAOptions( maxiter = g.maxiter(i), @@ -85,7 +92,7 @@ function (g::MMAOptionsGen)(i) store_trace = g.store_trace(i), show_trace = g.show_trace(i), auto_scale = g.auto_scale(i), - dual_options = g.dual_options(i) + dual_options = g.dual_options(i), ) end @@ -100,7 +107,7 @@ function (g::MMAOptionsGen)(options, i) store_trace = optionalcall(g, :store_trace, options, i), show_trace = optionalcall(g, :show_trace, options, i), auto_scale = optionalcall(g, :auto_scale, options, i), - dual_options = optionalcall(g, :dual_options, options, i) + dual_options = optionalcall(g, :dual_options, options, i), ) end function optionalcall(g, s, options, i) diff --git a/src/Algorithms/beso.jl b/src/Algorithms/beso.jl index 972e0693..1ab345db 100644 --- a/src/Algorithms/beso.jl +++ b/src/Algorithms/beso.jl @@ -10,11 +10,11 @@ end """ The BESO algorithm, see [HuangXie2010](@cite). """ -@params struct BESO{T} <: TopOptAlgorithm +@params struct BESO{T} <: TopOptAlgorithm comp::Compliance vol::Volume - vol_limit - filter + vol_limit::Any + filter::Any vars::Vector{T} topology::Vector{T} er::T @@ -22,14 +22,25 @@ The BESO algorithm, see [HuangXie2010](@cite). p::T sens::AbstractVector{T} old_sens::AbstractVector{T} - obj_trace::MVector{<:Any, T} + obj_trace::MVector{<:Any,T} tol::T sens_tol::T result::BESOResult{T} end Base.show(::IO, ::MIME{Symbol("text/plain")}, ::BESO) = println("TopOpt BESO algorithm") -function BESO(comp::Compliance, vol::Volume, vol_limit, filter; maxiter = 200, tol = 0.0001, p = 3.0, er = 0.02, sens_tol = tol/100, k = 10) +function BESO( + comp::Compliance, + vol::Volume, + vol_limit, + filter; + maxiter = 200, + tol = 0.0001, + p = 3.0, + er = 0.02, + sens_tol = tol / 100, + k = 10, +) solver = comp.solver T = eltype(solver.vars) solver = comp.solver @@ -41,9 +52,25 @@ function BESO(comp::Compliance, vol::Volume, vol_limit, filter; maxiter = 200, t vars = zeros(T, nvars) sens = zeros(T, nvars) old_sens = zeros(T, nvars) - obj_trace = zeros(MVector{k, T}) + obj_trace = zeros(MVector{k,T}) - return BESO(comp, vol, vol_limit, filter, vars, topology, er, maxiter, p, sens, old_sens, obj_trace, tol, sens_tol, result) + return BESO( + comp, + vol, + vol_limit, + filter, + vars, + topology, + er, + maxiter, + p, + sens, + old_sens, + obj_trace, + tol, + sens_tol, + result, + ) end update_penalty!(b::BESO, p::Number) = (b.p = p) @@ -59,7 +86,7 @@ function (b::BESO)(x0 = copy(b.obj.solver.vars)) k = length(obj_trace) # Initialize the topology - for i in 1:length(topology) + for i = 1:length(topology) if black[i] topology[i] = 1 elseif white[i] @@ -81,8 +108,8 @@ function (b::BESO)(x0 = copy(b.obj.solver.vars)) if iter > 1 old_sens .= sens end - vol = max(vol*(1-er), V) - for j in max(2, k-iter+2):k + vol = max(vol * (1 - er), V) + for j = max(2, k - iter + 2):k obj_trace[j-1] = obj_trace[j] end obj_trace[k], pb = Zygote.pullback(x -> b.comp(b.filter(x)), vars) @@ -94,7 +121,7 @@ function (b::BESO)(x0 = copy(b.obj.solver.vars)) l1, l2 = minimum(sens), maximum(sens) while (l2 - l1) / l2 > sens_tol th = (l1 + l2) / 2 - for i in 1:length(topology) + for i = 1:length(topology) if !black[i] && !white[i] topology[i] = T(sign(sens[varind[i]] - th) > 0) vars[varind[i]] = topology[i] @@ -110,7 +137,7 @@ function (b::BESO)(x0 = copy(b.obj.solver.vars)) if iter >= k l = sum(@view obj_trace[1:k÷2]) h = sum(@view obj_trace[k÷2+1:k]) - change = abs(l-h)/h + change = abs(l - h) / h end end diff --git a/src/Algorithms/geso.jl b/src/Algorithms/geso.jl index 2647b2ba..b0f05545 100644 --- a/src/Algorithms/geso.jl +++ b/src/Algorithms/geso.jl @@ -9,18 +9,18 @@ end """ The GESO algorithm, see [LiuYiLiShen2008](@cite). """ -struct GESO <: TopOptAlgorithm +struct GESO <: TopOptAlgorithm comp::Compliance vol::Volume - vol_limit - filter + vol_limit::Any + filter::Any vars::AbstractVector topology::AbstractVector - Pcmin - Pcmax - Pmmin - Pmmax - Pen + Pcmin::Any + Pcmax::Any + Pmmin::Any + Pmmax::Any + Pen::Any string_length::Int var_volumes::AbstractVector cum_var_volumes::AbstractVector @@ -29,17 +29,33 @@ struct GESO <: TopOptAlgorithm children::BitArray{2} var_black::BitVector maxiter::Int - penalty + penalty::Any sens::AbstractVector old_sens::AbstractVector obj_trace::MVector{10} - tol - sens_tol + tol::Any + sens_tol::Any result::GESOResult end Base.show(::IO, ::MIME{Symbol("text/plain")}, ::GESO) = println("TopOpt GESO algorithm") -function GESO(comp::Compliance, vol::Volume, vol_limit, filter; maxiter = 1000, tol = 0.001, p = 3., Pcmin = 0.6, Pcmax = 1., Pmmin = 0.5, Pmmax = 1., Pen = 3., sens_tol = tol/100, string_length = 4, k = 10) +function GESO( + comp::Compliance, + vol::Volume, + vol_limit, + filter; + maxiter = 1000, + tol = 0.001, + p = 3.0, + Pcmin = 0.6, + Pcmax = 1.0, + Pmmin = 0.5, + Pmmax = 1.0, + Pen = 3.0, + sens_tol = tol / 100, + string_length = 4, + k = 10, +) penalty = comp.solver.penalty setpenalty!(penalty, p) solver = comp.solver @@ -53,15 +69,42 @@ function GESO(comp::Compliance, vol::Volume, vol_limit, filter; maxiter = 1000, result = GESOResult(topology, T(NaN), T(NaN), false, 0) sens = zeros(T, nvars) old_sens = zeros(T, nvars) - obj_trace = zeros(MVector{k, T}) - var_volumes = vol.cellvolumes[.!black .& .!white] + obj_trace = zeros(MVector{k,T}) + var_volumes = vol.cellvolumes[.!black.&.!white] cum_var_volumes = zeros(T, nvars) order = zeros(Int, nvars) genotypes = trues(string_length, nvars) children = trues(string_length, nvars) var_black = trues(nvars) - return GESO(comp, vol, vol_limit, filter, vars, topology, Pcmin, Pcmax, Pmmin, Pmmax, Pen, string_length, var_volumes, cum_var_volumes, order, genotypes, children, var_black, maxiter, penalty, sens, old_sens, obj_trace, tol, sens_tol, result) + return GESO( + comp, + vol, + vol_limit, + filter, + vars, + topology, + Pcmin, + Pcmax, + Pmmin, + Pmmax, + Pen, + string_length, + var_volumes, + cum_var_volumes, + order, + genotypes, + children, + var_black, + maxiter, + penalty, + sens, + old_sens, + obj_trace, + tol, + sens_tol, + result, + ) end function Utilities.setpenalty!(b::GESO, p::Number) @@ -69,17 +112,25 @@ function Utilities.setpenalty!(b::GESO, p::Number) return b end -get_progress(current_volume, total_volume, design_volume) = clamp(min((total_volume - current_volume) / (total_volume - design_volume), current_volume / design_volume), 0, 1) +get_progress(current_volume, total_volume, design_volume) = clamp( + min( + (total_volume - current_volume) / (total_volume - design_volume), + current_volume / design_volume, + ), + 0, + 1, +) -get_probs(b::GESO, Prg) = (b.Pcmin + (b.Pcmax - b.Pcmin)*Prg^b.Pen, b.Pmmin + (b.Pmmax - b.Pmmin)*Prg^b.Pen) +get_probs(b::GESO, Prg) = + (b.Pcmin + (b.Pcmax - b.Pcmin) * Prg^b.Pen, b.Pmmin + (b.Pmmax - b.Pmmin) * Prg^b.Pen) function crossover!(children, genotypes, i, j) - for k in 1:size(genotypes, 1) + for k = 1:size(genotypes, 1) r = rand() if r < 0.5 - children[k,i] = genotypes[k,i] + children[k, i] = genotypes[k, i] else - children[k,i] = genotypes[k,j] + children[k, i] = genotypes[k, j] end end return @@ -96,7 +147,7 @@ function update!(var_black, children, genotypes, Pc, Pm, high_class, mid_class, while i == j j = rand(high_class) end - elseif r < 0.5 + 0.5*Pc + elseif r < 0.5 + 0.5 * Pc j = rand(mid_class) else j = rand(low_class) @@ -114,17 +165,17 @@ function update!(var_black, children, genotypes, Pc, Pm, high_class, mid_class, r = rand() j = i if length(mid_class) > 1 - if r < Pc + if r < Pc while i == j j = rand(mid_class) end - elseif r < 0.5 + 0.5*Pc + elseif r < 0.5 + 0.5 * Pc j = rand(high_class) else j = rand(low_class) end else - if r < 0.5 + 0.5*Pc + if r < 0.5 + 0.5 * Pc j = rand(high_class) else j = rand(low_class) @@ -140,7 +191,7 @@ function update!(var_black, children, genotypes, Pc, Pm, high_class, mid_class, while i == j j = rand(low_class) end - elseif r < 0.5 + 0.5*Pc + elseif r < 0.5 + 0.5 * Pc j = rand(mid_class) else j = rand(high_class) @@ -157,37 +208,37 @@ function update!(var_black, children, genotypes, Pc, Pm, high_class, mid_class, genotypes .= children for i in high_class - for j in 1:size(genotypes, 1) + for j = 1:size(genotypes, 1) r = rand() - if r < Pm && !genotypes[j,i] - genotypes[j,i] = !genotypes[j,i] + if r < Pm && !genotypes[j, i] + genotypes[j, i] = !genotypes[j, i] end end - if any(@view genotypes[:,i]) != var_black[i] + if any(@view genotypes[:, i]) != var_black[i] var_black[i] = !var_black[i] topology_changed = true end end for i in mid_class - for j in 1:size(genotypes, 1) + for j = 1:size(genotypes, 1) r = rand() - if r < Pm && genotypes[j,i] - genotypes[j,i] = !genotypes[j,i] + if r < Pm && genotypes[j, i] + genotypes[j, i] = !genotypes[j, i] end end - if any(@view genotypes[:,i]) != var_black[i] + if any(@view genotypes[:, i]) != var_black[i] var_black[i] = !var_black[i] topology_changed = true end end for i in low_class - for j in 1:size(genotypes, 1) + for j = 1:size(genotypes, 1) r = rand() - if r < Pm && genotypes[j,i] - genotypes[j,i] = !genotypes[j,i] + if r < Pm && genotypes[j, i] + genotypes[j, i] = !genotypes[j, i] end end - if any(@view genotypes[:,i]) != var_black[i] + if any(@view genotypes[:, i]) != var_black[i] var_black[i] = !var_black[i] topology_changed = true end @@ -197,7 +248,7 @@ function update!(var_black, children, genotypes, Pc, Pm, high_class, mid_class, return var_black end -function (b::GESO)(x0 = copy(b.comp.solver.vars); seed=NaN) +function (b::GESO)(x0 = copy(b.comp.solver.vars); seed = NaN) @unpack sens, old_sens, tol, maxiter = b @unpack obj_trace, topology, sens_tol, vars = b @unpack Pcmin, Pcmax, Pmmin, Pmmax, Pen = b @@ -216,7 +267,7 @@ function (b::GESO)(x0 = copy(b.comp.solver.vars); seed=NaN) isnan(seed) || Random.seed!(seed) # Initialize the topology - for i in 1:length(topology) + for i = 1:length(topology) if black[i] topology[i] = 1 elseif white[i] @@ -230,7 +281,7 @@ function (b::GESO)(x0 = copy(b.comp.solver.vars); seed=NaN) check(x) = x > design_volume - fixed_volume #rrmax = clamp(1 - design_volume/current_volume, 0, 1) current_volume = dot(vars, var_volumes) + fixed_volume - vol = current_volume/total_volume + vol = current_volume / total_volume # Main loop change = T(1) iter = 0 @@ -239,7 +290,7 @@ function (b::GESO)(x0 = copy(b.comp.solver.vars); seed=NaN) if iter > 1 old_sens .= sens end - for j in max(2, 10-iter+2):10 + for j = max(2, 10 - iter + 2):10 obj_trace[j-1] = obj_trace[j] end obj_trace[10], pb = Zygote.pullback(x -> b.comp(b.filter(x)), vars) @@ -248,9 +299,9 @@ function (b::GESO)(x0 = copy(b.comp.solver.vars); seed=NaN) if iter > 1 @. sens = (sens + old_sens) / 2 end - + # Classify the cells by their sensitivities - sortperm!(order, sens, rev=true) + sortperm!(order, sens, rev = true) accumulate!(+, cum_var_volumes, view(var_volumes, order)) N1 = findfirst(check, cum_var_volumes) - 1 N2 = (nel - N1) ÷ 2 @@ -261,27 +312,36 @@ function (b::GESO)(x0 = copy(b.comp.solver.vars); seed=NaN) # Crossover and mutation Prg = get_progress(current_volume, total_volume, design_volume) - Pc, Pm = get_probs(b, Prg) - vars .= update!(var_black, children, genotypes, Pc, Pm, high_class, mid_class, low_class) + Pc, Pm = get_probs(b, Prg) + vars .= update!( + var_black, + children, + genotypes, + Pc, + Pm, + high_class, + mid_class, + low_class, + ) # Update crossover and mutation probabilities current_volume = dot(vars, var_volumes) + fixed_volume - vol = current_volume/total_volume + vol = current_volume / total_volume if iter >= 10 l = sum(@view obj_trace[1:5]) h = sum(@view obj_trace[6:10]) - change = abs(l-h)/h + change = abs(l - h) / h end end - for i in 1:length(topology) + for i = 1:length(topology) if black[i] topology[i] = 1 elseif white[i] topology[i] = 0 else - topology[i] = vars[varind[i]] + topology[i] = vars[varind[i]] end end diff --git a/src/CheqFilters/CheqFilters.jl b/src/CheqFilters/CheqFilters.jl index 4d9e5f48..2f0eff55 100644 --- a/src/CheqFilters/CheqFilters.jl +++ b/src/CheqFilters/CheqFilters.jl @@ -10,34 +10,38 @@ using ForwardDiff using Nonconvex: Nonconvex using ChainRulesCore -export AbstractCheqFilter, - SensFilter, - DensityFilter, - ProjectedDensityFilter, - AbstractSensFilter, - AbstractDensityFilter +export AbstractCheqFilter, + SensFilter, + DensityFilter, + ProjectedDensityFilter, + AbstractSensFilter, + AbstractDensityFilter abstract type AbstractCheqFilter end abstract type AbstractSensFilter <: AbstractCheqFilter end abstract type AbstractDensityFilter <: AbstractCheqFilter end @params struct FilterMetadata - cell_neighbouring_nodes - cell_node_weights + cell_neighbouring_nodes::Any + cell_node_weights::Any end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::FilterMetadata) = println("TopOpt filter metadata") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::FilterMetadata) = + println("TopOpt filter metadata") -function FilterMetadata(::Type{T}, ::Type{TI}) where {T, TI} +function FilterMetadata(::Type{T}, ::Type{TI}) where {T,TI} cell_neighbouring_nodes = Vector{TI}[] cell_node_weights = Vector{T}[] return FilterMetadata(cell_neighbouring_nodes, cell_node_weights) end -function FilterMetadata(solver, rmin::T, ::Type{TI}) where {T, TI} +function FilterMetadata(solver, rmin::T, ::Type{TI}) where {T,TI} problem = solver.problem cell_neighbouring_nodes, cell_node_weights = get_neighbour_info(problem, rmin) - return FilterMetadata(RaggedArray(cell_neighbouring_nodes), RaggedArray(cell_node_weights)) + return FilterMetadata( + RaggedArray(cell_neighbouring_nodes), + RaggedArray(cell_node_weights), + ) end function get_neighbour_info(problem, rmin::T) where {T} @@ -78,7 +82,7 @@ function get_neighbour_info(problem, rmin::T) where {T} dist = norm(node.x - center.x) if dist < rmin push!(neighbouring_nodes, n) - push!(node_weights, max(rmin-dist, zero(T))) + push!(node_weights, max(rmin - dist, zero(T))) n_cells = node_cells[n] for c in n_cells next_cell_id = c[1] diff --git a/src/CheqFilters/density_filter.jl b/src/CheqFilters/density_filter.jl index 3a1ffcff..94ecb18d 100644 --- a/src/CheqFilters/density_filter.jl +++ b/src/CheqFilters/density_filter.jl @@ -1,19 +1,30 @@ -@params struct DensityFilter{_filtering, T} <: AbstractDensityFilter +@params struct DensityFilter{_filtering,T} <: AbstractDensityFilter filtering::Val{_filtering} metadata::FilterMetadata rmin::T jacobian::AbstractMatrix{T} end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::DensityFilter) = println("TopOpt density filter") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::DensityFilter) = + println("TopOpt density filter") Nonconvex.NonconvexCore.getdim(f::DensityFilter) = size(f.jacobian, 1) DensityFilter{true}(args...) = DensityFilter(Val(true), args...) DensityFilter{false}(args...) = DensityFilter(Val(false), args...) DensityFilter(solver; rmin) = DensityFilter(Val(true), solver, rmin) -function DensityFilter(::Val{filtering}, solver::AbstractFEASolver, args...) where {filtering} +function DensityFilter( + ::Val{filtering}, + solver::AbstractFEASolver, + args..., +) where {filtering} DensityFilter(Val(filtering), whichdevice(solver), solver, args...) end -function DensityFilter(::Val{true}, ::CPU, solver::TS, rmin::T, ::Type{TI}=Int) where {T, TI<:Integer, TS<:AbstractFEASolver} +function DensityFilter( + ::Val{true}, + ::CPU, + solver::TS, + rmin::T, + ::Type{TI} = Int, +) where {T,TI<:Integer,TS<:AbstractFEASolver} metadata = FilterMetadata(solver, rmin, TI) TM = typeof(metadata) problem = solver.problem @@ -28,14 +39,20 @@ function DensityFilter(::Val{true}, ::CPU, solver::TS, rmin::T, ::Type{TI}=Int) return DensityFilter(Val(true), metadata, rmin, jacobian) end -function DensityFilter(::Val{false}, ::CPU, solver::TS, rmin::T, ::Type{TI}=Int) where {T, TS<:AbstractFEASolver, TI<:Integer} +function DensityFilter( + ::Val{false}, + ::CPU, + solver::TS, + rmin::T, + ::Type{TI} = Int, +) where {T,TS<:AbstractFEASolver,TI<:Integer} metadata = FilterMetadata(T, TI) cell_weights = T[] jacobian = zeros(T, 0, 0) return DensityFilter(Val(false), metadata, rmin, jacobian) end -function (cf::DensityFilter{true, T})(x) where {T} +function (cf::DensityFilter{true,T})(x) where {T} cf.rmin <= 0 && return x @unpack jacobian = cf out = similar(x) @@ -60,7 +77,7 @@ function getJacobian(solver, metadata::FilterMetadata) I = Int[] J = Int[] V = T[] - for n in 1:nnodes + for n = 1:nnodes r = node_cells.offsets[n]:node_cells.offsets[n+1]-1 for i in r c = node_cells.values[i][1] @@ -81,7 +98,7 @@ function getJacobian(solver, metadata::FilterMetadata) I = Int[] J = Int[] V = T[] - for i in 1:length(black) + for i = 1:length(black) if black[i] || white[i] continue end @@ -107,7 +124,7 @@ end function scalecols!(A::SparseMatrixCSC) @unpack colptr, nzval = A T = eltype(A) - for col in 1:length(colptr)-1 + for col = 1:length(colptr)-1 inds = colptr[col]:colptr[col+1]-1 s = sum(nzval[inds]) if s != 0 @@ -119,10 +136,11 @@ end @params struct ProjectedDensityFilter <: AbstractDensityFilter filter::DensityFilter - preproj - postproj + preproj::Any + postproj::Any end -Nonconvex.NonconvexCore.getdim(f::ProjectedDensityFilter) = Nonconvex.NonconvexCore.getdim(f.filter) +Nonconvex.NonconvexCore.getdim(f::ProjectedDensityFilter) = + Nonconvex.NonconvexCore.getdim(f.filter) function (cf::ProjectedDensityFilter)(x) if cf.preproj isa Nothing fx = x diff --git a/src/CheqFilters/gpu_cheqfilter.jl b/src/CheqFilters/gpu_cheqfilter.jl index 0b5c3f86..2121aa14 100644 --- a/src/CheqFilters/gpu_cheqfilter.jl +++ b/src/CheqFilters/gpu_cheqfilter.jl @@ -19,8 +19,18 @@ function update_nodal_grad!(nodal_grad::CuVector, node_cells, args...) return end -function cheq_kernel1(nodal_grad, node_cells_offsets, node_cells_values, cell_weights, - cells, cellvolumes, black, white, varind, grad) +function cheq_kernel1( + nodal_grad, + node_cells_offsets, + node_cells_values, + cell_weights, + cells, + cellvolumes, + black, + white, + varind, + grad, +) T = eltype(nodal_grad) n = @thread_global_index() offset = @total_threads() @@ -62,14 +72,40 @@ function cheq_kernel2(nodal_grad, cell_weights) return end -function update_grad!(grad::CuVector, black, white, varind, cell_neighbouring_nodes, cell_node_weights, nodal_grad) +function update_grad!( + grad::CuVector, + black, + white, + varind, + cell_neighbouring_nodes, + cell_node_weights, + nodal_grad, +) T = eltype(grad) - allargs = (grad, black, white, varind, cell_neighbouring_nodes.offsets, cell_neighbouring_nodes.values, cell_node_weights.values, nodal_grad) + allargs = ( + grad, + black, + white, + varind, + cell_neighbouring_nodes.offsets, + cell_neighbouring_nodes.values, + cell_node_weights.values, + nodal_grad, + ) callkernel(dev, cheq_kernel3, allargs) CUDAdrv.synchronize(ctx) return end -function cheq_kernel3(grad, black, white, varind, cell_neighbouring_nodes_offsets, cell_neighbouring_nodes_values, cell_node_weights_values, nodal_grad) +function cheq_kernel3( + grad, + black, + white, + varind, + cell_neighbouring_nodes_offsets, + cell_neighbouring_nodes_values, + cell_node_weights_values, + nodal_grad, +) T = eltype(nodal_grad) i = @thread_global_index() offset = @total_threads() diff --git a/src/CheqFilters/sens_filter.jl b/src/CheqFilters/sens_filter.jl index 78b32cc5..c893afd2 100644 --- a/src/CheqFilters/sens_filter.jl +++ b/src/CheqFilters/sens_filter.jl @@ -1,4 +1,4 @@ -@params struct SensFilter{_filtering, T, TV <: AbstractVector{T}} <: AbstractSensFilter +@params struct SensFilter{_filtering,T,TV<:AbstractVector{T}} <: AbstractSensFilter filtering::Val{_filtering} elementinfo::ElementFEAInfo metadata::FilterMetadata @@ -7,7 +7,8 @@ last_grad::TV cell_weights::TV end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::SensFilter) = println("TopOpt sensitivity filter") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::SensFilter) = + println("TopOpt sensitivity filter") SensFilter{true}(args...) = SensFilter(Val(true), args...) SensFilter{false}(args...) = SensFilter(Val(false), args...) @@ -18,7 +19,13 @@ end function SensFilter(::Val{filtering}, solver::AbstractFEASolver, args...) where {filtering} return SensFilter(Val(filtering), whichdevice(solver), solver, args...) end -function SensFilter(::Val{true}, ::CPU, solver::TS, rmin::T, ::Type{TI}=Int) where {T, TI<:Integer, TS<:AbstractFEASolver} +function SensFilter( + ::Val{true}, + ::CPU, + solver::TS, + rmin::T, + ::Type{TI} = Int, +) where {T,TI<:Integer,TS<:AbstractFEASolver} metadata = FilterMetadata(solver, rmin, TI) TM = typeof(metadata) problem = solver.problem @@ -32,41 +39,92 @@ function SensFilter(::Val{true}, ::CPU, solver::TS, rmin::T, ::Type{TI}=Int) whe white = problem.white nel = length(black) nfc = sum(black) + sum(white) - last_grad = zeros(T, nel-nfc) + last_grad = zeros(T, nel - nfc) cell_weights = zeros(T, nnodes) - - return SensFilter(Val(true), elementinfo, metadata, rmin, nodal_grad, last_grad, cell_weights) + + return SensFilter( + Val(true), + elementinfo, + metadata, + rmin, + nodal_grad, + last_grad, + cell_weights, + ) end -function SensFilter(::Val{false}, ::CPU, solver::TS, rmin::T, ::Type{TI}=Int) where {T, TS<:AbstractFEASolver, TI<:Integer} +function SensFilter( + ::Val{false}, + ::CPU, + solver::TS, + rmin::T, + ::Type{TI} = Int, +) where {T,TS<:AbstractFEASolver,TI<:Integer} elementinfo = solver.elementinfo metadata = FilterMetadata(T, TI) nodal_grad = T[] last_grad = T[] cell_weights = T[] - return SensFilter(Val(false), elementinfo, metadata, rmin, nodal_grad, last_grad, cell_weights) + return SensFilter( + Val(false), + elementinfo, + metadata, + rmin, + nodal_grad, + last_grad, + cell_weights, + ) end (cf::SensFilter)(x) = x function ChainRulesCore.rrule(cf::SensFilter{true}, x) - x, Δ -> begin + x, + Δ -> begin cf.rmin <= 0 && return (nothing, Δ) newΔ = copy(Δ) @unpack elementinfo, nodal_grad, cell_weights, metadata = cf @unpack black, white, varind, cellvolumes, cells = elementinfo @unpack cell_neighbouring_nodes, cell_node_weights = metadata node_cells = elementinfo.metadata.node_cells - update_nodal_grad!(nodal_grad, node_cells, cell_weights, cells, cellvolumes, black, white, varind, Δ) + update_nodal_grad!( + nodal_grad, + node_cells, + cell_weights, + cells, + cellvolumes, + black, + white, + varind, + Δ, + ) normalize_grad!(nodal_grad, cell_weights) - update_grad!(newΔ, black, white, varind, cell_neighbouring_nodes, cell_node_weights, nodal_grad) + update_grad!( + newΔ, + black, + white, + varind, + cell_neighbouring_nodes, + cell_node_weights, + nodal_grad, + ) return (nothing, newΔ) end end -function update_nodal_grad!(nodal_grad::AbstractVector, node_cells, cell_weights, cells, cellvolumes, black, white, varind, grad) +function update_nodal_grad!( + nodal_grad::AbstractVector, + node_cells, + cell_weights, + cells, + cellvolumes, + black, + white, + varind, + grad, +) T = eltype(nodal_grad) - for n in 1:length(nodal_grad) + for n = 1:length(nodal_grad) nodal_grad[n] = zero(T) cell_weights[n] = zero(T) r = node_cells.offsets[n]:node_cells.offsets[n+1]-1 @@ -85,15 +143,23 @@ function update_nodal_grad!(nodal_grad::AbstractVector, node_cells, cell_weights end function normalize_grad!(nodal_grad::AbstractVector, cell_weights) - for n in 1:length(nodal_grad) + for n = 1:length(nodal_grad) if cell_weights[n] > 0 nodal_grad[n] /= cell_weights[n] end end end -function update_grad!(grad::AbstractVector, black, white, varind, cell_neighbouring_nodes, cell_node_weights, nodal_grad) - @inbounds for i in 1:length(black) +function update_grad!( + grad::AbstractVector, + black, + white, + varind, + cell_neighbouring_nodes, + cell_node_weights, + nodal_grad, +) + @inbounds for i = 1:length(black) if black[i] || white[i] continue end diff --git a/src/FEA/FEA.jl b/src/FEA/FEA.jl index ee7f003f..a01d6d67 100644 --- a/src/FEA/FEA.jl +++ b/src/FEA/FEA.jl @@ -7,20 +7,20 @@ using IterativeSolvers, StaticArrays, SparseArrays using LinearAlgebra using Parameters: @unpack -export AbstractFEASolver, - AbstractDisplacementSolver, - DirectDisplacementSolver, - PCGDisplacementSolver, - StaticMatrixFreeDisplacementSolver, - Displacement, - Direct, - CG, - MatrixFree, - FEASolver, - Assembly, - DefaultCriteria, - EnergyCriteria, - simulate +export AbstractFEASolver, + AbstractDisplacementSolver, + DirectDisplacementSolver, + PCGDisplacementSolver, + StaticMatrixFreeDisplacementSolver, + Displacement, + Direct, + CG, + MatrixFree, + FEASolver, + Assembly, + DefaultCriteria, + EnergyCriteria, + simulate const to = TimerOutput() @@ -40,4 +40,3 @@ include("solvers_api.jl") getcompliance(solver) = solver.u' * solver.globalinfo.K * solver.u end - diff --git a/src/FEA/assembly_cg_displacement_solvers.jl b/src/FEA/assembly_cg_displacement_solvers.jl index dd56523c..867580f7 100644 --- a/src/FEA/assembly_cg_displacement_solvers.jl +++ b/src/FEA/assembly_cg_displacement_solvers.jl @@ -1,7 +1,8 @@ -@params mutable struct PCGDisplacementSolver{T, dim, TP<:AbstractPenalty{T}} <: AbstractDisplacementSolver - problem::StiffnessTopOptProblem{dim, T} +@params mutable struct PCGDisplacementSolver{T,dim,TP<:AbstractPenalty{T}} <: + AbstractDisplacementSolver + problem::StiffnessTopOptProblem{dim,T} globalinfo::GlobalFEAInfo{T} - elementinfo::ElementFEAInfo{dim, T} + elementinfo::ElementFEAInfo{dim,T} u::AbstractVector{T} lhs::AbstractVector{T} rhs::AbstractVector{T} @@ -11,22 +12,24 @@ xmin::T cg_max_iter::Integer abstol::T - cg_statevars::CGStateVariables{T, <:AbstractVector{T}} - preconditioner + cg_statevars::CGStateVariables{T,<:AbstractVector{T}} + preconditioner::Any preconditioner_initialized::Ref{Bool} - conv + conv::Any end -Base.show(::IO, ::MIME{Symbol("text/plain")}, x::PCGDisplacementSolver) = println("TopOpt preconditioned conjugate gradient iterative solver") -function PCGDisplacementSolver(sp::StiffnessTopOptProblem{dim, T}; +Base.show(::IO, ::MIME{Symbol("text/plain")}, x::PCGDisplacementSolver) = + println("TopOpt preconditioned conjugate gradient iterative solver") +function PCGDisplacementSolver( + sp::StiffnessTopOptProblem{dim,T}; conv = DefaultCriteria(), - xmin = T(1)/1000, + xmin = T(1) / 1000, cg_max_iter = 700, abstol = zero(real(T)), penalty = PowerPenalty{T}(1), prev_penalty = deepcopy(penalty), preconditioner = identity, quad_order = default_quad_order(sp), -) where {dim, T} +) where {dim,T} elementinfo = ElementFEAInfo(sp, quad_order, Val{:Static}) globalinfo = GlobalFEAInfo(sp) @@ -36,9 +39,26 @@ function PCGDisplacementSolver(sp::StiffnessTopOptProblem{dim, T}; vars = fill(one(T), getncells(sp.ch.dh.grid) - sum(sp.black) - sum(sp.white)) varind = sp.varind us = similar(u) .= 0 - cg_statevars = CGStateVariables{eltype(u), typeof(u)}(us, similar(u), similar(u)) + cg_statevars = CGStateVariables{eltype(u),typeof(u)}(us, similar(u), similar(u)) - return PCGDisplacementSolver(sp, globalinfo, elementinfo, u, lhs, rhs, vars, penalty, prev_penalty, xmin, cg_max_iter, abstol, cg_statevars, preconditioner, Ref(false), conv) + return PCGDisplacementSolver( + sp, + globalinfo, + elementinfo, + u, + lhs, + rhs, + vars, + penalty, + prev_penalty, + xmin, + cg_max_iter, + abstol, + cg_statevars, + preconditioner, + Ref(false), + conv, + ) end function (s::PCGDisplacementSolver{T})( @@ -47,16 +67,24 @@ function (s::PCGDisplacementSolver{T})( rhs = assemble_f ? s.globalinfo.f : s.rhs, lhs = assemble_f ? s.u : s.lhs, kwargs..., -) where {T, safe} +) where {T,safe} globalinfo = s.globalinfo - assemble!(globalinfo, s.problem, s.elementinfo, s.vars, s.penalty, s.xmin, assemble_f = assemble_f) + assemble!( + globalinfo, + s.problem, + s.elementinfo, + s.vars, + s.penalty, + s.xmin, + assemble_f = assemble_f, + ) Tconv = typeof(s.conv) K, f = globalinfo.K, globalinfo.f if safe m = meandiag(K) - for i in 1:size(K,1) - if K[i,i] ≈ zero(T) - K[i,i] = m + for i = 1:size(K, 1) + if K[i, i] ≈ zero(T) + K[i, i] = m end end end @@ -73,8 +101,27 @@ function (s::PCGDisplacementSolver{T})( end op = MatrixOperator(_K, f, s.conv) if preconditioner === identity - return cg!(lhs, op, f, abstol=abstol, maxiter=cg_max_iter, log=false, statevars=cg_statevars, initially_zero=false) + return cg!( + lhs, + op, + f, + abstol = abstol, + maxiter = cg_max_iter, + log = false, + statevars = cg_statevars, + initially_zero = false, + ) else - return cg!(lhs, op, f, abstol=abstol, maxiter=cg_max_iter, log=false, statevars=cg_statevars, initially_zero=false, Pl = preconditioner) + return cg!( + lhs, + op, + f, + abstol = abstol, + maxiter = cg_max_iter, + log = false, + statevars = cg_statevars, + initially_zero = false, + Pl = preconditioner, + ) end end diff --git a/src/FEA/convergence_criteria.jl b/src/FEA/convergence_criteria.jl index fa564e99..086e09d4 100644 --- a/src/FEA/convergence_criteria.jl +++ b/src/FEA/convergence_criteria.jl @@ -5,8 +5,10 @@ mutable struct EnergyCriteria{T} <: ConvergenceCriteria end EnergyCriteria() = EnergyCriteria{Float64}(0.0) -const Iterable{Tmat} = Union{CGIterable{Tmat}, PCGIterable{<:Any, Tmat}} -function IterativeSolvers.isconverged(it::Iterable{<:AbstractMatrixOperator{<:EnergyCriteria}}) +const Iterable{Tmat} = Union{CGIterable{Tmat},PCGIterable{<:Any,Tmat}} +function IterativeSolvers.isconverged( + it::Iterable{<:AbstractMatrixOperator{<:EnergyCriteria}}, +) conv = it.A.conv T = eltype(it.x) xtr = dot(it.x, it.r) diff --git a/src/FEA/direct_displacement_solver.jl b/src/FEA/direct_displacement_solver.jl index 207ad26b..1a1e147b 100644 --- a/src/FEA/direct_displacement_solver.jl +++ b/src/FEA/direct_displacement_solver.jl @@ -2,10 +2,11 @@ abstract type AbstractFEASolver end abstract type AbstractDisplacementSolver <: AbstractFEASolver end -@params mutable struct DirectDisplacementSolver{T, dim, TP<:AbstractPenalty{T}} <: AbstractDisplacementSolver - problem::StiffnessTopOptProblem{dim, T} +@params mutable struct DirectDisplacementSolver{T,dim,TP<:AbstractPenalty{T}} <: + AbstractDisplacementSolver + problem::StiffnessTopOptProblem{dim,T} globalinfo::GlobalFEAInfo{T} - elementinfo::ElementFEAInfo{dim, T} + elementinfo::ElementFEAInfo{dim,T} u::AbstractVector{T} lhs::AbstractVector{T} rhs::AbstractVector{T} @@ -15,14 +16,16 @@ abstract type AbstractDisplacementSolver <: AbstractFEASolver end xmin::T qr::Bool end -Base.show(::IO, ::MIME{Symbol("text/plain")}, x::DirectDisplacementSolver) = println("TopOpt direct solver") -function DirectDisplacementSolver(sp::StiffnessTopOptProblem{dim, T}; - xmin=T(1)/1000, - penalty=PowerPenalty{T}(1), - prev_penalty=deepcopy(penalty), - quad_order=default_quad_order(sp), +Base.show(::IO, ::MIME{Symbol("text/plain")}, x::DirectDisplacementSolver) = + println("TopOpt direct solver") +function DirectDisplacementSolver( + sp::StiffnessTopOptProblem{dim,T}; + xmin = T(1) / 1000, + penalty = PowerPenalty{T}(1), + prev_penalty = deepcopy(penalty), + quad_order = default_quad_order(sp), qr = false, -) where {dim, T} +) where {dim,T} elementinfo = ElementFEAInfo(sp, quad_order, Val{:Static}) globalinfo = GlobalFEAInfo(sp) @@ -31,7 +34,19 @@ function DirectDisplacementSolver(sp::StiffnessTopOptProblem{dim, T}; rhs = similar(u) vars = fill(one(T), getncells(sp.ch.dh.grid) - sum(sp.black) - sum(sp.white)) varind = sp.varind - return DirectDisplacementSolver(sp, globalinfo, elementinfo, u, lhs, rhs, vars, penalty, prev_penalty, xmin, qr) + return DirectDisplacementSolver( + sp, + globalinfo, + elementinfo, + u, + lhs, + rhs, + vars, + penalty, + prev_penalty, + xmin, + qr, + ) end function (s::DirectDisplacementSolver{T})( ::Type{Val{safe}} = Val{false}, @@ -41,22 +56,30 @@ function (s::DirectDisplacementSolver{T})( rhs = assemble_f ? s.globalinfo.f : s.rhs, lhs = assemble_f ? s.u : s.lhs, kwargs..., -) where {T, safe, newT} +) where {T,safe,newT} globalinfo = s.globalinfo N = size(globalinfo.K, 1) - assemble!(globalinfo, s.problem, s.elementinfo, s.vars, getpenalty(s), s.xmin, assemble_f = assemble_f) + assemble!( + globalinfo, + s.problem, + s.elementinfo, + s.vars, + getpenalty(s), + s.xmin, + assemble_f = assemble_f, + ) K = globalinfo.K if safe m = meandiag(K) - for i in 1:size(K,1) - if K[i,i] ≈ zero(T) - K[i,i] = m + for i = 1:size(K, 1) + if K[i, i] ≈ zero(T) + K[i, i] = m end end end nans = false if !reuse_chol - try + try if T === newT if s.qr globalinfo.qrK = qr(K.data) diff --git a/src/FEA/gpu_solvers.jl b/src/FEA/gpu_solvers.jl index 9168ce1c..7fad54d5 100644 --- a/src/FEA/gpu_solvers.jl +++ b/src/FEA/gpu_solvers.jl @@ -11,14 +11,61 @@ for T in (:DirectDisplacementSolver, :PCGDisplacementSolver) @eval @inline CuArrays.cu(p::$T) = error("$T does not support the GPU.") end -function update_f!(f::CuVector{T}, values, prescribed_dofs, applyzero, dof_cells, cell_dofs, black, white, Kes, xmin, penalty, vars, varind, M) where {T} - args = (f, values, prescribed_dofs, applyzero, dof_cells.offsets, dof_cells.values, cell_dofs, black, white, Kes, xmin, penalty, vars, varind, M) +function update_f!( + f::CuVector{T}, + values, + prescribed_dofs, + applyzero, + dof_cells, + cell_dofs, + black, + white, + Kes, + xmin, + penalty, + vars, + varind, + M, +) where {T} + args = ( + f, + values, + prescribed_dofs, + applyzero, + dof_cells.offsets, + dof_cells.values, + cell_dofs, + black, + white, + Kes, + xmin, + penalty, + vars, + varind, + M, + ) callkernel(dev, bc_kernel, args) CUDAdrv.synchronize(ctx) - return + return end -function bc_kernel(f::AbstractVector{T}, values, prescribed_dofs, applyzero, dof_cells_offsets, dof_cells_values, cell_dofs, black, white, Kes, xmin, penalty, vars, varind, M) where {T} +function bc_kernel( + f::AbstractVector{T}, + values, + prescribed_dofs, + applyzero, + dof_cells_offsets, + dof_cells_values, + cell_dofs, + black, + white, + Kes, + xmin, + penalty, + vars, + varind, + M, +) where {T} ind = @thread_global_index() offset = @total_threads() @@ -27,45 +74,64 @@ function bc_kernel(f::AbstractVector{T}, values, prescribed_dofs, applyzero, dof v = values[ind] m = size(Kes[ind], 1) - r = dof_cells_offsets[d] : dof_cells_offsets[d+1]-1 - if !applyzero && v != 0 + r = dof_cells_offsets[d]:dof_cells_offsets[d+1]-1 + if !applyzero && v != 0 for idx in r (i, j) = dof_cells_values[idx] if PENALTY_BEFORE_INTERPOLATION - px = ifelse(black[i], one(T), - ifelse(white[i], xmin, - density(penalty(vars[varind[i]]), xmin))) + px = ifelse( + black[i], + one(T), + ifelse(white[i], xmin, density(penalty(vars[varind[i]]), xmin)), + ) else - px = ifelse(black[i], one(T), - ifelse(white[i], xmin, - penalty(density(vars[varind[i]], xmin)))) + px = ifelse( + black[i], + one(T), + ifelse(white[i], xmin, penalty(density(vars[varind[i]], xmin))), + ) end if eltype(Kes) <: Symmetric Ke = Kes[i].data else Ke = Kes[i] end - for row in 1:m - f[cell_dofs[row,i]] -= px * v * Ke[row,j] + for row = 1:m + f[cell_dofs[row, i]] -= px * v * Ke[row, j] end end end - f[d] = M*v + f[d] = M * v ind += offset end - return + return end -@define_cu(StaticMatrixFreeDisplacementSolver, :f, :problem, :vars, :cg_statevars, :elementinfo, :penalty, :prev_penalty, :u, :fixed_dofs, :free_dofs, :xes, :lhs, :rhs) +@define_cu( + StaticMatrixFreeDisplacementSolver, + :f, + :problem, + :vars, + :cg_statevars, + :elementinfo, + :penalty, + :prev_penalty, + :u, + :fixed_dofs, + :free_dofs, + :xes, + :lhs, + :rhs +) whichdevice(m::MatrixFreeOperator) = whichdevice(m.vars) -function mul!(y::TV, A::MatrixFreeOperator, x::TV) where {TV <: CuArrays.CuVector} +function mul!(y::TV, A::MatrixFreeOperator, x::TV) where {TV<:CuArrays.CuVector} T = eltype(y) nels = length(A.elementinfo.Kes) ndofs = length(A.elementinfo.fixedload) meandiag = A.meandiag - + @unpack Kes, metadata, black, white, varind = A.elementinfo @unpack cell_dofs, dof_cells = metadata @unpack penalty, xmin, vars, fixed_dofs, free_dofs, xes = A @@ -74,7 +140,8 @@ function mul!(y::TV, A::MatrixFreeOperator, x::TV) where {TV <: CuArrays.CuVecto callkernel(dev, mul_kernel1, args1) CUDAdrv.synchronize(ctx) - args2 = (y, x, dof_cells.offsets, dof_cells.values, xes, fixed_dofs, free_dofs, meandiag) + args2 = + (y, x, dof_cells.offsets, dof_cells.values, xes, fixed_dofs, free_dofs, meandiag) callkernel(dev, mul_kernel2, args2) CUDAdrv.synchronize(ctx) @@ -82,31 +149,43 @@ function mul!(y::TV, A::MatrixFreeOperator, x::TV) where {TV <: CuArrays.CuVecto end # CUDA kernels -function mul_kernel1(xes::AbstractVector{TV}, x, black, white, vars, varind, cell_dofs, Kes, xmin, penalty, nels) where {N, T, TV<:SVector{N, T}} +function mul_kernel1( + xes::AbstractVector{TV}, + x, + black, + white, + vars, + varind, + cell_dofs, + Kes, + xmin, + penalty, + nels, +) where {N,T,TV<:SVector{N,T}} i = @thread_global_index() offset = @total_threads() while i <= nels if PENALTY_BEFORE_INTERPOLATION - px = ifelse(black[i], one(T), - ifelse(white[i], xmin, - density(penalty(vars[varind[i]]), xmin) - ) + px = ifelse( + black[i], + one(T), + ifelse(white[i], xmin, density(penalty(vars[varind[i]]), xmin)), ) else - px = ifelse(black[i], one(T), - ifelse(white[i], xmin, - penalty(density(vars[varind[i]], xmin)) - ) + px = ifelse( + black[i], + one(T), + ifelse(white[i], xmin, penalty(density(vars[varind[i]], xmin))), ) end xe = xes[i] - for j in 1:N + for j = 1:N xe = @set xe[j] = x[cell_dofs[j, i]] end if eltype(Kes) <: Symmetric - xe = SVector{1, T}((px,)) .* (bcmatrix(Kes[i]).data * xe) + xe = SVector{1,T}((px,)) .* (bcmatrix(Kes[i]).data * xe) else - xe = SVector{1, T}((px,)) .* (bcmatrix(Kes[i]) * xe) + xe = SVector{1,T}((px,)) .* (bcmatrix(Kes[i]) * xe) end xes[i] = xe @@ -116,12 +195,21 @@ function mul_kernel1(xes::AbstractVector{TV}, x, black, white, vars, varind, cel return end -function mul_kernel2(y, x, dof_cells_offsets, dof_cells_values, xes, fixed_dofs, free_dofs, meandiag) +function mul_kernel2( + y, + x, + dof_cells_offsets, + dof_cells_values, + xes, + fixed_dofs, + free_dofs, + meandiag, +) T = eltype(y) offset = @total_threads() n_fixeddofs = length(fixed_dofs) ndofs = length(y) - + i = @thread_global_index() while i <= n_fixeddofs dof = fixed_dofs[i] @@ -131,9 +219,9 @@ function mul_kernel2(y, x, dof_cells_offsets, dof_cells_values, xes, fixed_dofs, i = @thread_global_index() while n_fixeddofs < i <= ndofs - dof = free_dofs[i - n_fixeddofs] + dof = free_dofs[i-n_fixeddofs] yi = zero(T) - r = dof_cells_offsets[dof] : dof_cells_offsets[dof+1]-1 + r = dof_cells_offsets[dof]:dof_cells_offsets[dof+1]-1 for ind in r k, m = dof_cells_values[ind] yi += xes[k][m] @@ -147,7 +235,7 @@ end whichdevice(r::LinearElasticityResult) = whichdevice(r.u) function fill_vars!(vars::GPUArray, topology; round) - if round + if round vars .= round.(typeof(vars)(topology)) else copyto!(vars, topology) diff --git a/src/FEA/grid_utils.jl b/src/FEA/grid_utils.jl index e69de29b..8b137891 100644 --- a/src/FEA/grid_utils.jl +++ b/src/FEA/grid_utils.jl @@ -0,0 +1 @@ + diff --git a/src/FEA/matrix_free_apply_bcs.jl b/src/FEA/matrix_free_apply_bcs.jl index 3cf4492d..391e1ae7 100644 --- a/src/FEA/matrix_free_apply_bcs.jl +++ b/src/FEA/matrix_free_apply_bcs.jl @@ -1,44 +1,95 @@ -function matrix_free_apply2f!(f::AbstractVector{T}, elementinfo::ElementFEAInfo{dim, T}, M, vars, problem::StiffnessTopOptProblem, penalty, xmin, applyzero::Bool=false) where {dim, T} +function matrix_free_apply2f!( + f::AbstractVector{T}, + elementinfo::ElementFEAInfo{dim,T}, + M, + vars, + problem::StiffnessTopOptProblem, + penalty, + xmin, + applyzero::Bool = false, +) where {dim,T} @unpack Kes, black, white, varind, metadata = elementinfo @unpack dof_cells, cell_dofs = metadata @unpack ch = problem @unpack values, prescribed_dofs = ch - update_f!(f, values, prescribed_dofs, applyzero, dof_cells, cell_dofs, black, white, Kes, xmin, penalty, vars, varind, M) + update_f!( + f, + values, + prescribed_dofs, + applyzero, + dof_cells, + cell_dofs, + black, + white, + Kes, + xmin, + penalty, + vars, + varind, + M, + ) return end -function update_f!(f::Vector{T}, values, prescribed_dofs, applyzero, dof_cells, cell_dofs, black, white, Kes, xmin, penalty, vars, varind, M) where {T} - for ind in 1:length(values) +function update_f!( + f::Vector{T}, + values, + prescribed_dofs, + applyzero, + dof_cells, + cell_dofs, + black, + white, + Kes, + xmin, + penalty, + vars, + varind, + M, +) where {T} + for ind = 1:length(values) d = prescribed_dofs[ind] v = values[ind] m = size(Kes[ind], 1) - r = dof_cells.offsets[d] : dof_cells.offsets[d+1]-1 - if !applyzero && v != 0 + r = dof_cells.offsets[d]:dof_cells.offsets[d+1]-1 + if !applyzero && v != 0 for idx in r - (i,j) = dof_cells.values[idx] + (i, j) = dof_cells.values[idx] if PENALTY_BEFORE_INTERPOLATION - px = ifelse(black[i], one(T), - ifelse(white[i], xmin, - px = density(penalty(vars[varind[i]]), xmin))) + px = ifelse( + black[i], + one(T), + ifelse( + white[i], + xmin, + px = density(penalty(vars[varind[i]]), xmin), + ), + ) else - px = ifelse(black[i], one(T), - ifelse(white[i], xmin, - px = penalty(density(vars[varind[i]], xmin)))) + px = ifelse( + black[i], + one(T), + ifelse( + white[i], + xmin, + px = penalty(density(vars[varind[i]], xmin)), + ), + ) end if eltype(Kes) <: Symmetric Ke = Kes[i].data else Ke = Kes[i] end - for row in 1:m - f[cell_dofs[row,i]] -= px * v * Ke[row,j] + for row = 1:m + f[cell_dofs[row, i]] -= px * v * Ke[row, j] end end end - f[d] = M*v + f[d] = M * v end - return + return end diff --git a/src/FEA/matrix_free_cg_displacement_solvers.jl b/src/FEA/matrix_free_cg_displacement_solvers.jl index 41ecc451..44fbee63 100644 --- a/src/FEA/matrix_free_cg_displacement_solvers.jl +++ b/src/FEA/matrix_free_cg_displacement_solvers.jl @@ -1,6 +1,7 @@ abstract type AbstractMatrixFreeSolver <: AbstractDisplacementSolver end -@params mutable struct StaticMatrixFreeDisplacementSolver{T, dim, TP <: AbstractPenalty{T}} <: AbstractDisplacementSolver +@params mutable struct StaticMatrixFreeDisplacementSolver{T,dim,TP<:AbstractPenalty{T}} <: + AbstractDisplacementSolver elementinfo::ElementFEAInfo{dim} problem::StiffnessTopOptProblem{dim} f::AbstractVector{T} @@ -9,34 +10,36 @@ abstract type AbstractMatrixFreeSolver <: AbstractDisplacementSolver end lhs::AbstractVector{T} rhs::AbstractVector{T} vars::AbstractVector{T} - xes - fixed_dofs - free_dofs + xes::Any + fixed_dofs::Any + free_dofs::Any penalty::TP prev_penalty::TP xmin::T cg_max_iter::Integer tol::T cg_statevars::CGStateVariables{T} - preconditioner + preconditioner::Any preconditioner_initialized::Base.RefValue{Bool} - conv + conv::Any end -Base.show(::IO, ::MIME{Symbol("text/plain")}, x::StaticMatrixFreeDisplacementSolver) = println("TopOpt matrix free conjugate gradient iterative solver") -StaticMatrixFreeDisplacementSolver(sp, args...; kwargs...) = StaticMatrixFreeDisplacementSolver(whichdevice(sp), sp, args...; kwargs...) +Base.show(::IO, ::MIME{Symbol("text/plain")}, x::StaticMatrixFreeDisplacementSolver) = + println("TopOpt matrix free conjugate gradient iterative solver") +StaticMatrixFreeDisplacementSolver(sp, args...; kwargs...) = + StaticMatrixFreeDisplacementSolver(whichdevice(sp), sp, args...; kwargs...) function StaticMatrixFreeDisplacementSolver( ::CPU, - sp::StiffnessTopOptProblem{dim, T}; - conv = DefaultCriteria(), - xmin = one(T) / 1000, - cg_max_iter = 700, - tol = xmin, - penalty = PowerPenalty{T}(1), + sp::StiffnessTopOptProblem{dim,T}; + conv = DefaultCriteria(), + xmin = one(T) / 1000, + cg_max_iter = 700, + tol = xmin, + penalty = PowerPenalty{T}(1), prev_penalty = deepcopy(penalty), - preconditioner = identity, + preconditioner = identity, quad_order = 2, -) where {dim, T} +) where {dim,T} elementinfo = ElementFEAInfo(sp, quad_order, Val{:Static}) if eltype(elementinfo.Kes) <: Symmetric f = x -> sumdiag(rawmatrix(x).data) @@ -55,18 +58,50 @@ function StaticMatrixFreeDisplacementSolver( fixed_dofs = sp.ch.prescribed_dofs free_dofs = setdiff(1:length(u), fixed_dofs) - return StaticMatrixFreeDisplacementSolver(elementinfo, sp, f, meandiag, u, lhs, rhs, vars, xes, fixed_dofs, free_dofs, penalty, prev_penalty, xmin, cg_max_iter, tol, cg_statevars, preconditioner, Ref(false), conv) + return StaticMatrixFreeDisplacementSolver( + elementinfo, + sp, + f, + meandiag, + u, + lhs, + rhs, + vars, + xes, + fixed_dofs, + free_dofs, + penalty, + prev_penalty, + xmin, + cg_max_iter, + tol, + cg_statevars, + preconditioner, + Ref(false), + conv, + ) end MatrixFreeOperator(solver::StaticMatrixFreeDisplacementSolver) = buildoperator(solver) function buildoperator(solver::StaticMatrixFreeDisplacementSolver) penalty = getpenalty(solver) @unpack elementinfo, meandiag, vars, xmin, fixed_dofs, free_dofs, xes, conv = solver - MatrixFreeOperator(solver.f, elementinfo, meandiag, vars, xes, fixed_dofs, free_dofs, xmin, penalty, conv) + MatrixFreeOperator( + solver.f, + elementinfo, + meandiag, + vars, + xes, + fixed_dofs, + free_dofs, + xmin, + penalty, + conv, + ) end -function (s::StaticMatrixFreeDisplacementSolver)( - ; assemble_f = true, +function (s::StaticMatrixFreeDisplacementSolver)(; + assemble_f = true, rhs = assemble_f ? s.f : s.rhs, lhs = assemble_f ? s.u : s.lhs, kwargs..., @@ -74,7 +109,15 @@ function (s::StaticMatrixFreeDisplacementSolver)( if assemble_f assemble_f!(s.f, s.problem, s.elementinfo, s.vars, getpenalty(s), s.xmin) end - matrix_free_apply2f!(rhs, s.elementinfo, s.meandiag, s.vars, s.problem, getpenalty(s), s.xmin) + matrix_free_apply2f!( + rhs, + s.elementinfo, + s.meandiag, + s.vars, + s.problem, + getpenalty(s), + s.xmin, + ) @unpack cg_max_iter, cg_statevars = s @unpack preconditioner_initialized, preconditioner, tol = s @@ -87,8 +130,27 @@ function (s::StaticMatrixFreeDisplacementSolver)( end end if preconditioner === identity - return cg!(lhs, operator, rhs, tol=tol, maxiter=cg_max_iter, log=false, statevars=cg_statevars, initially_zero=false) + return cg!( + lhs, + operator, + rhs, + tol = tol, + maxiter = cg_max_iter, + log = false, + statevars = cg_statevars, + initially_zero = false, + ) else - return cg!(lhs, operator, rhs, tol=tol, maxiter=cg_max_iter, log=false, statevars=cg_statevars, initially_zero=false, Pl=preconditioner) + return cg!( + lhs, + operator, + rhs, + tol = tol, + maxiter = cg_max_iter, + log = false, + statevars = cg_statevars, + initially_zero = false, + Pl = preconditioner, + ) end end diff --git a/src/FEA/matrix_free_operator.jl b/src/FEA/matrix_free_operator.jl index 4df33c65..8edc8ada 100644 --- a/src/FEA/matrix_free_operator.jl +++ b/src/FEA/matrix_free_operator.jl @@ -1,32 +1,34 @@ abstract type AbstractMatrixOperator{Tconv} end @params struct MatrixOperator{Tconv} <: AbstractMatrixOperator{Tconv} - K - f + K::Any + f::Any conv::Tconv end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::MatrixOperator) = println("TopOpt matrix linear operator") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::MatrixOperator) = + println("TopOpt matrix linear operator") LinearAlgebra.mul!(c, op::MatrixOperator, b) = mul!(c, op.K, b) Base.size(op::MatrixOperator, i...) = size(op.K, i...) Base.eltype(op::MatrixOperator) = eltype(op.K) LinearAlgebra.:*(op::MatrixOperator, b) = mul!(similar(b), op.K, b) -@params struct MatrixFreeOperator{Tconv, T, dim} <: AbstractMatrixOperator{Tconv} +@params struct MatrixFreeOperator{Tconv,T,dim} <: AbstractMatrixOperator{Tconv} f::AbstractVector{T} - elementinfo::ElementFEAInfo{dim, T} + elementinfo::ElementFEAInfo{dim,T} meandiag::T vars::AbstractVector{T} - xes - fixed_dofs - free_dofs + xes::Any + fixed_dofs::Any + free_dofs::Any xmin::T - penalty + penalty::Any conv::Tconv end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::MatrixFreeOperator) = println("TopOpt matrix-free linear operator") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::MatrixFreeOperator) = + println("TopOpt matrix-free linear operator") Base.size(op::MatrixFreeOperator) = (size(op, 1), size(op, 2)) Base.size(op::MatrixFreeOperator, i) = 1 <= i <= 2 ? length(op.elementinfo.fixedload) : 1 -Base.eltype(op::MatrixFreeOperator{<:Any, T}) where {T} = T +Base.eltype(op::MatrixFreeOperator{<:Any,T}) where {T} = T import LinearAlgebra: *, mul! @@ -36,7 +38,7 @@ function *(A::MatrixFreeOperator, x) y end -function mul!(y::TV, A::MatrixFreeOperator, x::TV) where {TV <: AbstractVector} +function mul!(y::TV, A::MatrixFreeOperator, x::TV) where {TV<:AbstractVector} T = eltype(y) nels = length(A.elementinfo.Kes) ndofs = length(A.elementinfo.fixedload) @@ -46,24 +48,24 @@ function mul!(y::TV, A::MatrixFreeOperator, x::TV) where {TV <: AbstractVector} @unpack Kes, metadata, black, white, varind = A.elementinfo @unpack cell_dofs, dof_cells = metadata @unpack penalty, xmin, vars, fixed_dofs, free_dofs, xes = A - - for i in 1:nels + + for i = 1:nels if PENALTY_BEFORE_INTERPOLATION - px = ifelse(black[i], one(T), - ifelse(white[i], xmin, - density(penalty(vars[varind[i]]), xmin) - ) + px = ifelse( + black[i], + one(T), + ifelse(white[i], xmin, density(penalty(vars[varind[i]]), xmin)), ) else - px = ifelse(black[i], one(T), - ifelse(white[i], xmin, - penalty(density(vars[varind[i]], xmin)) - ) + px = ifelse( + black[i], + one(T), + ifelse(white[i], xmin, penalty(density(vars[varind[i]], xmin))), ) end xe = xes[i] - for j in 1:dofspercell - xe = @set xe[j] = x[cell_dofs[j,i]] + for j = 1:dofspercell + xe = @set xe[j] = x[cell_dofs[j, i]] end if eltype(Kes) <: Symmetric xes[i] = px * (bcmatrix(Kes[i]).data * xe) @@ -72,14 +74,14 @@ function mul!(y::TV, A::MatrixFreeOperator, x::TV) where {TV <: AbstractVector} end end - for i in 1:length(fixed_dofs) + for i = 1:length(fixed_dofs) dof = fixed_dofs[i] y[dof] = meandiag * x[dof] end - for i in 1:length(free_dofs) + for i = 1:length(free_dofs) dof = free_dofs[i] yi = zero(T) - r = dof_cells.offsets[dof] : dof_cells.offsets[dof+1]-1 + r = dof_cells.offsets[dof]:dof_cells.offsets[dof+1]-1 for ind in r k, m = dof_cells.values[ind] yi += xes[k][m] diff --git a/src/FEA/simulate.jl b/src/FEA/simulate.jl index 94441a20..3b4ced3e 100644 --- a/src/FEA/simulate.jl +++ b/src/FEA/simulate.jl @@ -1,13 +1,20 @@ using TimerOutputs @params struct LinearElasticityResult - comp - u + comp::Any + u::Any end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::LinearElasticityResult) = println("TopOpt linear elasticity result") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::LinearElasticityResult) = + println("TopOpt linear elasticity result") -function simulate(problem::StiffnessTopOptProblem, topology = ones(getncells(TopOptProblems.getdh(problem).grid)); round = true, hard = true, xmin = 0.001) - if round +function simulate( + problem::StiffnessTopOptProblem, + topology = ones(getncells(TopOptProblems.getdh(problem).grid)); + round = true, + hard = true, + xmin = 0.001, +) + if round if hard solver = FEASolver(Direct, problem, xmin = 0.0) else @@ -20,11 +27,11 @@ function simulate(problem::StiffnessTopOptProblem, topology = ones(getncells(Top fill_vars!(vars, topology; round = round) solver(Val{true}) comp = dot(solver.u, solver.globalinfo.f) - return LinearElasticityResult(comp, copy(solver.u)) + return LinearElasticityResult(comp, copy(solver.u)) end function fill_vars!(vars::Array, topology; round) - if round + if round vars .= Base.round.(topology) else copyto!(vars, topology) diff --git a/src/FEA/solvers_api.jl b/src/FEA/solvers_api.jl index 8242a34b..2037a2fc 100644 --- a/src/FEA/solvers_api.jl +++ b/src/FEA/solvers_api.jl @@ -3,7 +3,7 @@ abstract type SolverType end abstract type SolverSubtype end struct Direct <: SolverType end -struct CG <: SolverType end +struct CG <: SolverType end struct MatrixFree <: SolverSubtype end struct Assembly <: SolverSubtype end @@ -14,7 +14,7 @@ function Utilities.setpenalty!(solver::AbstractFEASolver, p) if p isa AbstractPenalty solver.penalty = p elseif p isa Number - setpenalty!(solver.penalty, p) + setpenalty!(solver.penalty, p) else throw("Unsupported penalty value $p.") end @@ -39,7 +39,10 @@ function FEASolver(::Type{CG}, ::Type{Assembly}, problem; kwargs...) end function default_quad_order(problem) - if TopOptProblems.getdim(problem) == 2 && TopOptProblems.nnodespercell(problem) in (3, 6) || TopOptProblems.getdim(problem) == 3 && TopOptProblems.nnodespercell(problem) in (4, 10) + if TopOptProblems.getdim(problem) == 2 && + TopOptProblems.nnodespercell(problem) in (3, 6) || + TopOptProblems.getdim(problem) == 3 && + TopOptProblems.nnodespercell(problem) in (4, 10) return 3 end if TopOptProblems.getgeomorder(problem) == 2 diff --git a/src/Functions/Functions.jl b/src/Functions/Functions.jl index 755cce0d..cd9eab04 100644 --- a/src/Functions/Functions.jl +++ b/src/Functions/Functions.jl @@ -12,27 +12,27 @@ using TimerOutputs, Ferrite, StaticArrays, StatsFuns using SparseArrays, Statistics, ChainRulesCore, Zygote using Nonconvex: Nonconvex -export Volume, - Compliance, - Displacement, - MeanCompliance, - BlockCompliance, - AbstractFunction, - getfevals, - getmaxfevals, - maxedfevals, - MicroVonMisesStress, - MacroVonMisesStress, - project, - generate_scenarios, - hutch_rand!, - hadamard!, - TrussStress, - AssembleK, - TrussElementKσ, - ElementK, - apply_boundary_with_zerodiag!, - apply_boundary_with_meandiag! +export Volume, + Compliance, + Displacement, + MeanCompliance, + BlockCompliance, + AbstractFunction, + getfevals, + getmaxfevals, + maxedfevals, + MicroVonMisesStress, + MacroVonMisesStress, + project, + generate_scenarios, + hutch_rand!, + hadamard!, + TrussStress, + AssembleK, + TrussElementKσ, + ElementK, + apply_boundary_with_zerodiag!, + apply_boundary_with_meandiag! const to = TimerOutput() diff --git a/src/Functions/apply_boundary.jl b/src/Functions/apply_boundary.jl index 4f9137ef..39bad0b8 100644 --- a/src/Functions/apply_boundary.jl +++ b/src/Functions/apply_boundary.jl @@ -10,7 +10,7 @@ https://github.com/JuliaTopOpt/TopOpt.jl/wiki/Applying-boundary-conditions-to-th function apply_boundary_with_zerodiag!(Kσ, ch) T = eltype(Kσ) Ferrite.apply!(Kσ, T[], ch, true) - for i in 1:length(ch.values) + for i = 1:length(ch.values) d = ch.prescribed_dofs[i] Kσ[d, d] = zero(T) end @@ -20,7 +20,7 @@ end function ChainRulesCore.rrule(::typeof(apply_boundary_with_zerodiag!), Kσ, ch) project_to = ChainRulesCore.ProjectTo(Kσ) function pullback_fn(Δ) - return NoTangent(), apply_boundary_with_zerodiag!(project_to(Δ), ch) , NoTangent() + return NoTangent(), apply_boundary_with_zerodiag!(project_to(Δ), ch), NoTangent() end return apply_boundary_with_zerodiag!(Kσ, ch), pullback_fn end @@ -48,7 +48,10 @@ Apply boundary condition to a matrix. Zero-out the corresponding [i,:] and [:,j] i, j ∈ ch.prescribed_dofs, then fill in K[i,i] for i ∈ ch.prescribed_dofs with the mean diagonal of the original matrix. """ -function apply_boundary_with_meandiag!(K::Union{SparseMatrixCSC,Symmetric}, ch::ConstraintHandler) +function apply_boundary_with_meandiag!( + K::Union{SparseMatrixCSC,Symmetric}, + ch::ConstraintHandler, +) Ferrite.apply!(K, eltype(K)[], ch, false) return K end @@ -59,13 +62,13 @@ function ChainRulesCore.rrule(::typeof(apply_boundary_with_meandiag!), K, ch) jac_meandiag = sign.(diagK) / length(diagK) function pullback_fn(Δ) Δ_ch_diagsum = zero(eltype(K)) - for i in 1:length(ch.values) + for i = 1:length(ch.values) d = ch.prescribed_dofs[i] Δ_ch_diagsum += Δ[d, d] end ΔK = project_to(Δ) apply_boundary_with_zerodiag!(ΔK, ch) - for i in 1:size(K, 1) + for i = 1:size(K, 1) ΔK[i, i] += Δ_ch_diagsum * jac_meandiag[i] end return NoTangent(), ΔK, NoTangent() @@ -116,4 +119,4 @@ If i == j and !(i in prescribed_dofs) ######################################## -# TODO apply!(K, f, ch) if needed \ No newline at end of file +# TODO apply!(K, f, ch) if needed diff --git a/src/Functions/assemble_K.jl b/src/Functions/assemble_K.jl index 54625dcc..8ac5d60e 100644 --- a/src/Functions/assemble_K.jl +++ b/src/Functions/assemble_K.jl @@ -1,10 +1,11 @@ @params mutable struct AssembleK{T} <: AbstractFunction{T} - problem::StiffnessTopOptProblem + problem::StiffnessTopOptProblem K::AbstractMatrix{T} global_dofs::AbstractVector{<:Integer} # preallocated dof vector for a cell end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::AssembleK) = println("TopOpt global linear stiffness matrix assembly function") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::AssembleK) = + println("TopOpt global linear stiffness matrix assembly function") function AssembleK(problem::StiffnessTopOptProblem) dh = problem.ch.dh @@ -65,19 +66,23 @@ which can be shortened as: dg/dK_e = Delta[global_dofs, global_dofs] """ -function ChainRulesCore.rrule(ak::AssembleK{T}, Kes::AbstractVector{<:AbstractMatrix{T}}) where {T} +function ChainRulesCore.rrule( + ak::AssembleK{T}, + Kes::AbstractVector{<:AbstractMatrix{T}}, +) where {T} @unpack problem, K, global_dofs = ak dh = problem.ch.dh # * forward-pass K = ak(Kes) n_dofs = length(global_dofs) function assembleK_pullback(Δ) - ΔKes = [zeros(T, n_dofs, n_dofs) for _ in 1:getncells(dh.grid)] + ΔKes = [zeros(T, n_dofs, n_dofs) for _ = 1:getncells(dh.grid)] for (ci, _) in enumerate(CellIterator(dh)) celldofs!(global_dofs, dh, ci) ΔKes[ci] = Δ[global_dofs, global_dofs] end - return Tangent{typeof(ak)}(problem = NoTangent(), K = Δ, global_dofs = NoTangent()), ΔKes + return Tangent{typeof(ak)}(problem = NoTangent(), K = Δ, global_dofs = NoTangent()), + ΔKes end return K, assembleK_pullback end diff --git a/src/Functions/block_compliance.jl b/src/Functions/block_compliance.jl index 9d37df8c..7c7e1112 100644 --- a/src/Functions/block_compliance.jl +++ b/src/Functions/block_compliance.jl @@ -1,4 +1,4 @@ -@params mutable struct BlockCompliance{T, TC <: Compliance{T}, TM, TS} <: AbstractFunction{T} +@params mutable struct BlockCompliance{T,TC<:Compliance{T},TM,TS} <: AbstractFunction{T} compliance::TC method::TM F::TS @@ -8,7 +8,7 @@ decay::T end function BlockCompliance( - problem::MultiLoad, + problem::MultiLoad, solver::AbstractDisplacementSolver; method = :exact, sample_once = true, @@ -16,7 +16,7 @@ function BlockCompliance( V = nothing, sample_method = :hutch, decay = 1.0, - kwargs... + kwargs..., ) comp = Compliance(problem.problem, solver; kwargs...) if method == :exact @@ -29,10 +29,22 @@ function BlockCompliance( end if V === nothing nv = nv === nothing ? 1 : nv - method = DiagonalEstimation(problem.F, nv, length(comp.grad), sample_once, sample_method) + method = DiagonalEstimation( + problem.F, + nv, + length(comp.grad), + sample_once, + sample_method, + ) else nv = nv === nothing ? size(V, 2) : nv - method = DiagonalEstimation(problem.F, view(V, :, 1:nv), length(comp.grad), sample_once, sample_method) + method = DiagonalEstimation( + problem.F, + view(V, :, 1:nv), + length(comp.grad), + sample_once, + sample_method, + ) end end val = similar(comp.grad, size(problem.F, 2)) @@ -95,10 +107,10 @@ function compute_jtvp!_bc(out, bc, method::ExactDiagonal, w) @unpack Y, temp = method @unpack solver = bc.compliance out .= 0 - for i in 1:size(Y, 2) + for i = 1:size(Y, 2) temp .= 0 if w[i] != 0 - @views compute_inner(temp, Y[:,i], Y[:,i], solver) + @views compute_inner(temp, Y[:, i], Y[:, i], solver) out .+= w[i] .* temp end end @@ -113,8 +125,8 @@ function compute_exact_svd_bc(bc, F, US, V, Q, Y) @unpack solver = compliance raw_val .= 0 solver(assemble_f = false, rhs = US, lhs = Q) - for i in 1:length(raw_val) - raw_val[i] = (F[:,i]' * Q) * V[i,:] + for i = 1:length(raw_val) + raw_val[i] = (F[:, i]' * Q) * V[i, :] end return raw_val end @@ -124,14 +136,14 @@ function compute_jtvp!_bc(out, bc, method::ExactSVDDiagonal, w) X = V' * Diagonal(w) * V ns = size(US, 2) out .= 0 - for j in 1:ns, i in j:ns - if X[i,j] != 0 + for j = 1:ns, i = j:ns + if X[i, j] != 0 temp .= 0 - @views compute_inner(temp, Q[:,i], Q[:,j], solver) + @views compute_inner(temp, Q[:, i], Q[:, j], solver) if i != j - out .+= 2 * X[i,j] .* temp + out .+= 2 * X[i, j] .* temp else - out .+= X[i,j] .* temp + out .+= X[i, j] .* temp end end end @@ -147,13 +159,13 @@ function compute_approx_bc(bc, F, V, Y) nv = size(V, 2) raw_val .= 0 bc.method.sample_once || bc.method.sample_method(V) - for i in 1:nv - @views mul!(solver.rhs, F, V[:,i]) + for i = 1:nv + @views mul!(solver.rhs, F, V[:, i]) solver(assemble_f = false, reuse_chol = (i > 1)) invKFv = solver.lhs - Y[:,i] .= invKFv + Y[:, i] .= invKFv temp = F' * invKFv - @views raw_val .+= V[:,i] .* temp + @views raw_val .+= V[:, i] .* temp end raw_val ./= nv return raw_val @@ -163,14 +175,14 @@ function compute_jtvp!_bc(out, bc, method::DiagonalEstimation, w) @unpack F, V, Y, Q, temp = method nv = size(V, 2) out .= 0 - for i in 1:nv + for i = 1:nv temp .= 0 #q_i = K^-1 F (w .* v_i) - @views mul!(solver.rhs, F, w .* V[:,i]) + @views mul!(solver.rhs, F, w .* V[:, i]) solver(assemble_f = false, reuse_chol = (i > 1)) - Q[:,i] = solver.lhs + Q[:, i] = solver.lhs # - @views compute_inner(temp, Q[:,i], Y[:,i], solver) + @views compute_inner(temp, Q[:, i], Y[:, i], solver) out .+= temp end out ./= nv diff --git a/src/Functions/compliance.jl b/src/Functions/compliance.jl index 0c635dd2..cd030146 100644 --- a/src/Functions/compliance.jl +++ b/src/Functions/compliance.jl @@ -1,5 +1,5 @@ @params mutable struct Compliance{T} <: AbstractFunction{T} - problem::StiffnessTopOptProblem + problem::StiffnessTopOptProblem solver::AbstractDisplacementSolver comp::T cell_comp::AbstractVector @@ -20,20 +20,32 @@ function Compliance(problem, solver::AbstractDisplacementSolver, args...; kwargs end function Compliance( ::CPU, - problem::StiffnessTopOptProblem{dim, T}, + problem::StiffnessTopOptProblem{dim,T}, solver::AbstractDisplacementSolver, ::Type{TI} = Int; tracing = false, logarithm = false, maxfevals = 10^8, -) where {dim, T, TI} +) where {dim,T,TI} comp = T(0) cell_comp = zeros(T, getncells(problem.ch.dh.grid)) grad = fill(T(NaN), length(cell_comp) - sum(problem.black) - sum(problem.white)) topopt_trace = TopOptTrace{T,TI}() reuse = false fevals = TI(0) - return Compliance(problem, solver, comp, cell_comp, grad, tracing, topopt_trace, reuse, fevals, logarithm, maxfevals) + return Compliance( + problem, + solver, + comp, + cell_comp, + grad, + tracing, + topopt_trace, + reuse, + fevals, + logarithm, + maxfevals, + ) end function (o::Compliance{T})(x, grad = o.grad) where {T} @@ -58,8 +70,19 @@ function (o::Compliance{T})(x, grad = o.grad) where {T} solver.vars .= x solver() end - obj = compute_compliance(cell_comp, grad, cell_dofs, Kes, u, - black, white, varind, solver.vars, penalty, xmin) + obj = compute_compliance( + cell_comp, + grad, + cell_dofs, + Kes, + u, + black, + white, + varind, + solver.vars, + penalty, + xmin, + ) if o.logarithm o.comp = log(obj) @@ -83,8 +106,14 @@ function (o::Compliance{T})(x, grad = o.grad) where {T} push!(topopt_trace.add_hist, 0) push!(topopt_trace.rem_hist, 0) else - push!(topopt_trace.add_hist, sum(topopt_trace.x_hist[end] .> topopt_trace.x_hist[end-1])) - push!(topopt_trace.rem_hist, sum(topopt_trace.x_hist[end] .< topopt_trace.x_hist[end-1])) + push!( + topopt_trace.add_hist, + sum(topopt_trace.x_hist[end] .> topopt_trace.x_hist[end-1]), + ) + push!( + topopt_trace.rem_hist, + sum(topopt_trace.x_hist[end] .< topopt_trace.x_hist[end-1]), + ) end end end @@ -105,16 +134,27 @@ d(cell compliance)/d(x_e) = f_e^T * d(u_e)/d(x_e) = f_e^T * (- K_e^-1 * d(K_e)/d = - u_e^T * d(ρ_e)/d(x_e) * K_e * u_e = - d(ρ_e)/d(x_e) * cell_compliance """ -function compute_compliance(cell_comp::Vector{T}, grad, cell_dofs, Kes, u, - black, white, varind, x, penalty, xmin) where {T} +function compute_compliance( + cell_comp::Vector{T}, + grad, + cell_dofs, + Kes, + u, + black, + white, + varind, + x, + penalty, + xmin, +) where {T} obj = zero(T) grad .= 0 - @inbounds for i in 1:size(cell_dofs, 2) + @inbounds for i = 1:size(cell_dofs, 2) cell_comp[i] = zero(T) Ke = rawmatrix(Kes[i]) - for w in 1:size(Ke,2) - for v in 1:size(Ke, 1) - cell_comp[i] += u[cell_dofs[v,i]]*Ke[v,w]*u[cell_dofs[w,i]] + for w = 1:size(Ke, 2) + for v = 1:size(Ke, 1) + cell_comp[i] += u[cell_dofs[v, i]] * Ke[v, w] * u[cell_dofs[w, i]] end end @@ -122,18 +162,18 @@ function compute_compliance(cell_comp::Vector{T}, grad, cell_dofs, Kes, u, obj += cell_comp[i] elseif white[i] if PENALTY_BEFORE_INTERPOLATION - obj += xmin * cell_comp[i] + obj += xmin * cell_comp[i] else p = penalty(xmin) * cell_comp[i] end else ρe, dρe = get_ρ_dρ(x[varind[i]], penalty, xmin) - grad[varind[i]] = - dρe * cell_comp[i] + grad[varind[i]] = -dρe * cell_comp[i] obj += ρe * cell_comp[i] end end - return obj + return obj end function compute_inner(inner, u1, u2, solver) @@ -141,24 +181,46 @@ function compute_inner(inner, u1, u2, solver) @unpack metadata, Kes, black, white, varind = elementinfo @unpack cell_dofs = metadata penalty = getpenalty(solver) - return compute_inner(inner, u1, u2, cell_dofs, Kes, - black, white, varind, solver.vars, penalty, xmin) + return compute_inner( + inner, + u1, + u2, + cell_dofs, + Kes, + black, + white, + varind, + solver.vars, + penalty, + xmin, + ) end -function compute_inner(inner::AbstractVector{T}, u1, u2, cell_dofs, Kes, - black, white, varind, x, penalty, xmin) where {T} +function compute_inner( + inner::AbstractVector{T}, + u1, + u2, + cell_dofs, + Kes, + black, + white, + varind, + x, + penalty, + xmin, +) where {T} obj = zero(T) - @inbounds for i in 1:size(cell_dofs, 2) + @inbounds for i = 1:size(cell_dofs, 2) inner[i] = zero(T) cell_comp = zero(T) Ke = rawmatrix(Kes[i]) if !black[i] && !white[i] - for w in 1:size(Ke,2) - for v in 1:size(Ke, 1) - cell_comp += u1[cell_dofs[v,i]]*Ke[v,w]*u2[cell_dofs[w,i]] + for w = 1:size(Ke, 2) + for v = 1:size(Ke, 1) + cell_comp += u1[cell_dofs[v, i]] * Ke[v, w] * u2[cell_dofs[w, i]] end end ρe, dρe = get_ρ_dρ(x[varind[i]], penalty, xmin) - inner[varind[i]] = - dρe * cell_comp + inner[varind[i]] = -dρe * cell_comp end end diff --git a/src/Functions/displacement.jl b/src/Functions/displacement.jl index 81017092..4c6b5ffb 100644 --- a/src/Functions/displacement.jl +++ b/src/Functions/displacement.jl @@ -7,7 +7,8 @@ maxfevals::Int end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::Displacement) = println("TopOpt displacement function") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::Displacement) = + println("TopOpt displacement function") """ Displacement() @@ -65,7 +66,7 @@ function ChainRulesCore.rrule(dp::Displacement, x) solver.rhs .= Δ solver(reuse_chol = true, assemble_f = false) dudx_tmp .= 0 - for e in 1:length(x) + for e = 1:length(x) _, dρe = get_ρ_dρ(x[e], penalty, xmin) celldofs!(global_dofs, dh, e) Keu = bcmatrix(Kes[e]) * u[global_dofs] diff --git a/src/Functions/element_k.jl b/src/Functions/element_k.jl index b395b824..846906de 100644 --- a/src/Functions/element_k.jl +++ b/src/Functions/element_k.jl @@ -6,7 +6,8 @@ xmin::T end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::ElementK) = println("TopOpt element stiffness matrix construction function") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::ElementK) = + println("TopOpt element stiffness matrix construction function") function ElementK(solver::AbstractDisplacementSolver) @unpack elementinfo = solver @@ -46,7 +47,7 @@ end function (ek::ElementK{T})(x::AbstractVector{T}) where {T} @unpack solver, Kes = ek @assert getncells(solver.problem.ch.dh.grid) == length(x) - for ci in 1:length(x) + for ci = 1:length(x) Kes[ci] = ek(x[ci], ci) end return copy(Kes) @@ -67,13 +68,19 @@ function ChainRulesCore.rrule(ek::ElementK, x) """ function pullback_fn(Δ) Δx = similar(x) - for ci in 1:length(x) + for ci = 1:length(x) ek_cell_fn = xe -> vec(ek(xe, ci)) jac_cell = ForwardDiff.derivative(ek_cell_fn, x[ci]) Δx[ci] = jac_cell' * vec(Δ[ci]) end - return Tangent{typeof(ek)}(solver=NoTangent(), Kes=Δ, Kes_0=NoTangent(), penalty=NoTangent(), xmin=NoTangent()), Δx + return Tangent{typeof(ek)}( + solver = NoTangent(), + Kes = Δ, + Kes_0 = NoTangent(), + penalty = NoTangent(), + xmin = NoTangent(), + ), + Δx end return Kes, pullback_fn end - diff --git a/src/Functions/element_ksigma.jl b/src/Functions/element_ksigma.jl index ec986859..1490de88 100644 --- a/src/Functions/element_ksigma.jl +++ b/src/Functions/element_ksigma.jl @@ -10,9 +10,13 @@ using TopOpt.TrussTopOptProblems: truss_reinit! global_dofs::AbstractVector{<:Integer} end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::TrussElementKσ) = println("TopOpt element stress stiffness matrix (Kσ_e) construction function") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::TrussElementKσ) = + println("TopOpt element stress stiffness matrix (Kσ_e) construction function") -function TrussElementKσ(problem::TrussProblem{xdim, T}, solver::AbstractFEASolver) where {xdim, T} +function TrussElementKσ( + problem::TrussProblem{xdim,T}, + solver::AbstractFEASolver, +) where {xdim,T} Es = getE(problem) As = getA(problem) dh = problem.ch.dh @@ -21,7 +25,7 @@ function TrussElementKσ(problem::TrussProblem{xdim, T}, solver::AbstractFEASolv cellvalues = solver.elementinfo.cellvalues ndof_pc = ndofs_per_cell(dh) n_basefuncs = getnbasefunctions(cellvalues) - @assert ndof_pc == xdim*n_basefuncs "$ndof_pc, $n_basefuncs" + @assert ndof_pc == xdim * n_basefuncs "$ndof_pc, $n_basefuncs" @assert n_basefuncs == 2 global_dofs = zeros(Int, ndof_pc) @@ -39,12 +43,12 @@ function TrussElementKσ(problem::TrussProblem{xdim, T}, solver::AbstractFEASolv L = norm(cell.coords[1] - cell.coords[2]) R = compute_local_axes(cell.coords[1], cell.coords[2]) # local axial projection operator (local axis transformation) - γ = vcat(-R[:,1], R[:,1]) - push!(EALγ_s, (E*A/L)*γ) + γ = vcat(-R[:, 1], R[:, 1]) + push!(EALγ_s, (E * A / L) * γ) fill!(δmat, 0.0) - for i=2:size(R,2) - δ = vcat(-R[:,i], R[:,i]) + for i = 2:size(R, 2) + δ = vcat(-R[:, i], R[:, i]) # @assert δ' * γ ≈ 0 δmat .+= δ * δ' end @@ -75,7 +79,7 @@ see: https://people.duke.edu/~hpgavin/cee421/truss-finite-def.pdf function (eksig::TrussElementKσ)(u_e::AbstractVector, x_e::Number, ci::Integer) @unpack EALγ_s, δmat_s, L_s = eksig # x_e scales the cross section - q_cell = x_e*EALγ_s[ci]'*u_e + q_cell = x_e * EALγ_s[ci]' * u_e return q_cell / L_s[ci] * δmat_s[ci] end @@ -84,7 +88,7 @@ function (eksig::TrussElementKσ{T})(u::AbstractVector{T}, x::AbstractVector{T}) dh = problem.ch.dh @assert getncells(dh.grid) == length(x) @assert ndofs(dh) == length(u) - for ci in 1:length(x) + for ci = 1:length(x) celldofs!(global_dofs, dh, ci) Kσes[ci] = eksig(u[global_dofs], x[ci], ci) end @@ -98,7 +102,7 @@ function ChainRulesCore.rrule(eksig::TrussElementKσ{T}, u, x) where {T} function pullback_fn(Δ) Δu = zeros(T, size(u)) Δx = zeros(T, size(x)) - for ci in 1:length(x) + for ci = 1:length(x) celldofs!(global_dofs, dh, ci) function vec_eksig_fn(ux_vec) u_e = ux_vec[1:end-1] @@ -110,12 +114,20 @@ function ChainRulesCore.rrule(eksig::TrussElementKσ{T}, u, x) where {T} Δu[global_dofs] += jtv[1:end-1] Δx[ci] = jtv[end] end - return Tangent{typeof(eksig)}(problem=NoTangent(), Kσes=Δ, EALγ_s=NoTangent(), δmat_s=NoTangent(), - L_s=NoTangent(), global_dofs=NoTangent()), Δu, Δx + return Tangent{typeof(eksig)}( + problem = NoTangent(), + Kσes = Δ, + EALγ_s = NoTangent(), + δmat_s = NoTangent(), + L_s = NoTangent(), + global_dofs = NoTangent(), + ), + Δu, + Δx end return Kσes, pullback_fn end ##################################### -# TODO ElementKσ for volumetic cells \ No newline at end of file +# TODO ElementKσ for volumetic cells diff --git a/src/Functions/function_utils.jl b/src/Functions/function_utils.jl index 265e5e78..3bc7ceb5 100644 --- a/src/Functions/function_utils.jl +++ b/src/Functions/function_utils.jl @@ -1,4 +1,4 @@ -function get_ρ(x_e::T, penalty, xmin) where T +function get_ρ(x_e::T, penalty, xmin) where {T} if PENALTY_BEFORE_INTERPOLATION return density(penalty(x_e), xmin) else @@ -6,7 +6,7 @@ function get_ρ(x_e::T, penalty, xmin) where T end end -function get_ρ_dρ(x_e::T, penalty, xmin) where T +function get_ρ_dρ(x_e::T, penalty, xmin) where {T} d = ForwardDiff.Dual{T}(x_e, one(T)) if PENALTY_BEFORE_INTERPOLATION p = density(penalty(d), xmin) @@ -16,4 +16,3 @@ function get_ρ_dρ(x_e::T, penalty, xmin) where T g = p.partials[1] return p.value, g end - diff --git a/src/Functions/gpu_support.jl b/src/Functions/gpu_support.jl index f4c62303..50fa9f24 100644 --- a/src/Functions/gpu_support.jl +++ b/src/Functions/gpu_support.jl @@ -12,19 +12,51 @@ whichdevice(c::MeanCompliance) = whichdevice(c.compliance) hutch_rand!(v::CuArray) = v .= round.(CuArrays.CURAND.curand(size(v))) .* 2 .- 1 -function Compliance(::GPU, problem::StiffnessTopOptProblem{dim, T}, solver::AbstractDisplacementSolver, ::Type{TI}=Int; tracing = false, logarithm = false, maxfevals = 10^8) where {dim, T, TI} +function Compliance( + ::GPU, + problem::StiffnessTopOptProblem{dim,T}, + solver::AbstractDisplacementSolver, + ::Type{TI} = Int; + tracing = false, + logarithm = false, + maxfevals = 10^8, +) where {dim,T,TI} comp = T(0) cell_comp = zeros(CuVector{T}, getncells(problem.ch.dh.grid)) - grad = CuVector(fill(T(NaN), length(cell_comp) - sum(problem.black) - sum(problem.white))) + grad = + CuVector(fill(T(NaN), length(cell_comp) - sum(problem.black) - sum(problem.white))) topopt_trace = TopOptTrace{T,TI}() reuse = false fevals = TI(0) - return Compliance(problem, solver, comp, cell_comp, grad, tracing, topopt_trace, reuse, fevals, logarithm, maxfevals) + return Compliance( + problem, + solver, + comp, + cell_comp, + grad, + tracing, + topopt_trace, + reuse, + fevals, + logarithm, + maxfevals, + ) end @define_cu(Compliance, :solver, :cell_comp, :grad, :cheqfilter) -function compute_compliance(cell_comp::CuVector{T}, grad, cell_dofs, Kes, u, - black, white, varind, x, penalty, xmin) where {T} +function compute_compliance( + cell_comp::CuVector{T}, + grad, + cell_dofs, + Kes, + u, + black, + white, + varind, + x, + penalty, + xmin, +) where {T} args = (cell_comp, grad, cell_dofs, Kes, u, black, white, varind, x, penalty, xmin) callkernel(dev, comp_kernel1, args) @@ -35,20 +67,31 @@ function compute_compliance(cell_comp::CuVector{T}, grad, cell_dofs, Kes, u, end # CUDA kernels -function comp_kernel1(cell_comp::AbstractVector{T}, grad, cell_dofs, Kes, u, - black, white, varind, x, penalty, xmin) where {T} +function comp_kernel1( + cell_comp::AbstractVector{T}, + grad, + cell_dofs, + Kes, + u, + black, + white, + varind, + x, + penalty, + xmin, +) where {T} i = @thread_global_index() offset = @total_threads() @inbounds while i <= length(cell_comp) cell_comp[i] = zero(T) Ke = rawmatrix(Kes[i]) - for w in 1:size(Ke, 2) - for v in 1:size(Ke, 1) + for w = 1:size(Ke, 2) + for v = 1:size(Ke, 1) if Ke isa Symmetric - cell_comp[i] += u[cell_dofs[v,i]]*Ke.data[v,w]*u[cell_dofs[w,i]] + cell_comp[i] += u[cell_dofs[v, i]] * Ke.data[v, w] * u[cell_dofs[w, i]] else - cell_comp[i] += u[cell_dofs[v,i]]*Ke[v,w]*u[cell_dofs[w,i]] + cell_comp[i] += u[cell_dofs[v, i]] * Ke[v, w] * u[cell_dofs[w, i]] end end end @@ -67,7 +110,17 @@ function comp_kernel1(cell_comp::AbstractVector{T}, grad, cell_dofs, Kes, u, return end -function compute_obj(cell_comp::AbstractVector{T}, x, varind, black, white, penalty, xmin, ::Val{blocksize} = Val(80), ::Val{threads} = Val(256)) where {T, blocksize, threads} +function compute_obj( + cell_comp::AbstractVector{T}, + x, + varind, + black, + white, + penalty, + xmin, + ::Val{blocksize} = Val(80), + ::Val{threads} = Val(256), +) where {T,blocksize,threads} result = similar(cell_comp, T, (blocksize,)) args = (result, cell_comp, x, varind, black, white, penalty, xmin, Val(threads)) @cuda blocks = blocksize threads = threads comp_kernel2(args...) @@ -76,24 +129,48 @@ function compute_obj(cell_comp::AbstractVector{T}, x, varind, black, white, pena return obj end -function comp_kernel2(result, cell_comp::AbstractVector{T}, x, varind, black, white, penalty, xmin, ::Val{LMEM}) where {T, LMEM} - @mapreduce_block(i, length(cell_comp), +, T, LMEM, result, begin - w_comp(cell_comp[i], x[varind[i]], black[i], white[i], penalty, xmin) - end) +function comp_kernel2( + result, + cell_comp::AbstractVector{T}, + x, + varind, + black, + white, + penalty, + xmin, + ::Val{LMEM}, +) where {T,LMEM} + @mapreduce_block( + i, + length(cell_comp), + +, + T, + LMEM, + result, + begin + w_comp(cell_comp[i], x[varind[i]], black[i], white[i], penalty, xmin) + end + ) return end @inline function w_comp(comp::T, x, black, white, penalty, xmin) where {T} if PENALTY_BEFORE_INTERPOLATION - return ifelse(black, comp, - ifelse(white, xmin * comp, - (d = ForwardDiff.Dual{T}(x, one(T)); - p = density(penalty(d), xmin); p.value * comp))) + return ifelse( + black, + comp, + ifelse(white, xmin * comp, (d = ForwardDiff.Dual{T}(x, one(T)); + p = density(penalty(d), xmin); + p.value * comp)), + ) else - return ifelse(black, comp, - ifelse(white, xmin * comp, - (d = ForwardDiff.Dual{T}(x, one(T)); - p = penalty(density(d, xmin)); p.value * comp))) + return ifelse( + black, + comp, + ifelse(white, xmin * comp, (d = ForwardDiff.Dual{T}(x, one(T)); + p = penalty(density(d, xmin)); + p.value * comp)), + ) end end @@ -103,7 +180,16 @@ end whichdevice(v::Volume) = whichdevice(v.cellvolumes) @define_cu(Volume, :cellvolumes, :grad, :problem) # should be optimized to avoid replicating problem -function compute_volume(cellvolumes::CuVector{T}, x, fixed_volume, varind, black, white, ::Val{blocksize} = Val(80), ::Val{threads} = Val(256)) where {T, blocksize, threads} +function compute_volume( + cellvolumes::CuVector{T}, + x, + fixed_volume, + varind, + black, + white, + ::Val{blocksize} = Val(80), + ::Val{threads} = Val(256), +) where {T,blocksize,threads} result = similar(cellvolumes, T, (blocksize,)) args = (result, cellvolumes, x, varind, black, white, Val(threads)) @cuda blocks = blocksize threads = threads volume_kernel(args...) @@ -112,14 +198,30 @@ function compute_volume(cellvolumes::CuVector{T}, x, fixed_volume, varind, black return vol end -function volume_kernel(result, cellvolumes::AbstractVector{T}, x, varind, black, white, ::Val{LMEM}) where {T, LMEM} - @mapreduce_block(i, length(cellvolumes), +, T, LMEM, result, begin - if !(black[i]) && !(white[i]) - x[varind[i]]*cellvolumes[i] - else - zero(T) +function volume_kernel( + result, + cellvolumes::AbstractVector{T}, + x, + varind, + black, + white, + ::Val{LMEM}, +) where {T,LMEM} + @mapreduce_block( + i, + length(cellvolumes), + +, + T, + LMEM, + result, + begin + if !(black[i]) && !(white[i]) + x[varind[i]] * cellvolumes[i] + else + zero(T) + end end - end) + ) return end diff --git a/src/Functions/mean_compliance.jl b/src/Functions/mean_compliance.jl index e7c2c55e..1bd826bf 100644 --- a/src/Functions/mean_compliance.jl +++ b/src/Functions/mean_compliance.jl @@ -1,10 +1,20 @@ -@params mutable struct MeanCompliance{T, TC <: Compliance{T}, TM, TS} <: AbstractFunction{T} +@params mutable struct MeanCompliance{T,TC<:Compliance{T},TM,TS} <: AbstractFunction{T} compliance::TC method::TM F::TS grad_temp::AbstractVector{T} end -function MeanCompliance(problem::MultiLoad, solver::AbstractDisplacementSolver, args...; method = :exact_svd, sample_once = true, nv = nothing, V = nothing, sample_method = :hutch, kwargs...) +function MeanCompliance( + problem::MultiLoad, + solver::AbstractDisplacementSolver, + args...; + method = :exact_svd, + sample_once = true, + nv = nothing, + V = nothing, + sample_method = :hutch, + kwargs..., +) if method == :exact method = ExactMean(problem.F) elseif method == :exact_svd @@ -18,7 +28,8 @@ function MeanCompliance(problem::MultiLoad, solver::AbstractDisplacementSolver, method = TraceEstimationMean(problem.F, nv, sample_once, sample_method) else nv = nv === nothing ? size(V, 2) : nv - method = TraceEstimationMean(problem.F, view(V, :, 1:nv), sample_once, sample_method) + method = + TraceEstimationMean(problem.F, view(V, :, 1:nv), sample_once, sample_method) end else if sample_method isa Symbol @@ -29,7 +40,12 @@ function MeanCompliance(problem::MultiLoad, solver::AbstractDisplacementSolver, method = TraceEstimationSVDMean(problem.F, nv, sample_once, sample_method) else nv = nv === nothing ? size(V, 2) : nv - method = TraceEstimationSVDMean(problem.F, view(V, :, 1:nv), sample_once, sample_method) + method = TraceEstimationSVDMean( + problem.F, + view(V, :, 1:nv), + sample_once, + sample_method, + ) end end comp = Compliance(whichdevice(solver), problem.problem, solver, args...; kwargs...) @@ -49,7 +65,7 @@ function (ec::MeanCompliance{T})(x, grad = ec.grad) where {T} # return compliance.comp #end - penalty = getpenalty(ec) + penalty = getpenalty(ec) copyto!(solver.vars, x) compliance.fevals += 1 setpenalty!(solver, penalty.p) @@ -64,7 +80,7 @@ function (ec::MeanCompliance{T})(x, grad = ec.grad) where {T} if compliance.grad !== grad copyto!(compliance.grad, grad) end - + if compliance.tracing if ec.reuse ec.reuse = false @@ -75,8 +91,14 @@ function (ec::MeanCompliance{T})(x, grad = ec.grad) where {T} push!(topopt_trace.add_hist, 0) push!(topopt_trace.rem_hist, 0) else - push!(topopt_trace.add_hist, sum(topopt_trace.x_hist[end] .> topopt_trace.x_hist[end-1])) - push!(topopt_trace.rem_hist, sum(topopt_trace.x_hist[end] .< topopt_trace.x_hist[end-1])) + push!( + topopt_trace.add_hist, + sum(topopt_trace.x_hist[end] .> topopt_trace.x_hist[end-1]), + ) + push!( + topopt_trace.rem_hist, + sum(topopt_trace.x_hist[end] .< topopt_trace.x_hist[end-1]), + ) end end end @@ -93,16 +115,27 @@ function compute_exact_ec(ec, x, grad, F, n) @unpack elementinfo, u, xmin = solver @unpack metadata, Kes, black, white, varind = elementinfo @unpack cell_dofs = metadata - penalty = getpenalty(compliance) + penalty = getpenalty(compliance) T = eltype(grad) obj = zero(T) grad .= 0 - for i in 1:size(F, 2) - @views solver.rhs .= F[:,i] + for i = 1:size(F, 2) + @views solver.rhs .= F[:, i] solver(assemble_f = false, reuse_chol = (i > 1)) u = solver.lhs - obj += compute_compliance(cell_comp, grad_temp, cell_dofs, Kes, u, - black, white, varind, x, penalty, xmin) + obj += compute_compliance( + cell_comp, + grad_temp, + cell_dofs, + Kes, + u, + black, + white, + varind, + x, + penalty, + xmin, + ) grad .+= grad_temp end obj /= n @@ -125,12 +158,23 @@ function compute_approx_ec(ec, x, grad, F, V, n) obj = zero(T) grad .= 0 ec.method.sample_once || ec.method.sample_method(V) - for i in 1:nv - @views mul!(solver.rhs, F, V[:,i]) + for i = 1:nv + @views mul!(solver.rhs, F, V[:, i]) solver(assemble_f = false, reuse_chol = (i > 1)) invKFv = solver.lhs - obj += compute_compliance(cell_comp, grad_temp, cell_dofs, Kes, invKFv, - black, white, varind, x, penalty, xmin) + obj += compute_compliance( + cell_comp, + grad_temp, + cell_dofs, + Kes, + invKFv, + black, + white, + varind, + x, + penalty, + xmin, + ) grad .+= grad_temp end obj /= nv * n @@ -151,51 +195,58 @@ Utilities.getpenalty(c::MeanCompliance) = c.compliance |> getsolver |> getpenalt hutch_rand!(x::Array) = x .= rand.(Ref(-1.0:2.0:1.0)) function hadamard3!(V) - n, nv = size(V) - H = ones(Int, 1, 1) - while size(H, 1) < nv - H = [H H ; H -H] - end - H = H[:, 1:nv] - while size(H, 1) < n - n1 = nv ÷ 2 - H1 = H[:, 1:n1] - H2 = H[:, (n1+1):nv] - H = [H1 H2; H1 -H2] - end - V .= H[1:n, :] - return V + n, nv = size(V) + H = ones(Int, 1, 1) + while size(H, 1) < nv + H = [H H; H -H] + end + H = H[:, 1:nv] + while size(H, 1) < n + n1 = nv ÷ 2 + H1 = H[:, 1:n1] + H2 = H[:, (n1+1):nv] + H = [H1 H2; H1 -H2] + end + V .= H[1:n, :] + return V end function hadamard2!(V) - n, nv = size(V) - H = ones(Int, 1, 1) - while size(H, 1) < nv - H = [H H ; H -H] - end - H = H[:, 1:nv] - while size(H, 1) < n - H = [H; -H] - end - V .= H[1:n, :] - return V + n, nv = size(V) + H = ones(Int, 1, 1) + while size(H, 1) < nv + H = [H H; H -H] + end + H = H[:, 1:nv] + while size(H, 1) < n + H = [H; -H] + end + V .= H[1:n, :] + return V end function hadamard!(V) - n, nv = size(V) - H = ones(Int, 1, 1) - while size(H, 1) < nv - H = [H H ; H -H] - end - H = H[:, 1:nv] - H = repeat(H, ceil(Int, n/nv), 1) - V .= H[1:n, :] - return V + n, nv = size(V) + H = ones(Int, 1, 1) + while size(H, 1) < nv + H = [H H; H -H] + end + H = H[:, 1:nv] + H = repeat(H, ceil(Int, n / nv), 1) + V .= H[1:n, :] + return V end -function generate_scenarios(dof::Int, size::Tuple{Int, Int}, f, perturb = ()->(rand()-0.5)) +function generate_scenarios( + dof::Int, + size::Tuple{Int,Int}, + f, + perturb = () -> (rand() - 0.5), +) ndofs, nscenarios = size - I = Int[]; J = Int[]; V = Float64[]; - V = [f * (1 + perturb()) for s in 1:nscenarios] - I = [dof for s in 1:nscenarios] + I = Int[] + J = Int[] + V = Float64[] + V = [f * (1 + perturb()) for s = 1:nscenarios] + I = [dof for s = 1:nscenarios] J = 1:nscenarios return sparse(I, J, V, ndofs, nscenarios) end diff --git a/src/Functions/stress.jl b/src/Functions/stress.jl index 5b1a0845..a2587047 100644 --- a/src/Functions/stress.jl +++ b/src/Functions/stress.jl @@ -13,10 +13,10 @@ function StressTemp(solver) T = eltype(u) dim = TopOptProblems.getdim(problem) k = dim == 2 ? 3 : 6 - VTu = zero(MVector{k, T}) + VTu = zero(MVector{k,T}) Tu = similar(VTu) n_basefuncs = getnbasefunctions(solver.elementinfo.cellvalues) - Te = zero(MMatrix{k, dim*n_basefuncs, T}) + Te = zero(MMatrix{k,dim * n_basefuncs,T}) global_dofs = zeros(Int, ndofs_per_cell(dh)) return StressTemp(VTu, Tu, Te, global_dofs) @@ -27,7 +27,7 @@ Zygote.@nograd StressTemp utMu::AbstractVector{T} Mu::AbstractArray sigma_vm::AbstractVector{T} - solver + solver::Any global_dofs::AbstractVector{<:Integer} stress_temp::StressTemp fevals::Int @@ -40,12 +40,22 @@ function MacroVonMisesStress(solver; reuse = false, maxfevals = 10^8) k = ndofs_per_cell(dh) N = getncells(dh.grid) global_dofs = zeros(Int, k) - Mu = zeros(SVector{k, T}, N) + Mu = zeros(SVector{k,T}, N) utMu = zeros(T, N) stress_temp = StressTemp(solver) sigma_vm = similar(utMu) - return MacroVonMisesStress(utMu, Mu, sigma_vm, solver, global_dofs, stress_temp, 0, reuse, maxfevals) + return MacroVonMisesStress( + utMu, + Mu, + sigma_vm, + solver, + global_dofs, + stress_temp, + 0, + reuse, + maxfevals, + ) end function (ls::MacroVonMisesStress{T})(x) where {T} @unpack sigma_vm, Mu, utMu, stress_temp = ls @@ -73,20 +83,25 @@ function ChainRulesCore.rrule(vonmises::MacroVonMisesStress, x) vonmises.fevals += 1 sigma_vm = vonmises(x) - return sigma_vm, Δ -> (nothing, begin - for e in 1:length(Mu) - ρe = get_ρ(x[e], penalty, xmin) - Mu[e] *= Δ[e] * ρe^2 / sigma_vm[e] - end - lhs = backsolve!(solver, Mu, global_dofs) - map(1:length(Δ)) do e - ρe, dρe = get_ρ_dρ(x[e], penalty, xmin) - celldofs!(global_dofs, dh, e) - t1 = Δ[e] * sigma_vm[e] / ρe * dρe - @views t2 = -dot(lhs[global_dofs], bcmatrix(Kes[e]) * u[global_dofs]) * dρe - return t1 + t2 - end - end) + return sigma_vm, + Δ -> ( + nothing, + begin + for e = 1:length(Mu) + ρe = get_ρ(x[e], penalty, xmin) + Mu[e] *= Δ[e] * ρe^2 / sigma_vm[e] + end + lhs = backsolve!(solver, Mu, global_dofs) + map(1:length(Δ)) do e + ρe, dρe = get_ρ_dρ(x[e], penalty, xmin) + celldofs!(global_dofs, dh, e) + t1 = Δ[e] * sigma_vm[e] / ρe * dρe + @views t2 = + -dot(lhs[global_dofs], bcmatrix(Kes[e]) * u[global_dofs]) * dρe + return t1 + t2 + end + end, + ) end #getdim(f::MacroVonMisesStress) = length(f.sigma_vm) @@ -95,7 +110,8 @@ end @params struct MicroVonMisesStress{T} <: AbstractFunction{T} vonmises::MacroVonMisesStress{T} end -MicroVonMisesStress(args...; kwargs...) = MicroVonMisesStress(MacroVonMisesStress(args...; kwargs...)) +MicroVonMisesStress(args...; kwargs...) = + MicroVonMisesStress(MacroVonMisesStress(args...; kwargs...)) function (f::MicroVonMisesStress)(x) @unpack vonmises = f @unpack sigma_vm, Mu, utMu, stress_temp = vonmises @@ -123,18 +139,22 @@ function ChainRulesCore.rrule(f::MicroVonMisesStress, x) vonmises.fevals += 1 out = f(x) - return out, Δ -> (nothing, begin - for e in 1:length(Mu) - ρe = get_ρ(x[e], penalty, xmin) - Mu[e] *= Δ[e] * ρe / sigma_vm[e] - end - lhs = backsolve!(solver, Mu, global_dofs) - map(1:length(Δ)) do e - ρe, dρe = get_ρ_dρ(x[e], penalty, xmin) - celldofs!(global_dofs, dh, e) - return -dot(lhs[global_dofs], bcmatrix(Kes[e]) * u[global_dofs]) * dρe - end - end) + return out, + Δ -> ( + nothing, + begin + for e = 1:length(Mu) + ρe = get_ρ(x[e], penalty, xmin) + Mu[e] *= Δ[e] * ρe / sigma_vm[e] + end + lhs = backsolve!(solver, Mu, global_dofs) + map(1:length(Δ)) do e + ρe, dρe = get_ρ_dρ(x[e], penalty, xmin) + celldofs!(global_dofs, dh, e) + return -dot(lhs[global_dofs], bcmatrix(Kes[e]) * u[global_dofs]) * dρe + end + end, + ) end ######################################### @@ -143,7 +163,7 @@ end function backsolve!(solver, Mu, global_dofs) dh = getdh(solver.problem) solver.rhs .= 0 - for i in 1:length(Mu) + for i = 1:length(Mu) celldofs!(global_dofs, dh, i) solver.rhs[global_dofs] .+= Mu[i] end @@ -154,7 +174,7 @@ end get_sigma_vm(ρ_e, utMu_e) = ρ_e * sqrt(utMu_e) @inline function get_ϵ(u, ∇ϕ, i, j) - return 1/2*(u[i]*∇ϕ[j] + u[j]*∇ϕ[i]) + return 1 / 2 * (u[i] * ∇ϕ[j] + u[j] * ∇ϕ[i]) end @inline function apply_T!(Tu, u, dh, cellidx, global_dofs, cellvalues, ν, ::Val{2}) # assumes cellvalues is initialized before passing @@ -163,21 +183,21 @@ end q_point = 1 n_basefuncs = getnbasefunctions(cellvalues) dim = 2 - for a in 1:n_basefuncs + for a = 1:n_basefuncs ∇ϕ = shape_gradient(cellvalues, q_point, a) - _u = @view u[(@view global_dofs[dim*(a-1) + 1 : a*dim])] + _u = @view u[(@view global_dofs[dim*(a-1)+1:a*dim])] ϵ_11 = get_ϵ(_u, ∇ϕ, 1, 1) ϵ_22 = get_ϵ(_u, ∇ϕ, 2, 2) ϵ_12 = get_ϵ(_u, ∇ϕ, 1, 2) ϵ_sum = ϵ_11 + ϵ_22 - temp1 = ν/(1-ν^2) - temp2 = ν*(1+ν) + temp1 = ν / (1 - ν^2) + temp2 = ν * (1 + ν) - Tu[1] += temp1*ϵ_sum + temp2*ϵ_11 # σ[1,1] / E - Tu[2] += temp1*ϵ_sum + temp2*ϵ_22 # σ[2,2] / E - Tu[3] += temp2*ϵ_12 # σ[1,2] / E + Tu[1] += temp1 * ϵ_sum + temp2 * ϵ_11 # σ[1,1] / E + Tu[2] += temp1 * ϵ_sum + temp2 * ϵ_22 # σ[2,2] / E + Tu[3] += temp2 * ϵ_12 # σ[1,2] / E end return Tu end @@ -187,9 +207,9 @@ end q_point = 1 n_basefuncs = getnbasefunctions(cellvalues) dim = 3 - for a in 1:n_basefuncs + for a = 1:n_basefuncs ∇ϕ = shape_gradient(cellvalues, q_point, a) - _u = @view u[(@view global_dofs[dim*(a-1) + 1 : a*dim])] + _u = @view u[(@view global_dofs[dim*(a-1)+1:a*dim])] ϵ_11 = get_ϵ(_u, ∇ϕ, 1, 1) ϵ_22 = get_ϵ(_u, ∇ϕ, 2, 2) ϵ_33 = get_ϵ(_u, ∇ϕ, 3, 3) @@ -199,15 +219,15 @@ end ϵ_sum = ϵ_11 + ϵ_22 + ϵ_33 - temp1 = ν/(1-ν^2) - temp2 = ν*(1+ν) + temp1 = ν / (1 - ν^2) + temp2 = ν * (1 + ν) - Tu[1] += temp1*ϵ_sum + temp2*ϵ_11 # σ[1,1] / E - Tu[2] += temp1*ϵ_sum + temp2*ϵ_22 # σ[2,2] / E - Tu[3] += temp1*ϵ_sum + temp2*ϵ_33 # σ[3,3] / E - Tu[4] += temp2*ϵ_12 # σ[1,2] / E - Tu[5] += temp2*ϵ_23 # σ[2,3] / E - Tu[6] += temp2*ϵ_31 # σ[3,1] / E + Tu[1] += temp1 * ϵ_sum + temp2 * ϵ_11 # σ[1,1] / E + Tu[2] += temp1 * ϵ_sum + temp2 * ϵ_22 # σ[2,2] / E + Tu[3] += temp1 * ϵ_sum + temp2 * ϵ_33 # σ[3,3] / E + Tu[4] += temp2 * ϵ_12 # σ[1,2] / E + Tu[5] += temp2 * ϵ_23 # σ[2,3] / E + Tu[6] += temp2 * ϵ_31 # σ[3,1] / E end return Tu end @@ -215,28 +235,28 @@ end @inline function fill_T!(T, ::Val{3}, cellvalues, E0, ν) # assumes cellvalues is initialized before passing dim = 3 - temp1 = E0*ν/(1-ν^2) - temp2 = E0*ν*(1+ν) + temp1 = E0 * ν / (1 - ν^2) + temp2 = E0 * ν * (1 + ν) q_point = 1 n_basefuncs = size(T, 2) ÷ dim @assert size(T, 1) == 6 - for a in 1:n_basefuncs + for a = 1:n_basefuncs ∇ϕ = shape_gradient(cellvalues, q_point, a) - cols = dim * (a - 1) + 1 : dim * a + cols = dim*(a-1)+1:dim*a T[1, cols[1]] = (temp1 + temp2) * ∇ϕ[1] T[2, cols[1]] = temp1 * ∇ϕ[1] T[3, cols[1]] = temp1 * ∇ϕ[1] T[4, cols[1]] = temp2 * ∇ϕ[2] / 2 T[5, cols[1]] = 0 T[6, cols[1]] = temp2 * ∇ϕ[3] / 2 - + T[1, cols[2]] = temp1 * ∇ϕ[2] T[2, cols[2]] = (temp1 + temp2) * ∇ϕ[2] T[3, cols[2]] = temp1 * ∇ϕ[2] T[4, cols[2]] = temp2 * ∇ϕ[1] / 2 T[5, cols[2]] = temp2 * ∇ϕ[3] / 2 T[6, cols[2]] = 0 - + T[1, cols[3]] = temp1 * ∇ϕ[3] T[2, cols[3]] = temp1 * ∇ϕ[3] T[3, cols[3]] = (temp1 + temp2) * ∇ϕ[3] @@ -250,21 +270,21 @@ end @inline function fill_T!(T, ::Val{2}, cellvalues, E0, ν) # assumes cellvalues is initialized before passing dim = 2 - temp1 = E0*ν/(1-ν^2) - temp2 = E0*ν*(1+ν) + temp1 = E0 * ν / (1 - ν^2) + temp2 = E0 * ν * (1 + ν) q_point = 1 n_basefuncs = size(T, 2) ÷ dim @assert size(T, 1) == 3 - for a in 1:n_basefuncs + for a = 1:n_basefuncs ∇ϕ = shape_gradient(cellvalues, q_point, a) - cols = dim * (a - 1) + 1 : dim * a + cols = dim*(a-1)+1:dim*a T[1, cols[1]] = (temp1 + temp2) * ∇ϕ[1] T[2, cols[1]] = temp1 * ∇ϕ[1] T[3, cols[1]] = temp2 * ∇ϕ[2] / 2 - + T[1, cols[2]] = temp1 * ∇ϕ[2] T[2, cols[2]] = (temp1 + temp2) * ∇ϕ[2] - T[3, cols[2]] = temp2 * ∇ϕ[1] / 2 + T[3, cols[2]] = temp2 * ∇ϕ[1] / 2 end return T end @@ -280,17 +300,17 @@ function fill_Mu_utMu!(Mu, utMu, solver, stress_temp::StressTemp) end @inline function _fill_Mu_utMu!( - Mu::AbstractVector, - utMu::AbstractVector{T}, - dh::DofHandler{3}, - elementinfo, + Mu::AbstractVector, + utMu::AbstractVector{T}, + dh::DofHandler{3}, + elementinfo, u, E0, - ν, - global_dofs = zeros(Int, ndofs_per_cell(dh)), + ν, + global_dofs = zeros(Int, ndofs_per_cell(dh)), Tu = zeros(T, 6), - VTu = zeros(T, 6), - Te = zeros(T, 6, ndofs_per_cell(dh)) + VTu = zeros(T, 6), + Te = zeros(T, 6, ndofs_per_cell(dh)), ) where {T} dim = 3 @unpack cellvalues = elementinfo @@ -301,33 +321,33 @@ end celldofs!(global_dofs, dh, cellidx) @views mul!(Tu, Te, u[global_dofs]) - VTu[1] = Tu[1] - Tu[2]/2 - Tu[3]/2 - VTu[2] = -Tu[1]/2 + Tu[2] - Tu[3]/2 - VTu[3] = -Tu[1]/2 - Tu[2]/2 + Tu[3] - VTu[4] = 3*Tu[4] - VTu[5] = 3*Tu[5] - VTu[6] = 3*Tu[6] + VTu[1] = Tu[1] - Tu[2] / 2 - Tu[3] / 2 + VTu[2] = -Tu[1] / 2 + Tu[2] - Tu[3] / 2 + VTu[3] = -Tu[1] / 2 - Tu[2] / 2 + Tu[3] + VTu[4] = 3 * Tu[4] + VTu[5] = 3 * Tu[5] + VTu[6] = 3 * Tu[6] utMu_e = dot(Tu, VTu) @assert utMu_e >= 0 utMu[cellidx] = utMu_e Mu[cellidx] = Te' * VTu - end - return Mu, utMu + end + return Mu, utMu end @inline function _fill_Mu_utMu!( - Mu::AbstractVector, - utMu::AbstractVector{T}, - dh::DofHandler{2}, - elementinfo, - u, + Mu::AbstractVector, + utMu::AbstractVector{T}, + dh::DofHandler{2}, + elementinfo, + u, E0, - ν, - global_dofs = zeros(Int, ndofs_per_cell(dh)), + ν, + global_dofs = zeros(Int, ndofs_per_cell(dh)), Tu = zeros(T, 3), - VTu = zeros(T, 3), - Te = zeros(T, 3, ndofs_per_cell(dh)) + VTu = zeros(T, 3), + Te = zeros(T, 3, ndofs_per_cell(dh)), ) where {T} dim = 2 @unpack cellvalues = elementinfo @@ -337,15 +357,14 @@ end celldofs!(global_dofs, dh, cellidx) @views mul!(Tu, Te, u[global_dofs]) - VTu[1] = Tu[1] - Tu[2]/2 - VTu[2] = -Tu[1]/2 + Tu[2] - VTu[3] = 3*Tu[3] + VTu[1] = Tu[1] - Tu[2] / 2 + VTu[2] = -Tu[1] / 2 + Tu[2] + VTu[3] = 3 * Tu[3] utMu_e = dot(Tu, VTu) @assert utMu_e >= 0 utMu[cellidx] = utMu_e Mu[cellidx] = Te' * VTu - end - return Mu, utMu + end + return Mu, utMu end - diff --git a/src/Functions/trace.jl b/src/Functions/trace.jl index b27b535b..67581dee 100644 --- a/src/Functions/trace.jl +++ b/src/Functions/trace.jl @@ -20,12 +20,12 @@ function ExactSVDMean(F::SparseMatrixCSC) @show length(svdfact.S) inds = findall(x -> x > threshold, svdfact.S) @show length(inds) - US_dense = svdfact.U[:,inds] * Diagonal(svdfact.S[inds]) + US_dense = svdfact.U[:, inds] * Diagonal(svdfact.S[inds]) I = Int[] J = Int[] V = eltype(F)[] - for j in 1:length(inds) - for i in 1:length(rows) + for j = 1:length(inds) + for i = 1:length(rows) push!(I, rows[i]) push!(J, inds[j]) push!(V, US_dense[i, j]) @@ -35,32 +35,47 @@ function ExactSVDMean(F::SparseMatrixCSC) return ExactSVDMean(US, size(F, 2)) end -struct TraceEstimationMean{TF, TV, TM} <: AbstractTraceEstimationMeanMethod +struct TraceEstimationMean{TF,TV,TM} <: AbstractTraceEstimationMeanMethod F::TF V::TV sample_once::Bool sample_method::TM end -function TraceEstimationMean(F::SparseMatrixCSC, nv::Int, sample_once::Bool=true, sample_method=hutch_rand!) +function TraceEstimationMean( + F::SparseMatrixCSC, + nv::Int, + sample_once::Bool = true, + sample_method = hutch_rand!, +) V = zeros(eltype(F), size(F, 2), nv) sample_method(V) return TraceEstimationMean(F, V, sample_once, sample_method) end -struct TraceEstimationSVDMean{TUS, TV, TM} <: AbstractTraceEstimationMeanMethod +struct TraceEstimationSVDMean{TUS,TV,TM} <: AbstractTraceEstimationMeanMethod US::TUS n::Int V::TV sample_once::Bool sample_method::TM end -function TraceEstimationSVDMean(F::SparseMatrixCSC, nv::Int, sample_once::Bool=true, sample_method=hutch_rand!) +function TraceEstimationSVDMean( + F::SparseMatrixCSC, + nv::Int, + sample_once::Bool = true, + sample_method = hutch_rand!, +) US = ExactSVDMean(F).US V = zeros(eltype(F), size(US, 2), nv) sample_method(V) sample_once = sample_method === hadamard! || sample_once return TraceEstimationSVDMean(US, size(F, 2), V, sample_once, sample_method) end -function TraceEstimationSVDMean(F::SparseMatrixCSC, V::AbstractMatrix, sample_once::Bool=true, sample_method=hutch_rand!) +function TraceEstimationSVDMean( + F::SparseMatrixCSC, + V::AbstractMatrix, + sample_once::Bool = true, + sample_method = hutch_rand!, +) US = ExactSVDMean(F).US sample_once = sample_method === hadamard! || sample_once return TraceEstimationSVDMean(US, size(F, 2), V, sample_once, sample_method) @@ -70,7 +85,7 @@ abstract type AbstractDiagonalMethod end abstract type AbstractExactDiagonalMethod <: AbstractDiagonalMethod end abstract type AbstractDiagonalEstimationMethod <: AbstractDiagonalMethod end -struct ExactDiagonal{TF, TY, Ttemp} <: AbstractExactDiagonalMethod +struct ExactDiagonal{TF,TY,Ttemp} <: AbstractExactDiagonalMethod F::TF Y::TY # K^-1 F temp::Ttemp # f' K^-1 dK/d(filtered x_e) K^-1 f for all e and any one f @@ -79,7 +94,7 @@ function ExactDiagonal(F::SparseMatrixCSC, nE::Int) return ExactDiagonal(F, zeros(eltype(F), size(F)...), zeros(eltype(F), nE)) end -struct ExactSVDDiagonal{TF, TUS, TV, TQ, TY, Ttemp} <: AbstractExactDiagonalMethod +struct ExactSVDDiagonal{TF,TUS,TV,TQ,TY,Ttemp} <: AbstractExactDiagonalMethod F::TF US::TUS V::TV @@ -95,13 +110,13 @@ function ExactSVDDiagonal(F::SparseMatrixCSC, nE::Int) @show length(svdfact.S) inds = findall(x -> x > threshold, svdfact.S) @show length(inds) - US_dense = svdfact.U[:,inds] * Diagonal(svdfact.S[inds]) - V = svdfact.V[:,inds] + US_dense = svdfact.U[:, inds] * Diagonal(svdfact.S[inds]) + V = svdfact.V[:, inds] I = Int[] J = Int[] vals = eltype(F)[] - for j in 1:length(inds) - for i in 1:length(rows) + for j = 1:length(inds) + for i = 1:length(rows) push!(I, rows[i]) push!(J, inds[j]) push!(vals, US_dense[i, j]) @@ -114,7 +129,7 @@ function ExactSVDDiagonal(F::SparseMatrixCSC, nE::Int) return ExactSVDDiagonal(F, US, V, Q, Y, temp) end -struct DiagonalEstimation{TF, TY, TQ, TV, Ttemp, TM} <: AbstractDiagonalEstimationMethod +struct DiagonalEstimation{TF,TY,TQ,TV,Ttemp,TM} <: AbstractDiagonalEstimationMethod F::TF V::TV # all v_i Y::TY # K^-1 F v_i for all i @@ -123,7 +138,13 @@ struct DiagonalEstimation{TF, TY, TQ, TV, Ttemp, TM} <: AbstractDiagonalEstimati sample_once::Bool sample_method::TM end -function DiagonalEstimation(F::SparseMatrixCSC, nv::Int, nE::Int, sample_once::Bool=true, sample_method=hadamard!) +function DiagonalEstimation( + F::SparseMatrixCSC, + nv::Int, + nE::Int, + sample_once::Bool = true, + sample_method = hadamard!, +) V = zeros(eltype(F), size(F, 2), nv) sample_method(V) Y = zeros(eltype(F), size(F, 1), nv) @@ -132,7 +153,13 @@ function DiagonalEstimation(F::SparseMatrixCSC, nv::Int, nE::Int, sample_once::B sample_once = sample_method === hadamard! || sample_once return DiagonalEstimation(F, V, Y, Q, temp, sample_once, sample_method) end -function DiagonalEstimation(F::SparseMatrixCSC, V::AbstractMatrix, nE::Int, sample_once::Bool=true, sample_method=hadamard!) +function DiagonalEstimation( + F::SparseMatrixCSC, + V::AbstractMatrix, + nE::Int, + sample_once::Bool = true, + sample_method = hadamard!, +) nv = size(V, 2) Y = zeros(eltype(F), size(F, 1), nv) Q = similar(Y) @@ -141,6 +168,14 @@ function DiagonalEstimation(F::SparseMatrixCSC, V::AbstractMatrix, nE::Int, samp return DiagonalEstimation(F, V, Y, Q, temp, sample_once, sample_method) end -for T in (:ExactMean, :ExactSVDMean, :TraceEstimationMean, :TraceEstimationSVDMean, :ExactDiagonal, :ExactSVDDiagonal, :DiagonalEstimation) +for T in ( + :ExactMean, + :ExactSVDMean, + :TraceEstimationMean, + :TraceEstimationSVDMean, + :ExactDiagonal, + :ExactSVDDiagonal, + :DiagonalEstimation, +) @eval baretype(::$T) = $T end diff --git a/src/Functions/truss_stress.jl b/src/Functions/truss_stress.jl index ccb45a39..c399068f 100644 --- a/src/Functions/truss_stress.jl +++ b/src/Functions/truss_stress.jl @@ -6,7 +6,8 @@ maxfevals::Int end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::TrussStress) = println("TopOpt truss stress function") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::TrussStress) = + println("TopOpt truss stress function") """ TrussStress(solver; maxfevals=10^8) @@ -21,14 +22,14 @@ function TrussStress(solver::AbstractFEASolver; maxfevals = 10^8) σ = zeros(T, N) transf_matrices = Matrix{T}[] u_fn = Displacement(solver; maxfevals) - R = zeros(T, (2, 2*dim)) + R = zeros(T, (2, 2 * dim)) for (cellidx, cell) in enumerate(CellIterator(dh)) u, v = cell.coords[1], cell.coords[2] # R ∈ 2 x (2*dim) R_coord = compute_local_axes(u, v) fill!(R, 0.0) - R[1, 1:dim] = R_coord[:,1] - R[2, dim+1:2*dim] = R_coord[:,2] + R[1, 1:dim] = R_coord[:, 1] + R[2, dim+1:2*dim] = R_coord[:, 2] push!(transf_matrices, R) end return TrussStress(σ, u_fn, transf_matrices, 0, maxfevals) @@ -50,11 +51,11 @@ function (ts::TrussStress{T})(x) where {T} u = u_fn(x) As = getA(problem) @unpack Kes = solver.elementinfo - for e in 1:length(x) + for e = 1:length(x) # Ke = R' * K_local * R # F = R * (R' * K_local * R) * u celldofs!(global_dofs, dh, e) - σ[e] = -(transf_matrices[e] * Kes[e] * u[global_dofs])[1] / As[e] + σ[e] = -(transf_matrices[e]*Kes[e]*u[global_dofs])[1] / As[e] end return copy(σ) end @@ -89,4 +90,4 @@ d(u)/d(x_e)' * Δ = -d(ρ_e)/d(x_e) * u' * K_e * (K^-1 * Δ) # end # return nothing, dudx_tmp # J1' * v, J2' * v # end -# end \ No newline at end of file +# end diff --git a/src/Functions/volume.jl b/src/Functions/volume.jl index bfb8d3ac..422bedd4 100644 --- a/src/Functions/volume.jl +++ b/src/Functions/volume.jl @@ -1,17 +1,18 @@ -@params mutable struct Volume{T, dim} <: AbstractFunction{T} - problem::StiffnessTopOptProblem{dim, T} +@params mutable struct Volume{T,dim} <: AbstractFunction{T} + problem::StiffnessTopOptProblem{dim,T} solver::AbstractFEASolver cellvolumes::AbstractVector{T} grad::AbstractVector{T} total_volume::T fixed_volume::T tracing::Bool - topopt_trace::TopOptTrace{T} + topopt_trace::TopOptTrace{T} fraction::Bool fevals::Int maxfevals::Int end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::Volume) = println("TopOpt volume (fraction) function") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::Volume) = + println("TopOpt volume (fraction) function") Nonconvex.NonconvexCore.getdim(::Volume) = 1 @inline function Base.getproperty(vf::Volume, f::Symbol) f === :reuse && return false @@ -25,9 +26,9 @@ end function project(f::Volume, V, x) cellvolumes = f.cellvolumes if f.fraction - V = V * f.total_volume + V = V * f.total_volume end - inds = sortperm(x, rev=true) + inds = sortperm(x, rev = true) total = zero(V) i = 0 while i <= length(inds) @@ -42,8 +43,14 @@ function project(f::Volume, V, x) return x end -function Volume(problem::StiffnessTopOptProblem{dim, T}, solver::AbstractFEASolver, ::Type{TI} = Int; - fraction = true, tracing = true, maxfevals = 10^8) where {dim, T, TI} +function Volume( + problem::StiffnessTopOptProblem{dim,T}, + solver::AbstractFEASolver, + ::Type{TI} = Int; + fraction = true, + tracing = true, + maxfevals = 10^8, +) where {dim,T,TI} dh = problem.ch.dh varind = problem.varind black = problem.black @@ -61,7 +68,19 @@ function Volume(problem::StiffnessTopOptProblem{dim, T}, solver::AbstractFEASolv if fraction grad ./= total_volume end - return Volume(problem, solver, cellvolumes, grad, total_volume, fixed_volume, tracing, TopOptTrace{T, TI}(), fraction, 0, maxfevals) + return Volume( + problem, + solver, + cellvolumes, + grad, + total_volume, + fixed_volume, + tracing, + TopOptTrace{T,TI}(), + fraction, + 0, + maxfevals, + ) end function (v::Volume{T})(x, grad = nothing) where {T} varind = v.problem.varind @@ -85,7 +104,7 @@ function (v::Volume{T})(x, grad = nothing) where {T} grad .= v.grad end if tracing - push!(topopt_trace.v_hist, vol/total_volume) + push!(topopt_trace.v_hist, vol / total_volume) end return constrval @@ -97,10 +116,10 @@ end function compute_volume(cellvolumes::Vector, x, fixed_volume, varind, black, white) vol = fixed_volume - for i in 1:length(cellvolumes) + for i = 1:length(cellvolumes) if !(black[i]) && !(white[i]) #vol += density(x[varind[i]], xmin)*cellvolumes[i] - vol += x[varind[i]]*cellvolumes[i] + vol += x[varind[i]] * cellvolumes[i] end end return vol diff --git a/src/GPUUtils/GPUUtils.jl b/src/GPUUtils/GPUUtils.jl index e0538679..019c9af4 100644 --- a/src/GPUUtils/GPUUtils.jl +++ b/src/GPUUtils/GPUUtils.jl @@ -2,18 +2,18 @@ using ..CUDASupport import ..TopOpt: @init_cuda, whichdevice, CPU, GPU @init_cuda() -export CPU, - GPU, - whichdevice, - @define_cu, - @thread_local_index, - @total_threads_per_block, - @block_index, - @total_blocks, - @thread_global_index, - @total_threads, - callkernel, - @mapreduce_block +export CPU, + GPU, + whichdevice, + @define_cu, + @thread_local_index, + @total_threads_per_block, + @block_index, + @total_blocks, + @thread_global_index, + @total_threads, + callkernel, + @mapreduce_block whichdevice(s::AbstractArray) = s isa GPUArrays.GPUArray ? GPU() : CPU() @@ -50,7 +50,7 @@ function _define_cu(T, fields...) end) end -function _cu(s::T, f::F, ::Val{fn}) where {T, F, fn} +function _cu(s::T, f::F, ::Val{fn}) where {T,F,fn} if fn ∈ GPUUtils.cufieldnames(T) if F <: AbstractArray CuArrays.CuArray(f) @@ -63,7 +63,11 @@ function _cu(s::T, f::F, ::Val{fn}) where {T, F, fn} end macro thread_local_index() - :((threadIdx().z - 1) * blockDim().y * blockDim().x + (threadIdx().y - 1) * blockDim().x + threadIdx().x) + :( + (threadIdx().z - 1) * blockDim().y * blockDim().x + + (threadIdx().y - 1) * blockDim().x + + threadIdx().x + ) end macro total_threads_per_block() @@ -71,7 +75,11 @@ macro total_threads_per_block() end macro block_index() - :(blockIdx().x + (blockIdx().y - 1) * gridDim().x + (blockIdx().z - 1) * gridDim().x * gridDim().y) + :( + blockIdx().x + + (blockIdx().y - 1) * gridDim().x + + (blockIdx().z - 1) * gridDim().x * gridDim().y + ) end macro total_blocks() @@ -79,7 +87,10 @@ macro total_blocks() end macro thread_global_index() - :((@block_index() - 1) * (blockDim().x * blockDim().y * blockDim().z) + @thread_local_index()) + :( + (@block_index() - 1) * (blockDim().x * blockDim().y * blockDim().z) + + @thread_local_index() + ) end macro total_threads() @@ -94,40 +105,43 @@ function _mapreduce_block(indvar, limit, op, T, LMEM, result, mapexpr) out = gensym() tmp_local = gensym() local_index = gensym() - esc(quote - $indvar = @thread_global_index() - $offset = @total_threads() - $out = zero($T) - # # Loop sequentially over chunks of input vector - while $indvar <= $limit - $out = $op($out, $mapexpr) - $indvar += $offset - end + esc( + quote + $indvar = @thread_global_index() + $offset = @total_threads() + $out = zero($T) + # # Loop sequentially over chunks of input vector + while $indvar <= $limit + $out = $op($out, $mapexpr) + $indvar += $offset + end - # Perform parallel reduction - $tmp_local = @cuStaticSharedMem($T, $LMEM) - $local_index = @thread_local_index() - $tmp_local[$local_index] = $out - sync_threads() + # Perform parallel reduction + $tmp_local = @cuStaticSharedMem($T, $LMEM) + $local_index = @thread_local_index() + $tmp_local[$local_index] = $out + sync_threads() - $offset = @total_threads_per_block() ÷ 2 - while $offset > 0 - if ($local_index <= $offset) - $tmp_local[$local_index] = $op($tmp_local[$local_index], $tmp_local[$local_index + $offset]) + $offset = @total_threads_per_block() ÷ 2 + while $offset > 0 + if ($local_index <= $offset) + $tmp_local[$local_index] = + $op($tmp_local[$local_index], $tmp_local[$local_index+$offset]) + end + sync_threads() + $offset = $offset ÷ 2 end - sync_threads() - $offset = $offset ÷ 2 - end - if $local_index == 1 - $result[@block_index()] = $tmp_local[1] - end - end) + if $local_index == 1 + $result[@block_index()] = $tmp_local[1] + end + end, + ) end function callkernel(dev, kernel, args) blocks, threads = getvalidconfig(dev, kernel, args) #@show blocks, threads - @cuda blocks=blocks threads=threads kernel(args...) + @cuda blocks = blocks threads = threads kernel(args...) return end @@ -147,15 +161,21 @@ function getvalidconfig(dev, kernel, parallel_args) ## by the kernel kernel_threads = CUDAnative.maxthreads(parallel_kernel) ## by the device - block_threads = (x=CUDAdrv.attribute(dev, CUDAdrv.MAX_BLOCK_DIM_X), - y=CUDAdrv.attribute(dev, CUDAdrv.MAX_BLOCK_DIM_Y), - total=CUDAdrv.attribute(dev, CUDAdrv.MAX_THREADS_PER_BLOCK)) + block_threads = ( + x = CUDAdrv.attribute(dev, CUDAdrv.MAX_BLOCK_DIM_X), + y = CUDAdrv.attribute(dev, CUDAdrv.MAX_BLOCK_DIM_Y), + total = CUDAdrv.attribute(dev, CUDAdrv.MAX_THREADS_PER_BLOCK), + ) # figure out a legal launch configuration y_thr = min(nextpow(2, Rlength ÷ 512 + 1), 512, block_threads.y, kernel_threads) - x_thr = min(512 ÷ y_thr, Slength, block_threads.x, - ceil(Int, block_threads.total/y_thr), - ceil(Int, kernel_threads/y_thr)) + x_thr = min( + 512 ÷ y_thr, + Slength, + block_threads.x, + ceil(Int, block_threads.total / y_thr), + ceil(Int, kernel_threads / y_thr), + ) blk, thr = (Rlength - 1) ÷ y_thr + 1, (x_thr, y_thr, 1) blk = min(blk, ceil(Int, Rlength / prod(thr))) diff --git a/src/TopOpt.jl b/src/TopOpt.jl index fcb7fd38..c7bfb5b4 100644 --- a/src/TopOpt.jl +++ b/src/TopOpt.jl @@ -82,43 +82,43 @@ end @cuda_only Functions include("Functions/gpu_support.jl") @cuda_only Algorithms include("Algorithms/SIMP/gpu_simp.jl") -export TopOpt, - simulate, - TopOptTrace, - DirectDisplacementSolver, - PCGDisplacementSolver, - StaticMatrixFreeDisplacementSolver, - SensFilter, - DensityFilter, - Displacement, - Compliance, - CG, - Direct, - Assembly, - MatrixFree, - FEASolver, - Optimizer, - SIMP, - ContinuationSIMP, - BESO, - GESO, - PowerContinuation, - ExponentialContinuation, - LogarithmicContinuation, - CubicSplineContinuation, - SigmoidContinuation, - Continuation, - save_mesh, - CPU, - GPU, - DefaultCriteria, - EnergyCriteria, - PowerPenalty, - RationalPenalty, - SinhPenalty, - MMA87, - MMA02, - HeavisideProjection, - ProjectedPenalty, - PowerPenalty +export TopOpt, + simulate, + TopOptTrace, + DirectDisplacementSolver, + PCGDisplacementSolver, + StaticMatrixFreeDisplacementSolver, + SensFilter, + DensityFilter, + Displacement, + Compliance, + CG, + Direct, + Assembly, + MatrixFree, + FEASolver, + Optimizer, + SIMP, + ContinuationSIMP, + BESO, + GESO, + PowerContinuation, + ExponentialContinuation, + LogarithmicContinuation, + CubicSplineContinuation, + SigmoidContinuation, + Continuation, + save_mesh, + CPU, + GPU, + DefaultCriteria, + EnergyCriteria, + PowerPenalty, + RationalPenalty, + SinhPenalty, + MMA87, + MMA02, + HeavisideProjection, + ProjectedPenalty, + PowerPenalty end diff --git a/src/TopOptProblems/IO/INP/INP.jl b/src/TopOptProblems/IO/INP/INP.jl index b819a173..077608b5 100644 --- a/src/TopOptProblems/IO/INP/INP.jl +++ b/src/TopOptProblems/IO/INP/INP.jl @@ -1,11 +1,19 @@ module INP -export InpStiffness +export InpStiffness using ...TopOptProblems: Metadata, StiffnessTopOptProblem, QuadraticHexahedron using Ferrite using ....TopOpt.Utilities: find_black_and_white, find_varind -import ...TopOptProblems: nnodespercell, getE, getν, getgeomorder, getdensity, getpressuredict, getcloaddict, getfacesets +import ...TopOptProblems: + nnodespercell, + getE, + getν, + getgeomorder, + getdensity, + getpressuredict, + getcloaddict, + getfacesets include(joinpath("Parser", "Parser.jl")) using .Parser diff --git a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_cells.jl b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_cells.jl index 585839ee..cb53c444 100644 --- a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_cells.jl +++ b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_cells.jl @@ -1,4 +1,4 @@ -function extract_cells(file, ::Type{TI}=Int) where TI +function extract_cells(file, ::Type{TI} = Int) where {TI} line = readline(file) cell_idx_pattern = r"^(\d+)\s*," @@ -14,10 +14,14 @@ function extract_cells(file, ::Type{TI}=Int) where TI cells = [Tuple(nodes)] nextline = _extract_cells!(cells, file, first_cell_idx) - return cells, first_cell_idx-TI(1), nextline + return cells, first_cell_idx - TI(1), nextline end -function _extract_cells!(cells::AbstractVector{NTuple{nnodes, TI}}, file, prev_cell_idx::TI) where {nnodes, TI} +function _extract_cells!( + cells::AbstractVector{NTuple{nnodes,TI}}, + file, + prev_cell_idx::TI, +) where {nnodes,TI} cell_idx_pattern = r"^(\d+)\s*," node_idx_pattern = r",\s*(\d+)" nodes = zeros(TI, nnodes) @@ -34,7 +38,8 @@ function _extract_cells!(cells::AbstractVector{NTuple{nnodes, TI}}, file, prev_c for (i, m) in enumerate(eachmatch(node_idx_pattern, line)) nodes[i] = parse(TI, m[1]) end - all(nodes .!= zero(TI)) || throw("Cell $cell_idx has fewer nodes than it should.") + all(nodes .!= zero(TI)) || + throw("Cell $cell_idx has fewer nodes than it should.") push!(cells, Tuple(nodes)) prev_cell_idx = cell_idx diff --git a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_cload.jl b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_cload.jl index 926aaf86..ba9acc43 100644 --- a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_cload.jl +++ b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_cload.jl @@ -1,4 +1,8 @@ -function extract_cload!(cloads::Dict{TI, Vector{TF}}, file, ::Type{Val{dim}}) where {TI, TF, dim} +function extract_cload!( + cloads::Dict{TI,Vector{TF}}, + file, + ::Type{Val{dim}}, +) where {TI,TF,dim} pattern = r"(\d+)\s*,\s*(\d)\s*,\s*(\-?\d+\.\d*E[\+\-]\d{2})" line = readline(file) m = match(stopping_pattern, line) diff --git a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_dbcs.jl b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_dbcs.jl index b9384167..11fd34b2 100644 --- a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_dbcs.jl +++ b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_dbcs.jl @@ -1,4 +1,4 @@ -function extract_nodedbcs!(node_dbcs::Dict{String, Vector{Tuple{TI,TF}}}, file) where {TI, TF} +function extract_nodedbcs!(node_dbcs::Dict{String,Vector{Tuple{TI,TF}}}, file) where {TI,TF} pattern_zero = r"([^,]+)\s*,\s*(\d)" pattern_range = r"([^,]+)\s*,\s*(\d)\s*,\s*(\d)" pattern_other = r"([^,]+)\s*,\s*(\d)\s*,\s*(\d)\s*,\s*(\-?\d+\.\d*)" @@ -12,12 +12,12 @@ function extract_nodedbcs!(node_dbcs::Dict{String, Vector{Tuple{TI,TF}}}, file) dof2 = parse(TI, m[3]) val = parse(TF, m[4]) if haskey(node_dbcs, nodesetname) - for dof in dof1:dof2 + for dof = dof1:dof2 push!(node_dbcs[nodesetname], (dof, val)) end else node_dbcs[nodesetname] = [(dof1, val)] - for dof in dof1+1:dof2 + for dof = dof1+1:dof2 push!(node_dbcs[nodesetname], (dof, val)) end end @@ -31,12 +31,12 @@ function extract_nodedbcs!(node_dbcs::Dict{String, Vector{Tuple{TI,TF}}}, file) dof1 = parse(TI, m[2]) dof2 = parse(TI, m[3]) if haskey(node_dbcs, nodesetname) - for dof in dof1:dof2 + for dof = dof1:dof2 push!(node_dbcs[nodesetname], (dof, zero(TF))) end else node_dbcs[nodesetname] = [(dof1, zero(TF))] - for dof in dof1+1:dof2 + for dof = dof1+1:dof2 push!(node_dbcs[nodesetname], (dof, zero(TF))) end end diff --git a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_dload.jl b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_dload.jl index fc6914ed..20fe6bd9 100644 --- a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_dload.jl +++ b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_dload.jl @@ -1,4 +1,10 @@ -function extract_dload!(dloads::Dict{String, TF}, facesets::Dict{String, Vector{Tuple{TI,TI}}}, file, ::Type{Val{dim}}, offset::TI) where {TI, TF, dim} +function extract_dload!( + dloads::Dict{String,TF}, + facesets::Dict{String,Vector{Tuple{TI,TI}}}, + file, + ::Type{Val{dim}}, + offset::TI, +) where {TI,TF,dim} pattern = r"(\d+)\s*,\s*P(\d+)\s*,\s*(\-?\d+\.\d*)" dload_heading_pattern = r"\*DLOAD" @@ -8,7 +14,7 @@ function extract_dload!(dloads::Dict{String, TF}, facesets::Dict{String, Vector{ first = true prevload = zero(TF) load = zero(TF) - + line = readline(file) m = match(stopping_pattern, line) while m isa Nothing diff --git a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_material.jl b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_material.jl index 34619d15..c6c66088 100644 --- a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_material.jl +++ b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_material.jl @@ -1,4 +1,4 @@ -function extract_material(file, ::Type{TF}=Float64) where TF +function extract_material(file, ::Type{TF} = Float64) where {TF} elastic_heading_pattern = r"\*ELASTIC" Emu_pattern = r"(-?\d+\.?\d*)\s*,\s*(-?\d+\.?\d*)" line = readline(file) diff --git a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_nodes.jl b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_nodes.jl index 2781d456..a56ad8b8 100644 --- a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_nodes.jl +++ b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_nodes.jl @@ -1,9 +1,10 @@ -function extract_nodes(file, ::Type{TF}=Float64, ::Type{TI}=Int) where {TF, TI} +function extract_nodes(file, ::Type{TF} = Float64, ::Type{TI} = Int) where {TF,TI} line = readline(file) - pattern = r"(\d+)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)\s*(,\s*(-?\d+\.?\d*(e[-\+]?\d*)?))?" + pattern = + r"(\d+)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)\s*(,\s*(-?\d+\.?\d*(e[-\+]?\d*)?))?" m = match(pattern, line) - + first_node_idx = parse(TI, m[1]) first_node_idx == TI(1) || throw("First node index is not 1.") @@ -17,11 +18,16 @@ function extract_nodes(file, ::Type{TF}=Float64, ::Type{TI}=Int) where {TF, TI} return node_coords, nextline end -function _extract_nodes!(node_coords::AbstractVector{NTuple{dim, TF}}, file, prev_node_idx::TI) where {dim, TF, TI} +function _extract_nodes!( + node_coords::AbstractVector{NTuple{dim,TF}}, + file, + prev_node_idx::TI, +) where {dim,TF,TI} if dim === 2 pattern = r"(\d+)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)" elseif dim === 3 - pattern = r"(\d+)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)" + pattern = + r"(\d+)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)\s*,\s*(-?\d+\.?\d*(e[-\+]?\d*)?)" else error("Dimension is not supported.") end diff --git a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_set.jl b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_set.jl index 222f33ca..a517d7b4 100644 --- a/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_set.jl +++ b/src/TopOptProblems/IO/INP/Parser/FeatureExtractors/extract_set.jl @@ -1,7 +1,12 @@ -function extract_set!(sets::Dict{String, TV}, setname::AbstractString, file, offset=0) where {TI, TV<:AbstractVector{TI}} +function extract_set!( + sets::Dict{String,TV}, + setname::AbstractString, + file, + offset = 0, +) where {TI,TV<:AbstractVector{TI}} sets[setname] = Int[] vector = sets[setname] - + pattern_single = r"^(\d+)" pattern_subset = r"^([^,]+)" line = readline(file) @@ -9,7 +14,7 @@ function extract_set!(sets::Dict{String, TV}, setname::AbstractString, file, off while m isa Nothing m = match(pattern_single, line) if m != nothing - push!(vector, parse(TI, m[1])-offset) + push!(vector, parse(TI, m[1]) - offset) else m = match(pattern_subset, line) if m != nothing diff --git a/src/TopOptProblems/IO/INP/Parser/Parser.jl b/src/TopOptProblems/IO/INP/Parser/Parser.jl index c496b10e..708043bd 100644 --- a/src/TopOptProblems/IO/INP/Parser/Parser.jl +++ b/src/TopOptProblems/IO/INP/Parser/Parser.jl @@ -36,7 +36,7 @@ end - `facesets`: a dictionary mapping a face set name to a vector of `Tuple{Int,Int}` tuples where each tuple is a face index. The first integer is the cell index where the face is and the second integer is the local face index in the cell according to the VTK convention. - `dloads`: a dictionary of distributed loads mapping face set names to a normal traction load value. """ -struct InpContent{dim, TF, N, TI} +struct InpContent{dim,TF,N,TI} node_coords::Vector{NTuple{dim,TF}} celltype::String cells::Vector{NTuple{N,TI}} @@ -45,10 +45,10 @@ struct InpContent{dim, TF, N, TI} E::TF ν::TF density::TF - nodedbcs::Dict{String, Vector{Tuple{TI,TF}}} - cloads::Dict{Int, Vector{TF}} - facesets::Dict{String, Vector{Tuple{TI,TI}}} - dloads::Dict{String, TF} + nodedbcs::Dict{String,Vector{Tuple{TI,TF}}} + cloads::Dict{Int,Vector{TF}} + facesets::Dict{String,Vector{Tuple{TI,TI}}} + dloads::Dict{String,TF} end const stopping_pattern = r"^\*[^\*]" @@ -65,17 +65,17 @@ include(joinpath("inp_to_ferrite.jl")) function extract_inp(filepath_with_ext) file = open(filepath_with_ext, "r") - + local node_coords local celltype, cells, offset nodesets = Dict{String,Vector{Int}}() cellsets = Dict{String,Vector{Int}}() local E, mu - nodedbcs = Dict{String, Vector{Tuple{Int,Float64}}}() - cloads = Dict{Int, Vector{Float64}}() - facesets = Dict{String, Vector{Tuple{Int,Int}}}() - dloads = Dict{String, Float64}() - density = 0. # Should extract from the file + nodedbcs = Dict{String,Vector{Tuple{Int,Float64}}}() + cloads = Dict{Int,Vector{Float64}}() + facesets = Dict{String,Vector{Tuple{Int,Int}}}() + dloads = Dict{String,Float64}() + density = 0.0 # Should extract from the file node_heading_pattern = r"\*Node\s*,\s*NSET\s*=\s*([^,]*)" cell_heading_pattern = r"\*Element\s*,\s*TYPE\s*=\s*([^,]*)\s*,\s*ELSET\s*=\s*([^,]*)" @@ -98,7 +98,7 @@ function extract_inp(filepath_with_ext) m = match(cell_heading_pattern, line) if m != nothing celltype = String(m[1]) - cellsetname = String(m[2]) + cellsetname = String(m[2]) cells, offset, line = extract_cells(file) cellsets[cellsetname] = collect(1:length(cells)) continue @@ -141,7 +141,20 @@ function extract_inp(filepath_with_ext) close(file) - return InpContent(node_coords, celltype, cells, nodesets, cellsets, E, mu, density, nodedbcs, cloads, facesets, dloads) + return InpContent( + node_coords, + celltype, + cells, + nodesets, + cellsets, + E, + mu, + density, + nodedbcs, + cloads, + facesets, + dloads, + ) end end diff --git a/src/TopOptProblems/IO/INP/Parser/inp_to_ferrite.jl b/src/TopOptProblems/IO/INP/Parser/inp_to_ferrite.jl index 516a3814..e11a11ba 100644 --- a/src/TopOptProblems/IO/INP/Parser/inp_to_ferrite.jl +++ b/src/TopOptProblems/IO/INP/Parser/inp_to_ferrite.jl @@ -1,4 +1,4 @@ -function inpcelltype(::Type{CT}) where CT +function inpcelltype(::Type{CT}) where {CT} if CT === Triangle return "CPS3" elseif CT === QuadraticTriangle @@ -50,7 +50,7 @@ function inp_to_ferrite(problem::InpContent) celltype = QuadraticTetrahedron geom_order = 2 refshape = RefTetrahedron - dim = 3 + dim = 3 elseif _celltype == "CPS4" # Linear quadrilateral celltype = Quadrilateral @@ -75,9 +75,9 @@ function inp_to_ferrite(problem::InpContent) geom_order = 2 refshape = RefCube dim = 3 - #elseif _celltype == "C3D6" + #elseif _celltype == "C3D6" # Linear wedge - #elseif _celltype == "C3D15" + #elseif _celltype == "C3D15" # Quadratic wedge else throw("Unsupported cell type $_celltype.") @@ -96,19 +96,19 @@ function inp_to_ferrite(problem::InpContent) grid.facesets[k] = Set(problem.facesets[k]) end # Define boundary faces - grid.boundary_matrix = extract_boundary_matrix(grid); + grid.boundary_matrix = extract_boundary_matrix(grid) dh = DofHandler(grid) # Isoparametric - field_interpolation = Lagrange{dim, refshape, geom_order}() + field_interpolation = Lagrange{dim,refshape,geom_order}() push!(dh, :u, dim, field_interpolation) close!(dh) ch = ConstraintHandler(dh) for k in keys(problem.nodedbcs) vec = problem.nodedbcs[k] - f(x, t) = [vec[i][2] for i in 1:length(vec)] - components = [vec[i][1] for i in 1:length(vec)] + f(x, t) = [vec[i][2] for i = 1:length(vec)] + components = [vec[i][1] for i = 1:length(vec)] dbc = Dirichlet(:u, getnodeset(grid, k), f, components) add!(ch, dbc) close!(ch) @@ -118,12 +118,12 @@ function inp_to_ferrite(problem::InpContent) return ch end -function extract_boundary_matrix(grid::Grid{dim}) where dim +function extract_boundary_matrix(grid::Grid{dim}) where {dim} nfaces = length(Ferrite.faces(grid.cells[1])) ncells = length(grid.cells) countedbefore = Dict{NTuple{dim,Int},Bool}() boundary_matrix = ones(Bool, nfaces, ncells) # Assume all are boundary faces - for (ci, cell) in enumerate(getcells(grid)) + for (ci, cell) in enumerate(getcells(grid)) for (fi, face) in enumerate(Ferrite.faces(cell)) sface = Ferrite.sortface(face) # TODO: faces(cell) may as well just return the sorted list token = Base.ht_keyindex2!(countedbefore, sface) diff --git a/src/TopOptProblems/IO/INP/inpstiffness.jl b/src/TopOptProblems/IO/INP/inpstiffness.jl index 96bf975b..6844bc74 100644 --- a/src/TopOptProblems/IO/INP/inpstiffness.jl +++ b/src/TopOptProblems/IO/INP/inpstiffness.jl @@ -21,8 +21,18 @@ end - `white`: a `BitVector` of length equal to the number of elements where `white[e]` is 1 iff the `e`^th element must not be part of the final design - `varind`: an `AbstractVector{Int}` of length equal to the number of elements where `varind[e]` gives the index of the decision variable corresponding to element `e`. Because some elements can be fixed to be black or white, not every element has a decision variable associated. """ -struct InpStiffness{dim, N, TF, TI, TBool, Tch <: ConstraintHandler, GO, TInds <: AbstractVector{TI}, TMeta<:Metadata} <: StiffnessTopOptProblem{dim, TF} - inp_content::InpContent{dim, TF, N, TI} +struct InpStiffness{ + dim, + N, + TF, + TI, + TBool, + Tch<:ConstraintHandler, + GO, + TInds<:AbstractVector{TI}, + TMeta<:Metadata, +} <: StiffnessTopOptProblem{dim,TF} + inp_content::InpContent{dim,TF,N,TI} geom_order::Type{Val{GO}} ch::Tch black::TBool @@ -58,8 +68,8 @@ end getE(p::InpStiffness) = p.inp_content.E getν(p::InpStiffness) = p.inp_content.ν -nnodespercell(::InpStiffness{dim, N}) where {dim, N} = N -getgeomorder(p::InpStiffness{<:Any, <:Any, <:Any, <:Any, <:Any, <:Any, GO}) where {GO} = GO +nnodespercell(::InpStiffness{dim,N}) where {dim,N} = N +getgeomorder(p::InpStiffness{<:Any,<:Any,<:Any,<:Any,<:Any,<:Any,GO}) where {GO} = GO getdensity(p::InpStiffness) = p.inp_content.density getpressuredict(p::InpStiffness) = p.inp_content.dloads getcloaddict(p::InpStiffness) = p.inp_content.cloads diff --git a/src/TopOptProblems/IO/IO.jl b/src/TopOptProblems/IO/IO.jl index e5dae3eb..b852c636 100644 --- a/src/TopOptProblems/IO/IO.jl +++ b/src/TopOptProblems/IO/IO.jl @@ -1,7 +1,6 @@ module InputOutput -export InpStiffness, - save_mesh +export InpStiffness, save_mesh include(joinpath("INP", "INP.jl")) using .INP diff --git a/src/TopOptProblems/IO/VTK.jl b/src/TopOptProblems/IO/VTK.jl index fde0d884..f9e4771e 100644 --- a/src/TopOptProblems/IO/VTK.jl +++ b/src/TopOptProblems/IO/VTK.jl @@ -3,7 +3,7 @@ module VTK using ...TopOptProblems: TopOptProblems, StiffnessTopOptProblem, Ferrite using WriteVTK -export save_mesh +export save_mesh function save_mesh(filename, problem::StiffnessTopOptProblem) topology = ones(getncells(TopOptProblems.getdh(problem).grid)) @@ -22,7 +22,11 @@ function save_mesh(filename, problem, vars::AbstractVector) vtkfile = WriteVTK.vtk_grid(filename, problem, vars) outfiles = WriteVTK.vtk_save(vtkfile) end -function WriteVTK.vtk_grid(filename::AbstractString, problem::StiffnessTopOptProblem{dim, T}, vars::AbstractVector{T}) where {dim, T} +function WriteVTK.vtk_grid( + filename::AbstractString, + problem::StiffnessTopOptProblem{dim,T}, + vars::AbstractVector{T}, +) where {dim,T} varind = problem.varind black = problem.black white = problem.white diff --git a/src/TopOptProblems/TopOptProblems.jl b/src/TopOptProblems/TopOptProblems.jl index a91ad9ab..4c16a6df 100644 --- a/src/TopOptProblems/TopOptProblems.jl +++ b/src/TopOptProblems/TopOptProblems.jl @@ -30,6 +30,25 @@ using .InputOutput include("Visualization/Visualization.jl") using .Visualization -export RayProblem, PointLoadCantilever, HalfMBB, LBeam, TieBeam, InpStiffness, StiffnessTopOptProblem, AbstractTopOptProblem, GlobalFEAInfo, ElementFEAInfo, YoungsModulus, assemble, assemble_f!, RaggedArray, ElementMatrix, rawmatrix, bcmatrix, save_mesh, RandomMagnitude, MultiLoad +export RayProblem, + PointLoadCantilever, + HalfMBB, + LBeam, + TieBeam, + InpStiffness, + StiffnessTopOptProblem, + AbstractTopOptProblem, + GlobalFEAInfo, + ElementFEAInfo, + YoungsModulus, + assemble, + assemble_f!, + RaggedArray, + ElementMatrix, + rawmatrix, + bcmatrix, + save_mesh, + RandomMagnitude, + MultiLoad end # module diff --git a/src/TopOptProblems/Visualization/Visualization.jl b/src/TopOptProblems/Visualization/Visualization.jl index 7208135c..00bf28c5 100644 --- a/src/TopOptProblems/Visualization/Visualization.jl +++ b/src/TopOptProblems/Visualization/Visualization.jl @@ -6,10 +6,10 @@ using Ferrite, VTKDataTypes, Requires include("mesh_types.jl") function __init__() - @require Makie="ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" @eval begin + @require Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" @eval begin include("makie.jl") export visualize end end -end \ No newline at end of file +end diff --git a/src/TopOptProblems/Visualization/makie.jl b/src/TopOptProblems/Visualization/makie.jl index 2e17c9be..f8dab790 100644 --- a/src/TopOptProblems/Visualization/makie.jl +++ b/src/TopOptProblems/Visualization/makie.jl @@ -1,8 +1,7 @@ import .Makie using LinearAlgebra: norm using .Makie: lift, cam3d!, Point3f0, Vec3f0, Figure, Auto -using .Makie: DataAspect, Axis, labelslidergrid!, set_close_to!, - labelslider!, LScene +using .Makie: DataAspect, Axis, labelslidergrid!, set_close_to!, labelslider!, LScene using GeometryBasics: GLTriangleFace using ..TopOptProblems: getcloaddict @@ -15,7 +14,7 @@ function Makie.to_vertices(nodes::Vector{<:Ferrite.Node}) return Point3f0.([n.x for n in nodes]) end -function Makie.to_triangles(cells::AbstractVector{<: Ferrite.Cell}) +function Makie.to_triangles(cells::AbstractVector{<:Ferrite.Cell}) tris = GLTriangleFace[] for cell in cells to_triangle(tris, cell) @@ -24,7 +23,7 @@ function Makie.to_triangles(cells::AbstractVector{<: Ferrite.Cell}) end # https://github.com/JuliaPlots/AbstractPlotting.jl/blob/444813136a506eba8b5b03e2125c7a5f24e825cb/src/conversions.jl#L505 -function to_triangle(tris, cell::Union{Ferrite.Hexahedron, QuadraticHexahedron}) +function to_triangle(tris, cell::Union{Ferrite.Hexahedron,QuadraticHexahedron}) nodes = cell.nodes push!(tris, GLTriangleFace(nodes[1], nodes[2], nodes[5])) push!(tris, GLTriangleFace(nodes[5], nodes[2], nodes[6])) @@ -42,7 +41,7 @@ function to_triangle(tris, cell::Union{Ferrite.Hexahedron, QuadraticHexahedron}) push!(tris, GLTriangleFace(nodes[3], nodes[1], nodes[4])) end -function to_triangle(tris, cell::Union{Ferrite.Tetrahedron, Ferrite.QuadraticTetrahedron}) +function to_triangle(tris, cell::Union{Ferrite.Tetrahedron,Ferrite.QuadraticTetrahedron}) nodes = cell.nodes push!(tris, GLTriangleFace(nodes[1], nodes[3], nodes[2])) push!(tris, GLTriangleFace(nodes[3], nodes[4], nodes[2])) @@ -50,30 +49,41 @@ function to_triangle(tris, cell::Union{Ferrite.Tetrahedron, Ferrite.QuadraticTet push!(tris, GLTriangleFace(nodes[4], nodes[1], nodes[2])) end -function to_triangle(tris, cell::Union{Ferrite.Quadrilateral, Ferrite.QuadraticQuadrilateral}) +function to_triangle( + tris, + cell::Union{Ferrite.Quadrilateral,Ferrite.QuadraticQuadrilateral}, +) nodes = cell.nodes push!(tris, GLTriangleFace(nodes[1], nodes[2], nodes[3])) push!(tris, GLTriangleFace(nodes[3], nodes[4], nodes[1])) end -function to_triangle(tris, cell::Union{Ferrite.Triangle, Ferrite.QuadraticTriangle}) +function to_triangle(tris, cell::Union{Ferrite.Triangle,Ferrite.QuadraticTriangle}) nodes = cell.nodes push!(tris, GLTriangleFace(nodes[1], nodes[2], nodes[3])) end -function Makie.convert_arguments(P, x::AbstractVector{<:Ferrite.Node{N, T}}) where {N, T} - convert_arguments(P, reinterpret(Point{N, T}, x)) +function Makie.convert_arguments(P, x::AbstractVector{<:Ferrite.Node{N,T}}) where {N,T} + convert_arguments(P, reinterpret(Point{N,T}, x)) end ################################ -function visualize(mesh::Ferrite.Grid{dim, <:Ferrite.AbstractCell, TT}, u; - topology=undef, cloaddict=undef, - undeformed_mesh_color=(:gray, 0.4), - deformed_mesh_color=(:cyan, 0.4), - vector_arrowsize=1.0, vector_linewidth=1.0, - default_support_scale=1.0, default_load_scale=1.0, scale_range=1.0, - default_exagg_scale=1.0, exagg_range=1.0) where {dim, TT} +function visualize( + mesh::Ferrite.Grid{dim,<:Ferrite.AbstractCell,TT}, + u; + topology = undef, + cloaddict = undef, + undeformed_mesh_color = (:gray, 0.4), + deformed_mesh_color = (:cyan, 0.4), + vector_arrowsize = 1.0, + vector_linewidth = 1.0, + default_support_scale = 1.0, + default_load_scale = 1.0, + scale_range = 1.0, + default_exagg_scale = 1.0, + exagg_range = 1.0, +) where {dim,TT} T = eltype(u) nnodes = length(mesh.nodes) if topology !== undef @@ -95,7 +105,7 @@ function visualize(mesh::Ferrite.Grid{dim, <:Ferrite.AbstractCell, TT}, u; end u = [u; zeros(T, 1, nnodes)] - ax1 = Axis(fig[1,1]) + ax1 = Axis(fig[1, 1]) # tightlimits!(ax1) # ax1.aspect = AxisAspect(1) ax1.aspect = DataAspect() @@ -105,15 +115,20 @@ function visualize(mesh::Ferrite.Grid{dim, <:Ferrite.AbstractCell, TT}, u; # https://jkrumbiegel.github.io/MakieLayout.jl/v0.3/layoutables/#LScene-1 # https://makie.juliaplots.org/stable/cameras.html#D-Camera # ax1 = layout[1, 1] = LScene(scene, camera = cam3d!, raw = false) - ax1 = LScene(fig[1,1], scenekw = (camera = cam3d!, raw = false), height=750) + ax1 = LScene(fig[1, 1], scenekw = (camera = cam3d!, raw = false), height = 750) end # TODO show the ground mesh in another Axis https://makie.juliaplots.org/stable/makielayout/grids.html # ax1.title = "TopOpt result" # * support / load appearance / deformatione exaggeration control - lsgrid = labelslidergrid!(fig, - ["deformation exaggeration","support scale", "load scale"], - [LinRange(0.0:0.01:exagg_range), LinRange(0.0:0.01:scale_range), LinRange(0.0:0.01:scale_range)]; + lsgrid = labelslidergrid!( + fig, + ["deformation exaggeration", "support scale", "load scale"], + [ + LinRange(0.0:0.01:exagg_range), + LinRange(0.0:0.01:scale_range), + LinRange(0.0:0.01:scale_range), + ]; width = Auto(), # tellwidth = true, # horizontal = false, @@ -121,16 +136,20 @@ function visualize(mesh::Ferrite.Grid{dim, <:Ferrite.AbstractCell, TT}, u; set_close_to!(lsgrid.sliders[1], default_exagg_scale) set_close_to!(lsgrid.sliders[2], default_support_scale) set_close_to!(lsgrid.sliders[3], default_load_scale) - fig[2,1] = lsgrid.layout + fig[2, 1] = lsgrid.layout # * undeformed mesh - Makie.mesh!(ax1, nodes, mesh_cells, color = undeformed_mesh_color, shading = true); + Makie.mesh!(ax1, nodes, mesh_cells, color = undeformed_mesh_color, shading = true) # * deformed mesh if norm(u) > eps() - exagg_deformed_nodes = lift(s -> - [Ferrite.Node(Tuple([node.x[j] + s * u[j, i] for j=1:3])) for (i, node) in enumerate(nodes)], - lsgrid.sliders[1].value) + exagg_deformed_nodes = lift( + s -> [ + Ferrite.Node(Tuple([node.x[j] + s * u[j, i] for j = 1:3])) for + (i, node) in enumerate(nodes) + ], + lsgrid.sliders[1].value, + ) new_nodes = Vector{Ferrite.Node}(undef, length(nodes)) Makie.mesh!(ax1, exagg_deformed_nodes, mesh_cells, color = deformed_mesh_color) end @@ -150,11 +169,18 @@ function visualize(mesh::Ferrite.Grid{dim, <:Ferrite.AbstractCell, TT}, u; if cloaddict !== undef if length(cloaddict) > 0 loaded_nodes = Point3f0.(nodes[node_ind].x for (node_ind, _) in cloaddict) - Makie.arrows!(ax1, + Makie.arrows!( + ax1, loaded_nodes, - lift(s -> Vec3f0.(s .* load_vec for (_, load_vec) in cloaddict), lsgrid.sliders[3].value), - linecolor=:purple, arrowcolor=:purple, - arrowsize=vector_arrowsize, linewidth=vector_linewidth) + lift( + s -> Vec3f0.(s .* load_vec for (_, load_vec) in cloaddict), + lsgrid.sliders[3].value, + ), + linecolor = :purple, + arrowcolor = :purple, + arrowsize = vector_arrowsize, + linewidth = vector_linewidth, + ) Makie.scatter!(ax1, loaded_nodes) #, markersize = lift(s -> s * 3, lsgrid.sliders[2].value)) end end @@ -177,11 +203,15 @@ function visualize(mesh::Ferrite.Grid{dim, <:Ferrite.AbstractCell, TT}, u; end fixed_nodes = Point3f0.(nodes[node_ind].x for node_ind in node_ids) for v in vectors - Makie.arrows!(ax1, + Makie.arrows!( + ax1, fixed_nodes, - lift(s -> [Vec3f0(s .* v) for nid in node_ids], lsgrid.sliders[2].value), - linecolor=:orange, arrowcolor=:orange, - arrowsize=vector_arrowsize, linewidth=vector_linewidth) + lift(s -> [Vec3f0(s .* v) for nid in node_ids], lsgrid.sliders[2].value), + linecolor = :orange, + arrowcolor = :orange, + arrowsize = vector_arrowsize, + linewidth = vector_linewidth, + ) end Makie.scatter!(ax1, fixed_nodes) #, markersize = lift(s -> s * 3, lsgrid.sliders[1].value)) end @@ -192,8 +222,11 @@ end """ draw problem's initial grid with a given displacement vector `u` """ -function visualize(problem::StiffnessTopOptProblem{dim, T}, u::AbstractVector; - kwargs...) where {dim, T} +function visualize( + problem::StiffnessTopOptProblem{dim,T}, + u::AbstractVector; + kwargs..., +) where {dim,T} mesh = problem.ch.dh.grid node_dofs = problem.metadata.node_dofs nnodes = Ferrite.getnnodes(mesh) @@ -203,13 +236,13 @@ function visualize(problem::StiffnessTopOptProblem{dim, T}, u::AbstractVector; node_displacements = reshape(u[node_dofs], dim, nnodes) end cloaddict = getcloaddict(problem) - visualize(mesh, node_displacements; topology=undef, cloaddict=cloaddict, kwargs...) + visualize(mesh, node_displacements; topology = undef, cloaddict = cloaddict, kwargs...) end -function visualize(problem::StiffnessTopOptProblem{dim, T}; kwargs...) where {dim, T} +function visualize(problem::StiffnessTopOptProblem{dim,T}; kwargs...) where {dim,T} mesh = problem.ch.dh.grid nnodes = Ferrite.getnnodes(mesh) node_displacements = zeros(T, dim, nnodes) cloaddict = getcloaddict(problem) - visualize(mesh, node_displacements; cloaddict=cloaddict, kwargs...) -end \ No newline at end of file + visualize(mesh, node_displacements; cloaddict = cloaddict, kwargs...) +end diff --git a/src/TopOptProblems/Visualization/mesh_types.jl b/src/TopOptProblems/Visualization/mesh_types.jl index f40436f1..edee8943 100644 --- a/src/TopOptProblems/Visualization/mesh_types.jl +++ b/src/TopOptProblems/Visualization/mesh_types.jl @@ -4,31 +4,36 @@ using ..TopOptProblems: getdh """ map Ferrite cell type to VTKDataTypes cell type """ -const ferrite_to_vtk = Dict( Triangle => 5, - QuadraticTriangle => 22, - Quadrilateral => 9, - QuadraticQuadrilateral => 23, - Tetrahedron => 10, - QuadraticTetrahedron => 24, - Hexahedron => 12, - QuadraticHexahedron => 25 - ) +const ferrite_to_vtk = Dict( + Triangle => 5, + QuadraticTriangle => 22, + Quadrilateral => 9, + QuadraticQuadrilateral => 23, + Tetrahedron => 10, + QuadraticTetrahedron => 24, + Hexahedron => 12, + QuadraticHexahedron => 25, +) """ Converting a Ferrite grid to a VTKUnstructuredData from [VTKDataTypes](https://github.com/mohamed82008/VTKDataTypes.jl). """ -function VTKDataTypes.VTKUnstructuredData(grid::Ferrite.Grid{dim, <:Ferrite.Cell{dim,N,M}, T}) where {dim, N, M, T} +function VTKDataTypes.VTKUnstructuredData( + grid::Ferrite.Grid{dim,<:Ferrite.Cell{dim,N,M},T}, +) where {dim,N,M,T} celltype = ferrite_to_vtk[eltype(grid.cells)] - celltypes = [celltype for i in 1:length(grid.cells)] - connectivity = copy(reinterpret(NTuple{N, Int}, grid.cells)) + celltypes = [celltype for i = 1:length(grid.cells)] + connectivity = copy(reinterpret(NTuple{N,Int}, grid.cells)) node_coords = copy(reshape(reinterpret(Float64, grid.nodes), dim, length(grid.nodes))) return VTKUnstructuredData(node_coords, celltypes, connectivity) end function VTKDataTypes.VTKUnstructuredData(problem::AbstractTopOptProblem) return VTKUnstructuredData(getdh(problem).grid) end -VTKDataTypes.GLMesh(grid::Ferrite.Grid; kwargs...) = GLMesh(VTKUnstructuredData(grid); kwargs...) -VTKDataTypes.GLMesh(problem::AbstractTopOptProblem; kwargs...) = GLMesh(VTKUnstructuredData(problem); kwargs...) +VTKDataTypes.GLMesh(grid::Ferrite.Grid; kwargs...) = + GLMesh(VTKUnstructuredData(grid); kwargs...) +VTKDataTypes.GLMesh(problem::AbstractTopOptProblem; kwargs...) = + GLMesh(VTKUnstructuredData(problem); kwargs...) ``` workaround taken from https://github.com/JuliaPlots/Makie.jl/issues/647 @@ -36,16 +41,26 @@ TODO: should directly convert VTKDataTypes.VTKUnstructuredData to GeometryBasics Do not want to spend more time on this now... ``` function GeometryBasics.Mesh(glmesh::GeometryTypes.GLNormalVertexcolorMesh) - newverts = reinterpret(GeometryBasics.Point{3, Float32}, glmesh.vertices) - newfaces = reinterpret(GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}, glmesh.faces) - newnormals = reinterpret(GeometryBasics.Vec{3, Float32}, glmesh.normals) - return GeometryBasics.Mesh(GeometryBasics.meta(newverts; normals = newnormals, color = glmesh.color), newfaces) + newverts = reinterpret(GeometryBasics.Point{3,Float32}, glmesh.vertices) + newfaces = reinterpret( + GeometryBasics.NgonFace{3,GeometryBasics.OffsetInteger{-1,UInt32}}, + glmesh.faces, + ) + newnormals = reinterpret(GeometryBasics.Vec{3,Float32}, glmesh.normals) + return GeometryBasics.Mesh( + GeometryBasics.meta(newverts; normals = newnormals, color = glmesh.color), + newfaces, + ) end """ Get mesh of the topopt problem with a given topology indicator vector """ -function GeometryBasics.Mesh(problem::AbstractTopOptProblem, topology::Array{T,1}; kwargs...) where {T} +function GeometryBasics.Mesh( + problem::AbstractTopOptProblem, + topology::Array{T,1}; + kwargs..., +) where {T} mesh = VTKUnstructuredData(problem) topology = round.(topology) inds = findall(isequal(0), topology) @@ -56,4 +71,3 @@ function GeometryBasics.Mesh(problem::AbstractTopOptProblem, topology::Array{T,1 glmesh = GLMesh(mesh, color = "topology"; kwargs...) return GeometryBasics.Mesh(glmesh) end - diff --git a/src/TopOptProblems/assemble.jl b/src/TopOptProblems/assemble.jl index 46531b22..30903934 100644 --- a/src/TopOptProblems/assemble.jl +++ b/src/TopOptProblems/assemble.jl @@ -1,10 +1,24 @@ -function assemble(problem::StiffnessTopOptProblem{dim,T}, elementinfo::ElementFEAInfo{dim, T}, vars = ones(T, getncells(getdh(problem).grid)), penalty = PowerPenalty(T(1)), xmin = T(0.001)) where {dim,T} +function assemble( + problem::StiffnessTopOptProblem{dim,T}, + elementinfo::ElementFEAInfo{dim,T}, + vars = ones(T, getncells(getdh(problem).grid)), + penalty = PowerPenalty(T(1)), + xmin = T(0.001), +) where {dim,T} globalinfo = GlobalFEAInfo(problem) assemble!(globalinfo, problem, elementinfo, vars, penalty, xmin) return globalinfo end -function assemble!(globalinfo::GlobalFEAInfo{T}, problem::StiffnessTopOptProblem{dim,T}, elementinfo::ElementFEAInfo{dim, T, TK}, vars = ones(T, getncells(getdh(problem).grid)), penalty = PowerPenalty(T(1)), xmin = T(0.001); assemble_f = true) where {dim, T, TK} +function assemble!( + globalinfo::GlobalFEAInfo{T}, + problem::StiffnessTopOptProblem{dim,T}, + elementinfo::ElementFEAInfo{dim,T,TK}, + vars = ones(T, getncells(getdh(problem).grid)), + penalty = PowerPenalty(T(1)), + xmin = T(0.001); + assemble_f = true, +) where {dim,T,TK} ch = problem.ch dh = ch.dh K, f = globalinfo.K, globalinfo.f @@ -25,7 +39,7 @@ function assemble!(globalinfo::GlobalFEAInfo{T}, problem::StiffnessTopOptProblem Ke = zeros(T, size(rawmatrix(Kes[1]))) celliterator = CellIterator(dh) - for (i,cell) in enumerate(celliterator) + for (i, cell) in enumerate(celliterator) # get global_dofs for cell#i celldofs!(global_dofs, dh, i) fe = fes[i] @@ -67,21 +81,33 @@ function assemble!(globalinfo::GlobalFEAInfo{T}, problem::StiffnessTopOptProblem end #* apply boundary condition - _K = TK <: Symmetric ? K.data : K + _K = TK <: Symmetric ? K.data : K apply!(_K, f, ch) - return + return end -function assemble_f(problem::StiffnessTopOptProblem{dim,T}, elementinfo::ElementFEAInfo{dim, T}, vars::AbstractVector{T}, penalty, xmin = T(1)/1000) where {dim, T} +function assemble_f( + problem::StiffnessTopOptProblem{dim,T}, + elementinfo::ElementFEAInfo{dim,T}, + vars::AbstractVector{T}, + penalty, + xmin = T(1) / 1000, +) where {dim,T} f = get_f(problem, vars) assemble_f!(f, problem, elementinfo, vars, penalty, xmin) return f end get_f(problem, vars::Array) = zeros(T, ndofs(problem.ch.dh)) -function assemble_f!(f::AbstractVector, problem::StiffnessTopOptProblem, - elementinfo::ElementFEAInfo, vars::AbstractVector, penalty, xmin) +function assemble_f!( + f::AbstractVector, + problem::StiffnessTopOptProblem, + elementinfo::ElementFEAInfo, + vars::AbstractVector, + penalty, + xmin, +) black = elementinfo.black white = elementinfo.white varind = elementinfo.varind @@ -89,17 +115,37 @@ function assemble_f!(f::AbstractVector, problem::StiffnessTopOptProblem, dof_cells = elementinfo.metadata.dof_cells - update_f!(f, fes, elementinfo.fixedload, dof_cells, black, - white, penalty, vars, varind, xmin) + update_f!( + f, + fes, + elementinfo.fixedload, + dof_cells, + black, + white, + penalty, + vars, + varind, + xmin, + ) return f end -function update_f!(f::Vector, fes, fixedload, dof_cells, black, - white, penalty, vars, varind, xmin) - - @inbounds for dofidx in 1:length(f) +function update_f!( + f::Vector, + fes, + fixedload, + dof_cells, + black, + white, + penalty, + vars, + varind, + xmin, +) + + @inbounds for dofidx = 1:length(f) f[dofidx] = fixedload[dofidx] - r = dof_cells.offsets[dofidx] : dof_cells.offsets[dofidx+1]-1 + r = dof_cells.offsets[dofidx]:dof_cells.offsets[dofidx+1]-1 for i in r cellidx, localidx = dof_cells.values[i] if black[cellidx] @@ -110,14 +156,14 @@ function update_f!(f::Vector, fes, fixedload, dof_cells, black, else px = penalty(xmin) end - f[dofidx] += px * fes[cellidx][localidx] + f[dofidx] += px * fes[cellidx][localidx] else if PENALTY_BEFORE_INTERPOLATION px = density(penalty(vars[varind[cellidx]]), xmin) else px = penalty(density(vars[varind[cellidx]], xmin)) end - f[dofidx] += px * fes[cellidx][localidx] + f[dofidx] += px * fes[cellidx][localidx] end end end @@ -133,8 +179,8 @@ function assemble_f!(f::AbstractVector, problem, dloads) end function update_f!(f::Vector, dof_cells, dloads) - for dofidx in 1:length(f) - r = dof_cells.offsets[dofidx] : dof_cells.offsets[dofidx+1]-1 + for dofidx = 1:length(f) + r = dof_cells.offsets[dofidx]:dof_cells.offsets[dofidx+1]-1 for i in r cellidx, localidx = dof_cells.values[i] f[dofidx] += dloads[cellidx][localidx] diff --git a/src/TopOptProblems/buckling.jl b/src/TopOptProblems/buckling.jl index 72b3ade8..6017334c 100644 --- a/src/TopOptProblems/buckling.jl +++ b/src/TopOptProblems/buckling.jl @@ -1,6 +1,6 @@ using Einsum -function get_Kσs(sp::StiffnessTopOptProblem{xdim, TT}, u_dofs, cellvalues) where {xdim, TT} +function get_Kσs(sp::StiffnessTopOptProblem{xdim,TT}, u_dofs, cellvalues) where {xdim,TT} E = getE(sp) ν = getν(sp) dh = sp.ch.dh @@ -9,14 +9,14 @@ function get_Kσs(sp::StiffnessTopOptProblem{xdim, TT}, u_dofs, cellvalues) wher ndof_pc = ndofs_per_cell(dh) n_basefuncs = getnbasefunctions(cellvalues) global_dofs = zeros(Int, ndof_pc) - Kσs = [zeros(TT, ndof_pc, ndof_pc) for i in 1:getncells(dh.grid)] + Kσs = [zeros(TT, ndof_pc, ndof_pc) for i = 1:getncells(dh.grid)] Kσ_e = zeros(TT, ndof_pc, ndof_pc) # block-diagonal - block σ_e = σ_ij, i,j in xdim # ! shouldn't this be xdim*xdim by xdim*xdim? # ? ψ_e = zeros(TT, xdim*ndof_pc, xdim*ndof_pc) - ψ_e = zeros(TT, xdim*xdim, xdim*xdim) + ψ_e = zeros(TT, xdim * xdim, xdim * xdim) # ? G = zeros(TT, xdim*ndof_pc, ndof_pc) - G = zeros(TT, xdim*xdim, xdim*n_basefuncs) + G = zeros(TT, xdim * xdim, xdim * n_basefuncs) δ = Matrix(TT(1.0)I, xdim, xdim) ϵ = zeros(TT, xdim, xdim) σ = zeros(TT, xdim, xdim) @@ -27,27 +27,28 @@ function get_Kσs(sp::StiffnessTopOptProblem{xdim, TT}, u_dofs, cellvalues) wher reinit!(cellvalues, cell) # get cell's dof's global dof indices, i.e. CC_a^e celldofs!(global_dofs, dh, cellidx) - for q_point in 1:getnquadpoints(cellvalues) + for q_point = 1:getnquadpoints(cellvalues) dΩ = getdetJdV(cellvalues, q_point) - for d in 1:xdim + for d = 1:xdim ψ_e[(d-1)*xdim+1:d*xdim, (d-1)*xdim+1:d*xdim] .= 0 end - for a in 1:n_basefuncs + for a = 1:n_basefuncs ∇ϕ = shape_gradient(cellvalues, q_point, a) - _u = @view u_dofs[(@view global_dofs[xdim*(a-1) .+ (1:xdim)])] + _u = @view u_dofs[(@view global_dofs[xdim*(a-1).+(1:xdim)])] # u_i,j, i for spatial xdim, j for partial derivative - @einsum u_p[i,j] = _u[i]*∇ϕ[j] + @einsum u_p[i, j] = _u[i] * ∇ϕ[j] # effect of the quadratic term in the strain formula have on the stress field is ignored - @einsum ϵ[i,j] = 1/2*(u_p[i,j] + u_p[j,i]) + @einsum ϵ[i, j] = 1 / 2 * (u_p[i, j] + u_p[j, i]) # isotropic solid - @einsum σ[i,j] = E*ν/(1-ν^2)*δ[i,j]*ϵ[k,k] + E*ν*(1+ν)*ϵ[i,j] - for d in 1:xdim + @einsum σ[i, j] = + E * ν / (1 - ν^2) * δ[i, j] * ϵ[k, k] + E * ν * (1 + ν) * ϵ[i, j] + for d = 1:xdim # block diagonal - ψ_e[(d-1)*xdim .+ 1:d*xdim, (d-1)*xdim .+ 1:d*xdim] .+= σ + ψ_e[(d-1)*xdim.+1:d*xdim, (d-1)*xdim.+1:d*xdim] .+= σ G[(xdim*(d-1)+1):(xdim*d), (a-1)*xdim+d] .= ∇ϕ end end - Kσ_e .+= G'*ψ_e*G*dΩ + Kσ_e .+= G' * ψ_e * G * dΩ end Kσs[cellidx] .= Kσ_e end @@ -55,7 +56,7 @@ function get_Kσs(sp::StiffnessTopOptProblem{xdim, TT}, u_dofs, cellvalues) wher return Kσs end -function buckling(problem::StiffnessTopOptProblem{xdim, T}, ginfo, einfo) where {xdim, T} +function buckling(problem::StiffnessTopOptProblem{xdim,T}, ginfo, einfo) where {xdim,T} dh = problem.ch.dh u = ginfo.K \ ginfo.f @@ -76,7 +77,7 @@ function buckling(problem::StiffnessTopOptProblem{xdim, T}, ginfo, einfo) where celliteratortype = CellIterator{typeof(dh).parameters...} _celliterator::celliteratortype = CellIterator(dh) TK = eltype(Kσs) - for (i,cell) in enumerate(_celliterator) + for (i, cell) in enumerate(_celliterator) celldofs!(global_dofs, dh, i) if TK <: Symmetric Ferrite.assemble!(assembler, global_dofs, Kσs[i].data) @@ -87,4 +88,3 @@ function buckling(problem::StiffnessTopOptProblem{xdim, T}, ginfo, einfo) where return ginfo.K, Kσ end - diff --git a/src/TopOptProblems/elementinfo.jl b/src/TopOptProblems/elementinfo.jl index 64af5447..d68660b0 100644 --- a/src/TopOptProblems/elementinfo.jl +++ b/src/TopOptProblems/elementinfo.jl @@ -24,22 +24,25 @@ An instance of the `ElementFEAInfo` type stores element information such as: - `varind`: a vector such that `varind[i]` gives the decision variable index of element `i`. - `cells`: the cell connectivities. """ -@params struct ElementFEAInfo{dim, T} +@params struct ElementFEAInfo{dim,T} Kes::AbstractVector{<:AbstractMatrix{T}} fes::AbstractVector{<:AbstractVector{T}} fixedload::AbstractVector{T} cellvolumes::AbstractVector{T} - cellvalues::CellValues{dim, T, <:Any} - facevalues::FaceValues{<:Any, T, <:Any} + cellvalues::CellValues{dim,T,<:Any} + facevalues::FaceValues{<:Any,T,<:Any} metadata::Metadata black::AbstractVector white::AbstractVector varind::AbstractVector{Int} - cells + cells::Any end function Base.show(io::Base.IO, ::MIME"text/plain", efeainfo::ElementFEAInfo) - print(io, "ElementFEAInfo: Kes |$(length(efeainfo.Kes))|, fes |$(length(efeainfo.fes))|, fixedload |$(length(efeainfo.fixedload))|, cells |$(length(efeainfo.cells))|") + print( + io, + "ElementFEAInfo: Kes |$(length(efeainfo.Kes))|, fes |$(length(efeainfo.fes))|, fixedload |$(length(efeainfo.fixedload))|, cells |$(length(efeainfo.cells))|", + ) end """ @@ -56,12 +59,9 @@ function ElementFEAInfo( sp, quad_order = 2, ::Type{Val{mat_type}} = Val{:Static}, -) where {mat_type} - Kes, weights, dloads, cellvalues, facevalues = make_Kes_and_fes( - sp, - quad_order, - Val{mat_type}, - ) +) where {mat_type} + Kes, weights, dloads, cellvalues, facevalues = + make_Kes_and_fes(sp, quad_order, Val{mat_type}) element_Kes = convert( Vector{<:ElementMatrix}, Kes; @@ -99,17 +99,18 @@ An instance of `GlobalFEAInfo` hosts the global stiffness matrix `K`, the load v @params mutable struct GlobalFEAInfo{T} K::AbstractMatrix{T} f::AbstractVector{T} - cholK - qrK + cholK::Any + qrK::Any end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::GlobalFEAInfo) = println("TopOpt global FEA information") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::GlobalFEAInfo) = + println("TopOpt global FEA information") """ GlobalFEAInfo(::Type{T}=Float64) where {T} Constructs an empty instance of `GlobalFEAInfo` where the field `K` is an empty sparse matrix of element type `T` and the field `f` is an empty dense vector of element type `T`. """ -GlobalFEAInfo(::Type{T}=Float64) where {T} = GlobalFEAInfo{T}() +GlobalFEAInfo(::Type{T} = Float64) where {T} = GlobalFEAInfo{T}() function GlobalFEAInfo{T}() where {T} return GlobalFEAInfo(sparse(zeros(T, 0, 0)), zeros(T, 0), cholesky(one(T)), qr(one(T))) end @@ -125,12 +126,17 @@ function GlobalFEAInfo(sp::StiffnessTopOptProblem) return GlobalFEAInfo(K, f) end function GlobalFEAInfo( - K::Union{AbstractSparseMatrix, Symmetric{<:Any, <:AbstractSparseMatrix}}, + K::Union{AbstractSparseMatrix,Symmetric{<:Any,<:AbstractSparseMatrix}}, f, ) - chol = cholesky(spdiagm(0=>ones(size(K, 1)))) - qrfact = qr(spdiagm(0=>ones(size(K, 1)))) - return GlobalFEAInfo{eltype(K), typeof(K), typeof(f), typeof(chol), typeof(qrfact)}(K, f, chol, qrfact) + chol = cholesky(spdiagm(0 => ones(size(K, 1)))) + qrfact = qr(spdiagm(0 => ones(size(K, 1)))) + return GlobalFEAInfo{eltype(K),typeof(K),typeof(f),typeof(chol),typeof(qrfact)}( + K, + f, + chol, + qrfact, + ) end """ @@ -149,12 +155,15 @@ end Calculates an approximation of the element volumes by approximating the volume integral of 1 over each element using Gaussian quadrature. `cellvalues` is a `Ferrite` struct that facilitates the computation of the integral. To initialize `cellvalues` for an element with index `cell`, `Ferrite.reinit!(cellvalues, cell)` can be called. Calling `Ferrite.getdetJdV(cellvalues, q_point)` then computes the value of the determinant of the Jacobian of the geometric basis functions at the point `q_point` in the reference element. The sum of such values for all integration points is the volume approximation. """ -function get_cell_volumes(sp::StiffnessTopOptProblem{dim, T}, cellvalues) where {dim, T} +function get_cell_volumes(sp::StiffnessTopOptProblem{dim,T}, cellvalues) where {dim,T} dh = sp.ch.dh cellvolumes = zeros(T, getncells(dh.grid)) for (i, cell) in enumerate(CellIterator(dh)) reinit!(cellvalues, cell) - cellvolumes[i] = sum(Ferrite.getdetJdV(cellvalues, q_point) for q_point in 1:Ferrite.getnquadpoints(cellvalues)) + cellvolumes[i] = sum( + Ferrite.getdetJdV(cellvalues, q_point) for + q_point = 1:Ferrite.getnquadpoints(cellvalues) + ) end return cellvolumes end diff --git a/src/TopOptProblems/elementmatrix.jl b/src/TopOptProblems/elementmatrix.jl index 490035a0..b03fa5c9 100644 --- a/src/TopOptProblems/elementmatrix.jl +++ b/src/TopOptProblems/elementmatrix.jl @@ -7,13 +7,14 @@ An element stiffness matrix. `matrix` is the unconstrained element stiffness matrix. `mask` is a `BitVector` where `mask[i]` is 1 iff the local degree of freedom `i` is not constrained by a Dirichlet boundary condition. `meandiag` is the mean of the diagonal of the unconstrained element stiffness matrix. """ -@params struct ElementMatrix{T, TM <: AbstractMatrix{T}} <: AbstractMatrix{T} +@params struct ElementMatrix{T,TM<:AbstractMatrix{T}} <: AbstractMatrix{T} matrix::TM - mask + mask::Any meandiag::T end -ElementMatrix(matrix, mask) = ElementMatrix(matrix, mask, sumdiag(matrix)/size(matrix, 1)) -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::ElementMatrix) = println("TopOpt element matrix") +ElementMatrix(matrix, mask) = ElementMatrix(matrix, mask, sumdiag(matrix) / size(matrix, 1)) +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::ElementMatrix) = + println("TopOpt element matrix") """ rawmatrix(m::ElementMatrix) @@ -21,24 +22,31 @@ Base.show(::IO, ::MIME{Symbol("text/plain")}, ::ElementMatrix) = println("TopOpt Returns the unconstrained element stiffness matrix `m.matrix`. """ rawmatrix(m::ElementMatrix) = m.matrix -rawmatrix(m::Symmetric{T, <:ElementMatrix{T}}) where {T} = Symmetric(m.data.matrix) +rawmatrix(m::Symmetric{T,<:ElementMatrix{T}}) where {T} = Symmetric(m.data.matrix) """ bcmatrix(m::ElementMatrix{T, TM}) where {dim, T, TM <: StaticMatrix{dim, dim, T}} Returns the constrained element stiffness matrix where the elements in the rows and columns corresponding to any local degree of freedom with a Dirichlet boundary condition are replaced by 0. """ -@generated function bcmatrix(m::ElementMatrix{T, TM}) where {dim, T, TM <: StaticMatrix{dim, dim, T}} +@generated function bcmatrix( + m::ElementMatrix{T,TM}, +) where {dim,T,TM<:StaticMatrix{dim,dim,T}} expr = Expr(:tuple) - for j in 1:dim, i in 1:dim - push!(expr.args, :(ifelse(m.mask[$i] && m.mask[$j], m.matrix[$i,$j], zero(T)))) + for j = 1:dim, i = 1:dim + push!(expr.args, :(ifelse(m.mask[$i] && m.mask[$j], m.matrix[$i, $j], zero(T)))) end return :($(Expr(:meta, :inline)); $TM($expr)) end -@generated function bcmatrix(m::Symmetric{T, <:ElementMatrix{T, TM}}) where {dim, T, TM <: StaticMatrix{dim, dim, T}} +@generated function bcmatrix( + m::Symmetric{T,<:ElementMatrix{T,TM}}, +) where {dim,T,TM<:StaticMatrix{dim,dim,T}} expr = Expr(:tuple) - for j in 1:dim, i in 1:dim - push!(expr.args, :(ifelse(m.data.mask[$i] && m.data.mask[$j], m.data.matrix[$i,$j], zero(T)))) + for j = 1:dim, i = 1:dim + push!( + expr.args, + :(ifelse(m.data.mask[$i] && m.data.mask[$j], m.data.matrix[$i, $j], zero(T))), + ) end return :($(Expr(:meta, :inline)); Symmetric($TM($expr))) end @@ -56,11 +64,9 @@ function Base.convert( Kes::Vector{TM}; bc_dofs, dof_cells, -) where { - N, T, TM <: StaticMatrix{N, N, T}, -} +) where {N,T,TM<:StaticMatrix{N,N,T}} fill_matrix = zero(TM) - fill_mask = ones(SVector{N, Bool}) + fill_mask = ones(SVector{N,Bool}) element_Kes = fill(ElementMatrix(fill_matrix, fill_mask), length(Kes)) for i in bc_dofs d_cells = dof_cells[i] @@ -71,7 +77,7 @@ function Base.convert( element_Kes[cellid] = Symmetric(new_Ke) end end - for e in 1:length(element_Kes) + for e = 1:length(element_Kes) Ke = element_Kes[e] matrix = Kes[e] Ke = @set Ke.matrix = matrix @@ -81,14 +87,12 @@ function Base.convert( end function Base.convert( ::Type{Vector{<:ElementMatrix}}, - Kes::Vector{Symmetric{T, TM}}; + Kes::Vector{Symmetric{T,TM}}; bc_dofs, dof_cells, -) where { - N, T, TM <: StaticMatrix{N, N, T}, -} +) where {N,T,TM<:StaticMatrix{N,N,T}} fill_matrix = zero(TM) - fill_mask = ones(SVector{N, Bool}) + fill_mask = ones(SVector{N,Bool}) element_Kes = fill(Symmetric(ElementMatrix(fill_matrix, fill_mask)), length(Kes)) for i in bc_dofs d_cells = dof_cells[i] @@ -99,7 +103,7 @@ function Base.convert( element_Kes[cellid] = Symmetric(new_Ke) end end - for e in 1:length(element_Kes) + for e = 1:length(element_Kes) Ke = element_Kes[e].data matrix = Kes[e].data Ke = @set Ke.matrix = matrix @@ -112,13 +116,11 @@ function Base.convert( Kes::Vector{TM}; bc_dofs, dof_cells, -) where { - T, TM <: AbstractMatrix{T}, -} +) where {T,TM<:AbstractMatrix{T}} N = size(Kes[1], 1) fill_matrix = zero(TM) fill_mask = ones(Bool, N) - element_Kes = [deepcopy(ElementMatrix(fill_matrix, fill_mask)) for i in 1:length(Kes)] + element_Kes = [deepcopy(ElementMatrix(fill_matrix, fill_mask)) for i = 1:length(Kes)] for i in bc_dofs d_cells = dof_cells[i] for c in d_cells @@ -131,18 +133,15 @@ function Base.convert( end function Base.convert( ::Type{Vector{<:ElementMatrix}}, - Kes::Vector{Symmetric{T, TM}}; + Kes::Vector{Symmetric{T,TM}}; bc_dofs, dof_cells, -) where { - T, TM <: AbstractMatrix{T}, -} +) where {T,TM<:AbstractMatrix{T}} N = size(Kes[1], 1) fill_matrix = zero(TM) fill_mask = ones(Bool, N) - element_Kes = [ - Symmetric(deepcopy(ElementMatrix(fill_matrix, fill_mask))) for i in 1:length(Kes) - ] + element_Kes = + [Symmetric(deepcopy(ElementMatrix(fill_matrix, fill_mask))) for i = 1:length(Kes)] for i in bc_dofs d_cells = dof_cells[i] for c in d_cells @@ -154,10 +153,10 @@ function Base.convert( return element_Kes end -for TM in (:(StaticMatrix{m, m, T}), :(Symmetric{T, <:StaticMatrix{m, m, T}})) +for TM in (:(StaticMatrix{m,m,T}), :(Symmetric{T,<:StaticMatrix{m,m,T}})) @eval begin @generated function sumdiag(K::$TM) where {m,T} - return reduce((ex1,ex2) -> :($ex1 + $ex2), [:(K[$j,$j]) for j in 1:m]) + return reduce((ex1, ex2) -> :($ex1 + $ex2), [:(K[$j, $j]) for j = 1:m]) end end end diff --git a/src/TopOptProblems/gpu_support.jl b/src/TopOptProblems/gpu_support.jl index ebe8855d..ad2ababd 100644 --- a/src/TopOptProblems/gpu_support.jl +++ b/src/TopOptProblems/gpu_support.jl @@ -6,38 +6,72 @@ import ..TopOpt: whichdevice get_f(problem, vars::CuArray) = f = zeros(typeof(vars), ndofs(problem.ch.dh)) -function update_f!(f::CuVector{T}, fes, fixedload, dof_cells, black, - white, penalty, vars, varind, xmin) where {T} +function update_f!( + f::CuVector{T}, + fes, + fixedload, + dof_cells, + black, + white, + penalty, + vars, + varind, + xmin, +) where {T} - args = (f, fes, fixedload, dof_cells.offsets, dof_cells.values, black, - white, penalty, vars, varind, xmin, length(f)) + args = ( + f, + fes, + fixedload, + dof_cells.offsets, + dof_cells.values, + black, + white, + penalty, + vars, + varind, + xmin, + length(f), + ) callkernel(dev, assemble_kernel1, args) CUDAdrv.synchronize(ctx) end -function assemble_kernel1(f, fes, fixedload, dof_cells_offsets, dof_cells_values, black, - white, penalty, vars, varind, xmin, ndofs) +function assemble_kernel1( + f, + fes, + fixedload, + dof_cells_offsets, + dof_cells_values, + black, + white, + penalty, + vars, + varind, + xmin, + ndofs, +) dofidx = @thread_global_index() offset = @total_threads() while dofidx <= ndofs f[dofidx] = fixedload[dofidx] - r = dof_cells_offsets[dofidx] : dof_cells_offsets[dofidx+1]-1 + r = dof_cells_offsets[dofidx]:dof_cells_offsets[dofidx+1]-1 for i in r cellidx, localidx = dof_cells_values[i] if black[cellidx] f[dofidx] += fes[cellidx][localidx] elseif white[cellidx] px = xmin - f[dofidx] += px * fes[cellidx][localidx] + f[dofidx] += px * fes[cellidx][localidx] else if PENALTY_BEFORE_INTERPOLATION px = density(penalty(vars[varind[cellidx]]), xmin) else px = penalty(density(vars[varind[cellidx]], xmin)) end - f[dofidx] += px * fes[cellidx][localidx] + f[dofidx] += px * fes[cellidx][localidx] end end dofidx += offset @@ -51,7 +85,18 @@ whichdevice(ch::ConstraintHandler) = whichdevice(ch.dh) whichdevice(dh::DofHandler) = whichdevice(dh.grid) whichdevice(g::Ferrite.Grid) = whichdevice(g.cells) -@define_cu(ElementFEAInfo, :Kes, :fes, :fixedload, :cellvolumes, :metadata, :black, :white, :varind, :cells) +@define_cu( + ElementFEAInfo, + :Kes, + :fes, + :fixedload, + :cellvolumes, + :metadata, + :black, + :white, + :varind, + :cells +) @define_cu(TopOptProblems.Metadata, :cell_dofs, :dof_cells, :node_cells, :node_dofs) @define_cu(Ferrite.ConstraintHandler, :values, :prescribed_dofs, :dh) @define_cu(Ferrite.DofHandler, :grid) diff --git a/src/TopOptProblems/gpu_utils.jl b/src/TopOptProblems/gpu_utils.jl index a745818d..cecdd24f 100644 --- a/src/TopOptProblems/gpu_utils.jl +++ b/src/TopOptProblems/gpu_utils.jl @@ -2,7 +2,11 @@ const dev = CUDAdrv.device() const ctx = CUDAdrv.CuContext(dev) macro thread_local_index() - :((threadIdx().z - 1) * blockDim().y * blockDim().x + (threadIdx().y - 1) * blockDim().x + threadIdx().x) + :( + (threadIdx().z - 1) * blockDim().y * blockDim().x + + (threadIdx().y - 1) * blockDim().x + + threadIdx().x + ) end macro total_threads_per_block() @@ -10,7 +14,11 @@ macro total_threads_per_block() end macro block_index() - :(blockIdx().x + (blockIdx().y - 1) * gridDim().x + (blockIdx().z - 1) * gridDim().x * gridDim().y) + :( + blockIdx().x + + (blockIdx().y - 1) * gridDim().x + + (blockIdx().z - 1) * gridDim().x * gridDim().y + ) end macro total_blocks() @@ -18,7 +26,10 @@ macro total_blocks() end macro thread_global_index() - :((@block_index() - 1) * (blockDim().x * blockDim().y * blockDim().z) + @thread_local_index()) + :( + (@block_index() - 1) * (blockDim().x * blockDim().y * blockDim().z) + + @thread_local_index() + ) end macro total_threads() @@ -27,7 +38,7 @@ end function callkernel(dev, kernel, args) blocks, threads = getvalidconfig(dev, kernel, args) - @cuda blocks=blocks threads=threads kernel(args...) + @cuda blocks = blocks threads = threads kernel(args...) return end @@ -46,15 +57,21 @@ function getvalidconfig(dev, kernel, parallel_args) ## by the kernel kernel_threads = CUDAnative.maxthreads(parallel_kernel) ## by the device - block_threads = (x=CUDAdrv.attribute(dev, CUDAdrv.MAX_BLOCK_DIM_X), - y=CUDAdrv.attribute(dev, CUDAdrv.MAX_BLOCK_DIM_Y), - total=CUDAdrv.attribute(dev, CUDAdrv.MAX_THREADS_PER_BLOCK)) + block_threads = ( + x = CUDAdrv.attribute(dev, CUDAdrv.MAX_BLOCK_DIM_X), + y = CUDAdrv.attribute(dev, CUDAdrv.MAX_BLOCK_DIM_Y), + total = CUDAdrv.attribute(dev, CUDAdrv.MAX_THREADS_PER_BLOCK), + ) # figure out a legal launch configuration y_thr = min(nextpow(2, Rlength ÷ 512 + 1), 512, block_threads.y, kernel_threads) - x_thr = min(512 ÷ y_thr, Slength, block_threads.x, - ceil(Int, block_threads.total/y_thr), - ceil(Int, kernel_threads/y_thr)) + x_thr = min( + 512 ÷ y_thr, + Slength, + block_threads.x, + ceil(Int, block_threads.total / y_thr), + ceil(Int, kernel_threads / y_thr), + ) blk, thr = (Rlength - 1) ÷ y_thr + 1, (x_thr, y_thr, 1) blk = min(blk, ceil(Int, Rlength / prod(thr))) diff --git a/src/TopOptProblems/grids.jl b/src/TopOptProblems/grids.jl index 6cd5e3e4..1f1798bf 100644 --- a/src/TopOptProblems/grids.jl +++ b/src/TopOptProblems/grids.jl @@ -1,4 +1,4 @@ -abstract type AbstractGrid{dim, T} end +abstract type AbstractGrid{dim,T} end const Vec = Ferrite.Vec @@ -29,11 +29,12 @@ A type that represents a rectilinear grid with corner points `corners`. - `black_cells`: cells fixed to have material during optimization - `constant_cells`: cells fixed to be either void or have material during optimization """ -@params struct RectilinearGrid{dim, T, N, M, TG<:Ferrite.Grid{dim, <:Ferrite.Cell{dim,N,M}, T}} <: AbstractGrid{dim, T} +@params struct RectilinearGrid{dim,T,N,M,TG<:Ferrite.Grid{dim,<:Ferrite.Cell{dim,N,M},T}} <: + AbstractGrid{dim,T} grid::TG - nels::NTuple{dim, Int} - sizes::NTuple{dim, T} - corners::NTuple{2, Vec{dim,T}} + nels::NTuple{dim,Int} + sizes::NTuple{dim,T} + corners::NTuple{2,Vec{dim,T}} white_cells::BitVector black_cells::BitVector constant_cells::BitVector @@ -55,7 +56,11 @@ Example: rectgrid = RectilinearGrid((60,20), (1.0,1.0)) ``` """ -function RectilinearGrid(::Type{Val{CellType}}, nels::NTuple{dim,Int}, sizes::NTuple{dim,T}) where {dim, T, CellType} +function RectilinearGrid( + ::Type{Val{CellType}}, + nels::NTuple{dim,Int}, + sizes::NTuple{dim,T}, +) where {dim,T,CellType} if dim === 2 if CellType === :Linear geoshape = Quadrilateral @@ -67,16 +72,24 @@ function RectilinearGrid(::Type{Val{CellType}}, nels::NTuple{dim,Int}, sizes::NT end corner1 = Vec{dim}(fill(T(0), dim)) corner2 = Vec{dim}((nels .* sizes)) - grid = generate_grid(geoshape, nels, corner1, corner2); + grid = generate_grid(geoshape, nels, corner1, corner2) N = nnodes(geoshape) M = Ferrite.nfaces(geoshape) ncells = prod(nels) - return RectilinearGrid(grid, nels, sizes, (corner1, corner2), falses(ncells), falses(ncells), falses(ncells)) + return RectilinearGrid( + grid, + nels, + sizes, + (corner1, corner2), + falses(ncells), + falses(ncells), + falses(ncells), + ) end -nnodespercell(::RectilinearGrid{dim,T,N,M}) where {dim, T, N, M} = N -nfacespercell(::RectilinearGrid{dim,T,N,M}) where {dim, T, N, M} = M +nnodespercell(::RectilinearGrid{dim,T,N,M}) where {dim,T,N,M} = N +nfacespercell(::RectilinearGrid{dim,T,N,M}) where {dim,T,N,M} = M left(rectgrid::RectilinearGrid, x) = x[1] ≈ rectgrid.corners[1][1] right(rectgrid::RectilinearGrid, x) = x[1] ≈ rectgrid.corners[2][1] @@ -84,11 +97,14 @@ bottom(rectgrid::RectilinearGrid, x) = x[2] ≈ rectgrid.corners[1][2] top(rectgrid::RectilinearGrid, x) = x[2] ≈ rectgrid.corners[2][2] back(rectgrid::RectilinearGrid, x) = x[3] ≈ rectgrid.corners[1][3] front(rectgrid::RectilinearGrid, x) = x[3] ≈ rectgrid.corners[2][3] -middlex(rectgrid::RectilinearGrid, x) = x[1] ≈ (rectgrid.corners[1][1] + rectgrid.corners[2][1]) / 2 -middley(rectgrid::RectilinearGrid, x) = x[2] ≈ (rectgrid.corners[1][2] + rectgrid.corners[2][2]) / 2 -middlez(rectgrid::RectilinearGrid, x) = x[3] ≈ (rectgrid.corners[1][3] + rectgrid.corners[2][3]) / 2 - -nnodes(cell::Type{Ferrite.Cell{dim,N,M}}) where {dim, N, M} = N +middlex(rectgrid::RectilinearGrid, x) = + x[1] ≈ (rectgrid.corners[1][1] + rectgrid.corners[2][1]) / 2 +middley(rectgrid::RectilinearGrid, x) = + x[2] ≈ (rectgrid.corners[1][2] + rectgrid.corners[2][2]) / 2 +middlez(rectgrid::RectilinearGrid, x) = + x[3] ≈ (rectgrid.corners[1][3] + rectgrid.corners[2][3]) / 2 + +nnodes(cell::Type{Ferrite.Cell{dim,N,M}}) where {dim,N,M} = N nnodes(cell::Ferrite.Cell) = nnodes(typeof(cell)) """ @@ -121,15 +137,33 @@ LGrid(upperslab = 30, lowerslab = 70) LGrid(Val{:Linear}, (2, 4), (2, 2), Vec{2,Float64}((0.0,0.0)), Vec{2,Float64}((2.0, 4.0)), Vec{2,Float64}((4.0, 2.0))) ``` """ -function LGrid(::Type{Val{CellType}}, ::Type{T}; length = 100, height = 100, upperslab = 50, lowerslab = 50) where {T, CellType} +function LGrid( + ::Type{Val{CellType}}, + ::Type{T}; + length = 100, + height = 100, + upperslab = 50, + lowerslab = 50, +) where {T,CellType} @assert length > upperslab @assert height > lowerslab - LGrid(Val{CellType}, (upperslab, height), (length-upperslab, lowerslab), - Vec{2,T}((0.0,0.0)), Vec{2,T}((T(upperslab), T(height))), - Vec{2,T}((T(length), T(lowerslab)))) + LGrid( + Val{CellType}, + (upperslab, height), + (length - upperslab, lowerslab), + Vec{2,T}((0.0, 0.0)), + Vec{2,T}((T(upperslab), T(height))), + Vec{2,T}((T(length), T(lowerslab))), + ) end -function LGrid(::Type{Val{CellType}}, nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, - LL::Vec{2,T}, UR::Vec{2,T}, MR::Vec{2,T}) where {CellType, T} +function LGrid( + ::Type{Val{CellType}}, + nel1::NTuple{2,Int}, + nel2::NTuple{2,Int}, + LL::Vec{2,T}, + UR::Vec{2,T}, + MR::Vec{2,T}, +) where {CellType,T} if CellType === :Linear return _LinearLGrid(nel1, nel2, LL, UR, MR) @@ -138,24 +172,31 @@ function LGrid(::Type{Val{CellType}}, nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, end end -function _LinearLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, - LL::Vec{2,T}, UR::Vec{2,T}, MR::Vec{2,T}) where {T} +function _LinearLGrid( + nel1::NTuple{2,Int}, + nel2::NTuple{2,Int}, + LL::Vec{2,T}, + UR::Vec{2,T}, + MR::Vec{2,T}, +) where {T} @assert nel1[2] > nel2[2] - midpointindy = round(Int, nel2[2]/2) + 1 + midpointindy = round(Int, nel2[2] / 2) + 1 nodes = Node{2,T}[] cells = Quadrilateral[] boundary = Tuple{Int,Int}[] - facesets = Dict{String, Set{Tuple{Int,Int}}}() - facesets["right"] = Set{Tuple{Int,Int}}() - facesets["top"] = Set{Tuple{Int,Int}}() - nodesets = Dict{String, Set{Int}}() + facesets = Dict{String,Set{Tuple{Int,Int}}}() + facesets["right"] = Set{Tuple{Int,Int}}() + facesets["top"] = Set{Tuple{Int,Int}}() + nodesets = Dict{String,Set{Int}}() nodesets["load"] = Set{Int}() - + # Lower left rectangle - nel_x1 = nel1[1]; nel_y1 = nel2[2]; - n_nodes_x1 = nel_x1 + 1; n_nodes_y1 = nel_y1 + 1 + nel_x1 = nel1[1] + nel_y1 = nel2[2] + n_nodes_x1 = nel_x1 + 1 + n_nodes_y1 = nel_y1 + 1 n_nodes1 = n_nodes_x1 * n_nodes_y1 _LR = Vec{2,T}((UR[1], LL[2])) @@ -164,8 +205,16 @@ function _LinearLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, Ferrite._generate_2d_nodes!(nodes, n_nodes_x1, n_nodes_y1, LL, _LR, _UR, _UL) node_array1 = reshape(collect(1:n_nodes1), (n_nodes_x1, n_nodes_y1)) - for j in 1:nel_y1, i in 1:nel_x1 - push!(cells, Quadrilateral((node_array1[i,j], node_array1[i+1,j], node_array1[i+1,j+1], node_array1[i,j+1]))) + for j = 1:nel_y1, i = 1:nel_x1 + push!( + cells, + Quadrilateral(( + node_array1[i, j], + node_array1[i+1, j], + node_array1[i+1, j+1], + node_array1[i, j+1], + )), + ) if i == 1 push!(boundary, (length(cells), 4)) end @@ -173,13 +222,15 @@ function _LinearLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, push!(boundary, (length(cells), 1)) end end - + # Lower right rectangle - offsetstep = (MR[1] - _LR[1])/nel2[1] + offsetstep = (MR[1] - _LR[1]) / nel2[1] indexoffset = length(nodes) - nel_x2 = nel2[1] - 1; nel_y2 = nel2[2] - n_nodes_x2 = nel_x2 + 1; n_nodes_y2 = nel_y2 + 1 + nel_x2 = nel2[1] - 1 + nel_y2 = nel2[2] + n_nodes_x2 = nel_x2 + 1 + n_nodes_y2 = nel_y2 + 1 n_nodes2 = n_nodes_x2 * n_nodes_y2 _LL = Vec{2,T}((_LR[1] + offsetstep, _LR[2])) @@ -187,17 +238,34 @@ function _LinearLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, _UL = Vec{2,T}((_UR[1] + offsetstep, MR[2])) Ferrite._generate_2d_nodes!(nodes, n_nodes_x2, n_nodes_y2, _LL, _LR, MR, _UL) - node_array2 = reshape(collect(indexoffset+1:indexoffset+n_nodes2), (n_nodes_x2, n_nodes_y2)) - for j in 1:nel_y2 - push!(cells, Quadrilateral((node_array1[end,j], node_array2[1,j], node_array2[1,j+1], node_array1[end,j+1]))) + node_array2 = + reshape(collect(indexoffset+1:indexoffset+n_nodes2), (n_nodes_x2, n_nodes_y2)) + for j = 1:nel_y2 + push!( + cells, + Quadrilateral(( + node_array1[end, j], + node_array2[1, j], + node_array2[1, j+1], + node_array1[end, j+1], + )), + ) j == 1 && push!(boundary, (length(cells), 1)) j == nel_y2 && push!(boundary, (length(cells), 3)) if nel_x2 == 1 push!(boundary, (length(cells), 2)) push!(facesets["right"], (length(cells), 2)) end - for i in 1:nel_x2 - push!(cells, Quadrilateral((node_array2[i,j], node_array2[i+1,j], node_array2[i+1,j+1], node_array2[i,j+1]))) + for i = 1:nel_x2 + push!( + cells, + Quadrilateral(( + node_array2[i, j], + node_array2[i+1, j], + node_array2[i+1, j+1], + node_array2[i, j+1], + )), + ) if i == nel_x2 push!(boundary, (length(cells), 2)) push!(facesets["right"], (length(cells), 2)) @@ -210,11 +278,13 @@ function _LinearLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, push!(nodesets["load"], node_array2[end, midpointindy]) # Upper left rectangle - offsetstep = (UR[2] - MR[2])/(nel1[2] - nel2[2]) + offsetstep = (UR[2] - MR[2]) / (nel1[2] - nel2[2]) indexoffset = length(nodes) - nel_x3 = nel1[1]; nel_y3 = nel1[2] - nel2[2] - 1 - n_nodes_x3 = nel_x3 + 1; n_nodes_y3 = nel_y3 + 1 + nel_x3 = nel1[1] + nel_y3 = nel1[2] - nel2[2] - 1 + n_nodes_x3 = nel_x3 + 1 + n_nodes_y3 = nel_y3 + 1 n_nodes3 = n_nodes_x3 * n_nodes_y3 _LL = Vec{2,T}((LL[1], MR[2] + offsetstep)) @@ -223,15 +293,32 @@ function _LinearLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, Ferrite._generate_2d_nodes!(nodes, n_nodes_x3, n_nodes_y3, _LL, _LR, UR, _UL) # Generate cells - node_array3 = reshape(collect(indexoffset+1:indexoffset+n_nodes3), (n_nodes_x3, n_nodes_y3)) - - for i in 1:nel_x3 - push!(cells, Quadrilateral((node_array1[i,end], node_array1[i+1,end], node_array3[i+1,1], node_array3[i,1]))) + node_array3 = + reshape(collect(indexoffset+1:indexoffset+n_nodes3), (n_nodes_x3, n_nodes_y3)) + + for i = 1:nel_x3 + push!( + cells, + Quadrilateral(( + node_array1[i, end], + node_array1[i+1, end], + node_array3[i+1, 1], + node_array3[i, 1], + )), + ) i == 1 && push!(boundary, (length(cells), 4)) i == nel_x3 && push!(boundary, (length(cells), 2)) end - for j in 1:nel_y3, i in 1:nel_x3 - push!(cells, Quadrilateral((node_array3[i,j], node_array3[i+1,j], node_array3[i+1,j+1], node_array3[i,j+1]))) + for j = 1:nel_y3, i = 1:nel_x3 + push!( + cells, + Quadrilateral(( + node_array3[i, j], + node_array3[i+1, j], + node_array3[i+1, j+1], + node_array3[i, j+1], + )), + ) i == 1 && push!(boundary, (length(cells), 4)) i == nel_x3 && push!(boundary, (length(cells), 2)) if j == nel_y3 @@ -239,31 +326,43 @@ function _LinearLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, push!(facesets["top"], (length(cells), 3)) end end - + boundary_matrix = Ferrite.boundaries_to_sparse(boundary) - return Grid(cells, nodes, facesets=facesets, nodesets=nodesets, - boundary_matrix=boundary_matrix) + return Grid( + cells, + nodes, + facesets = facesets, + nodesets = nodesets, + boundary_matrix = boundary_matrix, + ) end -function _QuadraticLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, - LL::Vec{2,T}, UR::Vec{2,T}, MR::Vec{2,T}) where {T} +function _QuadraticLGrid( + nel1::NTuple{2,Int}, + nel2::NTuple{2,Int}, + LL::Vec{2,T}, + UR::Vec{2,T}, + MR::Vec{2,T}, +) where {T} @assert nel1[2] > nel2[2] - midpointindy = round(Int, nel2[2]/2) + 1 + midpointindy = round(Int, nel2[2] / 2) + 1 nodes = Node{2,T}[] cells = QuadraticQuadrilateral[] boundary = Tuple{Int,Int}[] - facesets = Dict{String, Set{Tuple{Int,Int}}}() - facesets["right"] = Set{Tuple{Int,Int}}() - facesets["top"] = Set{Tuple{Int,Int}}() - nodesets = Dict{String, Set{Int}}() + facesets = Dict{String,Set{Tuple{Int,Int}}}() + facesets["right"] = Set{Tuple{Int,Int}}() + facesets["top"] = Set{Tuple{Int,Int}}() + nodesets = Dict{String,Set{Int}}() nodesets["load"] = Set{Int}() - + # Lower left rectangle - nel_x1 = nel1[1]; nel_y1 = nel2[2]; - n_nodes_x1 = 2*nel_x1 + 1; n_nodes_y1 = 2*nel_y1 + 1 + nel_x1 = nel1[1] + nel_y1 = nel2[2] + n_nodes_x1 = 2 * nel_x1 + 1 + n_nodes_y1 = 2 * nel_y1 + 1 n_nodes1 = n_nodes_x1 * n_nodes_y1 _LR = Vec{2,T}((UR[1], LL[2])) @@ -272,12 +371,21 @@ function _QuadraticLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, Ferrite._generate_2d_nodes!(nodes, n_nodes_x1, n_nodes_y1, LL, _LR, _UR, _UL) node_array1 = reshape(collect(1:n_nodes1), (n_nodes_x1, n_nodes_y1)) - for j in 1:nel_y1, i in 1:nel_x1 - push!(cells, QuadraticQuadrilateral((node_array1[2*i-1,2*j-1], node_array1[2*i+1,2*j-1], - node_array1[2*i+1,2*j+1],node_array1[2*i-1,2*j+1], - node_array1[2*i,2*j-1],node_array1[2*i+1,2*j], - node_array1[2*i,2*j+1],node_array1[2*i-1,2*j], - node_array1[2*i,2*j]))) + for j = 1:nel_y1, i = 1:nel_x1 + push!( + cells, + QuadraticQuadrilateral(( + node_array1[2*i-1, 2*j-1], + node_array1[2*i+1, 2*j-1], + node_array1[2*i+1, 2*j+1], + node_array1[2*i-1, 2*j+1], + node_array1[2*i, 2*j-1], + node_array1[2*i+1, 2*j], + node_array1[2*i, 2*j+1], + node_array1[2*i-1, 2*j], + node_array1[2*i, 2*j], + )), + ) if i == 1 push!(boundary, (length(cells), 4)) end @@ -285,13 +393,15 @@ function _QuadraticLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, push!(boundary, (length(cells), 1)) end end - + # Lower right rectangle - offsetstep = (MR[1] - _LR[1])/nel2[1]/2 + offsetstep = (MR[1] - _LR[1]) / nel2[1] / 2 indexoffset = length(nodes) - nel_x2 = nel2[1] - 1; nel_y2 = nel2[2] - n_nodes_x2 = 2*nel_x2 + 2; n_nodes_y2 = 2*nel_y2 + 1 + nel_x2 = nel2[1] - 1 + nel_y2 = nel2[2] + n_nodes_x2 = 2 * nel_x2 + 2 + n_nodes_y2 = 2 * nel_y2 + 1 n_nodes2 = n_nodes_x2 * n_nodes_y2 _LL = Vec{2,T}((_LR[1] + offsetstep, _LR[2])) @@ -299,29 +409,44 @@ function _QuadraticLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, _UL = Vec{2,T}((_UR[1] + offsetstep, MR[2])) Ferrite._generate_2d_nodes!(nodes, n_nodes_x2, n_nodes_y2, _LL, _LR, MR, _UL) - node_array2 = reshape(collect(indexoffset+1:indexoffset+n_nodes2), (n_nodes_x2, n_nodes_y2)) - for j in 1:nel_y2 - push!(cells, QuadraticQuadrilateral((node_array1[end,2*j-1], node_array2[2,2*j-1], - node_array2[2,2*j+1], node_array1[end,2*j+1], - node_array2[1,2*j-1], node_array2[2,2*j], - node_array2[1,2*j+1], node_array1[end,2*j], - node_array2[1,2*j]))) + node_array2 = + reshape(collect(indexoffset+1:indexoffset+n_nodes2), (n_nodes_x2, n_nodes_y2)) + for j = 1:nel_y2 + push!( + cells, + QuadraticQuadrilateral(( + node_array1[end, 2*j-1], + node_array2[2, 2*j-1], + node_array2[2, 2*j+1], + node_array1[end, 2*j+1], + node_array2[1, 2*j-1], + node_array2[2, 2*j], + node_array2[1, 2*j+1], + node_array1[end, 2*j], + node_array2[1, 2*j], + )), + ) j == 1 && push!(boundary, (length(cells), 1)) j == nel_y2 && push!(boundary, (length(cells), 3)) if nel_x2 == 1 push!(boundary, (length(cells), 2)) push!(facesets["right"], (length(cells), 2)) end - for i in 1:nel_x2 - push!(cells, QuadraticQuadrilateral((node_array2[2*i,2*j-1], - node_array2[2*i+2,2*j-1], - node_array2[2*i+2,2*j+1], - node_array2[2*i,2*j+1], - node_array2[2*i+1,2*j-1], - node_array2[2*i+2,2*j], - node_array2[2*i+1,2*j+1], - node_array2[2*i,2*j], - node_array2[2*i+1,2*j]))) + for i = 1:nel_x2 + push!( + cells, + QuadraticQuadrilateral(( + node_array2[2*i, 2*j-1], + node_array2[2*i+2, 2*j-1], + node_array2[2*i+2, 2*j+1], + node_array2[2*i, 2*j+1], + node_array2[2*i+1, 2*j-1], + node_array2[2*i+2, 2*j], + node_array2[2*i+1, 2*j+1], + node_array2[2*i, 2*j], + node_array2[2*i+1, 2*j], + )), + ) if i == nel_x2 push!(boundary, (length(cells), 2)) push!(facesets["right"], (length(cells), 2)) @@ -334,11 +459,13 @@ function _QuadraticLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, push!(nodesets["load"], node_array2[end, midpointindy]) # Upper left rectangle - offsetstep = (UR[2] - MR[2])/(nel1[2] - nel2[2])/2 + offsetstep = (UR[2] - MR[2]) / (nel1[2] - nel2[2]) / 2 indexoffset = length(nodes) - nel_x3 = nel1[1]; nel_y3 = nel1[2] - nel2[2] - 1 - n_nodes_x3 = 2*nel_x3 + 1; n_nodes_y3 = 2*nel_y3 + 2 + nel_x3 = nel1[1] + nel_y3 = nel1[2] - nel2[2] - 1 + n_nodes_x3 = 2 * nel_x3 + 1 + n_nodes_y3 = 2 * nel_y3 + 2 n_nodes3 = n_nodes_x3 * n_nodes_y3 _LL = Vec{2,T}((LL[1], MR[2] + offsetstep)) @@ -347,27 +474,42 @@ function _QuadraticLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, Ferrite._generate_2d_nodes!(nodes, n_nodes_x3, n_nodes_y3, _LL, _LR, UR, _UL) # Generate cells - node_array3 = reshape(collect(indexoffset+1:indexoffset+n_nodes3), (n_nodes_x3, n_nodes_y3)) - - for i in 1:nel_x3 - push!(cells, QuadraticQuadrilateral((node_array1[2i-1,end], node_array1[2i+1,end], - node_array3[2i+1,2], node_array3[2i-1,2], - node_array1[2i,end], node_array3[2i+1,1], - node_array3[2i,2], node_array3[2i-1,1], - node_array3[2i,1]))) + node_array3 = + reshape(collect(indexoffset+1:indexoffset+n_nodes3), (n_nodes_x3, n_nodes_y3)) + + for i = 1:nel_x3 + push!( + cells, + QuadraticQuadrilateral(( + node_array1[2i-1, end], + node_array1[2i+1, end], + node_array3[2i+1, 2], + node_array3[2i-1, 2], + node_array1[2i, end], + node_array3[2i+1, 1], + node_array3[2i, 2], + node_array3[2i-1, 1], + node_array3[2i, 1], + )), + ) i == 1 && push!(boundary, (length(cells), 4)) i == nel_x3 && push!(boundary, (length(cells), 2)) end - for j in 1:nel_y3, i in 1:nel_x3 - push!(cells, QuadraticQuadrilateral((node_array3[2i-1,2j], - node_array3[2i+1,2j], - node_array3[2i+1,2j+2], - node_array3[2i-1,2j+2], - node_array3[2i,2j], - node_array3[2i+1,2j+1], - node_array3[2i,2j+2], - node_array3[2i-1,2j+1], - node_array3[2i,2j+1]))) + for j = 1:nel_y3, i = 1:nel_x3 + push!( + cells, + QuadraticQuadrilateral(( + node_array3[2i-1, 2j], + node_array3[2i+1, 2j], + node_array3[2i+1, 2j+2], + node_array3[2i-1, 2j+2], + node_array3[2i, 2j], + node_array3[2i+1, 2j+1], + node_array3[2i, 2j+2], + node_array3[2i-1, 2j+1], + node_array3[2i, 2j+1], + )), + ) i == 1 && push!(boundary, (length(cells), 4)) i == nel_x3 && push!(boundary, (length(cells), 2)) @@ -376,14 +518,23 @@ function _QuadraticLGrid(nel1::NTuple{2,Int}, nel2::NTuple{2,Int}, push!(facesets["top"], (length(cells), 3)) end end - + boundary_matrix = Ferrite.boundaries_to_sparse(boundary) - return Grid(cells, nodes, facesets=facesets, nodesets=nodesets, - boundary_matrix=boundary_matrix) + return Grid( + cells, + nodes, + facesets = facesets, + nodesets = nodesets, + boundary_matrix = boundary_matrix, + ) end -function TieBeamGrid(::Type{Val{CellType}}, ::Type{T}=Float64, refine=1) where {T, CellType} +function TieBeamGrid( + ::Type{Val{CellType}}, + ::Type{T} = Float64, + refine = 1, +) where {T,CellType} if CellType === :Linear return _LinearTieBeamGrid(T, refine) else @@ -391,21 +542,23 @@ function TieBeamGrid(::Type{Val{CellType}}, ::Type{T}=Float64, refine=1) where { end end -function _LinearTieBeamGrid(::Type{T}=Float64, refine=1) where {T} +function _LinearTieBeamGrid(::Type{T} = Float64, refine = 1) where {T} nodes = Node{2,T}[] cells = Quadrilateral[] boundary = Tuple{Int,Int}[] - facesets = Dict{String, Set{Tuple{Int,Int}}}() - facesets["leftfixed"] = Set{Tuple{Int,Int}}() - facesets["toproller"] = Set{Tuple{Int,Int}}() - facesets["rightload"] = Set{Tuple{Int,Int}}() + facesets = Dict{String,Set{Tuple{Int,Int}}}() + facesets["leftfixed"] = Set{Tuple{Int,Int}}() + facesets["toproller"] = Set{Tuple{Int,Int}}() + facesets["rightload"] = Set{Tuple{Int,Int}}() facesets["bottomload"] = Set{Tuple{Int,Int}}() - + # Lower left rectangle - nel_x1 = 32 * refine; nel_y1 = 3 * refine; - n_nodes_x1 = nel_x1 + 1; n_nodes_y1 = nel_y1 + 1 + nel_x1 = 32 * refine + nel_y1 = 3 * refine + n_nodes_x1 = nel_x1 + 1 + n_nodes_y1 = nel_y1 + 1 n_nodes1 = n_nodes_x1 * n_nodes_y1 - + LL = Vec{2,T}((0, 0)) LR = Vec{2,T}((T(nel_x1 / refine), T(0))) UR = Vec{2,T}((T(nel_x1 / refine), T(nel_y1 / refine))) @@ -413,9 +566,16 @@ function _LinearTieBeamGrid(::Type{T}=Float64, refine=1) where {T} Ferrite._generate_2d_nodes!(nodes, n_nodes_x1, n_nodes_y1, LL, LR, UR, UL) node_array1 = reshape(collect(1:n_nodes1), (n_nodes_x1, n_nodes_y1)) - for j in 1:nel_y1, i in 1:nel_x1 - push!(cells, Quadrilateral((node_array1[i,j], node_array1[i+1,j], - node_array1[i+1,j+1],node_array1[i,j+1]))) + for j = 1:nel_y1, i = 1:nel_x1 + push!( + cells, + Quadrilateral(( + node_array1[i, j], + node_array1[i+1, j], + node_array1[i+1, j+1], + node_array1[i, j+1], + )), + ) if i == 1 cidx = length(cells) push!(boundary, (cidx, 4)) @@ -439,24 +599,32 @@ function _LinearTieBeamGrid(::Type{T}=Float64, refine=1) where {T} end end - nel_x2 = 1 * refine; nel_y2 = 3 * refine + refine - 1 - n_nodes_x2 = nel_x2 + 1; n_nodes_y2 = nel_y2 + 1 + nel_x2 = 1 * refine + nel_y2 = 3 * refine + refine - 1 + n_nodes_x2 = nel_x2 + 1 + n_nodes_y2 = nel_y2 + 1 n_nodes2 = n_nodes_x2 * n_nodes_y2 indexoffset = length(nodes) LL = Vec{2,T}((T(30), (nel_y1 + T(1)) / refine)) LR = Vec{2,T}((T(31), (nel_y1 + T(1)) / refine)) UR = Vec{2,T}((T(31), nel_y1 / refine + T(4))) UL = Vec{2,T}((T(30), nel_y1 / refine + T(4))) - + Ferrite._generate_2d_nodes!(nodes, n_nodes_x2, n_nodes_y2, LL, LR, UR, UL) - node_array2 = reshape(collect(indexoffset+1:indexoffset+n_nodes2), (n_nodes_x2, n_nodes_y2)) + node_array2 = + reshape(collect(indexoffset+1:indexoffset+n_nodes2), (n_nodes_x2, n_nodes_y2)) t = 30 - for i in 1:refine - push!(cells, Quadrilateral((node_array1[t*refine+i, nel_y1 + 1], - node_array1[t*refine+i+1, nel_y1 + 1], - node_array2[i+1,1], - node_array2[i,1]))) + for i = 1:refine + push!( + cells, + Quadrilateral(( + node_array1[t*refine+i, nel_y1+1], + node_array1[t*refine+i+1, nel_y1+1], + node_array2[i+1, 1], + node_array2[i, 1], + )), + ) if i == 1 cidx = length(cells) push!(boundary, (cidx, 4)) @@ -467,9 +635,16 @@ function _LinearTieBeamGrid(::Type{T}=Float64, refine=1) where {T} end end - for j in 1:nel_y2, i in 1:nel_x2 - push!(cells, Quadrilateral((node_array2[i,j], node_array2[i+1,j], - node_array2[i+1,j+1], node_array2[i,j+1]))) + for j = 1:nel_y2, i = 1:nel_x2 + push!( + cells, + Quadrilateral(( + node_array2[i, j], + node_array2[i+1, j], + node_array2[i+1, j+1], + node_array2[i, j+1], + )), + ) if i == 1 cidx = length(cells) push!(boundary, (cidx, 4)) @@ -486,38 +661,48 @@ function _LinearTieBeamGrid(::Type{T}=Float64, refine=1) where {T} end boundary_matrix = Ferrite.boundaries_to_sparse(boundary) - return Grid(cells, nodes, facesets=facesets, - boundary_matrix=boundary_matrix) + return Grid(cells, nodes, facesets = facesets, boundary_matrix = boundary_matrix) end -function _QuadraticTieBeamGrid(::Type{T}=Float64, refine = 1) where {T} +function _QuadraticTieBeamGrid(::Type{T} = Float64, refine = 1) where {T} nodes = Node{2,T}[] cells = QuadraticQuadrilateral[] boundary = Tuple{Int,Int}[] - facesets = Dict{String, Set{Tuple{Int,Int}}}() - facesets["leftfixed"] = Set{Tuple{Int,Int}}() - facesets["toproller"] = Set{Tuple{Int,Int}}() - facesets["rightload"] = Set{Tuple{Int,Int}}() + facesets = Dict{String,Set{Tuple{Int,Int}}}() + facesets["leftfixed"] = Set{Tuple{Int,Int}}() + facesets["toproller"] = Set{Tuple{Int,Int}}() + facesets["rightload"] = Set{Tuple{Int,Int}}() facesets["bottomload"] = Set{Tuple{Int,Int}}() - + # Lower left rectangle - nel_x1 = 32*refine; nel_y1 = 3*refine; - n_nodes_x1 = 2*nel_x1 + 1; n_nodes_y1 = 2*nel_y1 + 1 + nel_x1 = 32 * refine + nel_y1 = 3 * refine + n_nodes_x1 = 2 * nel_x1 + 1 + n_nodes_y1 = 2 * nel_y1 + 1 n_nodes1 = n_nodes_x1 * n_nodes_y1 - + LL = Vec{2,T}((0, 0)) - LR = Vec{2,T}((T(nel_x1/refine), T(0))) - UR = Vec{2,T}((T(nel_x1/refine), T(nel_y1/refine))) - UL = Vec{2,T}((T(0), T(nel_y1/refine))) + LR = Vec{2,T}((T(nel_x1 / refine), T(0))) + UR = Vec{2,T}((T(nel_x1 / refine), T(nel_y1 / refine))) + UL = Vec{2,T}((T(0), T(nel_y1 / refine))) Ferrite._generate_2d_nodes!(nodes, n_nodes_x1, n_nodes_y1, LL, LR, UR, UL) node_array1 = reshape(collect(1:n_nodes1), (n_nodes_x1, n_nodes_y1)) - for j in 1:nel_y1, i in 1:nel_x1 - push!(cells, QuadraticQuadrilateral((node_array1[2*i-1,2*j-1], node_array1[2*i+1,2*j-1], - node_array1[2*i+1,2*j+1],node_array1[2*i-1,2*j+1], - node_array1[2*i,2*j-1],node_array1[2*i+1,2*j], - node_array1[2*i,2*j+1],node_array1[2*i-1,2*j], - node_array1[2*i,2*j]))) + for j = 1:nel_y1, i = 1:nel_x1 + push!( + cells, + QuadraticQuadrilateral(( + node_array1[2*i-1, 2*j-1], + node_array1[2*i+1, 2*j-1], + node_array1[2*i+1, 2*j+1], + node_array1[2*i-1, 2*j+1], + node_array1[2*i, 2*j-1], + node_array1[2*i+1, 2*j], + node_array1[2*i, 2*j+1], + node_array1[2*i-1, 2*j], + node_array1[2*i, 2*j], + )), + ) if i == 1 cidx = length(cells) push!(boundary, (cidx, 4)) @@ -541,29 +726,37 @@ function _QuadraticTieBeamGrid(::Type{T}=Float64, refine = 1) where {T} end end - nel_x2 = 1*refine; nel_y2 = 3*refine + refine - 1 - n_nodes_x2 = 2*nel_x2 + 1; n_nodes_y2 = 2*nel_y2 + 2 + nel_x2 = 1 * refine + nel_y2 = 3 * refine + refine - 1 + n_nodes_x2 = 2 * nel_x2 + 1 + n_nodes_y2 = 2 * nel_y2 + 2 n_nodes2 = n_nodes_x2 * n_nodes_y2 indexoffset = length(nodes) LL = Vec{2,T}((T(30), (nel_y1 + T(0.5)) / refine)) LR = Vec{2,T}((T(31), (nel_y1 + T(0.5)) / refine)) - UR = Vec{2,T}((T(31), nel_y1/refine + T(4))) - UL = Vec{2,T}((T(30), nel_y1/refine + T(4))) - + UR = Vec{2,T}((T(31), nel_y1 / refine + T(4))) + UL = Vec{2,T}((T(30), nel_y1 / refine + T(4))) + Ferrite._generate_2d_nodes!(nodes, n_nodes_x2, n_nodes_y2, LL, LR, UR, UL) - node_array2 = reshape(collect(indexoffset+1:indexoffset+n_nodes2), (n_nodes_x2, n_nodes_y2)) + node_array2 = + reshape(collect(indexoffset+1:indexoffset+n_nodes2), (n_nodes_x2, n_nodes_y2)) t = 30 - for i in 1:refine - push!(cells, QuadraticQuadrilateral((node_array1[2*(refine*t+i-1)+1, 2*nel_y1+1], - node_array1[2*(refine*t+i-1)+3, 2*nel_y1+1], - node_array2[1+2i, 2], - node_array2[2i-1, 2], - node_array1[2*(refine*t+i-1)+2, 2*nel_y1+1], - node_array2[1+2i, 1], - node_array2[2i, 2], - node_array2[2i-1, 1], - node_array2[2i, 1]))) + for i = 1:refine + push!( + cells, + QuadraticQuadrilateral(( + node_array1[2*(refine*t+i-1)+1, 2*nel_y1+1], + node_array1[2*(refine*t+i-1)+3, 2*nel_y1+1], + node_array2[1+2i, 2], + node_array2[2i-1, 2], + node_array1[2*(refine*t+i-1)+2, 2*nel_y1+1], + node_array2[1+2i, 1], + node_array2[2i, 2], + node_array2[2i-1, 1], + node_array2[2i, 1], + )), + ) if i == 1 cidx = length(cells) @@ -571,16 +764,25 @@ function _QuadraticTieBeamGrid(::Type{T}=Float64, refine = 1) where {T} end if i == refine cidx = length(cells) - push!(boundary, (cidx, 2)) + push!(boundary, (cidx, 2)) end end - for j in 1:nel_y2, i in 1:nel_x2 - push!(cells, QuadraticQuadrilateral((node_array2[2*i-1,2*j], node_array2[2*i+1,2*j], - node_array2[2*i+1,2*j+2],node_array2[2*i-1,2*j+2], - node_array2[2*i,2*j],node_array2[2*i+1,2*j+1], - node_array2[2*i,2*j+2],node_array2[2*i-1,2*j+1], - node_array2[2*i,2*j+1]))) + for j = 1:nel_y2, i = 1:nel_x2 + push!( + cells, + QuadraticQuadrilateral(( + node_array2[2*i-1, 2*j], + node_array2[2*i+1, 2*j], + node_array2[2*i+1, 2*j+2], + node_array2[2*i-1, 2*j+2], + node_array2[2*i, 2*j], + node_array2[2*i+1, 2*j+1], + node_array2[2*i, 2*j+2], + node_array2[2*i-1, 2*j+1], + node_array2[2*i, 2*j+1], + )), + ) if i == 1 cidx = length(cells) push!(boundary, (cidx, 4)) @@ -597,6 +799,5 @@ function _QuadraticTieBeamGrid(::Type{T}=Float64, refine = 1) where {T} end boundary_matrix = Ferrite.boundaries_to_sparse(boundary) - return Grid(cells, nodes, facesets=facesets, - boundary_matrix=boundary_matrix) + return Grid(cells, nodes, facesets = facesets, boundary_matrix = boundary_matrix) end diff --git a/src/TopOptProblems/matrices_and_vectors.jl b/src/TopOptProblems/matrices_and_vectors.jl index 45207190..38b0fe9e 100644 --- a/src/TopOptProblems/matrices_and_vectors.jl +++ b/src/TopOptProblems/matrices_and_vectors.jl @@ -2,43 +2,43 @@ function gettypes( ::Type{T}, # number type ::Type{Val{:Static}}, # matrix type ::Type{Val{Kesize}}, # matrix size -) where {T, Kesize} - return SMatrix{Kesize, Kesize, T, Kesize^2}, SVector{Kesize, T} +) where {T,Kesize} + return SMatrix{Kesize,Kesize,T,Kesize^2}, SVector{Kesize,T} end function gettypes( ::Type{T}, # number type ::Type{Val{:SMatrix}}, # matrix type ::Type{Val{Kesize}}, # matrix size -) where {T, Kesize} - return SMatrix{Kesize, Kesize, T, Kesize^2}, SVector{Kesize, T} +) where {T,Kesize} + return SMatrix{Kesize,Kesize,T,Kesize^2}, SVector{Kesize,T} end function gettypes( ::Type{T}, # number type ::Type{Val{:MMatrix}}, # matrix type ::Type{Val{Kesize}}, # matrix size -) where {T, Kesize} - return MMatrix{Kesize, Kesize, T, Kesize^2}, MVector{Kesize, T} +) where {T,Kesize} + return MMatrix{Kesize,Kesize,T,Kesize^2}, MVector{Kesize,T} end function gettypes( ::Type{BigFloat}, # number type ::Type{Val{:Static}}, # matrix type ::Type{Val{Kesize}}, # matrix size ) where {Kesize} - return SizedMatrix{Kesize, Kesize, BigFloat, Kesize^2}, SizedVector{Kesize, BigFloat} + return SizedMatrix{Kesize,Kesize,BigFloat,Kesize^2}, SizedVector{Kesize,BigFloat} end function gettypes( ::Type{BigFloat}, # number type ::Type{Val{:SMatrix}}, # matrix type ::Type{Val{Kesize}}, # matrix size ) where {Kesize} - return SizedMatrix{Kesize, Kesize, BigFloat, Kesize^2}, SizedVector{Kesize, BigFloat} + return SizedMatrix{Kesize,Kesize,BigFloat,Kesize^2}, SizedVector{Kesize,BigFloat} end function gettypes( ::Type{BigFloat}, # number type ::Type{Val{:MMatrix}}, # matrix type ::Type{Val{Kesize}}, # matrix size ) where {Kesize} - return SizedMatrix{Kesize, Kesize, BigFloat, Kesize^2}, SizedVector{Kesize, BigFloat} + return SizedMatrix{Kesize,Kesize,BigFloat,Kesize^2}, SizedVector{Kesize,BigFloat} end function gettypes( ::Type{T}, # number type @@ -50,12 +50,12 @@ end initialize_K(sp::StiffnessTopOptProblem) = Symmetric(create_sparsity_pattern(sp.ch.dh)) -initialize_f(sp::StiffnessTopOptProblem{dim, T}) where {dim, T} = zeros(T, ndofs(sp.ch.dh)) +initialize_f(sp::StiffnessTopOptProblem{dim,T}) where {dim,T} = zeros(T, ndofs(sp.ch.dh)) -function make_Kes_and_fes(problem, quad_order=2) +function make_Kes_and_fes(problem, quad_order = 2) make_Kes_and_fes(problem, quad_order, Val{:Static}) end -function make_Kes_and_fes(problem, ::Type{Val{mat_type}}) where mat_type +function make_Kes_and_fes(problem, ::Type{Val{mat_type}}) where {mat_type} make_Kes_and_fes(problem, 2, Val{mat_type}) end function make_Kes_and_fes(problem, quad_order, ::Type{Val{mat_type}}) where {mat_type} @@ -69,42 +69,59 @@ function make_Kes_and_fes(problem, quad_order, ::Type{Val{mat_type}}) where {mat refshape = Ferrite.getrefshape(dh.field_interpolations[1]) - λ = E*ν / ((1 + ν) * (1 - 2*ν)) - μ = E / (2*(1 + ν)) - δ(i,j) = i == j ? T(1) : T(0) - g(i,j,k,l) = λ*δ(i,j)*δ(k,l) + μ*(δ(i,k)*δ(j,l) + δ(i,l)*δ(j,k)) - C = SymmetricTensor{4, dim}(g) + λ = E * ν / ((1 + ν) * (1 - 2 * ν)) + μ = E / (2 * (1 + ν)) + δ(i, j) = i == j ? T(1) : T(0) + g(i, j, k, l) = λ * δ(i, j) * δ(k, l) + μ * (δ(i, k) * δ(j, l) + δ(i, l) * δ(j, k)) + C = SymmetricTensor{4,dim}(g) # Shape functions and quadrature rule - interpolation_space = Lagrange{dim, refshape, geom_order}() - quadrature_rule = QuadratureRule{dim, refshape}(quad_order) + interpolation_space = Lagrange{dim,refshape,geom_order}() + quadrature_rule = QuadratureRule{dim,refshape}(quad_order) cellvalues = CellScalarValues(quadrature_rule, interpolation_space) - facevalues = FaceScalarValues(QuadratureRule{dim-1, refshape}(quad_order), interpolation_space) + facevalues = + FaceScalarValues(QuadratureRule{dim - 1,refshape}(quad_order), interpolation_space) # Calculate element stiffness matrices n_basefuncs = getnbasefunctions(cellvalues) - - Kesize = dim*n_basefuncs + + Kesize = dim * n_basefuncs MatrixType, VectorType = gettypes(T, Val{mat_type}, Val{Kesize}) - Kes, weights = _make_Kes_and_weights(dh, Tuple{MatrixType, VectorType}, Val{n_basefuncs}, Val{dim*n_basefuncs}, C, ρ, quadrature_rule, cellvalues) + Kes, weights = _make_Kes_and_weights( + dh, + Tuple{MatrixType,VectorType}, + Val{n_basefuncs}, + Val{dim * n_basefuncs}, + C, + ρ, + quadrature_rule, + cellvalues, + ) dloads = _make_dloads(weights, problem, facevalues) return Kes, weights, dloads, cellvalues, facevalues end -const g = [0., 9.81, 0.] # N/kg or m/s^2 +const g = [0.0, 9.81, 0.0] # N/kg or m/s^2 # Element stiffness matrices are StaticArrays # `weights` : a vector of `xdim` vectors, element_id => self-weight load vector -function _make_Kes_and_weights(dh::DofHandler{dim, N, T}, ::Type{Tuple{MatrixType, VectorType}}, - ::Type{Val{n_basefuncs}}, ::Type{Val{Kesize}}, C, ρ, quadrature_rule, cellvalues) where { - dim, N, T, MatrixType <: StaticArray, VectorType, n_basefuncs, Kesize} +function _make_Kes_and_weights( + dh::DofHandler{dim,N,T}, + ::Type{Tuple{MatrixType,VectorType}}, + ::Type{Val{n_basefuncs}}, + ::Type{Val{Kesize}}, + C, + ρ, + quadrature_rule, + cellvalues, +) where {dim,N,T,MatrixType<:StaticArray,VectorType,n_basefuncs,Kesize} # Calculate element stiffness matrices nel = getncells(dh.grid) body_force = ρ .* g # Force per unit volume - Kes = Symmetric{T, MatrixType}[] + Kes = Symmetric{T,MatrixType}[] sizehint!(Kes, nel) - weights = [zeros(VectorType) for i in 1:nel] + weights = [zeros(VectorType) for i = 1:nel] Ke_e = zeros(T, dim, dim) fe = zeros(T, Kesize) Ke_0 = Matrix{T}(undef, Kesize, Kesize) @@ -113,19 +130,19 @@ function _make_Kes_and_weights(dh::DofHandler{dim, N, T}, ::Type{Tuple{MatrixTyp Ke_0 .= 0 reinit!(cellvalues, cell) fe = weights[k] - for q_point in 1:getnquadpoints(cellvalues) + for q_point = 1:getnquadpoints(cellvalues) dΩ = getdetJdV(cellvalues, q_point) - for b in 1:n_basefuncs + for b = 1:n_basefuncs ∇ϕb = shape_gradient(cellvalues, q_point, b) ϕb = shape_value(cellvalues, q_point, b) - for d2 in 1:dim - fe = @set fe[(b-1)*dim + d2] += ϕb * body_force[d2] * dΩ - for a in 1:n_basefuncs + for d2 = 1:dim + fe = @set fe[(b-1)*dim+d2] += ϕb * body_force[d2] * dΩ + for a = 1:n_basefuncs ∇ϕa = shape_gradient(cellvalues, q_point, a) Ke_e .= dotdot(∇ϕa, C, ∇ϕb) * dΩ - for d1 in 1:dim + for d1 = 1:dim #if dim*(b-1) + d2 >= dim*(a-1) + d1 - Ke_0[dim*(a-1) + d1, dim*(b-1) + d2] += Ke_e[d1,d2] + Ke_0[dim*(a-1)+d1, dim*(b-1)+d2] += Ke_e[d1, d2] #end end end @@ -143,44 +160,42 @@ function _make_Kes_and_weights(dh::DofHandler{dim, N, T}, ::Type{Tuple{MatrixTyp end # Fallback function _make_Kes_and_weights( - dh::DofHandler{dim, N, T}, - ::Type{Tuple{MatrixType, VectorType}}, + dh::DofHandler{dim,N,T}, + ::Type{Tuple{MatrixType,VectorType}}, ::Type{Val{n_basefuncs}}, ::Type{Val{Kesize}}, C, ρ, quadrature_rule, cellvalues, -) where { - dim, N, T, MatrixType, VectorType, n_basefuncs, Kesize, -} +) where {dim,N,T,MatrixType,VectorType,n_basefuncs,Kesize} # Calculate element stiffness matrices nel = getncells(dh.grid) body_force = ρ .* g # Force per unit volume - Kes = let Kesize=Kesize, nel=nel + Kes = let Kesize = Kesize, nel = nel [Symmetric(zeros(T, Kesize, Kesize), :U) for i = 1:nel] end - weights = let Kesize=Kesize, nel=nel + weights = let Kesize = Kesize, nel = nel [zeros(T, Kesize) for i = 1:nel] end - Ke_e = zeros(T, dim, dim) + Ke_e = zeros(T, dim, dim) celliterator = CellIterator(dh) for (k, cell) in enumerate(celliterator) reinit!(cellvalues, cell) fe = weights[k] - for q_point in 1:getnquadpoints(cellvalues) + for q_point = 1:getnquadpoints(cellvalues) dΩ = getdetJdV(cellvalues, q_point) - for b in 1:n_basefuncs + for b = 1:n_basefuncs ∇ϕb = shape_gradient(cellvalues, q_point, b) ϕb = shape_value(cellvalues, q_point, b) - for d2 in 1:dim - fe[(b-1)*dim + d2] += ϕb * body_force[d2] * dΩ - for a in 1:n_basefuncs + for d2 = 1:dim + fe[(b-1)*dim+d2] += ϕb * body_force[d2] * dΩ + for a = 1:n_basefuncs ∇ϕa = shape_gradient(cellvalues, q_point, a) Ke_e .= dotdot(∇ϕa, C, ∇ϕb) * dΩ - for d1 in 1:dim + for d1 = 1:dim #if dim*(b-1) + d2 >= dim*(a-1) + d1 - Kes[k].data[dim*(a-1) + d1, dim*(b-1) + d2] += Ke_e[d1,d2] + Kes[k].data[dim*(a-1)+d1, dim*(b-1)+d2] += Ke_e[d1, d2] #end end end @@ -201,7 +216,7 @@ function _make_dloads(fes, problem, facevalues) N = nnodespercell(problem) T = floattype(problem) dloads = deepcopy(fes) - for i in 1:length(dloads) + for i = 1:length(dloads) if eltype(dloads) <: SArray dloads[i] = zero(eltype(dloads)) else @@ -212,26 +227,27 @@ function _make_dloads(fes, problem, facevalues) dh = getdh(problem) grid = dh.grid boundary_matrix = grid.boundary_matrix - cell_coords = zeros(Ferrite.Vec{dim, T}, N) + cell_coords = zeros(Ferrite.Vec{dim,T}, N) n_basefuncs = getnbasefunctions(facevalues) for k in keys(pressuredict) t = -pressuredict[k] # traction = negative the pressure faceset = getfacesets(problem)[k] for (cellid, faceid) in faceset - boundary_matrix[faceid, cellid] || throw("Face $((cellid, faceid)) not on boundary.") + boundary_matrix[faceid, cellid] || + throw("Face $((cellid, faceid)) not on boundary.") fe = dloads[cellid] getcoordinates!(cell_coords, grid, cellid) reinit!(facevalues, cell_coords, faceid) - for q_point in 1:getnquadpoints(facevalues) + for q_point = 1:getnquadpoints(facevalues) dΓ = getdetJdV(facevalues, q_point) # Face area normal = getnormal(facevalues, q_point) # Nomral vector at quad point - for i in 1:n_basefuncs + for i = 1:n_basefuncs ϕ = shape_value(facevalues, q_point, i) # Shape function value for d = 1:dim if fe isa SArray - fe = @set fe[(i-1)*dim + d] += ϕ * t * normal[d] * dΓ + fe = @set fe[(i-1)*dim+d] += ϕ * t * normal[d] * dΓ else - fe[(i-1)*dim + d] += ϕ * t * normal[d] * dΓ + fe[(i-1)*dim+d] += ϕ * t * normal[d] * dΓ end end end @@ -239,7 +255,7 @@ function _make_dloads(fes, problem, facevalues) dloads[cellid] = fe end end - + return dloads end diff --git a/src/TopOptProblems/metadata.jl b/src/TopOptProblems/metadata.jl index 5742d76e..880089f9 100644 --- a/src/TopOptProblems/metadata.jl +++ b/src/TopOptProblems/metadata.jl @@ -17,14 +17,14 @@ An instance of the `Metadata` type stores ... information such as: node_dofs[j,nodeidx] = j-th dof of node `nodeidx` """ @params struct Metadata - cell_dofs - dof_cells + cell_dofs::Any + dof_cells::Any #node_first_cells::TTupleVec - node_cells - node_dofs + node_cells::Any + node_dofs::Any end -function Metadata(dh::DofHandler{dim}) where dim +function Metadata(dh::DofHandler{dim}) where {dim} cell_dofs = get_cell_dofs_matrix(dh) dof_cells = get_dof_cells_matrix(dh, cell_dofs) #node_first_cells = get_node_first_cells(dh) @@ -39,10 +39,10 @@ Returns a `ndof_per_cell` x `ncells` matrix that maps `[localdofidx, cellidx]` i """ function get_cell_dofs_matrix(dh) cell_dofs = zeros(Int, ndofs_per_cell(dh), getncells(dh.grid)) - for i in 1:size(cell_dofs, 2) + for i = 1:size(cell_dofs, 2) r = dh.cell_dofs_offset[i]:dh.cell_dofs_offset[i+1]-1 - for j in 1:length(r) - cell_dofs[j,i] = dh.cell_dofs[r[j]] + for j = 1:length(r) + cell_dofs[j, i] = dh.cell_dofs[r[j]] end end cell_dofs @@ -61,23 +61,23 @@ Returns dof_cells_matrix[dofidx] = [(cellidx, cell's local dof idx), ...] """ function get_dof_cells_matrix(dh, cell_dofs) - dof_cells_vecofvecs = [Vector{Tuple{Int,Int}}() for i in 1:ndofs(dh)] + dof_cells_vecofvecs = [Vector{Tuple{Int,Int}}() for i = 1:ndofs(dh)] l = 0 - for cellidx in 1:size(cell_dofs, 2) - for localidx in 1:size(cell_dofs, 1) + for cellidx = 1:size(cell_dofs, 2) + for localidx = 1:size(cell_dofs, 1) dofidx = cell_dofs[localidx, cellidx] push!(dof_cells_vecofvecs[dofidx], (cellidx, localidx)) l += 1 end end - return RaggedArray(dof_cells_vecofvecs) + return RaggedArray(dof_cells_vecofvecs) end function get_node_first_cells(dh) - node_first_cells = fill((0,0), getnnodes(dh.grid)) + node_first_cells = fill((0, 0), getnnodes(dh.grid)) visited = falses(getnnodes(dh.grid)) - for cellidx in 1:getncells(dh.grid) + for cellidx = 1:getncells(dh.grid) for (local_node_idx, global_node_idx) in enumerate(dh.grid.cells[cellidx].nodes) if !visited[global_node_idx] visited[global_node_idx] = true @@ -96,7 +96,7 @@ Returns dof_cells_matrix[nodeidx] = [(cellidx, cell's local nodal idx), ...] """ function get_node_cells(dh) - node_cells_vecofvecs = [Vector{Tuple{Int,Int}}() for i in 1:ndofs(dh)] + node_cells_vecofvecs = [Vector{Tuple{Int,Int}}() for i = 1:ndofs(dh)] l = 0 for (cellidx, cell) in enumerate(CellIterator(dh)) for (localidx, nodeidx) in enumerate(cell.nodes) @@ -122,18 +122,18 @@ function get_node_dofs(dh::DofHandler) _celldofs = fill(0, ndofs_per_cell(dh)) node_dofs = zeros(Int, ndofspernode, nnodes) visited = falses(nnodes) - for field in 1:nfields + for field = 1:nfields field_dim = dh.field_dims[field] node_offset = node_field_offset(dh, field) offset = Ferrite.field_offset(dh, dh.field_names[field]) for (cellidx, cell) in enumerate(dh.grid.cells) celldofs!(_celldofs, dh, cellidx) # update the dofs for this cell - for idx in 1:min(interpol_points, length(cell.nodes)) + for idx = 1:min(interpol_points, length(cell.nodes)) node = cell.nodes[idx] if !visited[node] - noderange = (offset + (idx-1)*field_dim + 1):(offset + idx*field_dim) # the dofs in this node - for i in 1:field_dim - node_dofs[node_offset+i,node] = _celldofs[noderange[i]] + noderange = (offset+(idx-1)*field_dim+1):(offset+idx*field_dim) # the dofs in this node + for i = 1:field_dim + node_dofs[node_offset+i, node] = _celldofs[noderange[i]] end visited[node] = true end diff --git a/src/TopOptProblems/multiload.jl b/src/TopOptProblems/multiload.jl index 171b2030..f1f141b9 100644 --- a/src/TopOptProblems/multiload.jl +++ b/src/TopOptProblems/multiload.jl @@ -11,12 +11,14 @@ f3 = RandomMagnitude(normalize([-1, -1]), Uniform(0.5, 1.5)) base_problem = PointLoadCantilever(Val{:Linear}, (160, 40), (1.0, 1.0), 1.0, 0.3, 1.0) problem = MultiLoad(base_problem, [(160, 20) => f1, (80, 40) => f2, (120, 0) => f3], 10000) """ -struct MultiLoad{dim, T, TP <: StiffnessTopOptProblem{dim, T}, TF} <: StiffnessTopOptProblem{dim, T} +struct MultiLoad{dim,T,TP<:StiffnessTopOptProblem{dim,T},TF} <: + StiffnessTopOptProblem{dim,T} problem::TP F::TF end @forward_property MultiLoad problem -for F in (:getE, :getν, :nnodespercell, :getcloaddict, :getdim, :getpressuredict, :getfacesets) +for F in + (:getE, :getν, :nnodespercell, :getcloaddict, :getdim, :getpressuredict, :getfacesets) @eval $F(p::MultiLoad) = $F(p.problem) end function MultiLoad(problem::StiffnessTopOptProblem, N::Int, load_rules::Vector{<:Pair}) @@ -25,7 +27,7 @@ function MultiLoad(problem::StiffnessTopOptProblem, N::Int, load_rules::Vector{< V = Float64[] for (pos, f) in load_rules dofs = find_nearest_dofs(problem, pos) - for i in 1:N + for i = 1:N load = f() append!(I, dofs) push!(J, fill(i, length(dofs))...) @@ -50,7 +52,7 @@ function find_nearest_dofs(problem, p) closest = 0 for (i, n) in enumerate(grid.nodes) dist = norm(n.x .- p) - if dist < shortest + if dist < shortest shortest = dist closest = i end @@ -59,7 +61,7 @@ function find_nearest_dofs(problem, p) return problem.metadata.node_dofs[:, closest] end -struct RandomMagnitude{Tf, Tdist} <: Function +struct RandomMagnitude{Tf,Tdist} <: Function f::Tf dist::Tdist end @@ -71,19 +73,19 @@ function random_direction() end function get_surface_dofs(problem::StiffnessTopOptProblem) - dh = problem.ch.dh - boundary_matrix = dh.grid.boundary_matrix - interpolation = dh.field_interpolations[1] - celliterator = Ferrite.CellIterator(dh) - node_dofs = problem.metadata.node_dofs + dh = problem.ch.dh + boundary_matrix = dh.grid.boundary_matrix + interpolation = dh.field_interpolations[1] + celliterator = Ferrite.CellIterator(dh) + node_dofs = problem.metadata.node_dofs - faces, cells, _ = findnz(boundary_matrix) + faces, cells, _ = findnz(boundary_matrix) surface_node_inds = Int[] - for i in 1:length(cells) - cellind = cells[i] - faceind = faces[i] - face = [Ferrite.faces(interpolation)[faceind]...] - Ferrite.reinit!(celliterator, cellind) + for i = 1:length(cells) + cellind = cells[i] + faceind = faces[i] + face = [Ferrite.faces(interpolation)[faceind]...] + Ferrite.reinit!(celliterator, cellind) nodeinds = celliterator.nodes[face] append!(surface_node_inds, nodeinds) end @@ -101,15 +103,15 @@ function generate_random_loads( surface_dofs = get_surface_dofs(problem) FI = Int[] - FJ = Int[] - FV = Float64[] + FJ = Int[] + FV = Float64[] nodeinds = rand(1:size(surface_dofs, 2), N) - for i in 1:N + for i = 1:N load = loadrule() dofs = surface_dofs[:, nodeinds[i]] - append!(FI, dofs) - push!(FJ, i, i) - append!(FV, load) - end - return sparse(FI, FJ, FV) + append!(FI, dofs) + push!(FJ, i, i) + append!(FV, load) + end + return sparse(FI, FJ, FV) end diff --git a/src/TopOptProblems/problem_types.jl b/src/TopOptProblems/problem_types.jl index cfc8d3a7..1972be73 100644 --- a/src/TopOptProblems/problem_types.jl +++ b/src/TopOptProblems/problem_types.jl @@ -10,20 +10,20 @@ An abstract stiffness topology optimization problem. All subtypes must have the - `white`: a `BitVector` of length equal to the number of elements where `white[e]` is 1 iff the `e`^th element must not be part of the final design - `varind`: an `AbstractVector{Int}` of length equal to the number of elements where `varind[e]` gives the index of the decision variable corresponding to element `e`. Because some elements can be fixed to be black or white, not every element has a decision variable associated. """ -abstract type StiffnessTopOptProblem{dim, T} <: AbstractTopOptProblem end +abstract type StiffnessTopOptProblem{dim,T} <: AbstractTopOptProblem end # Fallbacks -getdim(::StiffnessTopOptProblem{dim, T}) where {dim, T} = dim -floattype(::StiffnessTopOptProblem{dim, T}) where {dim, T} = T +getdim(::StiffnessTopOptProblem{dim,T}) where {dim,T} = dim +floattype(::StiffnessTopOptProblem{dim,T}) where {dim,T} = T getE(p::StiffnessTopOptProblem) = p.E getν(p::StiffnessTopOptProblem) = p.ν getgeomorder(p::StiffnessTopOptProblem) = nnodespercell(p) == 9 ? 2 : 1 -getdensity(::StiffnessTopOptProblem{dim, T}) where {dim, T} = T(0) +getdensity(::StiffnessTopOptProblem{dim,T}) where {dim,T} = T(0) getmetadata(p::StiffnessTopOptProblem) = p.metadata getdh(p::StiffnessTopOptProblem) = p.ch.dh -getcloaddict(p::StiffnessTopOptProblem{dim, T}) where {dim, T} = Dict{String, Vector{T}}() -getpressuredict(p::StiffnessTopOptProblem{dim, T}) where {dim, T} = Dict{String, T}() -getfacesets(p::StiffnessTopOptProblem{dim, T}) where {dim, T} = Dict{String, Tuple{Int, T}}() +getcloaddict(p::StiffnessTopOptProblem{dim,T}) where {dim,T} = Dict{String,Vector{T}}() +getpressuredict(p::StiffnessTopOptProblem{dim,T}) where {dim,T} = Dict{String,T}() +getfacesets(p::StiffnessTopOptProblem{dim,T}) where {dim,T} = Dict{String,Tuple{Int,T}}() Ferrite.getncells(problem::StiffnessTopOptProblem) = Ferrite.getncells(getdh(problem).grid) @@ -65,11 +65,11 @@ end - `white`: a `BitVector` of length equal to the number of elements where `white[e]` is 1 iff the `e`^th element must not be part of the final design - `varind`: an `AbstractVector{Int}` of length equal to the number of elements where `varind[e]` gives the index of the decision variable corresponding to element `e`. Because some elements can be fixed to be black or white, not every element has a decision variable associated. """ -@params struct PointLoadCantilever{dim, T, N, M} <: StiffnessTopOptProblem{dim, T} - rect_grid::RectilinearGrid{dim, T, N, M} +@params struct PointLoadCantilever{dim,T,N,M} <: StiffnessTopOptProblem{dim,T} + rect_grid::RectilinearGrid{dim,T,N,M} E::T ν::T - ch::ConstraintHandler{<:DofHandler{dim, <:Cell{dim,N,M}, T}, T} + ch::ConstraintHandler{<:DofHandler{dim,<:Cell{dim,N,M},T},T} force::T force_dof::Integer black::AbstractVector @@ -77,7 +77,8 @@ end varind::AbstractVector{Int} metadata::Metadata end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::PointLoadCantilever) = println("TopOpt point load cantilever beam problem") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::PointLoadCantilever) = + println("TopOpt point load cantilever beam problem") """ PointLoadCantilever(::Type{Val{CellType}}, nels::NTuple{dim,Int}, sizes::NTuple{dim}, E, ν, force) where {dim, CellType} @@ -107,8 +108,16 @@ celltype = :Linear problem = PointLoadCantilever(Val{celltype}, nels, sizes, E, ν, force) ``` """ -function PointLoadCantilever(::Type{Val{CellType}}, nels::NTuple{dim,Int}, sizes::NTuple{dim}, E = 1.0, ν = 0.3, force = 1.0) where {dim, CellType} - iseven(nels[2]) && (length(nels) < 3 || iseven(nels[3])) || throw("Grid does not have an even number of elements along the y and/or z axes.") +function PointLoadCantilever( + ::Type{Val{CellType}}, + nels::NTuple{dim,Int}, + sizes::NTuple{dim}, + E = 1.0, + ν = 0.3, + force = 1.0, +) where {dim,CellType} + iseven(nels[2]) && (length(nels) < 3 || iseven(nels[3])) || + throw("Grid does not have an even number of elements along the y and/or z axes.") _T = promote_type(eltype(sizes), typeof(E), typeof(ν), typeof(force)) if _T <: Integer @@ -122,38 +131,47 @@ function PointLoadCantilever(::Type{Val{CellType}}, nels::NTuple{dim,Int}, sizes rect_grid = RectilinearGrid(Val{:Quadratic}, nels, T.(sizes)) end - if haskey(rect_grid.grid.facesets, "fixed_all") + if haskey(rect_grid.grid.facesets, "fixed_all") pop!(rect_grid.grid.facesets, "fixed_all") end #addfaceset!(rect_grid.grid, "fixed_all", x -> left(rect_grid, x)); - addnodeset!(rect_grid.grid, "fixed_all", x -> left(rect_grid, x)); - - if haskey(rect_grid.grid.nodesets, "down_force") + addnodeset!(rect_grid.grid, "fixed_all", x -> left(rect_grid, x)) + + if haskey(rect_grid.grid.nodesets, "down_force") pop!(rect_grid.grid.nodesets, "down_force") end - addnodeset!(rect_grid.grid, "down_force", x -> right(rect_grid, x) && middley(rect_grid, x)); + addnodeset!( + rect_grid.grid, + "down_force", + x -> right(rect_grid, x) && middley(rect_grid, x), + ) # Create displacement field u dh = DofHandler(rect_grid.grid) if CellType === :Linear || dim === 3 push!(dh, :u, dim) # Add a displacement field else - ip = Lagrange{2, RefCube, 2}() + ip = Lagrange{2,RefCube,2}() push!(dh, :u, dim, ip) # Add a displacement field end close!(dh) - + ch = ConstraintHandler(dh) #dbc = Dirichlet(:u, getfaceset(rect_grid.grid, "fixed_all"), (x,t) -> zeros(T, dim), collect(1:dim)) - dbc = Dirichlet(:u, getnodeset(rect_grid.grid, "fixed_all"), (x,t) -> zeros(T, dim), collect(1:dim)) + dbc = Dirichlet( + :u, + getnodeset(rect_grid.grid, "fixed_all"), + (x, t) -> zeros(T, dim), + collect(1:dim), + ) add!(ch, dbc) close!(ch) t = T(0) update!(ch, t) metadata = Metadata(dh) - + fnode = Tuple(getnodeset(rect_grid.grid, "down_force"))[1] node_dofs = metadata.node_dofs force_dof = node_dofs[2, fnode] @@ -163,8 +181,19 @@ function PointLoadCantilever(::Type{Val{CellType}}, nels::NTuple{dim,Int}, sizes black, white = find_black_and_white(dh) varind = find_varind(black, white) - - return PointLoadCantilever(rect_grid, E, ν, ch, force, force_dof, black, white, varind, metadata) + + return PointLoadCantilever( + rect_grid, + E, + ν, + ch, + force, + force_dof, + black, + white, + varind, + metadata, + ) end """ @@ -208,11 +237,11 @@ end - `white`: a `BitVector` of length equal to the number of elements where `white[e]` is 1 iff the `e`^th element must not be part of the final design - `varind`: an `AbstractVector{Int}` of length equal to the number of elements where `varind[e]` gives the index of the decision variable corresponding to element `e`. Because some elements can be fixed to be black or white, not every element has a decision variable associated. """ -@params struct HalfMBB{dim, T, N, M} <: StiffnessTopOptProblem{dim, T} - rect_grid::RectilinearGrid{dim, T, N, M} +@params struct HalfMBB{dim,T,N,M} <: StiffnessTopOptProblem{dim,T} + rect_grid::RectilinearGrid{dim,T,N,M} E::T ν::T - ch::ConstraintHandler{<:DofHandler{dim, <:Cell{dim,N,M}, T}, T} + ch::ConstraintHandler{<:DofHandler{dim,<:Cell{dim,N,M},T},T} force::T force_dof::Integer black::AbstractVector @@ -220,7 +249,8 @@ end varind::AbstractVector{Int} metadata::Metadata end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::HalfMBB) = println("TopOpt half MBB problem") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::HalfMBB) = + println("TopOpt half MBB problem") """ HalfMBB(::Type{Val{CellType}}, nels::NTuple{dim,Int}, sizes::NTuple{dim}, E, ν, force) where {dim, CellType} @@ -250,7 +280,14 @@ celltype = :Linear problem = HalfMBB(Val{celltype}, nels, sizes, E, ν, force) ``` """ -function HalfMBB(::Type{Val{CellType}}, nels::NTuple{dim,Int}, sizes::NTuple{dim}, E = 1.0, ν = 0.3, force = 1.0) where {dim, CellType} +function HalfMBB( + ::Type{Val{CellType}}, + nels::NTuple{dim,Int}, + sizes::NTuple{dim}, + E = 1.0, + ν = 0.3, + force = 1.0, +) where {dim,CellType} _T = promote_type(eltype(sizes), typeof(E), typeof(ν), typeof(force)) if _T <: Integer T = Float64 @@ -267,33 +304,37 @@ function HalfMBB(::Type{Val{CellType}}, nels::NTuple{dim,Int}, sizes::NTuple{dim pop!(rect_grid.grid.facesets, "fixed_u1") end #addfaceset!(rect_grid.grid, "fixed_u1", x -> left(rect_grid, x)); - addnodeset!(rect_grid.grid, "fixed_u1", x -> left(rect_grid, x)); - + addnodeset!(rect_grid.grid, "fixed_u1", x -> left(rect_grid, x)) + if haskey(rect_grid.grid.nodesets, "fixed_u2") pop!(rect_grid.grid.nodesets, "fixed_u2") end - addnodeset!(rect_grid.grid, "fixed_u2", x -> bottom(rect_grid, x) && right(rect_grid, x)); + addnodeset!( + rect_grid.grid, + "fixed_u2", + x -> bottom(rect_grid, x) && right(rect_grid, x), + ) if haskey(rect_grid.grid.nodesets, "down_force") pop!(rect_grid.grid.nodesets, "down_force") end - addnodeset!(rect_grid.grid, "down_force", x -> top(rect_grid, x) && left(rect_grid, x)); + addnodeset!(rect_grid.grid, "down_force", x -> top(rect_grid, x) && left(rect_grid, x)) # Create displacement field u dh = DofHandler(rect_grid.grid) if CellType === :Linear || dim === 3 push!(dh, :u, dim) else - ip = Lagrange{2, RefCube, 2}() + ip = Lagrange{2,RefCube,2}() push!(dh, :u, dim, ip) end close!(dh) - + ch = ConstraintHandler(dh) #dbc1 = Dirichlet(:u, getfaceset(rect_grid.grid, "fixed_u1"), (x,t)->T[0], [1]) - dbc1 = Dirichlet(:u, getnodeset(rect_grid.grid, "fixed_u1"), (x,t)->T[0], [1]) + dbc1 = Dirichlet(:u, getnodeset(rect_grid.grid, "fixed_u1"), (x, t) -> T[0], [1]) add!(ch, dbc1) - dbc2 = Dirichlet(:u, getnodeset(rect_grid.grid, "fixed_u2"), (x,t)->T[0], [2]) + dbc2 = Dirichlet(:u, getnodeset(rect_grid.grid, "fixed_u2"), (x, t) -> T[0], [2]) add!(ch, dbc2) close!(ch) @@ -315,11 +356,11 @@ function HalfMBB(::Type{Val{CellType}}, nels::NTuple{dim,Int}, sizes::NTuple{dim return HalfMBB(rect_grid, E, ν, ch, force, force_dof, black, white, varind, metadata) end -nnodespercell(p::Union{PointLoadCantilever, HalfMBB}) = nnodespercell(p.rect_grid) -function getcloaddict(p::Union{PointLoadCantilever{dim, T}, HalfMBB{dim, T}}) where {dim, T} +nnodespercell(p::Union{PointLoadCantilever,HalfMBB}) = nnodespercell(p.rect_grid) +function getcloaddict(p::Union{PointLoadCantilever{dim,T},HalfMBB{dim,T}}) where {dim,T} f = T[0, -p.force, 0] fnode = Tuple(getnodeset(p.rect_grid.grid, "down_force"))[1] - return Dict{Int, Vector{T}}(fnode => f) + return Dict{Int,Vector{T}}(fnode => f) end """ @@ -363,10 +404,10 @@ end - `white`: a `BitVector` of length equal to the number of elements where `white[e]` is 1 iff the `e`^th element must not be part of the final design - `varind`: an `AbstractVector{Int}` of length equal to the number of elements where `varind[e]` gives the index of the decision variable corresponding to element `e`. Because some elements can be fixed to be black or white, not every element has a decision variable associated. """ -@params struct LBeam{T, N, M} <: StiffnessTopOptProblem{2, T} +@params struct LBeam{T,N,M} <: StiffnessTopOptProblem{2,T} E::T ν::T - ch::ConstraintHandler{<:DofHandler{2, <:Cell{2,N,M}, T}, T} + ch::ConstraintHandler{<:DofHandler{2,<:Cell{2,N,M},T},T} force::T force_dof::Integer black::AbstractVector @@ -401,22 +442,38 @@ celltype = :Linear problem = LBeam(Val{celltype}, E = E, ν = ν, force = force) ``` """ -function LBeam(::Type{Val{CellType}}, ::Type{T}=Float64; length = 100, height = 100, upperslab = 50, lowerslab = 50, E = 1.0, ν = 0.3, force = 1.0) where {T, CellType} +function LBeam( + ::Type{Val{CellType}}, + ::Type{T} = Float64; + length = 100, + height = 100, + upperslab = 50, + lowerslab = 50, + E = 1.0, + ν = 0.3, + force = 1.0, +) where {T,CellType} # Create displacement field u - grid = LGrid(Val{CellType}, T, length=length, height=height, upperslab=upperslab, - lowerslab=lowerslab) + grid = LGrid( + Val{CellType}, + T, + length = length, + height = height, + upperslab = upperslab, + lowerslab = lowerslab, + ) dh = DofHandler(grid) if CellType === :Linear push!(dh, :u, 2) else - ip = Lagrange{2, RefCube, 2}() + ip = Lagrange{2,RefCube,2}() push!(dh, :u, 2, ip) end close!(dh) - + ch = ConstraintHandler(dh) - dbc = Dirichlet(:u, getfaceset(grid, "top"), (x,t)->T[0, 0], [1, 2]) + dbc = Dirichlet(:u, getfaceset(grid, "top"), (x, t) -> T[0, 0], [1, 2]) add!(ch, dbc) close!(ch) @@ -437,16 +494,16 @@ function LBeam(::Type{Val{CellType}}, ::Type{T}=Float64; length = 100, height = return LBeam(E, ν, ch, force, force_dof, black, white, varind, metadata) end -function boundingbox(grid::Ferrite.Grid{dim}) where dim - xmin1 = minimum(n->n.x[1], grid.nodes) - xmax1 = maximum(n->n.x[1], grid.nodes) - xmin2 = minimum(n->n.x[2], grid.nodes) - xmax2 = maximum(n->n.x[2], grid.nodes) +function boundingbox(grid::Ferrite.Grid{dim}) where {dim} + xmin1 = minimum(n -> n.x[1], grid.nodes) + xmax1 = maximum(n -> n.x[1], grid.nodes) + xmin2 = minimum(n -> n.x[2], grid.nodes) + xmax2 = maximum(n -> n.x[2], grid.nodes) if dim === 2 return ((xmin1, xmin2), (xmax1, xmax2)) else - xmin3 = minimum(n->n.x[3], grid.nodes) - xmax3 = maximum(n->n.x[3], grid.nodes) + xmin3 = minimum(n -> n.x[3], grid.nodes) + xmax3 = maximum(n -> n.x[3], grid.nodes) return ((xmin1, xmin2, xmin3), (xmax1, xmax2, xmax3)) end end @@ -459,7 +516,8 @@ function RectilinearTopology(b, topology = ones(getncells(getdh(b).grid))) if go === 1 rectgrid = generate_grid(Quadrilateral, nels, Vec{dim}(bb[1]), Vec{dim}(bb[2])) elseif go === 2 - rectgrid = generate_grid(QuadraticQuadrilateral, nels, Vec{dim}(bb[1]), Vec{dim}(bb[2])) + rectgrid = + generate_grid(QuadraticQuadrilateral, nels, Vec{dim}(bb[1]), Vec{dim}(bb[2])) else throw("Unsupported geometry.") end @@ -472,12 +530,12 @@ function RectilinearTopology(b, topology = ones(getncells(getdh(b).grid))) return copy(reshape(new_topology, nels)') end -nnodespercell(p::LBeam{T, N}) where {T, N} = N +nnodespercell(p::LBeam{T,N}) where {T,N} = N getdim(::LBeam) = 2 function getcloaddict(p::LBeam{T}) where {T} f = T[0, -p.force] fnode = Tuple(getnodeset(getdh(p).grid, "load"))[1] - return Dict{Int, Vector{T}}(fnode => f) + return Dict{Int,Vector{T}}(fnode => f) end """ @@ -521,17 +579,18 @@ end - `white`: a `BitVector` of length equal to the number of elements where `white[e]` is 1 iff the `e`^th element must not be part of the final design - `varind`: an `AbstractVector{Int}` of length equal to the number of elements where `varind[e]` gives the index of the decision variable corresponding to element `e`. Because some elements can be fixed to be black or white, not every element has a decision variable associated. """ -@params struct TieBeam{T, N, M} <: StiffnessTopOptProblem{2, T} +@params struct TieBeam{T,N,M} <: StiffnessTopOptProblem{2,T} E::T ν::T force::T - ch::ConstraintHandler{<:DofHandler{2, <:Cell{2,N,M}, T}, T} + ch::ConstraintHandler{<:DofHandler{2,<:Cell{2,N,M},T},T} black::AbstractVector white::AbstractVector varind::AbstractVector{Int} metadata::Metadata end -Base.show(::IO, ::MIME{Symbol("text/plain")}, ::TieBeam) = println("TopOpt tie-beam problem") +Base.show(::IO, ::MIME{Symbol("text/plain")}, ::TieBeam) = + println("TopOpt tie-beam problem") """ TieBeam(::Type{Val{CellType}}, ::Type{T} = Float64, refine = 1, force = T(1); E = T(1), ν = T(0.3)) where {T, CellType} @@ -543,21 +602,28 @@ Base.show(::IO, ::MIME{Symbol("text/plain")}, ::TieBeam) = println("TopOpt tie-b - `refine`: an integer value of 1 or greater that specifies the mesh refinement extent. A value of 1 gives the standard tie-beam problem in literature. - `CellType`: can be either `:Linear` or `:Quadratic` to determine the order of the geometric and field basis functions and element type. Only isoparametric elements are supported for now. """ -function TieBeam(::Type{Val{CellType}}, ::Type{T} = Float64, refine = 1, force = T(1); E = T(1), ν = T(0.3)) where {T, CellType} +function TieBeam( + ::Type{Val{CellType}}, + ::Type{T} = Float64, + refine = 1, + force = T(1); + E = T(1), + ν = T(0.3), +) where {T,CellType} grid = TieBeamGrid(Val{CellType}, T, refine) dh = DofHandler(grid) if CellType === :Linear push!(dh, :u, 2) else - ip = Lagrange{2, RefCube, 2}() + ip = Lagrange{2,RefCube,2}() push!(dh, :u, 2, ip) end close!(dh) - + ch = ConstraintHandler(dh) - dbc1 = Dirichlet(:u, getfaceset(grid, "leftfixed"), (x,t)->T[0, 0], [1, 2]) + dbc1 = Dirichlet(:u, getfaceset(grid, "leftfixed"), (x, t) -> T[0, 0], [1, 2]) add!(ch, dbc1) - dbc2 = Dirichlet(:u, getfaceset(grid, "toproller"), (x,t)->T[0], [2]) + dbc2 = Dirichlet(:u, getfaceset(grid, "toproller"), (x, t) -> T[0], [2]) add!(ch, dbc2) close!(ch) @@ -573,8 +639,9 @@ function TieBeam(::Type{Val{CellType}}, ::Type{T} = Float64, refine = 1, force = end getdim(::TieBeam) = 2 -nnodespercell(::TieBeam{T, N}) where {T, N} = N -getpressuredict(p::TieBeam{T}) where {T} = Dict{String, T}("rightload"=>2*p.force, "bottomload"=>-p.force) +nnodespercell(::TieBeam{T,N}) where {T,N} = N +getpressuredict(p::TieBeam{T}) where {T} = + Dict{String,T}("rightload" => 2 * p.force, "bottomload" => -p.force) getfacesets(p::TieBeam) = getdh(p).grid.facesets @@ -598,18 +665,22 @@ Constructs an instance of the type `RayProblem` that is a 2D beam with: - Loads specified in `loads` where `loads` is a dictionary mapping the location of each load to its vector value, e.g. `Dict([10, 18] => [1.0, -1.0], [5, 5] => [1.0, -1.0])` which defines a load of `[1.0, -1.0]` at the point located at `[10, 18]` and a similar load at the point located at `[5, 5]`. ``` """ -@params struct RayProblem{T, N, M} <: StiffnessTopOptProblem{2, T} - rect_grid::RectilinearGrid{2, T, N, M} +@params struct RayProblem{T,N,M} <: StiffnessTopOptProblem{2,T} + rect_grid::RectilinearGrid{2,T,N,M} E::T ν::T - ch::ConstraintHandler{<:DofHandler{2, <:Cell{2, N, M}, T}, T} + ch::ConstraintHandler{<:DofHandler{2,<:Cell{2,N,M},T},T} loads::Dict black::AbstractVector white::AbstractVector varind::AbstractVector{Int} metadata::Metadata end -function RayProblem(nels::NTuple{2, Int}, pins::Vector{<:Vector}, loads::Dict{<:Vector, <:Vector}) +function RayProblem( + nels::NTuple{2,Int}, + pins::Vector{<:Vector}, + loads::Dict{<:Vector,<:Vector}, +) T = Float64 rect_grid = RectilinearGrid(Val{:Linear}, nels, (1.0, 1.0)) dim = length(nels) @@ -621,22 +692,27 @@ function RayProblem(nels::NTuple{2, Int}, pins::Vector{<:Vector}, loads::Dict{<: addnodeset!(rect_grid.grid, "fixed$i", x -> x ≈ pin) end for (i, k) in enumerate(keys(loads)) - if haskey(rect_grid.grid.nodesets, "force$i") + if haskey(rect_grid.grid.nodesets, "force$i") pop!(rect_grid.grid.nodesets, "force$i") end - addnodeset!(rect_grid.grid, "force$i", x -> x ≈ k); + addnodeset!(rect_grid.grid, "force$i", x -> x ≈ k) end # Create displacement field u dh = DofHandler(rect_grid.grid) - ip = Lagrange{2, RefCube, 1}() + ip = Lagrange{2,RefCube,1}() push!(dh, :u, dim, ip) # Add a displacement field close!(dh) ch = ConstraintHandler(dh) - for i in 1:length(pins) - dbc = Dirichlet(:u, getnodeset(rect_grid.grid, "fixed$i"), (x,t) -> zeros(T, dim), collect(1:dim)) + for i = 1:length(pins) + dbc = Dirichlet( + :u, + getnodeset(rect_grid.grid, "fixed$i"), + (x, t) -> zeros(T, dim), + collect(1:dim), + ) add!(ch, dbc) end close!(ch) @@ -646,11 +722,13 @@ function RayProblem(nels::NTuple{2, Int}, pins::Vector{<:Vector}, loads::Dict{<: metadata = Metadata(dh) black, white = find_black_and_white(dh) varind = find_varind(black, white) - - loadsdict = Dict{Int, Vector{Float64}}(map(enumerate(keys(loads))) do (i, k) - fnode = Tuple(getnodeset(rect_grid.grid, "force$i"))[1] - (fnode => loads[k]) - end) + + loadsdict = Dict{Int,Vector{Float64}}( + map(enumerate(keys(loads))) do (i, k) + fnode = Tuple(getnodeset(rect_grid.grid, "force$i"))[1] + (fnode => loads[k]) + end, + ) return RayProblem(rect_grid, 1.0, 0.3, ch, loadsdict, black, white, varind, metadata) end diff --git a/src/TrussTopOptProblems/TrussIO/TrussIO.jl b/src/TrussTopOptProblems/TrussIO/TrussIO.jl index b884731c..d241ca91 100644 --- a/src/TrussTopOptProblems/TrussIO/TrussIO.jl +++ b/src/TrussTopOptProblems/TrussIO/TrussIO.jl @@ -20,4 +20,3 @@ include("parse_geo.jl") export load_truss_json, load_truss_geo end - diff --git a/src/TrussTopOptProblems/TrussIO/parse_geo.jl b/src/TrussTopOptProblems/TrussIO/parse_geo.jl index e7fc7da6..1ed8e930 100644 --- a/src/TrussTopOptProblems/TrussIO/parse_geo.jl +++ b/src/TrussTopOptProblems/TrussIO/parse_geo.jl @@ -14,8 +14,8 @@ function load_truss_geo(io::IOStream) name = readline(io) nnodes, nelements = parse.(iT, split(strip(chomp(readline(io))))) - node_points = Dict{iT, SVector{ndim, T}}() - for i in 1:nnodes + node_points = Dict{iT,SVector{ndim,T}}() + for i = 1:nnodes strs = split(strip(chomp(readline(io)))) node_id = parse(iT, strs[1]) point = parse.(T, strs[2:end]) @@ -23,8 +23,8 @@ function load_truss_geo(io::IOStream) node_points[node_id] = SVector{ndim,T}(point...) end - elements = Dict{iT, Tuple{iT,iT}}() - for i in 1:nelements + elements = Dict{iT,Tuple{iT,iT}}() + for i = 1:nelements strs = split(strip(chomp(readline(io)))) elem_id = parse(iT, strs[1]) node_ids = parse.(iT, strs[2:end]) @@ -37,8 +37,8 @@ function load_truss_geo(io::IOStream) @assert nloadcases == 1 load_cases = Dict() for lc_ind = 1:nloadcases - ploads = Dict{iT, SVector{ndim, T}}() - for pl in 1:nloaded_nodes + ploads = Dict{iT,SVector{ndim,T}}() + for pl = 1:nloaded_nodes strs = split(strip(chomp(readline(io)))) node_id = parse(iT, strs[1]) load = parse.(T, strs[2:end]) @@ -49,8 +49,8 @@ function load_truss_geo(io::IOStream) end nfixities = parse(iT, strip(chomp(readline(io)))) - fixities = Dict{iT, SVector{ndim, Bool}}() - for i=1:nfixities + fixities = Dict{iT,SVector{ndim,Bool}}() + for i = 1:nfixities strs = split(strip(chomp(readline(io)))) node_id = parse(iT, strs[1]) condition = map(!, parse.(Bool, strs[2:end])) @@ -59,4 +59,4 @@ function load_truss_geo(io::IOStream) end return node_points, elements, fixities, load_cases -end \ No newline at end of file +end diff --git a/src/TrussTopOptProblems/TrussIO/parse_json.jl b/src/TrussTopOptProblems/TrussIO/parse_json.jl index acc91301..146a7561 100644 --- a/src/TrussTopOptProblems/TrussIO/parse_json.jl +++ b/src/TrussTopOptProblems/TrussIO/parse_json.jl @@ -16,7 +16,7 @@ function load_truss_json(io::IO) #io::Stream{format"JSON"}) iT = Int T = Float64 - node_points = Dict{iT, SVector{ndim, T}}() + node_points = Dict{iT,SVector{ndim,T}}() for (i, ndata) in enumerate(data["nodes"]) node_points[i] = convert(SVector{ndim,T}, ndata["point"]) if "node_ind" in keys(ndata) @@ -25,12 +25,12 @@ function load_truss_json(io::IO) #io::Stream{format"JSON"}) end @assert length(node_points) == n - elements = Dict{iT, Tuple{iT,iT}}() + elements = Dict{iT,Tuple{iT,iT}}() element_inds_from_tag = Dict() for (i, edata) in enumerate(data["elements"]) elements[i] = (edata["end_node_inds"]...,) .+ 1 if "elem_ind" in keys(edata) - @assert 1+edata["elem_ind"] == i + @assert 1 + edata["elem_ind"] == i end elem_tag = edata["elem_tag"] if elem_tag ∉ keys(element_inds_from_tag) @@ -99,9 +99,9 @@ function load_truss_json(io::IO) #io::Stream{format"JSON"}) # TODO only translation dof for now @assert(length(data["supports"]) > 0) - fixities = Dict{iT, SVector{ndim, Bool}}() + fixities = Dict{iT,SVector{ndim,Bool}}() for sdata in data["supports"] - supp_v = iT(sdata["node_ind"])+1 + supp_v = iT(sdata["node_ind"]) + 1 fixities[supp_v] = sdata["condition"][1:ndim] end @@ -109,13 +109,13 @@ function load_truss_json(io::IO) #io::Stream{format"JSON"}) for (lc_ind, lc_data) in data["loadcases"] nploads = length(lc_data["ploads"]) @assert nploads > 0 - ploads = Dict{iT, SVector{ndim, T}}() + ploads = Dict{iT,SVector{ndim,T}}() for pl in lc_data["ploads"] - load_v = pl["node_ind"]+1 + load_v = pl["node_ind"] + 1 ploads[load_v] = convert(SVector{ndim,T}, pl["force"]) end load_cases[lc_ind] = ploads end return node_points, elements, mats, crosssecs, fixities, load_cases -end \ No newline at end of file +end diff --git a/src/TrussTopOptProblems/TrussVisualization/TrussVisualization.jl b/src/TrussTopOptProblems/TrussVisualization/TrussVisualization.jl index a6361236..4c406037 100644 --- a/src/TrussTopOptProblems/TrussVisualization/TrussVisualization.jl +++ b/src/TrussTopOptProblems/TrussVisualization/TrussVisualization.jl @@ -3,10 +3,10 @@ module TrussVisualization using Requires function __init__() - @require Makie="ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" @eval begin + @require Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" @eval begin include("makie.jl") export visualize end end -end \ No newline at end of file +end diff --git a/src/TrussTopOptProblems/TrussVisualization/makie.jl b/src/TrussTopOptProblems/TrussVisualization/makie.jl index 252de42f..56de3d02 100644 --- a/src/TrussTopOptProblems/TrussVisualization/makie.jl +++ b/src/TrussTopOptProblems/TrussVisualization/makie.jl @@ -1,7 +1,6 @@ import .Makie using .Makie: lift, cam3d!, Point3f0, Vec3f0, Figure, Auto, linesegments!, Point2f0 -using .Makie: DataAspect, Axis, labelslidergrid!, set_close_to!, - labelslider!, LScene +using .Makie: DataAspect, Axis, labelslidergrid!, set_close_to!, labelslider!, LScene using ...TopOpt.TopOptProblems: getdim using ..TrussTopOptProblems: TrussProblem, get_fixities_node_set_name @@ -18,15 +17,23 @@ using LinearAlgebra: norm # return scene, layout # end -function visualize(problem::TrussProblem, u; - topology=nothing, +function visualize( + problem::TrussProblem, + u; + topology = nothing, # stress=nothing, - undeformed_mesh_color=(:gray, 1.0), - deformed_mesh_color=(:cyan, 0.4), - vector_arrowsize=0.3, vector_linewidth=1.0, - default_support_scale=1.0, default_load_scale=1.0, scale_range=1.0, - default_exagg_scale=1.0, exagg_range=10.0, - default_element_linewidth_scale=6.0, element_linewidth_range=10.0) + undeformed_mesh_color = (:gray, 1.0), + deformed_mesh_color = (:cyan, 0.4), + vector_arrowsize = 0.3, + vector_linewidth = 1.0, + default_support_scale = 1.0, + default_load_scale = 1.0, + scale_range = 1.0, + default_exagg_scale = 1.0, + exagg_range = 10.0, + default_element_linewidth_scale = 6.0, + element_linewidth_range = 10.0, +) ndim = getdim(problem) ncells = Ferrite.getncells(problem) nnodes = Ferrite.getnnodes(problem) @@ -35,10 +42,10 @@ function visualize(problem::TrussProblem, u; if topology !== nothing @assert(ncells == length(topology)) - a = reshape([topology topology]', 2*ncells) + a = reshape([topology topology]', 2 * ncells) # a ./= maximum(a) else - a = ones(2*ncells) + a = ones(2 * ncells) end # if stress !== nothing # @assert(ncells == length(stress)) @@ -57,10 +64,13 @@ function visualize(problem::TrussProblem, u; nodes = problem.truss_grid.grid.nodes PtT = ndim == 2 ? Point2f0 : Point3f0 - edges_pts = [PtT(nodes[cell.nodes[1]].x) => PtT(nodes[cell.nodes[2]].x) for cell in problem.truss_grid.grid.cells] + edges_pts = [ + PtT(nodes[cell.nodes[1]].x) => PtT(nodes[cell.nodes[2]].x) for + cell in problem.truss_grid.grid.cells + ] if ndim == 2 - ax1 = Axis(fig[1,1]) + ax1 = Axis(fig[1, 1]) # tightlimits!(ax1) # ax1.aspect = AxisAspect(1) ax1.aspect = DataAspect() @@ -68,18 +78,21 @@ function visualize(problem::TrussProblem, u; # https://jkrumbiegel.github.io/MakieLayout.jl/v0.3/layoutables/#LScene-1 # https://makie.juliaplots.org/stable/cameras.html#D-Camera # ax1 = layout[1, 1] = LScene(scene, camera = cam3d!, raw = false) - ax1 = LScene(fig[1,1], scenekw = (camera = cam3d!, raw = false), height=750) + ax1 = LScene(fig[1, 1], scenekw = (camera = cam3d!, raw = false), height = 750) end # TODO show the ground mesh in another Axis https://makie.juliaplots.org/stable/makielayout/grids.html # ax1.title = "Truss TopOpt result" # sl1 = layout[2, 1] = LSlider(scene, range = 0.01:0.01:10, startvalue = 1.0) - lsgrid = labelslidergrid!(fig, - ["deformation exaggeration", - "support scale", "load scale", - "element linewidth"], - [LinRange(0.0:0.01:exagg_range), LinRange(0.0:0.01:scale_range), LinRange(0.0:0.01:scale_range), - LinRange(0.0:0.01:element_linewidth_range)]; + lsgrid = labelslidergrid!( + fig, + ["deformation exaggeration", "support scale", "load scale", "element linewidth"], + [ + LinRange(0.0:0.01:exagg_range), + LinRange(0.0:0.01:scale_range), + LinRange(0.0:0.01:scale_range), + LinRange(0.0:0.01:element_linewidth_range), + ]; width = Auto(), tellheight = false, ) @@ -87,60 +100,69 @@ function visualize(problem::TrussProblem, u; set_close_to!(lsgrid.sliders[2], default_support_scale) set_close_to!(lsgrid.sliders[3], default_load_scale) set_close_to!(lsgrid.sliders[4], default_element_linewidth_scale) - fig[2,1] = lsgrid.layout + fig[2, 1] = lsgrid.layout # * undeformed truss elements - element_linewidth = lift(s -> a.*s, lsgrid.sliders[4].value) - linesegments!(edges_pts, - linewidth = element_linewidth, - color = undeformed_mesh_color) + element_linewidth = lift(s -> a .* s, lsgrid.sliders[4].value) + linesegments!(edges_pts, linewidth = element_linewidth, color = undeformed_mesh_color) # # * deformed truss elements if norm(u) > eps() node_dofs = problem.metadata.node_dofs @assert length(u) == ndim * nnodes - exagg_edge_pts = lift(s -> - [PtT(nodes[cell.nodes[1]].x) + PtT(u[node_dofs[:,cell.nodes[1]]]*s) => - PtT(nodes[cell.nodes[2]].x) + PtT(u[node_dofs[:,cell.nodes[2]]]*s) for cell in problem.truss_grid.grid.cells], - lsgrid.sliders[1].value) - linesegments!(exagg_edge_pts, - linewidth = element_linewidth, - color = deformed_mesh_color) + exagg_edge_pts = lift( + s -> [ + PtT(nodes[cell.nodes[1]].x) + PtT(u[node_dofs[:, cell.nodes[1]]] * s) => + PtT(nodes[cell.nodes[2]].x) + PtT(u[node_dofs[:, cell.nodes[2]]] * s) for + cell in problem.truss_grid.grid.cells + ], + lsgrid.sliders[1].value, + ) + linesegments!( + exagg_edge_pts, + linewidth = element_linewidth, + color = deformed_mesh_color, + ) end # * fixties vectors - for i=1:ndim + for i = 1:ndim nodeset_name = get_fixities_node_set_name(i) fixed_node_ids = Ferrite.getnodeset(problem.truss_grid.grid, nodeset_name) dir = zeros(ndim) dir[i] = 1.0 - scaled_base_pts = lift(s->[PtT(nodes[node_id].x) - PtT(dir*s) for node_id in fixed_node_ids], - lsgrid.sliders[2].value) - scaled_fix_dirs = lift(s->fill(PtT(dir*s), length(fixed_node_ids)), lsgrid.sliders[2].value) + scaled_base_pts = lift( + s -> [PtT(nodes[node_id].x) - PtT(dir * s) for node_id in fixed_node_ids], + lsgrid.sliders[2].value, + ) + scaled_fix_dirs = + lift(s -> fill(PtT(dir * s), length(fixed_node_ids)), lsgrid.sliders[2].value) Makie.arrows!( scaled_base_pts, scaled_fix_dirs, - arrowcolor=:orange, - arrowsize=vector_arrowsize, - linecolor=:orange, - linewidth=vector_linewidth, + arrowcolor = :orange, + arrowsize = vector_arrowsize, + linecolor = :orange, + linewidth = vector_linewidth, ) end # * load vectors - scaled_load_dirs = lift(s->[PtT(force/norm(force)*s) for force in values(problem.force)], - lsgrid.sliders[3].value) + scaled_load_dirs = lift( + s -> [PtT(force / norm(force) * s) for force in values(problem.force)], + lsgrid.sliders[3].value, + ) Makie.arrows!( [PtT(nodes[node_id].x) for node_id in keys(problem.force)], scaled_load_dirs, - arrowcolor=:purple, - arrowsize=vector_arrowsize, - linecolor=:purple, - linewidth=vector_linewidth, + arrowcolor = :purple, + arrowsize = vector_arrowsize, + linecolor = :purple, + linewidth = vector_linewidth, ) fig end -function visualize(problem::TrussProblem{xdim, T}; kwargs...) where {xdim, T} +function visualize(problem::TrussProblem{xdim,T}; kwargs...) where {xdim,T} nnodes = Ferrite.getnnodes(problem) u = zeros(T, xdim * nnodes) visualize(problem, u; kwargs...) diff --git a/src/TrussTopOptProblems/elementinfo.jl b/src/TrussTopOptProblems/elementinfo.jl index 0e731fbe..d75e0e8f 100644 --- a/src/TrussTopOptProblems/elementinfo.jl +++ b/src/TrussTopOptProblems/elementinfo.jl @@ -11,11 +11,19 @@ Constructs an instance of `ElementFEAInfo` from a stiffness **truss** problem `s The static matrices and vectors are more performant and GPU-compatible therefore they are used by default. """ -function ElementFEAInfo(sp::TrussProblem, quad_order = 1, ::Type{Val{mat_type}} = Val{:Static},) where {mat_type} +function ElementFEAInfo( + sp::TrussProblem, + quad_order = 1, + ::Type{Val{mat_type}} = Val{:Static}, +) where {mat_type} # weights: self-weight element load vectors, all zeros now Kes, weights, cellvalues, facevalues = make_Kes_and_fes(sp, quad_order, Val{mat_type}) - element_Kes = convert(Vector{<:ElementMatrix}, Kes; - bc_dofs = sp.ch.prescribed_dofs, dof_cells = sp.metadata.dof_cells) + element_Kes = convert( + Vector{<:ElementMatrix}, + Kes; + bc_dofs = sp.ch.prescribed_dofs, + dof_cells = sp.metadata.dof_cells, + ) # * concentrated load # ? why convert a sparse vector back to a Vector? @@ -25,20 +33,33 @@ function ElementFEAInfo(sp::TrussProblem, quad_order = 1, ::Type{Val{mat_type}} cellvolumes = get_cell_volumes(sp, cellvalues) cells = sp.ch.dh.grid.cells - ElementFEAInfo(element_Kes, weights, fixedload, cellvolumes, - cellvalues, facevalues, - sp.metadata, sp.black, sp.white, sp.varind, cells) + ElementFEAInfo( + element_Kes, + weights, + fixedload, + cellvolumes, + cellvalues, + facevalues, + sp.metadata, + sp.black, + sp.white, + sp.varind, + cells, + ) end #################################### -function get_cell_volumes(sp::TrussProblem{xdim, T}, cellvalues) where {xdim, T} +function get_cell_volumes(sp::TrussProblem{xdim,T}, cellvalues) where {xdim,T} dh = sp.ch.dh As = getA(sp) cellvolumes = zeros(T, getncells(dh.grid)) for (i, cell) in enumerate(CellIterator(dh)) truss_reinit!(cellvalues, cell, As[i]) - cellvolumes[i] = sum(Ferrite.getdetJdV(cellvalues, q_point) for q_point in 1:Ferrite.getnquadpoints(cellvalues)) + cellvolumes[i] = sum( + Ferrite.getdetJdV(cellvalues, q_point) for + q_point = 1:Ferrite.getnquadpoints(cellvalues) + ) end return cellvolumes end @@ -62,13 +83,13 @@ function compute_local_axes(end_vert_u, end_vert_v) @assert length(end_vert_u) == length(end_vert_v) @assert length(end_vert_u) == 2 || length(end_vert_u) == 3 xdim = length(end_vert_u) - L = norm(end_vert_u-end_vert_v) + L = norm(end_vert_u - end_vert_v) @assert L > eps() # by convention, the new x axis is along the element's direction # directional cosine of the new x axis in the global world frame - c_x = (end_vert_v[1] - end_vert_u[1])/L - c_y = (end_vert_v[2] - end_vert_u[2])/L - R = zeros(xdim,xdim) + c_x = (end_vert_v[1] - end_vert_u[1]) / L + c_y = (end_vert_v[2] - end_vert_u[2]) / L + R = zeros(xdim, xdim) if 3 == xdim c_z = (end_vert_v[3] - end_vert_u[3]) / L if abs(abs(c_z) - 1.0) < eps() @@ -79,7 +100,7 @@ function compute_local_axes(end_vert_u, end_vert_v) # local x_axis = element's vector new_x = [c_x, c_y, c_z] # local y axis = cross product with global z axis - new_y = -cross(new_x, [0,0,1.0]) + new_y = -cross(new_x, [0, 0, 1.0]) new_y /= norm(new_y) new_z = cross(new_x, new_y) R[:, 1] = new_x @@ -87,11 +108,10 @@ function compute_local_axes(end_vert_u, end_vert_v) R[:, 3] = new_z end elseif 2 == xdim - R = [c_x -c_y; - c_y c_x] + R = [ + c_x -c_y + c_y c_x + ] end return R end - - - diff --git a/src/TrussTopOptProblems/grids.jl b/src/TrussTopOptProblems/grids.jl index 5c2b6a36..1638d240 100644 --- a/src/TrussTopOptProblems/grids.jl +++ b/src/TrussTopOptProblems/grids.jl @@ -2,7 +2,8 @@ using ..TopOpt.TopOptProblems: AbstractGrid const Vec = Ferrite.Vec # @params struct TrussGrid{xdim,N,M,C<:Ferrite.Cell{xdim,N,M},T} <: AbstractGrid{xdim, T} -struct TrussGrid{xdim,T,N,M,TG<:Ferrite.Grid{xdim,<:Ferrite.Cell{xdim,N,M},T}} <: AbstractGrid{xdim, T} +struct TrussGrid{xdim,T,N,M,TG<:Ferrite.Grid{xdim,<:Ferrite.Cell{xdim,N,M},T}} <: + AbstractGrid{xdim,T} grid::TG white_cells::BitVector black_cells::BitVector @@ -15,31 +16,38 @@ end nnodespercell(::TrussGrid{xdim,T,N,M}) where {xdim,T,N,M} = N nfacespercell(::TrussGrid{xdim,T,N,M}) where {xdim,T,N,M} = M -nnodes(cell::Type{Ferrite.Cell{xdim,N,M}}) where {xdim, N, M} = N +nnodes(cell::Type{Ferrite.Cell{xdim,N,M}}) where {xdim,N,M} = N nnodes(cell::Ferrite.Cell) = nnodes(typeof(cell)) Ferrite.getncells(tg::TrussGrid) = Ferrite.getncells(tg.grid) -function TrussGrid(node_points::Dict{iT, SVector{xdim, T}}, elements::Dict{iT, Tuple{iT, iT}}, - boundary::Dict{iT, SVector{xdim, fT}}; crosssecs=TrussFEACrossSec{T}(1.0)) where {xdim, T, iT, fT} +function TrussGrid( + node_points::Dict{iT,SVector{xdim,T}}, + elements::Dict{iT,Tuple{iT,iT}}, + boundary::Dict{iT,SVector{xdim,fT}}; + crosssecs = TrussFEACrossSec{T}(1.0), +) where {xdim,T,iT,fT} grid = _LinearTrussGrid(node_points, elements, boundary) ncells = getncells(grid) if crosssecs isa Vector @assert length(crosssecs) == ncells crosssecs = convert(Vector{TrussFEACrossSec{T}}, crosssecs) elseif crosssecs isa TrussFEACrossSec - crosssecs = [convert(TrussFEACrossSec{T}, crosssecs) for i=1:ncells] + crosssecs = [convert(TrussFEACrossSec{T}, crosssecs) for i = 1:ncells] else error("Invalid crossecs: $(crossecs)") end return TrussGrid(grid, falses(ncells), falses(ncells), falses(ncells), crosssecs) end -function _LinearTrussGrid(node_points::Dict{iT, SVector{xdim, T}}, elements::Dict{iT, Tuple{iT, iT}}, - boundary::Dict{iT, SVector{xdim, fT}}) where {xdim, T, iT, fT} +function _LinearTrussGrid( + node_points::Dict{iT,SVector{xdim,T}}, + elements::Dict{iT,Tuple{iT,iT}}, + boundary::Dict{iT,SVector{xdim,fT}}, +) where {xdim,T,iT,fT} n_nodes = length(node_points) # * Generate cells, Line2d or Line3d - @assert xdim ∈ [2,3] + @assert xdim ∈ [2, 3] CellType = xdim == 2 ? Line2D : Line3D cells = Vector{CellType}(undef, length(elements)) for (e_ind, element) in elements @@ -59,9 +67,12 @@ function Base.show(io::Base.IO, mime::MIME"text/plain", tg::TrussGrid) println(io, "TrussGrid:") print(io, "\t-") Base.show(io, mime, tg.grid) - println(io,"") + println(io, "") print(io, "\t-") - println(io, "white cells:T|$(sum(tg.white_cells))|, black cells:T|$(sum(tg.black_cells))|, const cells:T|$(sum(tg.constant_cells))|") + println( + io, + "white cells:T|$(sum(tg.white_cells))|, black cells:T|$(sum(tg.black_cells))|, const cells:T|$(sum(tg.constant_cells))|", + ) end ################################ @@ -71,4 +82,4 @@ end # end # const extra_celltypes = Dict{DataType, String}(Line2D => "Line2D", -# Line3D => "Line3D") \ No newline at end of file +# Line3D => "Line3D") diff --git a/src/TrussTopOptProblems/matrices_and_vectors.jl b/src/TrussTopOptProblems/matrices_and_vectors.jl index 09adbd43..e1ce8634 100644 --- a/src/TrussTopOptProblems/matrices_and_vectors.jl +++ b/src/TrussTopOptProblems/matrices_and_vectors.jl @@ -7,15 +7,19 @@ using ChainRulesCore """ Generate element stiffness matrices """ -function make_Kes_and_fes(problem::TrussProblem, quad_order=1) +function make_Kes_and_fes(problem::TrussProblem, quad_order = 1) make_Kes_and_fes(problem, quad_order, Val{:Static}) end -function make_Kes_and_fes(problem::TrussProblem, ::Type{Val{mat_type}}) where mat_type +function make_Kes_and_fes(problem::TrussProblem, ::Type{Val{mat_type}}) where {mat_type} make_Kes_and_fes(problem, 1, Val{mat_type}) end -function make_Kes_and_fes(problem::TrussProblem{xdim, T}, quad_order, ::Type{Val{mat_type}}) where {xdim, T, mat_type} +function make_Kes_and_fes( + problem::TrussProblem{xdim,T}, + quad_order, + ::Type{Val{mat_type}}, +) where {xdim,T,mat_type} dh = getdh(problem) Es = getE(problem) # ν = getν(problem) @@ -23,23 +27,34 @@ function make_Kes_and_fes(problem::TrussProblem{xdim, T}, quad_order, ::Type{Val As = getA(problem) # * Shape functions and quadrature rule - interpolation_space = Ferrite.default_interpolation(getcelltype(problem.truss_grid.grid)) + interpolation_space = + Ferrite.default_interpolation(getcelltype(problem.truss_grid.grid)) # Lagrange{ξdim, refshape, geom_order}() ξdim = getdim(interpolation_space) refshape = getrefshape(dh.field_interpolations[1]) - quadrature_rule = QuadratureRule{ξdim, refshape}(quad_order) - cellvalues = GenericCellScalarValues(T, quadrature_rule, interpolation_space; xdim=xdim) + quadrature_rule = QuadratureRule{ξdim,refshape}(quad_order) + cellvalues = + GenericCellScalarValues(T, quadrature_rule, interpolation_space; xdim = xdim) # * A Line element's faces are not meaningful in truss problems # placeholder to make type right - facevalues = FaceScalarValues(QuadratureRule{ξdim-1, refshape}(quad_order), interpolation_space) + facevalues = + FaceScalarValues(QuadratureRule{ξdim - 1,refshape}(quad_order), interpolation_space) # * Calculate element stiffness matrices n_basefuncs = getnbasefunctions(cellvalues) - Kesize = xdim*n_basefuncs + Kesize = xdim * n_basefuncs MatrixType, VectorType = gettypes(T, Val{mat_type}, Val{Kesize}) - Kes, weights = _make_Kes_and_weights(dh, Tuple{MatrixType, VectorType}, Val{n_basefuncs}, Val{xdim*n_basefuncs}, - Es, As, quadrature_rule, cellvalues) + Kes, weights = _make_Kes_and_weights( + dh, + Tuple{MatrixType,VectorType}, + Val{n_basefuncs}, + Val{xdim * n_basefuncs}, + Es, + As, + quadrature_rule, + cellvalues, + ) # ! distributed load, not used in a truss problem # dloads = _make_dloads(weights, problem, facevalues) @@ -70,42 +85,63 @@ struct GenericCellScalarValues{ξdim,xdim,T,refshape} <: CellValues{xdim,T,refsh qr_weights::Vector{T} end -function GenericCellScalarValues(quad_rule::QuadratureRule, func_interpol::Interpolation, - geom_interpol::Interpolation=func_interpol) +function GenericCellScalarValues( + quad_rule::QuadratureRule, + func_interpol::Interpolation, + geom_interpol::Interpolation = func_interpol, +) TrussCellScalarValues(Float64, quad_rule, func_interpol, geom_interpol) end -function GenericCellScalarValues(::Type{T}, quad_rule::QuadratureRule{ξdim,shape}, func_interpol::Interpolation, - geom_interpol::Interpolation=func_interpol; xdim=ξdim) where {ξdim,T,shape<:Ferrite.AbstractRefShape} +function GenericCellScalarValues( + ::Type{T}, + quad_rule::QuadratureRule{ξdim,shape}, + func_interpol::Interpolation, + geom_interpol::Interpolation = func_interpol; + xdim = ξdim, +) where {ξdim,T,shape<:Ferrite.AbstractRefShape} @assert getdim(func_interpol) == getdim(geom_interpol) @assert getrefshape(func_interpol) == getrefshape(geom_interpol) == shape n_qpoints = length(getweights(quad_rule)) # * Function interpolation n_func_basefuncs = getnbasefunctions(func_interpol) - N = fill(zero(T) * T(NaN), n_func_basefuncs, n_qpoints) + N = fill(zero(T) * T(NaN), n_func_basefuncs, n_qpoints) dNdx = fill(zero(Vec{xdim,T}) * T(NaN), n_func_basefuncs, n_qpoints) dNdξ = fill(zero(Vec{ξdim,T}) * T(NaN), n_func_basefuncs, n_qpoints) # * Geometry interpolation n_geom_basefuncs = getnbasefunctions(geom_interpol) - M = fill(zero(T) * T(NaN), n_geom_basefuncs, n_qpoints) + M = fill(zero(T) * T(NaN), n_geom_basefuncs, n_qpoints) dMdξ = fill(zero(Vec{ξdim,T}) * T(NaN), n_geom_basefuncs, n_qpoints) for (qp, ξ) in enumerate(quad_rule.points) - for i in 1:n_func_basefuncs + for i = 1:n_func_basefuncs dNdξ[i, qp], N[i, qp] = gradient(ξ -> value(func_interpol, i, ξ), ξ, :all) end - for i in 1:n_geom_basefuncs + for i = 1:n_geom_basefuncs dMdξ[i, qp], M[i, qp] = gradient(ξ -> value(geom_interpol, i, ξ), ξ, :all) end end detJdV = fill(T(NaN), n_qpoints) - GenericCellScalarValues{ξdim,xdim,T,shape}(N, dNdx, dNdξ, detJdV, M, dMdξ, quad_rule.weights) + GenericCellScalarValues{ξdim,xdim,T,shape}( + N, + dNdx, + dNdξ, + detJdV, + M, + dMdξ, + quad_rule.weights, + ) end # common values using Ferrite: getnbasefunctions, getngeobasefunctions, getnquadpoints getn_scalarbasefunctions(cv::GenericCellScalarValues) = size(cv.N, 1) -@propagate_inbounds shape_gradient(cv::GenericCellScalarValues, q_point::Int, base_func::Int) = cv.dNdx[base_func, q_point] -@propagate_inbounds getdetJdV(cv::GenericCellScalarValues, q_point::Int) = cv.detJdV[q_point] +@propagate_inbounds shape_gradient( + cv::GenericCellScalarValues, + q_point::Int, + base_func::Int, +) = cv.dNdx[base_func, q_point] +@propagate_inbounds getdetJdV(cv::GenericCellScalarValues, q_point::Int) = + cv.detJdV[q_point] ############################ @@ -114,12 +150,12 @@ getn_scalarbasefunctions(cv::GenericCellScalarValues) = size(cv.N, 1) Compute the pseudo-inverse of a Vector tensor. """ -function pinv(t::Vec{dim, T}) where {dim, T} - LinearAlgebra.Transpose{T, Vec{dim, T}}(t / sum(t.^2)) +function pinv(t::Vec{dim,T}) where {dim,T} + LinearAlgebra.Transpose{T,Vec{dim,T}}(t / sum(t .^ 2)) end -function ChainRulesCore.rrule(::typeof(pinv), t::Vec{dim, T}) where {dim, T} - s = sum(t.^2) - TT = LinearAlgebra.Transpose{T, Vec{dim, T}} +function ChainRulesCore.rrule(::typeof(pinv), t::Vec{dim,T}) where {dim,T} + s = sum(t .^ 2) + TT = LinearAlgebra.Transpose{T,Vec{dim,T}} TT(t / s), Δ -> begin nothing, TT(Δ) end @@ -133,17 +169,20 @@ end `weights` : a vector of `xdim*n_basefuncs` vectors, element_id => self-weight load vector, in truss elements, they are all zeros. """ function _make_Kes_and_weights( - dh::DofHandler{xdim, N, T}, - ::Type{Tuple{MatrixType, VectorType}}, + dh::DofHandler{xdim,N,T}, + ::Type{Tuple{MatrixType,VectorType}}, ::Type{Val{n_basefuncs}}, ::Type{Val{Kesize}}, - Es::Vector{T}, As::Vector{T}, - quadrature_rule, cellvalues::GenericCellScalarValues) where {xdim, N, T, MatrixType <: StaticArray, VectorType, n_basefuncs, Kesize} + Es::Vector{T}, + As::Vector{T}, + quadrature_rule, + cellvalues::GenericCellScalarValues, +) where {xdim,N,T,MatrixType<:StaticArray,VectorType,n_basefuncs,Kesize} nel = getncells(dh.grid) - Kes = Symmetric{T, MatrixType}[] + Kes = Symmetric{T,MatrixType}[] sizehint!(Kes, nel) # body_force = ρ .* g # Force per unit volume - weights = [zeros(VectorType) for i in 1:nel] + weights = [zeros(VectorType) for i = 1:nel] Ke_e = zeros(T, xdim, xdim) fe = zeros(T, Kesize) Ke_0 = Matrix{T}(undef, Kesize, Kesize) @@ -153,21 +192,21 @@ function _make_Kes_and_weights( Ke_0 .= 0 truss_reinit!(cellvalues, cell, As[k]) # fe = weights[k] - for q_point in 1:getnquadpoints(cellvalues) + for q_point = 1:getnquadpoints(cellvalues) dΩ = getdetJdV(cellvalues, q_point) - for b in 1:n_basefuncs + for b = 1:n_basefuncs ∇ϕb = shape_gradient(cellvalues, q_point, b) # ϕb = shape_value(cellvalues, q_point, b) - for d2 in 1:xdim + for d2 = 1:xdim # self weight force calculation # fe = @set fe[(b-1)*dim + d2] += ϕb * body_force[d2] * dΩ - for a in 1:n_basefuncs + for a = 1:n_basefuncs ∇ϕa = shape_gradient(cellvalues, q_point, a) # TODO specialized KroneckerDelta struct to make dotdot more efficient Ke_e .= Es[k] * ∇ϕa ⊗ ∇ϕb * dΩ - for d1 in 1:xdim + for d1 = 1:xdim #if dim*(b-1) + d2 >= dim*(a-1) + d1 - Ke_0[xdim*(a-1) + d1, xdim*(b-1) + d2] += Ke_e[d1,d2] + Ke_0[xdim*(a-1)+d1, xdim*(b-1)+d2] += Ke_e[d1, d2] #end end end @@ -184,7 +223,11 @@ function _make_Kes_and_weights( return Kes, weights end -@inline function truss_reinit!(cv::GenericCellScalarValues{ξdim,xdim,T}, ci::CellIterator{xdim,N,T}, crossec::T) where {ξdim,xdim,N,T} +@inline function truss_reinit!( + cv::GenericCellScalarValues{ξdim,xdim,T}, + ci::CellIterator{xdim,N,T}, + crossec::T, +) where {ξdim,xdim,N,T} Ferrite.check_compatible_geointerpolation(cv, ci) truss_reinit!(cv, ci.coords, crossec) end @@ -192,16 +235,20 @@ end """ Reinit a cell for a truss element, using the nodal coordinates `x`, cross section `crossec` """ -function truss_reinit!(cv::GenericCellScalarValues{ξdim,xdim,T}, x::AbstractVector{Vec{xdim,T}}, crossec::T) where {ξdim,xdim,T} +function truss_reinit!( + cv::GenericCellScalarValues{ξdim,xdim,T}, + x::AbstractVector{Vec{xdim,T}}, + crossec::T, +) where {ξdim,xdim,T} n_geom_basefuncs = getngeobasefunctions(cv) n_func_basefuncs = getn_scalarbasefunctions(cv) @assert length(x) == n_geom_basefuncs isa(cv, CellVectorValues) && (n_func_basefuncs *= xdim) - @inbounds for i in 1:length(cv.qr_weights) + @inbounds for i = 1:length(cv.qr_weights) w = cv.qr_weights[i] dxdξ = zero(Tensor{1,xdim}) - for j in 1:n_geom_basefuncs + for j = 1:n_geom_basefuncs # in a truss element, x_j ∈ R, dMdξ_j ∈ R, ξ ∈ R # cv.dMdξ[j, i] is a 1-1 tensor here dxdξ += x[j] * cv.dMdξ[j, i][1] @@ -211,9 +258,9 @@ function truss_reinit!(cv::GenericCellScalarValues{ξdim,xdim,T}, x::AbstractVec detJ > 0.0 || throw(ArgumentError("det(J) is not positive: det(J) = $(detJ)")) cv.detJdV[i] = detJ * w * crossec Jinv = pinv(dxdξ) - for j in 1:n_func_basefuncs + for j = 1:n_func_basefuncs # cv.dNdξ[j, i] is a 1-1 tensor here cv.dNdx[j, i] = cv.dNdξ[j, i][1] * Jinv' end end -end \ No newline at end of file +end diff --git a/src/TrussTopOptProblems/problem_types.jl b/src/TrussTopOptProblems/problem_types.jl index 331d3f46..275a29b4 100644 --- a/src/TrussTopOptProblems/problem_types.jl +++ b/src/TrussTopOptProblems/problem_types.jl @@ -6,7 +6,7 @@ get_fixities_node_set_name(i) = "fixed_u$(i)" truss_grid::TrussGrid{xdim,T,N,M} # ground truss mesh materials::Vector{TrussFEAMaterial{T}} ch::ConstraintHandler{<:DofHandler{xdim,<:Ferrite.Cell{xdim,N,M},T},T} - force::Dict{Int, SVector{xdim, T}} + force::Dict{Int,SVector{xdim,T}} black::AbstractVector white::AbstractVector varind::AbstractVector{Int} # variable dof => free dof, based on black & white @@ -22,8 +22,15 @@ TopOpt.TopOptProblems.getν(sp::TrussProblem) = [m.ν for m in sp.materials] getA(sp::TrussProblem) = [cs.A for cs in sp.truss_grid.crosssecs] Ferrite.getnnodes(problem::StiffnessTopOptProblem) = Ferrite.getnnodes(getdh(problem).grid) -function TrussProblem(::Type{Val{CellType}}, node_points::Dict{iT, SVector{xdim, T}}, elements::Dict{iT, Tuple{iT, iT}}, - loads::Dict{iT, SVector{xdim, T}}, supports::Dict{iT, SVector{xdim, fT}}, mats=TrussFEAMaterial{T}(1.0, 0.3), crosssecs=TrussFEACrossSec{T}(1.0)) where {xdim, T, iT, fT, CellType} +function TrussProblem( + ::Type{Val{CellType}}, + node_points::Dict{iT,SVector{xdim,T}}, + elements::Dict{iT,Tuple{iT,iT}}, + loads::Dict{iT,SVector{xdim,T}}, + supports::Dict{iT,SVector{xdim,fT}}, + mats = TrussFEAMaterial{T}(1.0, 0.3), + crosssecs = TrussFEACrossSec{T}(1.0), +) where {xdim,T,iT,fT,CellType} # unify number type # _T = promote_type(eltype(sizes), typeof(mats), typeof(ν), typeof(force)) # if _T <: Integer @@ -45,7 +52,7 @@ function TrussProblem(::Type{Val{CellType}}, node_points::Dict{iT, SVector{xdim, @assert length(mats) == ncells mats = convert(Vector{TrussFEAMaterial{T}}, mats) elseif mats isa TrussFEAMaterial - mats = [convert(TrussFEAMaterial{T}, mats) for i=1:ncells] + mats = [convert(TrussFEAMaterial{T}, mats) for i = 1:ncells] else error("Invalid mats: $(mats)") end @@ -56,13 +63,13 @@ function TrussProblem(::Type{Val{CellType}}, node_points::Dict{iT, SVector{xdim, pop!(truss_grid.grid.nodesets, "load") end load_nodesets = Set{Int}() - for (k,_) in loads + for (k, _) in loads push!(load_nodesets, k) end addnodeset!(truss_grid.grid, "load", load_nodesets) # * support nodeset - for i=1:xdim + for i = 1:xdim if haskey(truss_grid.grid.nodesets, get_fixities_node_set_name(i)) pop!(truss_grid.grid.nodesets, get_fixities_node_set_name(i)) end @@ -80,7 +87,7 @@ function TrussProblem(::Type{Val{CellType}}, node_points::Dict{iT, SVector{xdim, if CellType === :Linear # truss linear # interpolation_space - ip = Lagrange{ξdim, RefCube, geom_order}() + ip = Lagrange{ξdim,RefCube,geom_order}() push!(dh, :u, xdim, ip) else # TODO truss 2-order @@ -91,8 +98,13 @@ function TrussProblem(::Type{Val{CellType}}, node_points::Dict{iT, SVector{xdim, close!(dh) ch = ConstraintHandler(dh) - for i=1:xdim - dbc = Dirichlet(:u, getnodeset(truss_grid.grid, get_fixities_node_set_name(i)), (x,t)->zeros(T,1), [i]) + for i = 1:xdim + dbc = Dirichlet( + :u, + getnodeset(truss_grid.grid, get_fixities_node_set_name(i)), + (x, t) -> zeros(T, 1), + [i], + ) add!(ch, dbc) end close!(ch) @@ -131,10 +143,10 @@ TopOpt.TopOptProblems.nnodespercell(p::TrussProblem) = nnodespercell(p.truss_gri Get a dict (node_idx => force vector) for concentrated loads """ -function TopOpt.TopOptProblems.getcloaddict(p::TrussProblem{xdim,T}) where {xdim, T} +function TopOpt.TopOptProblems.getcloaddict(p::TrussProblem{xdim,T}) where {xdim,T} return p.force end function default_quad_order(::TrussProblem) return 1 -end \ No newline at end of file +end diff --git a/src/Utilities/Utilities.jl b/src/Utilities/Utilities.jl index 4eed7053..55ee4999 100644 --- a/src/Utilities/Utilities.jl +++ b/src/Utilities/Utilities.jl @@ -2,29 +2,29 @@ module Utilities using ForwardDiff, Ferrite, IterativeSolvers, Requires -export AbstractPenalty, - PowerPenalty, - RationalPenalty, - HeavisideProjection, - SigmoidProjection, - ProjectedPenalty, - setpenalty, - TopOptTrace, - RaggedArray, - @debug, - compliance, - meandiag, - density, - find_black_and_white, - find_varind, - YoungsModulus, - PoissonRatio, - getpenalty, - getprevpenalty, - setpenalty!, - getsolver, - @params, - @forward_property +export AbstractPenalty, + PowerPenalty, + RationalPenalty, + HeavisideProjection, + SigmoidProjection, + ProjectedPenalty, + setpenalty, + TopOptTrace, + RaggedArray, + @debug, + compliance, + meandiag, + density, + find_black_and_white, + find_varind, + YoungsModulus, + PoissonRatio, + getpenalty, + getprevpenalty, + setpenalty!, + getsolver, + @params, + @forward_property function getpenalty end function getprevpenalty end diff --git a/src/Utilities/gpu_utilities.jl b/src/Utilities/gpu_utilities.jl index 7a132869..ce36832d 100644 --- a/src/Utilities/gpu_utilities.jl +++ b/src/Utilities/gpu_utilities.jl @@ -9,7 +9,11 @@ abstract type AbstractGPUPenalty{T} <: AbstractPenalty{T} end whichdevice(::AbstractCPUPenalty) = CPU() whichdevice(::AbstractGPUPenalty) = GPU() -CUDAnative.pow(d::TD, p::AbstractFloat) where {T, TV, TD <: ForwardDiff.Dual{T, TV, 1}} = ForwardDiff.Dual{T}(CUDAnative.pow(d.value, p), p * d.partials[1] * CUDAnative.pow(d.value, p - 1)) +CUDAnative.pow(d::TD, p::AbstractFloat) where {T,TV,TD<:ForwardDiff.Dual{T,TV,1}} = + ForwardDiff.Dual{T}( + CUDAnative.pow(d.value, p), + p * d.partials[1] * CUDAnative.pow(d.value, p - 1), + ) struct GPUPowerPenalty{T} <: AbstractGPUPenalty{T} p::T @@ -24,7 +28,7 @@ end struct GPUSinhPenalty{T} <: AbstractGPUPenalty{T} p::T end -@inline (P::GPUSinhPenalty)(x) = CUDAnative.sinh(R.p*x)/CUDAnative.sinh(R.p) +@inline (P::GPUSinhPenalty)(x) = CUDAnative.sinh(R.p * x) / CUDAnative.sinh(R.p) for T in (:PowerPenalty, :RationalPenalty) fname = Symbol(:GPU, T) diff --git a/src/Utilities/penalties.jl b/src/Utilities/penalties.jl index a05322dc..4b523a05 100644 --- a/src/Utilities/penalties.jl +++ b/src/Utilities/penalties.jl @@ -15,14 +15,14 @@ end mutable struct SinhPenalty{T} <: AbstractCPUPenalty{T} p::T end -@inline (R::SinhPenalty)(x) = sinh(R.p*x)/sinh(R.p) +@inline (R::SinhPenalty)(x) = sinh(R.p * x) / sinh(R.p) -struct ProjectedPenalty{T, Tpen <: AbstractPenalty{T}, Tproj} <: AbstractCPUPenalty{T} +struct ProjectedPenalty{T,Tpen<:AbstractPenalty{T},Tproj} <: AbstractCPUPenalty{T} penalty::Tpen proj::Tproj end function ProjectedPenalty(penalty::AbstractPenalty{T}) where {T} - return ProjectedPenalty(penalty, HeavisideProjection(10*one(T))) + return ProjectedPenalty(penalty, HeavisideProjection(10 * one(T))) end @inline (P::ProjectedPenalty)(x) = P.penalty(P.proj(x)) @forward_property ProjectedPenalty penalty @@ -30,7 +30,7 @@ end mutable struct HeavisideProjection{T} <: AbstractProjection β::T end -@inline (P::HeavisideProjection)(x) = 1 - exp(-P.β*x) + x * exp(-P.β) +@inline (P::HeavisideProjection)(x) = 1 - exp(-P.β * x) + x * exp(-P.β) mutable struct SigmoidProjection{T} <: AbstractProjection β::T end diff --git a/src/Utilities/traces.jl b/src/Utilities/traces.jl index 4256357f..6b245254 100644 --- a/src/Utilities/traces.jl +++ b/src/Utilities/traces.jl @@ -1,26 +1,37 @@ import Base: length, append!, sizehint! -@params struct TopOptTrace{T, TI <: Integer} - c_hist::AbstractVector{T} +@params struct TopOptTrace{T,TI<:Integer} + c_hist::AbstractVector{T} v_hist::AbstractVector{T} - x_hist::AbstractVector{<:AbstractVector{T}} - add_hist::AbstractVector{TI} - rem_hist::AbstractVector{TI} + x_hist::AbstractVector{<:AbstractVector{T}} + add_hist::AbstractVector{TI} + rem_hist::AbstractVector{TI} end -TopOptTrace{T, TI}() where {T, TI<:Integer} = TopOptTrace(Vector{T}(), Vector{T}(), Vector{Vector{T}}(), Vector{TI}(), Vector{TI}()) +TopOptTrace{T,TI}() where {T,TI<:Integer} = + TopOptTrace(Vector{T}(), Vector{T}(), Vector{Vector{T}}(), Vector{TI}(), Vector{TI}()) length(t::TopOptTrace) = length(t.v_hist) topopt_trace_fields = fieldnames(TopOptTrace) macro append_fields_t1_t2() - return esc(Expr(:block, [Expr(:call, :append!, :(t1.$f), :(t2.$f)) for f in topopt_trace_fields]...)) + return esc( + Expr( + :block, + [Expr(:call, :append!, :(t1.$f), :(t2.$f)) for f in topopt_trace_fields]..., + ), + ) end function append!(t1::TopOptTrace, t2::TopOptTrace) @append_fields_t1_t2() end macro sizehint!_fields_t() - return esc(Expr(:block, [Expr(:call, :sizehint!, :(t.$f), :n) for f in topopt_trace_fields]...)) + return esc( + Expr( + :block, + [Expr(:call, :sizehint!, :(t.$f), :n) for f in topopt_trace_fields]..., + ), + ) end function sizehint!(t::TopOptTrace, n) @sizehint!_fields_t() @@ -29,7 +40,7 @@ end function append!(ts::Vector{<:TopOptTrace}) sizehint!(ts[1], sum(length.(ts))) - for i in 2:length(ts) + for i = 2:length(ts) append!(ts[1], ts[i]) end return ts[1] diff --git a/src/Utilities/utils.jl b/src/Utilities/utils.jl index 26dd6ec2..a2bf6892 100644 --- a/src/Utilities/utils.jl +++ b/src/Utilities/utils.jl @@ -32,7 +32,7 @@ macro params(struct_expr) header = struct_expr.args[2] fields = @view struct_expr.args[3].args[2:2:end] params = [] - for i in 1:length(fields) + for i = 1:length(fields) x = fields[i] T = gensym() if x isa Symbol @@ -64,14 +64,14 @@ macro params(struct_expr) esc(struct_expr) end -struct RaggedArray{TO, TV} +struct RaggedArray{TO,TV} offsets::TO values::TV end -function RaggedArray(vv::Vector{Vector{T}}) where T +function RaggedArray(vv::Vector{Vector{T}}) where {T} offsets = [1; 1 .+ accumulate(+, collect(length(v) for v in vv))] - values = Vector{T}(undef, offsets[end]-1) + values = Vector{T}(undef, offsets[end] - 1) for (i, v) in enumerate(vv) r = offsets[i]:offsets[i+1]-1 values[r] .= v @@ -98,12 +98,12 @@ function Base.setindex!(ra::RaggedArray, v, i, j) ra.values[r[i]] = v end -function find_varind(black, white, ::Type{TI}=Int) where TI +function find_varind(black, white, ::Type{TI} = Int) where {TI} nel = length(black) nel == length(white) || throw("Black and white vectors should be of the same length") varind = zeros(TI, nel) k = 1 - for i in 1:nel + for i = 1:nel if !black[i] && !white[i] varind[i] = k k += 1 @@ -125,7 +125,7 @@ function find_black_and_white(dh) white[c] = true end end - + return black, white end @@ -134,9 +134,9 @@ PoissonRatio(p) = getν(p) function compliance(Ke, u, dofs) comp = zero(eltype(u)) - for i in 1:length(dofs) - for j in 1:length(dofs) - comp += u[dofs[i]]*Ke[i,j]*u[dofs[j]] + for i = 1:length(dofs) + for j = 1:length(dofs) + comp += u[dofs[i]] * Ke[i, j] * u[dofs[j]] end end comp @@ -144,13 +144,13 @@ end function meandiag(K::AbstractMatrix) z = zero(eltype(K)) - for i in 1:size(K, 1) + for i = 1:size(K, 1) z += abs(K[i, i]) end return z / size(K, 1) end -density(var, xmin) = var*(1-xmin) + xmin +density(var, xmin) = var * (1 - xmin) + xmin macro debug(expr) return quote @@ -160,17 +160,19 @@ macro debug(expr) end end -@generated function _getproperty(c::T, ::Val{fallback}, ::Val{f}) where {T, fallback, f} +@generated function _getproperty(c::T, ::Val{fallback}, ::Val{f}) where {T,fallback,f} f ∈ fieldnames(T) && return :(getfield(c, $(QuoteNode(f)))) return :(getproperty(getfield(c, $(QuoteNode(fallback))), $(QuoteNode(f)))) end -@generated function _setproperty!(c::T, ::Val{fallback}, ::Val{f}, val) where {T, fallback, f} +@generated function _setproperty!(c::T, ::Val{fallback}, ::Val{f}, val) where {T,fallback,f} f ∈ fieldnames(T) && return :(setfield!(c, $(QuoteNode(f)), val)) return :(setproperty!(getfield(c, $(QuoteNode(fallback))), $(QuoteNode(f)), val)) end macro forward_property(T, field) quote - Base.getproperty(c::$(esc(T)), f::Symbol) = _getproperty(c, Val($(QuoteNode(field))), Val(f)) - Base.setproperty!(c::$(esc(T)), f::Symbol, val) = _setproperty!(c, Val($(QuoteNode(field))), Val(f), val) + Base.getproperty(c::$(esc(T)), f::Symbol) = + _getproperty(c, Val($(QuoteNode(field))), Val(f)) + Base.setproperty!(c::$(esc(T)), f::Symbol, val) = + _setproperty!(c, Val($(QuoteNode(field))), Val(f), val) end end diff --git a/test/Functions/test_buckling_fns.jl b/test/Functions/test_buckling_fns.jl index 24c97a95..3b069e56 100644 --- a/test/Functions/test_buckling_fns.jl +++ b/test/Functions/test_buckling_fns.jl @@ -17,14 +17,14 @@ gm_ins_dir = joinpath(@__DIR__, "..", "truss_topopt_problems", "instances", "gro einfo = ElementFEAInfo(problem) k = size(einfo.Kes[1], 1) N = length(einfo.Kes) - for _ in 1:3 + for _ = 1:3 v = rand(T, total_ndof) - f = Kx -> sum(ak(Kx)*v) - Kes = [rand(T,k,k) for _ in 1:N] + f = Kx -> sum(ak(Kx) * v) + Kes = [rand(T, k, k) for _ = 1:N] Kes .= transpose.(Kes) .+ Kes - val1, grad1 = NonconvexCore.value_gradient(f, Kes); - val2, grad2 = f(Kes), Zygote.gradient(f, Kes)[1]; - grad3 = FDM.grad(central_fdm(5, 1), f, Kes)[1]; + val1, grad1 = NonconvexCore.value_gradient(f, Kes) + val2, grad2 = f(Kes), Zygote.gradient(f, Kes)[1] + grad3 = FDM.grad(central_fdm(5, 1), f, Kes)[1] @test val1 == val2 @test norm(grad1 - grad2) == 0 map(1:length(grad2)) do i @@ -53,18 +53,18 @@ end @test k1 ≈ k0 end - for _ in 1:3 - vs = [rand(T,k,k) for i in 1:N] - f = x -> begin + for _ = 1:3 + vs = [rand(T, k, k) for i = 1:N] + f = x -> begin Kes = ek(x) - sum([sum(Kes[i]*vs[i]) for i in 1:length(x)]) + sum([sum(Kes[i] * vs[i]) for i = 1:length(x)]) end x = clamp.(rand(prod(nels)), 0.1, 1.0) - val1, grad1 = NonconvexCore.value_gradient(f, x); - val2, grad2 = f(x), Zygote.gradient(f, x)[1]; - grad3 = FDM.grad(central_fdm(5, 1), f, x)[1]; + val1, grad1 = NonconvexCore.value_gradient(f, x) + val2, grad2 = f(x), Zygote.gradient(f, x)[1] + grad3 = FDM.grad(central_fdm(5, 1), f, x)[1] @test val1 == val2 @test norm(grad1 - grad2) == 0 @test norm(grad1 - grad3) <= 1e-5 @@ -75,15 +75,16 @@ end file_name = "tim_$(problem_dim).json" problem_file = joinpath(gm_ins_dir, file_name) - mats = TrussFEAMaterial(1.0, 0.3); - crossecs = TrussFEACrossSec(800.0); + mats = TrussFEAMaterial(1.0, 0.3) + crossecs = TrussFEACrossSec(800.0) - node_points, elements, _, _ , fixities, load_cases = load_truss_json(problem_file) + node_points, elements, _, _, fixities, load_cases = load_truss_json(problem_file) ndim, nnodes, ncells = length(node_points[1]), length(node_points), length(elements) loads = load_cases["0"] - problem = TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mats, crossecs); - solver = FEASolver(Direct, problem); + problem = + TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mats, crossecs) + solver = FEASolver(Direct, problem) solver() u = solver.u @@ -97,11 +98,11 @@ end # * check geometric stiffness matrix consistency Kσs_0 = get_truss_Kσs(problem, u, solver.elementinfo.cellvalues) - for _ in 1:3 - vs = [rand(T,k,k) for i in 1:N] - f = x -> begin + for _ = 1:3 + vs = [rand(T, k, k) for i = 1:N] + f = x -> begin Keσs = esigk(u, x) - sum([sum(Keσs[i]*vs[i]) for i in 1:length(x)]) + sum([sum(Keσs[i] * vs[i]) for i = 1:length(x)]) end x = clamp.(rand(nels), 0.1, 1.0) @@ -111,9 +112,9 @@ end @test k1 ≈ k0 * x[ci] end - val1, grad1 = NonconvexCore.value_gradient(f, x); - val2, grad2 = f(x), Zygote.gradient(f, x)[1]; - grad3 = FDM.grad(central_fdm(5, 1), f, x)[1]; + val1, grad1 = NonconvexCore.value_gradient(f, x) + val2, grad2 = f(x), Zygote.gradient(f, x)[1] + grad3 = FDM.grad(central_fdm(5, 1), f, x)[1] @test val1 == val2 @test norm(grad1 - grad2) == 0 @test norm(grad1 - grad3) <= 1e-5 @@ -128,7 +129,7 @@ end T = eltype(problem.E) total_ndof = ndofs(dh) - for _ in 1:3 + for _ = 1:3 v = rand(T, total_ndof) K = sprand(Float64, total_ndof, total_ndof, 0.75) K = K + K' @@ -136,20 +137,20 @@ end function f1(x) M = K * sum(x) M = apply_boundary_with_zerodiag!(M, ch) - return sum(M*v) + return sum(M * v) end function f2(x) M = K * sum(x) M = apply_boundary_with_meandiag!(M, ch) - return sum(M*v) + return sum(M * v) end x = rand(total_ndof) for f in [f2] #f1, - val1, grad1 = NonconvexCore.value_gradient(f, x); - val2, grad2 = f(x), Zygote.gradient(f, x)[1]; - grad3 = FDM.grad(central_fdm(5, 1), f, x)[1]; + val1, grad1 = NonconvexCore.value_gradient(f, x) + val2, grad2 = f(x), Zygote.gradient(f, x)[1] + grad3 = FDM.grad(central_fdm(5, 1), f, x)[1] # @test val1 == val2 # @test norm(grad1 - grad2) == 0 @test norm(grad1 - grad3) <= 1e-5 @@ -161,21 +162,22 @@ end file_name = "tim_$(problem_dim).json" problem_file = joinpath(gm_ins_dir, file_name) - mats = TrussFEAMaterial(1.0, 0.3); - crossecs = TrussFEACrossSec(800.0); + mats = TrussFEAMaterial(1.0, 0.3) + crossecs = TrussFEACrossSec(800.0) - node_points, elements, _, _ , fixities, load_cases = load_truss_json(problem_file) + node_points, elements, _, _, fixities, load_cases = load_truss_json(problem_file) ndim, nnodes, ncells = length(node_points[1]), length(node_points), length(elements) loads = load_cases["0"] - problem = TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mats, crossecs); + problem = + TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mats, crossecs) xmin = 0.0001 # minimum density p = 4.0 # penalty V = 0.5 # maximum volume fraction x0 = fill(1.0, ncells) # initial design - solver = FEASolver(Direct, problem); + solver = FEASolver(Direct, problem) ch = problem.ch dh = problem.ch.dh total_ndof = ndofs(dh) @@ -210,29 +212,29 @@ end Kσ = assemble_k(Kσs) Kσ = apply_boundary_with_zerodiag!(Kσ, ch) - return Array(K + c*Kσ) + return Array(K + c * Kσ) end # * check initial design stability @test isfinite(logdet(cholesky(buckling_matrix_constr(x0)))) - for _ in 1:3 + for _ = 1:3 v = rand(eltype(x0), total_ndof) - f = x -> sum(buckling_matrix_constr(x)*v) + f = x -> sum(buckling_matrix_constr(x) * v) x = clamp.(rand(ncells), 0.1, 1.0) solver.vars = x solver() - K, G = buckling(problem, solver.globalinfo, solver.elementinfo, x; u=solver.u); - @test K+G ≈ buckling_matrix_constr(x) + K, G = buckling(problem, solver.globalinfo, solver.elementinfo, x; u = solver.u) + @test K + G ≈ buckling_matrix_constr(x) - val1, grad1 = NonconvexCore.value_gradient(f, x); - val2, grad2 = f(x), Zygote.gradient(f, x)[1]; - grad3 = FDM.grad(central_fdm(5, 1), f, x)[1]; + val1, grad1 = NonconvexCore.value_gradient(f, x) + val2, grad2 = f(x), Zygote.gradient(f, x)[1] + grad3 = FDM.grad(central_fdm(5, 1), f, x)[1] @test val1 == val2 @test norm(grad1 - grad2) == 0 @test norm(grad1 - grad3) <= 1e-5 end -end \ No newline at end of file +end diff --git a/test/Functions/test_common_fns.jl b/test/Functions/test_common_fns.jl index 00b3dfa0..f9b8fb77 100644 --- a/test/Functions/test_common_fns.jl +++ b/test/Functions/test_common_fns.jl @@ -11,7 +11,7 @@ Random.seed!(1) for p in (1.0, 2.0, 3.0) solver = FEASolver(Direct, problem, xmin = 0.01, penalty = TopOpt.PowerPenalty(p)) comp = Compliance(problem, solver) - for i in 1:3 + for i = 1:3 x = clamp.(rand(prod(nels)), 0.1, 1.0) val1, grad1 = NonconvexCore.value_gradient(comp, x) val2, grad2 = comp(x), Zygote.gradient(comp, x)[1] @@ -30,16 +30,16 @@ end solver = FEASolver(Direct, problem, xmin = 0.01, penalty = TopOpt.PowerPenalty(p)) dp = Displacement(solver) u = dp(solver.vars) - for _ in 1:3 - x = clamp.(rand(prod(nels)), 0.1, 1.0) - v = rand(length(u)) - f = x -> dot(dp(x), v) - val1, grad1 = NonconvexCore.value_gradient(f, x) - val2, grad2 = f(x), Zygote.gradient(f, x)[1] - grad3 = FDM.grad(central_fdm(5, 1), f, x)[1] - @test val1 == val2 - @test norm(grad1 - grad2) == 0 - @test norm(grad2 - grad3) <= 1e-4 + for _ = 1:3 + x = clamp.(rand(prod(nels)), 0.1, 1.0) + v = rand(length(u)) + f = x -> dot(dp(x), v) + val1, grad1 = NonconvexCore.value_gradient(f, x) + val2, grad2 = f(x), Zygote.gradient(f, x)[1] + grad3 = FDM.grad(central_fdm(5, 1), f, x)[1] + @test val1 == val2 + @test norm(grad1 - grad2) == 0 + @test norm(grad2 - grad3) <= 1e-4 end end end @@ -51,7 +51,7 @@ end solver = FEASolver(Direct, problem, xmin = 0.01, penalty = TopOpt.PowerPenalty(p)) vol = Volume(problem, solver) constr = x -> vol(x) - 0.3 - for i in 1:3 + for i = 1:3 x = rand(prod(nels)) val1, grad1 = NonconvexCore.value_gradient(constr, x) val2, grad2 = constr(x), Zygote.gradient(constr, x)[1] @@ -69,7 +69,7 @@ end for p in (1.0, 2.0, 3.0) solver = FEASolver(Direct, problem, xmin = 0.01, penalty = TopOpt.PowerPenalty(p)) filter = TopOpt.DensityFilter(solver, rmin = 4.0) - for i in 1:3 + for i = 1:3 x = rand(prod(nels)) v = rand(prod(nels)) f = FunctionWrapper(x -> dot(filter(x), v), 1) @@ -86,9 +86,7 @@ end @testset "SensFilter" begin nels = (2, 2) problem = PointLoadCantilever(Val{:Linear}, nels, (1.0, 1.0)) - solver = FEASolver( - Direct, problem, xmin = 1e-3, penalty = PowerPenalty(3.0), - ) + solver = FEASolver(Direct, problem, xmin = 1e-3, penalty = PowerPenalty(3.0)) sensfilter = SensFilter(solver, rmin = 4.0) x = rand(length(solver.vars)) y = rand(length(x)) @@ -105,15 +103,23 @@ end dense_rank = 3 F = spzeros(TopOpt.Ferrite.ndofs(base_problem.ch.dh), nloads) Fsize = size(F) - for i in 1:dense_rank - F += sparsevec(dense_load_inds, randn(length(dense_load_inds)) / dense_rank, Fsize[1]) * randn(Fsize[2])' + for i = 1:dense_rank + F += + sparsevec( + dense_load_inds, + randn(length(dense_load_inds)) / dense_rank, + Fsize[1], + ) * randn(Fsize[2])' end problem = MultiLoad(base_problem, F) for p in (1.0, 2.0, 3.0) solver = FEASolver(Direct, problem, xmin = 0.01, penalty = TopOpt.PowerPenalty(p)) exact_svd_block = BlockCompliance(problem, solver, method = :exact) - constr = Nonconvex.FunctionWrapper(x -> exact_svd_block(x) .- 1000.0, length(exact_svd_block(solver.vars))) - for i in 1:3 + constr = Nonconvex.FunctionWrapper( + x -> exact_svd_block(x) .- 1000.0, + length(exact_svd_block(solver.vars)), + ) + for i = 1:3 x = clamp.(rand(prod(nels)), 0.1, 1.0) v = rand(nloads) f = FunctionWrapper(x -> dot(constr(x), v), 1) @@ -132,9 +138,10 @@ end problem = PointLoadCantilever(Val{:Quadratic}, nels, (1.0, 1.0), 1.0, 0.3, 1.0) for F in (MacroVonMisesStress, MicroVonMisesStress) for p in (1.0, 2.0, 3.0) - solver = FEASolver(Direct, problem, xmin = 0.001, penalty = TopOpt.PowerPenalty(p)) + solver = + FEASolver(Direct, problem, xmin = 0.001, penalty = TopOpt.PowerPenalty(p)) stress = F(solver) - for i in 1:3 + for i = 1:3 x = clamp.(rand(prod(nels)), 0.1, 1.0) v = rand(prod(nels)) f = FunctionWrapper(x -> dot(stress(x), v), 1) @@ -152,9 +159,10 @@ end problem = PointLoadCantilever(Val{:Quadratic}, nels, (1.0, 1.0, 1.0), 1.0, 0.3, 1.0) for F in (MacroVonMisesStress, MicroVonMisesStress) for p in (1.0, 2.0, 3.0) - solver = FEASolver(Direct, problem, xmin = 0.001, penalty = TopOpt.PowerPenalty(p)) + solver = + FEASolver(Direct, problem, xmin = 0.001, penalty = TopOpt.PowerPenalty(p)) stress = F(solver) - for i in 1:3 + for i = 1:3 x = clamp.(rand(prod(nels)), 0.1, 1.0) v = rand(prod(nels)) f = FunctionWrapper(x -> dot(stress(x), v), 1) @@ -167,4 +175,4 @@ end end end end -end \ No newline at end of file +end diff --git a/test/examples/csimp.jl b/test/examples/csimp.jl index 6b3c1d8c..d0b89c22 100644 --- a/test/examples/csimp.jl +++ b/test/examples/csimp.jl @@ -5,10 +5,11 @@ v = 0.3 # Poisson’s ratio f = 1.0 # downward force problems = Any[#PointLoadCantilever(Val{:Linear}, (60, 20, 20), (1.0, 1.0, 1.0), E, v, f), - PointLoadCantilever(Val{:Linear}, (160, 40), (1.0, 1.0), E, v, f), - HalfMBB(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), - LBeam(Val{:Linear}, Float64, force = f), - TieBeam(Val{:Quadratic}, Float64)] + PointLoadCantilever(Val{:Linear}, (160, 40), (1.0, 1.0), E, v, f), + HalfMBB(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), + LBeam(Val{:Linear}, Float64, force = f), + TieBeam(Val{:Quadratic}, Float64), +] problem_names = [ #"3d cantilever beam", "cantilever beam", @@ -21,7 +22,7 @@ problem_names = [ println("Continuation SIMP") println("-"^10) -for i in 1:length(problems) +for i = 1:length(problems) println(problem_names[i]) # Define the problem problem = problems[i] @@ -35,7 +36,7 @@ for i in 1:length(problems) penalty = TopOpt.PowerPenalty(1.0) pcont = Continuation(penalty, steps = steps, xmin = xmin, pmax = 5.0) - mma_options = options = MMAOptions(maxiter=1000) + mma_options = options = MMAOptions(maxiter = 1000) maxtol = 0.01 # maximum tolerance mintol = 0.001 # minimum tolerance b = log(mintol / maxtol) / steps @@ -46,9 +47,9 @@ for i in 1:length(problems) kkttol_gen = ExponentialContinuation(a, b, 0.0, steps + 1, mintol), ) csimp_options = TopOpt.CSIMPOptions( - steps = steps, - options_gen = mma_options_gen, - p_gen = pcont, + steps = steps, + options_gen = mma_options_gen, + p_gen = pcont, reuse = reuse, ) @@ -70,12 +71,16 @@ for i in 1:length(problems) # Define subproblem optimizer x0 = fill(V, length(solver.vars)) optimizer = Optimizer( - obj, constr, x0, MMA87(), - options = mma_options, convcriteria = convcriteria, + obj, + constr, + x0, + MMA87(), + options = mma_options, + convcriteria = convcriteria, ) # Define continuation SIMP optimizer simp = SIMP(optimizer, solver, penalty.p) - cont_simp = ContinuationSIMP(simp, steps, csimp_options) + cont_simp = ContinuationSIMP(simp, steps, csimp_options) # Solve result = cont_simp(x0) diff --git a/test/examples/global_stress.jl b/test/examples/global_stress.jl index bc71ea9a..1a068906 100644 --- a/test/examples/global_stress.jl +++ b/test/examples/global_stress.jl @@ -7,22 +7,17 @@ f = 1.0 # downward force rmin = 3.0 problems = Any[ - PointLoadCantilever(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), - HalfMBB(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), - LBeam(Val{:Linear}, Float64), + PointLoadCantilever(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), + HalfMBB(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), + LBeam(Val{:Linear}, Float64), TieBeam(Val{:Quadratic}, Float64), ] -problem_names = [ - "Cantilever beam", - "Half MBB beam", - "L-beam", - "Tie-beam", -] +problem_names = ["Cantilever beam", "Half MBB beam", "L-beam", "Tie-beam"] println("Global Stress") println("-"^10) -for i in 1:length(problems) +for i = 1:length(problems) println(problem_names[i]) problem = problems[i] # Parameter settings @@ -32,9 +27,7 @@ for i in 1:length(problems) convcriteria = Nonconvex.KKTCriteria() penalty = TopOpt.PowerPenalty(1.0) # Define a finite element solver - solver = FEASolver( - Direct, problem, xmin = xmin, penalty = penalty, - ) + solver = FEASolver(Direct, problem, xmin = xmin, penalty = penalty) # Define compliance objective stress = TopOpt.MicroVonMisesStress(solver) filter = if problem isa TopOptProblems.TieBeam @@ -48,13 +41,15 @@ for i in 1:length(problems) constr = x -> norm(stress(filter(x)), 5) - 1.0 # Define subproblem optimizer x0 = fill(1.0, length(solver.vars)) - options = MMAOptions( - maxiter=2000, tol = Nonconvex.Tolerance(kkt = 1e-4), - ) + options = MMAOptions(maxiter = 2000, tol = Nonconvex.Tolerance(kkt = 1e-4)) #options = PercivalOptions() optimizer = Optimizer( - obj, constr, x0, MMA87(), #PercivalAlg(), - options = options, convcriteria = convcriteria, + obj, + constr, + x0, + MMA87(), #PercivalAlg(), + options = options, + convcriteria = convcriteria, ) # Define continuation SIMP optimizer diff --git a/test/examples/local_stress.jl b/test/examples/local_stress.jl index ddfa2f83..2d6850c8 100644 --- a/test/examples/local_stress.jl +++ b/test/examples/local_stress.jl @@ -9,17 +9,13 @@ f = 1.0 # downward force rmin = 3.0 problems = Any[ - PointLoadCantilever(Val{:Linear}, (160, 40), (1.0, 1.0), E, v, f), - HalfMBB(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), + PointLoadCantilever(Val{:Linear}, (160, 40), (1.0, 1.0), E, v, f), + HalfMBB(Val{:Linear}, (60, 20), (1.0, 1.0), E, v, f), LBeam(Val{:Linear}, Float64), ] -problem_names = [ - "Cantilever beam", - "Half MBB beam", - "L-beam", -] +problem_names = ["Cantilever beam", "Half MBB beam", "L-beam"] -for i in 1:length(problems) +for i = 1:length(problems) println(problem_names[i]) problem = problems[i] # Parameter settings @@ -33,9 +29,7 @@ for i in 1:length(problems) #penalty = TopOpt.PowerPenalty(1.0) global penalty = TopOpt.PowerPenalty(p) # Define a finite element solver - solver = FEASolver( - Direct, problem, xmin = xmin, penalty = penalty, - ) + solver = FEASolver(Direct, problem, xmin = xmin, penalty = penalty) # Define compliance objective global stress = TopOpt.MicroVonMisesStress(solver) global filter = DensityFilter(solver, rmin = rmin) @@ -45,17 +39,11 @@ for i in 1:length(problems) constr = x -> begin s = stress(filter(x)) thr = 10 - vcat( - (s .- thr) / 100, - logsumexp(s) - log(length(s)) - thr, - ) + vcat((s .- thr) / 100, logsumexp(s) - log(length(s)) - thr) end alg = PercivalAlg() options = PercivalOptions() - optimizer = Optimizer( - obj, constr, x0, alg, - options = options, - ) + optimizer = Optimizer(obj, constr, x0, alg, options = options) # Define continuation SIMP optimizer simp = SIMP(optimizer, solver, p) # Solve diff --git a/test/examples/test_examples.jl b/test/examples/test_examples.jl index 7166fd27..4681eae4 100644 --- a/test/examples/test_examples.jl +++ b/test/examples/test_examples.jl @@ -1,28 +1,28 @@ # Test the example scripts module TestSIMPExample - println("PointLoadCantilever Example") - mktempdir() do dir - cd(dir) do - include(joinpath(@__DIR__, "../../docs/src/literate/simp.jl")) - end +println("PointLoadCantilever Example") +mktempdir() do dir + cd(dir) do + include(joinpath(@__DIR__, "../../docs/src/literate/simp.jl")) end end +end module TestBESOExample - println("BESO Example") - mktempdir() do dir - cd(dir) do - include(joinpath(@__DIR__, "../../docs/src/literate/beso.jl")) - end +println("BESO Example") +mktempdir() do dir + cd(dir) do + include(joinpath(@__DIR__, "../../docs/src/literate/beso.jl")) end end +end module TestGESOExample - println("GESO Example") - mktempdir() do dir - cd(dir) do - include(joinpath(@__DIR__, "../../docs/src/literate/geso.jl")) - end +println("GESO Example") +mktempdir() do dir + cd(dir) do + include(joinpath(@__DIR__, "../../docs/src/literate/geso.jl")) end end +end diff --git a/test/fea/solvers.jl b/test/fea/solvers.jl index 4dd78e72..4f62a22c 100644 --- a/test/fea/solvers.jl +++ b/test/fea/solvers.jl @@ -1,7 +1,7 @@ using TopOpt -nels = (20, 10, 10) -sizes = (1.0,1.0,1.0) +nels = (20, 10, 10) +sizes = (1.0, 1.0, 1.0) E = 1.0 ν = 0.3 force = -1.0 diff --git a/test/inp_parser/parser.jl b/test/inp_parser/parser.jl index d66521ea..af134222 100644 --- a/test/inp_parser/parser.jl +++ b/test/inp_parser/parser.jl @@ -28,7 +28,7 @@ force_node = collect(keys(raw_inp.cloads))[1] @test raw_inp.node_coords[force_node] == (10, 10, 10) @test raw_inp.cloads[force_node] == [0, -1, 0] -@test raw_inp.facesets["DLOAD_SET_1"] == [(1,3), (5,2)] +@test raw_inp.facesets["DLOAD_SET_1"] == [(1, 3), (5, 2)] @test raw_inp.dloads["DLOAD_SET_1"] == 1 @test raw_inp.nodedbcs["FemConstraintDisplacement"] == [(1, 0), (2, 0), (3, 0)] diff --git a/test/runtests.jl b/test/runtests.jl index 988892a2..e7bcf5f5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -31,7 +31,7 @@ end @safetestset "Local Stress" begin include("examples/local_stress.jl") end - @safetestset "More examples" begin + @safetestset "More examples" begin include("examples/test_examples.jl") end end diff --git a/test/topopt_problems/element_stiffness_matrix.jl b/test/topopt_problems/element_stiffness_matrix.jl index 82ee1e8e..5ce86b56 100644 --- a/test/topopt_problems/element_stiffness_matrix.jl +++ b/test/topopt_problems/element_stiffness_matrix.jl @@ -6,17 +6,29 @@ E = 1 nu = 0.3 - k = [1/2-nu/6, 1/8+nu/8, -1/4-nu/12, -1/8+3*nu/8, -1/4+nu/12, -1/8-nu/8, nu/6, 1/8-3*nu/8] - Ke = E/(1-nu^2)*[k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8]; - k[2] k[1] k[8] k[7] k[6] k[5] k[4] k[3]; - k[3] k[8] k[1] k[6] k[7] k[4] k[5] k[2]; - k[4] k[7] k[6] k[1] k[8] k[3] k[2] k[5]; - k[5] k[6] k[7] k[8] k[1] k[2] k[3] k[4]; - k[6] k[5] k[4] k[3] k[2] k[1] k[8] k[7]; - k[7] k[4] k[5] k[2] k[3] k[8] k[1] k[6]; - k[8] k[3] k[2] k[5] k[4] k[7] k[6] k[1]] - - problem = HalfMBB((2,2), (1.,1.), 1., 0.3, 1.); + k = [ + 1 / 2 - nu / 6, + 1 / 8 + nu / 8, + -1 / 4 - nu / 12, + -1 / 8 + 3 * nu / 8, + -1 / 4 + nu / 12, + -1 / 8 - nu / 8, + nu / 6, + 1 / 8 - 3 * nu / 8, + ] + Ke = + E / (1 - nu^2) * [ + k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8] + k[2] k[1] k[8] k[7] k[6] k[5] k[4] k[3] + k[3] k[8] k[1] k[6] k[7] k[4] k[5] k[2] + k[4] k[7] k[6] k[1] k[8] k[3] k[2] k[5] + k[5] k[6] k[7] k[8] k[1] k[2] k[3] k[4] + k[6] k[5] k[4] k[3] k[2] k[1] k[8] k[7] + k[7] k[4] k[5] k[2] k[3] k[8] k[1] k[6] + k[8] k[3] k[2] k[5] k[4] k[7] k[6] k[1] + ] + + problem = HalfMBB((2, 2), (1.0, 1.0), 1.0, 0.3, 1.0) #@test ElementFEAInfo(problem).Kes[1] ≈ Ke end diff --git a/test/topopt_problems/metadata.jl b/test/topopt_problems/metadata.jl index 0c94d3c4..8223b949 100644 --- a/test/topopt_problems/metadata.jl +++ b/test/topopt_problems/metadata.jl @@ -50,45 +50,53 @@ # 7 5 15 13 # 8 6 16 14 - problem = HalfMBB(Val{:Linear}, (2,2), (1.,1.), 1., 0.3, 1.); + problem = HalfMBB(Val{:Linear}, (2, 2), (1.0, 1.0), 1.0, 0.3, 1.0) - coords = [(0.0, 0.0), - (1.0, 0.0), - (2.0, 0.0), - (0.0, 1.0), - (1.0, 1.0), - (2.0, 1.0), - (0.0, 2.0), - (1.0, 2.0), - (2.0, 2.0)] + coords = [ + (0.0, 0.0), + (1.0, 0.0), + (2.0, 0.0), + (0.0, 1.0), + (1.0, 1.0), + (2.0, 1.0), + (0.0, 2.0), + (1.0, 2.0), + (2.0, 2.0), + ] for (i, node) in enumerate(problem.ch.dh.grid.nodes) @test node.x.data == coords[i] end - cells = [Ferrite.Cell{2,4,4}((1, 2, 5, 4)), + cells = [ + Ferrite.Cell{2,4,4}((1, 2, 5, 4)), Ferrite.Cell{2,4,4}((2, 3, 6, 5)), Ferrite.Cell{2,4,4}((4, 5, 8, 7)), - Ferrite.Cell{2,4,4}((5, 6, 9, 8))] + Ferrite.Cell{2,4,4}((5, 6, 9, 8)), + ] @test problem.ch.dh.grid.cells == cells - node_dofs = [1 3 9 7 5 11 15 13 17; - 2 4 10 8 6 12 16 14 18] + node_dofs = [ + 1 3 9 7 5 11 15 13 17 + 2 4 10 8 6 12 16 14 18 + ] @test problem.metadata.node_dofs == node_dofs - cell_dofs = [1 3 7 5; - 2 4 8 6; - 3 9 5 11; - 4 10 6 12; - 5 11 13 17; - 6 12 14 18; - 7 5 15 13; - 8 6 16 14] + cell_dofs = [ + 1 3 7 5 + 2 4 8 6 + 3 9 5 11 + 4 10 6 12 + 5 11 13 17 + 6 12 14 18 + 7 5 15 13 + 8 6 16 14 + ] @test problem.metadata.cell_dofs == cell_dofs dof_cells = problem.metadata.dof_cells cell_dofs = problem.metadata.cell_dofs - for i in 1:Ferrite.ndofs(problem.ch.dh) + for i = 1:Ferrite.ndofs(problem.ch.dh) d_cells = dof_cells[i] for c in d_cells (cellid, localdof) = c @@ -96,15 +104,8 @@ end end - node_first_cells = [(1, 1), - (1, 2), - (2, 2), - (1, 4), - (1, 3), - (2, 3), - (3, 4), - (3, 3), - (4, 3)] + node_first_cells = + [(1, 1), (1, 2), (2, 2), (1, 4), (1, 3), (2, 3), (3, 4), (3, 3), (4, 3)] # First node is the first cell's first node. @test problem.metadata.node_cells[1] == [(1, 1)] # Second node is the first cell's second node, diff --git a/test/topopt_problems/problems.jl b/test/topopt_problems/problems.jl index 223af6cd..ce1fc94f 100644 --- a/test/topopt_problems/problems.jl +++ b/test/topopt_problems/problems.jl @@ -11,8 +11,8 @@ force = 1.0 # Cantilever beam problem tests @testset "Point load cantilever beam" begin global E, ν, force - problem = PointLoadCantilever(Val{:Linear}, (160,40), (1.0,1.0), E, ν, force); - ncells = 160*40 + problem = PointLoadCantilever(Val{:Linear}, (160, 40), (1.0, 1.0), E, ν, force) + ncells = 160 * 40 @test problem.E == E @test problem.ν == ν @test problem.black == problem.white == falses(ncells) @@ -21,42 +21,43 @@ force = 1.0 @test problem.varind == 1:ncells grid = problem.ch.dh.grid @test length(grid.cells) == ncells - for i in 1:2, j in 1:2 + for i = 1:2, j = 1:2 @test boundingbox(grid)[i][j] ≈ problem.rect_grid.corners[i][j] atol = 1e-8 end - @test length(grid.boundary_matrix.nzval) == 2*160 + 2*40 + @test length(grid.boundary_matrix.nzval) == 2 * 160 + 2 * 40 for (c, f) in grid.facesets["bottom"] @test f == 1 - for n in grid.cells[c].nodes[[1,2]] + for n in grid.cells[c].nodes[[1, 2]] @test grid.nodes[n].x[2] == 0 end end for (c, f) in grid.facesets["right"] @test f == 2 - for n in grid.cells[c].nodes[[2,3]] + for n in grid.cells[c].nodes[[2, 3]] @test grid.nodes[n].x[1] ≈ 160 atol = 1e-8 end end for (c, f) in grid.facesets["top"] @test f == 3 - for n in grid.cells[c].nodes[[3,4]] + for n in grid.cells[c].nodes[[3, 4]] @test grid.nodes[n].x[2] ≈ 40 atol = 1e-8 end end for (c, f) in grid.facesets["left"] @test f == 4 - for n in grid.cells[c].nodes[[1,4]] + for n in grid.cells[c].nodes[[1, 4]] @test grid.nodes[n].x[1] == 0 end end - @test sum(length, getindex.((grid.facesets,), ["bottom", "right", "top", "left"])) == 2*160 + 2*40 + @test sum(length, getindex.((grid.facesets,), ["bottom", "right", "top", "left"])) == + 2 * 160 + 2 * 40 end # Half MBB beam problem @testset "Half MBB beam" begin global E, ν, force - problem = HalfMBB(Val{:Linear}, (60,20), (1.,1.), E, ν, force); - ncells = 60*20 + problem = HalfMBB(Val{:Linear}, (60, 20), (1.0, 1.0), E, ν, force) + ncells = 60 * 20 @test problem.E == E @test problem.ν == ν @test problem.black == problem.white == falses(ncells) @@ -65,42 +66,43 @@ end @test problem.varind == 1:ncells grid = problem.ch.dh.grid @test length(grid.cells) == ncells - for i in 1:2, j in 1:2 + for i = 1:2, j = 1:2 @test boundingbox(grid)[i][j] ≈ problem.rect_grid.corners[i][j] atol = 1e-8 end - @test length(grid.boundary_matrix.nzval) == 2*60 + 2*20 + @test length(grid.boundary_matrix.nzval) == 2 * 60 + 2 * 20 for (c, f) in grid.facesets["bottom"] @test f == 1 - for n in grid.cells[c].nodes[[1,2]] + for n in grid.cells[c].nodes[[1, 2]] @test grid.nodes[n].x[2] == 0 end end for (c, f) in grid.facesets["right"] @test f == 2 - for n in grid.cells[c].nodes[[2,3]] + for n in grid.cells[c].nodes[[2, 3]] @test grid.nodes[n].x[1] ≈ 60 atol = 1e-8 end end for (c, f) in grid.facesets["top"] @test f == 3 - for n in grid.cells[c].nodes[[3,4]] + for n in grid.cells[c].nodes[[3, 4]] @test grid.nodes[n].x[2] ≈ 20 atol = 1e-8 end end for (c, f) in grid.facesets["left"] @test f == 4 - for n in grid.cells[c].nodes[[1,4]] + for n in grid.cells[c].nodes[[1, 4]] @test grid.nodes[n].x[1] == 0 end end - @test sum(length, getindex.((grid.facesets,), ["bottom", "right", "top", "left"])) == 2*60 + 2*20 + @test sum(length, getindex.((grid.facesets,), ["bottom", "right", "top", "left"])) == + 2 * 60 + 2 * 20 end # L-beam problem @testset "L-beam" begin global E, ν, force - problem = LBeam(Val{:Linear}, Float64, force=force); - ncells = 100*50 + 50*50 + problem = LBeam(Val{:Linear}, Float64, force = force) + ncells = 100 * 50 + 50 * 50 @test problem.E == E @test problem.ν == ν @test problem.black == problem.white == falses(ncells) @@ -110,28 +112,28 @@ end grid = problem.ch.dh.grid @test length(grid.cells) == ncells corners = [[0.0, 0.0], [100.0, 100.0]] - for i in 1:2, j in 1:2 + for i = 1:2, j = 1:2 @test boundingbox(grid)[i][j] ≈ corners[i][j] atol = 1e-8 end @test length(grid.boundary_matrix.nzval) == 100 * 2 + 50 * 4 for (c, f) in grid.facesets["right"] @test f == 2 - for n in grid.cells[c].nodes[[2,3]] + for n in grid.cells[c].nodes[[2, 3]] @test grid.nodes[n].x[1] ≈ 100 atol = 1e-8 end end for (c, f) in grid.facesets["top"] @test f == 3 - for n in grid.cells[c].nodes[[3,4]] + for n in grid.cells[c].nodes[[3, 4]] @test grid.nodes[n].x[2] ≈ 100 atol = 1e-8 end end - @test sum(length, getindex.((grid.facesets,), ["right", "top"])) == 2*50 + @test sum(length, getindex.((grid.facesets,), ["right", "top"])) == 2 * 50 end # Tie-beam problem @testset "Tie-beam" begin - problem = TopOptProblems.TieBeam(Val{:Quadratic}, Float64); + problem = TopOptProblems.TieBeam(Val{:Quadratic}, Float64) ncells = 100 @test problem.E == 1 @test problem.ν == 0.3 @@ -141,35 +143,38 @@ end grid = problem.ch.dh.grid @test length(grid.cells) == ncells corners = [[0.0, 0.0], [32.0, 7.0]] - for i in 1:2, j in 1:2 + for i = 1:2, j = 1:2 @test boundingbox(grid)[i][j] ≈ corners[i][j] atol = 1e-8 end @test length(grid.boundary_matrix.nzval) == 32 * 2 + 3 * 2 + 4 * 2 for (c, f) in grid.facesets["bottomload"] @test f == 1 - for n in grid.cells[c].nodes[[1,2]] + for n in grid.cells[c].nodes[[1, 2]] @test grid.nodes[n].x[2] == 0 end end for (c, f) in grid.facesets["rightload"] @test f == 2 - for n in grid.cells[c].nodes[[2,3]] + for n in grid.cells[c].nodes[[2, 3]] @test grid.nodes[n].x[1] ≈ 32 atol = 1e-8 end end for (c, f) in grid.facesets["toproller"] @test f == 3 - for n in grid.cells[c].nodes[[3,4]] + for n in grid.cells[c].nodes[[3, 4]] @test grid.nodes[n].x[2] ≈ 7 atol = 1e-8 end end for (c, f) in grid.facesets["leftfixed"] @test f == 4 - for n in grid.cells[c].nodes[[1,4]] + for n in grid.cells[c].nodes[[1, 4]] @test grid.nodes[n].x[1] == 0 end end - @test sum(length, getindex.((grid.facesets,), ["bottomload", "rightload", "toproller", "leftfixed"])) == 8 - @test Ferrite.getorder(problem.ch.dh.field_interpolations[1]) == 2 - @test Ferrite.nnodes(grid.cells[1]) == 9 + @test sum( + length, + getindex.((grid.facesets,), ["bottomload", "rightload", "toproller", "leftfixed"]), + ) == 8 + @test Ferrite.getorder(problem.ch.dh.field_interpolations[1]) == 2 + @test Ferrite.nnodes(grid.cells[1]) == 9 end diff --git a/test/truss_topopt_problems/test_buckling.jl b/test/truss_topopt_problems/test_buckling.jl index f2a695fa..98a564e5 100644 --- a/test/truss_topopt_problems/test_buckling.jl +++ b/test/truss_topopt_problems/test_buckling.jl @@ -123,7 +123,7 @@ gm_ins_dir = joinpath(@__DIR__, "instances", "ground_meshes"); # iter += 1; # end # u0[:] = u1[:]; # update the displacement - + # step = step + 1 # # save displacement record for plotting # tip_displacement[step, :] .= u1[2*2-1:2*2] @@ -137,4 +137,4 @@ gm_ins_dir = joinpath(@__DIR__, "instances", "ground_meshes"); # # using TopOpt.TrussTopOptProblems.TrussVisualization: visualize # # fig = visualize(problem, u1) -# end # end test set \ No newline at end of file +# end # end test set diff --git a/test/truss_topopt_problems/test_buckling_optimize.jl b/test/truss_topopt_problems/test_buckling_optimize.jl index a97b5bed..49292686 100644 --- a/test/truss_topopt_problems/test_buckling_optimize.jl +++ b/test/truss_topopt_problems/test_buckling_optimize.jl @@ -89,21 +89,22 @@ gm_ins_dir = joinpath(@__DIR__, "instances", "ground_meshes"); file_name = "tim_$(problem_dim).json" problem_file = joinpath(gm_ins_dir, file_name) - mats = TrussFEAMaterial(10.0, 0.3); - crossecs = TrussFEACrossSec(800.0); + mats = TrussFEAMaterial(10.0, 0.3) + crossecs = TrussFEACrossSec(800.0) - node_points, elements, _, _ , fixities, load_cases = load_truss_json(problem_file) + node_points, elements, _, _, fixities, load_cases = load_truss_json(problem_file) ndim, nnodes, ncells = length(node_points[1]), length(node_points), length(elements) loads = load_cases["0"] - problem = TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mats, crossecs); + problem = + TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mats, crossecs) xmin = 0.0001 # minimum density p = 4.0 # penalty V = 0.5 # maximum volume fraction x0 = fill(V, ncells) # initial design - solver = FEASolver(Direct, problem); + solver = FEASolver(Direct, problem) ch = problem.ch dh = problem.ch.dh @@ -137,7 +138,7 @@ gm_ins_dir = joinpath(@__DIR__, "instances", "ground_meshes"); Kσ = assemble_k(Kσs) Kσ = apply_boundary_with_zerodiag!(Kσ, ch) - return Array(K + c*Kσ) + return Array(K + c * Kσ) end function vol_constr(x) @@ -155,8 +156,8 @@ gm_ins_dir = joinpath(@__DIR__, "instances", "ground_meshes"); Nonconvex.add_sd_constraint!(m, buckling_matrix_constr) Nonconvex.NonconvexCore.show_residuals[] = false - alg = SDPBarrierAlg(sub_alg=IpoptAlg()) - options = SDPBarrierOptions(sub_options=IpoptOptions(max_iter=200)) + alg = SDPBarrierAlg(sub_alg = IpoptAlg()) + options = SDPBarrierOptions(sub_options = IpoptOptions(max_iter = 200)) r = Nonconvex.optimize(m, alg, x0, options = options) # println("$(r.convstate)") @@ -168,4 +169,4 @@ gm_ins_dir = joinpath(@__DIR__, "instances", "ground_meshes"); # using TopOpt.TrussTopOptProblems.TrussVisualization: visualize # fig = visualize(problem; topology=r.minimizer) # Makie.display(fig) -end \ No newline at end of file +end diff --git a/test/truss_topopt_problems/test_fea.jl b/test/truss_topopt_problems/test_fea.jl index 8e33b98b..2b9a4c06 100644 --- a/test/truss_topopt_problems/test_fea.jl +++ b/test/truss_topopt_problems/test_fea.jl @@ -4,7 +4,8 @@ using Ferrite using Ferrite: cellid, getcoordinates, CellIterator using TopOpt -using TopOpt.TopOptProblems: boundingbox, nnodespercell, getgeomorder, getmetadata, getdh, getE, getdim +using TopOpt.TopOptProblems: + boundingbox, nnodespercell, getgeomorder, getmetadata, getdh, getE, getdim using TopOpt.TrussTopOptProblems: getA, default_quad_order, compute_local_axes # if get(ENV, "CI", nothing) != "true" # import Makie @@ -15,22 +16,24 @@ include("utils.jl") problem_json = ["mgz_truss1.json", "mgz_truss2.json", "mgz_truss3.json"] u_solutions = [ - 1e-3 * vcat([2.41, 0.72], zeros(2*2)), - 1e-3 * vcat([0.1783, 2.7222, -0.4863], zeros(4*3)), - 1e-3 * vcat([0.871, 1.244], [0,0], [-0.193, 0]), + 1e-3 * vcat([2.41, 0.72], zeros(2 * 2)), + 1e-3 * vcat([0.1783, 2.7222, -0.4863], zeros(4 * 3)), + 1e-3 * vcat([0.871, 1.244], [0, 0], [-0.193, 0]), ] ins_dir = joinpath(@__DIR__, "instances", "fea_examples"); -@testset "Truss problem solve - $(problem_json[i])" for i in 1:length(problem_json) +@testset "Truss problem solve - $(problem_json[i])" for i = 1:length(problem_json) # i = 3 file_name = problem_json[i] problem_file = joinpath(ins_dir, file_name) - node_points, elements, mats, crosssecs, fixities, load_cases = load_truss_json(problem_file); + node_points, elements, mats, crosssecs, fixities, load_cases = + load_truss_json(problem_file) ndim, nnodes, ncells = length(node_points[1]), length(node_points), length(elements) loads = load_cases["0"] - problem = TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mats, crosssecs); + problem = + TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mats, crosssecs) @test getdim(problem) == ndim @test Ferrite.getncells(problem) == ncells @@ -44,8 +47,8 @@ ins_dir = joinpath(@__DIR__, "instances", "fea_examples"); @test getgeomorder(problem) == 1 @test nnodespercell(problem) == 2 - quad_order=default_quad_order(problem) - elementinfo = ElementFEAInfo(problem, quad_order, Val{:Static}); + quad_order = default_quad_order(problem) + elementinfo = ElementFEAInfo(problem, quad_order, Val{:Static}) As = getA(problem) Es = getE(problem) for cell in CellIterator(getdh(problem)) @@ -56,12 +59,12 @@ ins_dir = joinpath(@__DIR__, "instances", "fea_examples"); E = Es[cellidx] @test elementinfo.cellvolumes[cellidx] ≈ L * A - Γ = zeros(2,ndim*2) + Γ = zeros(2, ndim * 2) R = compute_local_axes(coords[1], coords[2]) - Γ[1,1:ndim] = R[:,1] - Γ[2,ndim+1:2*ndim] = R[:,1] + Γ[1, 1:ndim] = R[:, 1] + Γ[2, ndim+1:2*ndim] = R[:, 1] - Ke_m = (A*E/L)*Γ'*[1 -1; -1 1]*Γ + Ke_m = (A * E / L) * Γ' * [1 -1; -1 1] * Γ Ke = elementinfo.Kes[cellidx] @test Ke_m ≈ Ke end @@ -82,4 +85,4 @@ ins_dir = joinpath(@__DIR__, "instances", "fea_examples"); to_K_full = solver.globalinfo.K.data @assert norm(solver.u - u_solutions[i]) < 3e-4 -end # end test set \ No newline at end of file +end # end test set diff --git a/test/truss_topopt_problems/test_problem.jl b/test/truss_topopt_problems/test_problem.jl index 1d6e293e..9570c85f 100644 --- a/test/truss_topopt_problems/test_problem.jl +++ b/test/truss_topopt_problems/test_problem.jl @@ -11,20 +11,21 @@ using Base.Iterators ins_dir = joinpath(@__DIR__, "instances", "ground_meshes"); @testset "Test parsing $file_format" for file_format in [".geo", ".json"] - file_name = "tim_2d"*file_format + file_name = "tim_2d" * file_format problem_file = joinpath(ins_dir, file_name) - mat = TrussFEAMaterial(1.0, 0.3); - crossec = TrussFEACrossSec(800.0); + mat = TrussFEAMaterial(1.0, 0.3) + crossec = TrussFEACrossSec(800.0) if file_format == ".geo" node_points, elements, fixities, load_cases = load_truss_geo(problem_file) loads = load_cases[1] else - node_points, elements, _, _, fixities, load_cases = load_truss_json(problem_file); + node_points, elements, _, _, fixities, load_cases = load_truss_json(problem_file) loads = load_cases["0"] end - problem = TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mat, crossec); + problem = + TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mat, crossec) - solver = FEASolver(Direct, problem); + solver = FEASolver(Direct, problem) solver() @test !all(@. isnan(solver.u)) end @@ -37,21 +38,22 @@ end file_name = "tim_$(problem_dim).json" problem_file = joinpath(ins_dir, file_name) - node_points, elements, mats, crosssecs, fixities, load_cases = load_truss_json(problem_file); + node_points, elements, mats, crosssecs, fixities, load_cases = + load_truss_json(problem_file) loads = load_cases[string(lc_ind)] - problem = TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mats, crosssecs); + problem = + TrussProblem(Val{:Linear}, node_points, elements, loads, fixities, mats, crosssecs) ndim, nnodes, ncells = length(node_points[1]), length(node_points), length(elements) @test getE(problem) == [m.E for m in mats] V = 0.3 # volume fraction xmin = 0.001 # minimum density - rmin = 4.0; # density filter radius + rmin = 4.0 # density filter radius penalty = TopOpt.PowerPenalty(1.0) # 1 - solver = FEASolver(Direct, problem, xmin = xmin, - penalty = penalty); + solver = FEASolver(Direct, problem, xmin = xmin, penalty = penalty) ## call solver to trigger assemble! solver() @@ -61,9 +63,7 @@ end volfrac = TopOpt.Volume(problem, solver) constr = x -> volfrac(x) - V - options = MMAOptions( - maxiter = 3000, tol = Nonconvex.Tolerance(kkt = 0.001), - ) + options = MMAOptions(maxiter = 3000, tol = Nonconvex.Tolerance(kkt = 0.001)) convcriteria = Nonconvex.KKTCriteria() x0 = fill(V, length(solver.vars)) nelem = length(x0) @@ -80,10 +80,12 @@ end # r.minimizer TopOpt.setpenalty!(solver, penalty.p) - result = Nonconvex.optimize(m, MMA87(), x0, options = options); + result = Nonconvex.optimize(m, MMA87(), x0, options = options) println("="^10) - println("tim-$(problem_dim) - LC $(lc_ind) - #elements $(ncells), #dof: $(ncells*ndim): opt iter $(result.iter)") + println( + "tim-$(problem_dim) - LC $(lc_ind) - #elements $(ncells), #dof: $(ncells*ndim): opt iter $(result.iter)", + ) println("$(result.convstate)") # if get(ENV, "CI", nothing) != "true" @@ -95,4 +97,4 @@ end # ) # Makie.display(fig) # end -end # end testset \ No newline at end of file +end # end testset diff --git a/test/truss_topopt_problems/utils.jl b/test/truss_topopt_problems/utils.jl index 2d7a12fa..d81c43d4 100644 --- a/test/truss_topopt_problems/utils.jl +++ b/test/truss_topopt_problems/utils.jl @@ -20,7 +20,7 @@ This matrix formulation is equivalent to the one used in # Outputs `Kσs` = a list of 2*ndim x 2*ndim element geometric stiffness matrix (in global cooridnate) """ -function get_truss_Kσs(problem::TrussProblem{xdim, TT}, u, cellvalues) where {xdim, TT} +function get_truss_Kσs(problem::TrussProblem{xdim,TT}, u, cellvalues) where {xdim,TT} Es = getE(problem) As = getA(problem) dh = problem.ch.dh @@ -28,11 +28,11 @@ function get_truss_Kσs(problem::TrussProblem{xdim, TT}, u, cellvalues) where {x # usually ndof_pc = xdim * n_basefuncs ndof_pc = ndofs_per_cell(dh) n_basefuncs = getnbasefunctions(cellvalues) - @assert ndof_pc == xdim*n_basefuncs "$ndof_pc, $n_basefuncs" + @assert ndof_pc == xdim * n_basefuncs "$ndof_pc, $n_basefuncs" @assert n_basefuncs == 2 global_dofs = zeros(Int, ndof_pc) - Kσs = [zeros(TT, ndof_pc, ndof_pc) for i in 1:getncells(dh.grid)] + Kσs = [zeros(TT, ndof_pc, ndof_pc) for i = 1:getncells(dh.grid)] Kσ_e = zeros(TT, ndof_pc, ndof_pc) for (cellidx, cell) in enumerate(CellIterator(dh)) @@ -44,15 +44,15 @@ function get_truss_Kσs(problem::TrussProblem{xdim, TT}, u, cellvalues) where {x L = norm(cell.coords[1] - cell.coords[2]) R = compute_local_axes(cell.coords[1], cell.coords[2]) # local axial projection operator (local axis transformation) - γ = vcat(-R[:,1], R[:,1]) + γ = vcat(-R[:, 1], R[:, 1]) u_cell = @view u[global_dofs] # compute axial force: first-order approx of bar force # better approx would be: EA/L * (u3-u1 + 1/(2*L0)*(u4-u2)^2) = EA/L * (γ'*u + 1/2*(δ'*u)^2) # see: https://people.duke.edu/~hpgavin/cee421/truss-finite-def.pdf - q_cell = E*A/L*(γ'*u_cell) + q_cell = E * A / L * (γ' * u_cell) Kσ_e .= 0 - for i=2:size(R,2) - δ = vcat(-R[:,i], R[:,i]) + for i = 2:size(R, 2) + δ = vcat(-R[:, i], R[:, i]) # @assert δ' * γ ≈ 0 Kσ_e .+= δ * δ' end @@ -79,7 +79,14 @@ Assembly global geometric stiffness matrix of the given truss problem. `K` = global first-order stiffness matrix (same as ginfo.K) `Kσ` = global geometric stiffness matrix """ -function buckling(problem::TrussProblem{xdim, T}, ginfo, einfo, vars=ones(T, getncells(getdh(problem).grid)), xmin = T(0.0); u=undef) where {xdim, T} +function buckling( + problem::TrussProblem{xdim,T}, + ginfo, + einfo, + vars = ones(T, getncells(getdh(problem).grid)), + xmin = T(0.0); + u = undef, +) where {xdim,T} dh = problem.ch.dh black = problem.black white = problem.white @@ -109,7 +116,7 @@ function buckling(problem::TrussProblem{xdim, T}, ginfo, einfo, vars=ones(T, get celliteratortype = CellIterator{typeof(dh).parameters...} celliterator::celliteratortype = CellIterator(dh) TK = eltype(Kσs) - for (i,cell) in enumerate(celliterator) + for (i, cell) in enumerate(celliterator) celldofs!(global_dofs, dh, i) Kσ_e = TK isa Symmetric ? Kσs[i].data : Kσs[i] if black[i] @@ -127,7 +134,7 @@ function buckling(problem::TrussProblem{xdim, T}, ginfo, einfo, vars=ones(T, get # if PENALTY_BEFORE_INTERPOLATION # px = density(penalty(vars[varind[i]]), xmin) # else - # px = penalty(density(vars[varind[i]], xmin)) + # px = penalty(density(vars[varind[i]], xmin)) # end Kσ_e = px * Kσ_e Ferrite.assemble!(assembler, global_dofs, Kσ_e) @@ -138,4 +145,4 @@ function buckling(problem::TrussProblem{xdim, T}, ginfo, einfo, vars=ones(T, get Kσ = apply_boundary_with_zerodiag!(Kσ, problem.ch) return ginfo.K, Kσ -end \ No newline at end of file +end