refactor contact and tests

This commit is contained in:
Jukka Aho
2016-06-25 04:12:53 +03:00
parent 6d3e33c3ff
commit 90f7c581c5
28 changed files with 874 additions and 420 deletions
+43 -6
View File
@@ -25,7 +25,27 @@ export AbstractPoint, Point, IntegrationPoint, IP, Node
include("elements.jl") # common element routines
export Node, AbstractElement, Element, update!, get_connectivity, get_basis, get_dbasis
include("lagrange_macro.jl") # Continuous Galerkin (Lagrange) elements generated using macro
export Seg2, Seg3, Tri3, Tri6, Quad4, Hex8, Tet4, Tet10
type Poi1 <: AbstractElement
end
function size(element::Element{Poi1})
return (0, 1)
end
function length(element::Element{Poi1})
return 1
end
function get_basis(element::Element{Poi1}, ip, time)
return [1]
end
function call(element::Element{Poi1}, ip, time, ::Type{Val{:detJ}})
return 1.0
end
export Poi1, Seg2, Seg3, Tri3, Tri6, Quad4, Hex8, Tet4, Tet10
include("nurbs.jl")
export NSeg, NSurf, NSolid, is_nurbs
@@ -80,10 +100,11 @@ export calculate_normals,
calculate_normals!,
project_from_slave_to_master,
project_from_master_to_slave,
Mortar
Mortar, get_slave_elements
### Contact mechanics ###
#include("contact.jl")
### Mortar methods, contact mechanics extension ###
include("contact.jl")
export Contact
# rest of things
include("utils.jl")
@@ -96,16 +117,31 @@ end
module Preprocess
include("preprocess.jl")
export create_elements
export create_elements, Mesh,
add_node!, add_nodes!,
add_element!, add_elements!,
add_element_to_element_set!,
add_node_to_node_set!,
find_nearest_nodes
include("preprocess_abaqus_reader.jl")
include("preprocess_abaqus_reader_old.jl")
include("preprocess_aster_reader.jl")
export aster_create_elements, parse_aster_med_file, is_aster_mail_keyword,
parse_aster_header, aster_parse_nodes, aster_renumber_nodes!,
aster_renumber_elements!, aster_combine_meshes, aster_read_mesh,
filter_by_element_set, filter_by_element_id
filter_by_element_set, filter_by_element_id, MEDFile
end
function get_mesh(mesh_name::ASCIIString, args...; kwargs...)
return get_mesh(Val{Symbol(mesh_name)}, args...; kwargs...)
end
function get_model(model_name::ASCIIString, args...; kwargs...)
return get_model(Val{Symbol(model_name)}, args...; kwargs...)
end
export get_mesh, get_model
module Postprocess
include("postprocess_utils.jl")
export calc_nodal_values!, get_nodal_vector
@@ -120,6 +156,7 @@ if VERSION >= v"0.5-"
else
using BaseTestNext
end
export @test, @testset, @test_throws
#include("test.jl")
end
+185 -107
View File
@@ -1,130 +1,208 @@
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
"""
Currently two strategies exists:
a) Remove inactive inequality constraints in element level. This is done in
assemble! if normal_condition is set to :Contact. For some reason this
leads to convergence issues.
b) Remove inactive inequality constraints in assembly level. This is done in
posthook algorithm if inequality_constraints is set to true. This gives
more robust behavior.
Either use inequality_constraints=True OR :Contact + :Slip, but do not mix.
minimum_distance can be used to roughly skip integration of mortar
projections for elements that are "far enough" from each other. Increases
performance.
"""
type Mortar <: BoundaryProblem
formulation :: Symbol # :total, :incremental, :autodiff
dual_basis :: Bool
inequality_constraints :: Bool # Launch PDASS to solve inequality constraints
normal_condition :: Symbol # Tie or Contact
tangential_condition :: Symbol # Stick or Slip
maximum_distance :: Float64 # don't check for a contact if elements are far enough
store_debug_info :: Bool # for making debugging easier
always_inactive :: Vector{Int64}
always_in_contact :: Vector{Int64} # nodes in this list always in contact
always_in_stick :: Vector{Int64} # nodes in this list always in stick
always_in_slip :: Vector{Int64} # nodes in this list always in slip
contact :: Bool
friction :: Bool
gap_sign :: Int # gap sign convention
type Contact <: BoundaryProblem
dimension :: Int
rotate_normals :: Bool
finite_sliding :: Bool
friction :: Bool
dual_basis :: Bool
use_forwarddiff :: Bool
minimum_active_set_size :: Int
end
function Mortar()
Mortar(:total, true, false, :Tie, :Stick, Inf, false, [], [], [], [], false, false, -1, false)
function Contact()
return Contact(-1, false, false, false, true, false, 0)
end
function get_unknown_field_name(::Type{Mortar})
function get_unknown_field_name(problem::Problem{Contact})
return "reaction force"
end
function get_formulation_type(problem::Problem{Mortar})
return problem.properties.formulation
function get_formulation_type(problem::Problem{Contact})
return :incremental
end
macro debug(msg)
haskey(ENV, "DEBUG") || return
return msg
typealias ContactElements2D Union{Seg2}
function assemble!(problem::Problem{Contact}, time::Real)
if problem.properties.dimension == -1
problem.properties.dimension = dim = size(first(problem.elements), 1)
info("assuming dimension of mesh tie surface is $dim")
info("if this is wrong set is manually using problem.properties.dimension")
end
dimension = Val{problem.properties.dimension}
finite_sliding = Val{problem.properties.finite_sliding}
friction = Val{problem.properties.friction}
dual_basis = Val{problem.properties.dual_basis}
use_forwarddiff = Val{problem.properties.use_forwarddiff}
assemble!(problem, time, dimension, finite_sliding, friction, dual_basis, use_forwarddiff)
end
function assemble!(problem::Problem{Mortar}, time::Real)
elements = get_elements(problem)
if length(elements) == 0
info("$(typeof(problem)) : forget to add elements?")
return
""" Frictionless 2d small sliding contact with dual basis without forwarddiff. """
function assemble!(problem::Problem{Contact}, time::Real,
::Type{Val{1}}, ::Type{Val{false}}, ::Type{Val{false}},
::Type{Val{true}}, ::Type{Val{false}}; debug=false)
props = problem.properties
field_dim = get_unknown_field_dimension(problem)
field_name = get_parent_field_name(problem)
slave_elements = get_slave_elements(problem)
# 1. calculate nodal normals and tangents for slave element nodes j ∈ S
normals, tangents = calculate_normals(slave_elements, time, Val{1}; rotate_normals=props.rotate_normals)
update!(slave_elements, "normal", normals)
update!(slave_elements, "tangent", tangents)
# 2. loop all slave elements
for slave_element in slave_elements
X1 = slave_element["geometry"](time)
u1 = slave_element["displacement"](time)
la1 = slave_element["reaction force"](time)
x1 = X1 + u1
n1 = slave_element["normal"](time)
t1 = slave_element["tangent"](time)
Q1_ = [n1[1] t1[1]]
Q2_ = [n1[2] t1[2]]
Z = zeros(2, 2)
Q2 = [Q1_ Z; Z Q2_]
# 3. loop all master elements
for master_element in slave_element["master elements"](time)
X2 = master_element["geometry"](time)
u2 = master_element["displacement"](time)
x2 = X2 + u2
# 3.1 calculate segmentation
xi1a = project_from_master_to_slave(slave_element, X2[1], time)
xi1b = project_from_master_to_slave(slave_element, X2[end], time)
xi1 = clamp([xi1a; xi1b], -1.0, 1.0)
l = 1/2*abs(xi1[2]-xi1[1])
isapprox(l, 0.0) && continue # no contribution in this master element
# 3.2. bi-orthogonal basis
nsl = length(slave_element)
nm = length(master_element)
De = zeros(nsl, nsl)
Me = zeros(nsl, nsl)
for ip in get_integration_points(slave_element, 3)
detJ = slave_element(ip, time, Val{:detJ})
w = ip.weight*detJ*l
xi = ip.coords[1]
xi_s = dot([1/2*(1-xi); 1/2*(1+xi)], xi1)
N1 = vec(get_basis(slave_element, xi_s, time))
De += w*diagm(N1)
Me += w*N1*N1'
end
Ae = De*inv(Me)
# 3.3. loop integration points of one integration segment and calculate
# local mortar matrices
fill!(De, 0.0)
fill!(Me, 0.0)
ge = zeros(field_dim*nsl)
lae = zeros(field_dim*nsl)
for ip in get_integration_points(slave_element, 3)
detJ = slave_element(ip, time, Val{:detJ})
w = ip.weight*detJ*l
xi = ip.coords[1]
xi_s = dot([1/2*(1-xi); 1/2*(1+xi)], xi1)
N1 = vec(get_basis(slave_element, xi_s, time))
Phi = Ae*N1
# project gauss point from slave element to master element in direction n_s
X_s = N1*X1 # coordinate in gauss point
n_s = N1*n1 # normal direction in gauss point
xi_m = project_from_slave_to_master(master_element, X_s, n_s, time)
N2 = vec(get_basis(master_element, xi_m, time))
X_m = N2*X2
De += w*Phi*N1'
Me += w*Phi*N2'
x_s = X_s + N1*u1
x_m = X_m + N2*u2
la_s = Phi*la1
ge += w*vec((x_m-x_s)*Phi')
lae += w*vec(la_s*Phi')
end
# add contribution to contact virtual work
sdofs = get_gdofs(problem, slave_element)
mdofs = get_gdofs(problem, master_element)
nsldofs = length(sdofs)
nmdofs = length(mdofs)
D2 = zeros(nsldofs, nsldofs)
M2 = zeros(nmdofs, nmdofs)
for i=1:field_dim
D2[i:field_dim:end, i:field_dim:end] += De
M2[i:field_dim:end, i:field_dim:end] += Me
end
add!(problem.assembly.C1, sdofs, sdofs, D2)
add!(problem.assembly.C1, sdofs, mdofs, -M2)
add!(problem.assembly.C2, sdofs, sdofs, Q2'*D2)
add!(problem.assembly.C2, sdofs, mdofs, -Q2'*M2)
add!(problem.assembly.g, sdofs, Q2'*ge)
add!(problem.assembly.c, sdofs, Q2'*lae)
end # master elements done
end # slave elements done, contact virtual work ready
S = sort(collect(keys(normals))) # slave element nodes
C1 = sparse(problem.assembly.C1)
ndofs = size(C1, 1)
debug && info("ndofs = $ndofs")
C2 = sparse(problem.assembly.C2)
D = spzeros(ndofs, ndofs)
g = sparse(problem.assembly.g)
g = full(g)
c = sparse(problem.assembly.c)
c = full(c)
debug && info("Contact slave nodes: $S")
# constitutive modelling in tangent direction, frictionless contact
for j in S
dofs = [2*(j-1)+1, 2*(j-1)+2]
C2[dofs[2],:] = 0.0
g[dofs[2]] = 0.0
D[dofs[2], dofs] = tangents[j]
end
# returns 3 if eldim 2 (tri3, quad4, ...) for 3d problems etc.
eldim = size(elements[1], 1)+1
assemble!(problem, time, Val{eldim})
end
debug && info("Constitutive modelling ready")
include("mortar_2d.jl")
include("mortar_2d_autodiff.jl")
include("mortar_3d.jl")
include("mortar_3d_autodiff.jl")
""" Remove inactive inequality constraints by using primal-dual active set strategy. """
function boundary_assembly_posthook!(solver::Solver, problem::Problem{Mortar}, C1, C2, D, g)
problem.properties.inequality_constraints || return
info("PDASS: Starting primal-dual active set strategy to determine active constraints")
S = Set{Int64}()
for element in get_elements(problem)
haskey(element, "master elements") || continue
push!(S, get_connectivity(element)...)
end
S = sort(collect(S))
dim = get_unknown_field_dimension(problem)
ndofs = solver.ndofs
nnodes = round(Int, ndofs/dim)
c = reshape(full(problem.assembly.c, ndofs, 1), dim, nnodes)
A = find(c[1,:] .> 0)
A = intersect(A, S)
I = setdiff(S, A)
info("PDASS: contact nodes: $(sort(collect(S)))")
info("PDASS: active nodes: $(sort(collect(A)))")
info("PDASS: inactive nodes: $(sort(collect(I)))")
# remove any inactive nodes
for j in I
dofs = [dim*(j-1)+i for i=1:dim]
C1[dofs,:] = 0
C2[dofs,:] = 0
D[dofs,:] = 0
g[dofs,:] = 0
end
# handle tangential condition for active nodes
if problem.properties.tangential_condition == :Slip
for j in A
dofs = [dim*(j-1)+i for i=1:dim]
tangential_dofs = dofs[2:end]
D[tangential_dofs,dofs] = C2[tangential_dofs,dofs]
C2[tangential_dofs,:] = 0
g[tangential_dofs,:] = 0
# active / inactive node detection
A = Set()
I = Set()
la = problem.assembly.la
for j in S
dofs = [2*(j-1)+1, 2*(j-1)+2]
Cn = -g[dofs[1]]
if length(la) != 0
Cn += dot(normals[j], la[dofs])
debug && info("slave $j: $(normals[j]) | $(la[dofs]) | $(c[dofs]) | $(g[dofs]) | $Cn")
else
debug && info("slave $j: $(normals[j]) | | $(c[dofs]) | $(g[dofs]) | $Cn")
end
if Cn < 0
push!(I, j)
debug && info("slave $j INACTIVE")
C1[dofs,:] = 0.0
C2[dofs,:] = 0.0
D[dofs,:] = 0.0
g[dofs,:] = 0.0
else
push!(A, j)
end
end
debug && info("active nodes: $A, inactive nodes: $I")
problem.assembly.C1 = C1
problem.assembly.C2 = C2
problem.assembly.D = D
problem.assembly.g = g
return
end
function assemble_prehook!(problem::Problem{Mortar}, time::Real)
info("mortar assemble prehook at time $time")
slaves = Set{Element}()
for element in get_elements(problem)
haskey(element, "master elements") || continue
push!(slaves, element)
end
info("$(length(slaves)) slave elements")
length(slaves) != 0 || error("no slave elements found for problem (forget to add masters?).")
info("mortar: update normal-tangential system.")
calculate_normal_tangential_coordinates!(collect(slaves), time)
info("mortar assemble prehook done.")
end
+2 -2
View File
@@ -157,11 +157,11 @@ function assemble{El<:Union{Tri3,Tri6,Quad4}}(problem::Problem{Elasticity}, elem
return Km, Kg, f
end
function assemble{El<:Union{Seg2,Seg3}}(problem::Problem{Elasticity}, element::Element{El}, time::Real, ::Type{Val{:plane}})
function assemble{El<:Union{Poi1,Seg2,Seg3}}(problem::Problem{Elasticity}, element::Element{El}, time::Real, ::Type{Val{:plane}})
props = problem.properties
dim = get_unknown_field_dimension(problem)
nnodes = size(element, 2)
nnodes = length(element)
Km = zeros(dim*nnodes, dim*nnodes)
Kg = zeros(dim*nnodes, dim*nnodes)
f = zeros(dim*nnodes)
+1 -1
View File
@@ -67,7 +67,7 @@ function call(element::Element, field_name::ASCIIString, time)
return element[field_name](time)
end
function call(element::Element, field_name::ASCIIString, ip, time)
function call(element::Element, field_name::ASCIIString, ip, time::Real)
field = element(field_name, time)
isa(field, DCTI) && return field.data
basis = element(ip, time)
+6
View File
@@ -58,6 +58,12 @@ end
### "cartesian" elements, integration rules comes from tensor product
### 0d elements
function get_integration_points(element::Poi1)
[ (1.0, [] ) ]
end
### 1d elements
typealias CartesianLineElement Union{Seg2, Seg3, NSeg}
+8 -5
View File
@@ -6,13 +6,14 @@ type Mortar <: BoundaryProblem
rotate_normals :: Bool
adjust :: Bool
tolerance :: Float64
dual_basis :: Bool
end
function Mortar()
return Mortar(-1, false, false, 0.0)
return Mortar(-1, false, false, 0.0, false)
end
function get_unknown_field_name(::Type{Mortar})
function get_unknown_field_name(problem::Problem{Mortar})
return "reaction force"
end
@@ -38,7 +39,7 @@ function cross2(a, b)
cross([a; 0], [b; 0])[3]
end
function get_slave_elements(problem::Problem{Mortar})
function get_slave_elements(problem::Problem)
filter(el -> haskey(el, "master elements"), get_elements(problem))
end
@@ -109,8 +110,10 @@ function calculate_normals!(elements, time, ::Type{Val{1}}; rotate_normals=false
end
function assemble!(problem::Problem{Mortar}, time::Real)
if problem.dimension == -1
error("set interface dimension: problem.properties.dimension = 1 or 2")
if problem.properties.dimension == -1
problem.properties.dimension = dim = size(first(problem.elements), 1)
info("assuming dimension of mesh tie surface is $dim")
info("if this is wrong set is manually using problem.properties.dimension")
end
assemble!(problem, time, Val{problem.properties.dimension})
end
+6 -1
View File
@@ -134,7 +134,12 @@ function xdmf_save_field!(xdmf, elements, time, field_name; field_type="Scalar")
g = element[field_name](time)
conn = get_connectivity(element)
for (i, c) in enumerate(conn)
f[c] = g[i]
gi = g[i]
if (field_type == "Vector") && (length(gi) < 3)
# paraview goes crazy if 2d model with 2d displacement vector
gi = [gi; 0.0]
end
f[c] = gi
end
end
+31
View File
@@ -21,11 +21,30 @@ function add_node!(mesh::Mesh, nid::Int, ncoords::Vector{Float64})
mesh.nodes[nid] = ncoords
end
function add_nodes!(mesh::Mesh, nodes::Dict{Int64, Vector{Float64}})
for (nid, ncoords) in nodes
add_node!(mesh, nid, ncoords)
end
end
function add_node_to_node_set!(mesh::Mesh, set_name::ASCIIString, nids...)
if !haskey(mesh.node_sets, set_name)
mesh.node_sets[set_name] = Set{Int64}()
end
push!(mesh.node_sets[set_name], nids...)
end
function add_element!(mesh::Mesh, elid::Int, eltype::Symbol, connectivity::Vector{Int64})
mesh.elements[elid] = connectivity
mesh.element_types[elid] = eltype
end
function add_elements!(mesh::Mesh, elements::Dict{Int64, Tuple{Symbol, Vector{Int64}}})
for (elid, (eltype, elcon)) in elements
add_element!(mesh, elid, eltype, elcon)
end
end
function add_element_to_element_set!(mesh::Mesh, set_name::ASCIIString, elids...)
if !haskey(mesh.element_sets, set_name)
mesh.element_sets[set_name] = Set{Int64}()
@@ -73,3 +92,15 @@ function create_elements(mesh::Mesh, element_set::ASCIIString)
return create_elements(filter_by_element_set(mesh, element_set))
end
""" find npts nearest nodes form mesh and return id numbers as list. """
function find_nearest_nodes(mesh::Mesh, coords::Vector, npts=1)
dist = Dict{Int64, Float64}()
for (nid, c) in mesh.nodes
dist[nid] = norm(coords-c)
end
s = sort(collect(dist), by=x->x[2])
nd = s[1:npts] # [(id1, dist1), (id2, dist2), ..., (id_npts, dist_npts)]
node_ids = [n[1] for n in nd]
return node_ids
end
+47 -18
View File
@@ -262,23 +262,38 @@ function get_mesh_names(med::MEDFile)
return collect(keys(med.data["FAS"]))
end
function get_nodes(med::MEDFile, mesh_name)
function get_nodes(med::MEDFile, nsets, mesh_name)
increments = keys(med.data["ENS_MAA"][mesh_name])
@assert length(increments) == 1
increment = first(increments)
nodes = med.data["ENS_MAA"][mesh_name][increment]["NOE"]
node_ids = nodes["NUM"]
nset_ids = nodes["FAM"]
nnodes = length(node_ids)
node_coords = nodes["COO"]
dim = round(Int, length(node_coords)/nnodes)
node_coords = reshape(node_coords, nnodes, dim)'
d = Dict{Int64}{Vector{Float64}}()
d = Dict{Int64}{Tuple{Symbol, Vector{Float64}}}()
for i=1:nnodes
d[node_ids[i]] = node_coords[:, i]
nset = Symbol(nsets[nset_ids[i]])
d[node_ids[i]] = (nset, node_coords[:, i])
end
return d
end
function get_node_sets(med::MEDFile, mesh_name)
ns = Dict{Int64, Symbol}(0 => :NALL)
haskey(med.data["FAS"][mesh_name], "NOEUD") || return ns
nsets = med.data["FAS"][mesh_name]["NOEUD"]
for nset in keys(nsets)
k = split(nset, "_")
nset_id = parse(Int, k[2])
nset_name = ascii(pointer(convert(Vector{UInt8}, nsets[nset]["GRO"]["NOM"][1])))
ns[nset_id] = Symbol(nset_name)
end
return ns
end
function get_element_sets(med::MEDFile, mesh_name)
es = Dict{Int64, Symbol}()
if !haskey(med.data["FAS"][mesh_name], "ELEME")
@@ -304,7 +319,8 @@ global const med_elmap = Dict{Symbol, Vector{Int}}(
:QU4 => [1, 2, 3, 4],
:HE8 => [4, 8, 7, 3, 1, 5, 6, 2], # ..?
:TE4 => [3, 2, 1, 4],
:T10 => [3, 2, 1, 4, 6, 5, 7, 10, 9, 8]
:T10 => [3, 2, 1, 4, 6, 5, 7, 10, 9, 8],
:PO1 => [1]
# :T10 => [3, 4, 1, 2, 10, 8, 7, 6, 9, 5]
# :T10 => [5, 9, 6, 7, 8, 10, 2, 1, 4, 3]
)
@@ -354,7 +370,7 @@ Returns
Dict containing fields "nodes" and "connectivity".
"""
function parse_aster_med_file(fn::ASCIIString, mesh_name=nothing)
function parse_aster_med_file(fn::ASCIIString, mesh_name=nothing; debug=false)
med = MEDFile(fn)
if isa(mesh_name, Void)
mesh_names = get_mesh_names(med::MEDFile)
@@ -362,10 +378,15 @@ function parse_aster_med_file(fn::ASCIIString, mesh_name=nothing)
length(mesh_names) == 1 || error("several meshes found from med, pick one: $all_meshes")
mesh_name = mesh_names[1]
end
nsets = get_node_sets(med, mesh_name)
elsets = get_element_sets(med, mesh_name)
elset_names = join(values(elsets), ", ")
info("Found $(length(elsets)) element sets: $elset_names")
nodes = get_nodes(med, mesh_name)
if debug
elset_names = join(values(elsets), ", ")
info("Code Aster .med reader: found $(length(elsets)) element sets: $elset_names")
nset_names = join(values(nsets), ", ")
info("Code ASter .med reader: found $(length(nsets)) node sets: $nset_names")
end
nodes = get_nodes(med, nsets, mesh_name)
conn = get_connectivity(med, elsets, mesh_name)
result = Dict{ASCIIString, Any}()
result["nodes"] = nodes
@@ -373,25 +394,33 @@ function parse_aster_med_file(fn::ASCIIString, mesh_name=nothing)
return result
end
global const mapping = Dict(
:PO1 => :Poi1,
:SE2 => :Seg2,
:SE3 => :Seg3,
:TR3 => :Tri3,
:TR6 => :Tri6,
:QU4 => :Quad4,
:QU8 => :Quad8,
:QU9 => :Quad9,
:HE8 => :Hex8,
:H20 => :Hex20,
:TE4 => :Tet4,
:T10 => :Tet10)
function aster_read_mesh(fn::ASCIIString, mesh_name=nothing)
result = parse_aster_med_file(fn, mesh_name)
mesh = Mesh()
for (nid, ncoords) in result["nodes"]
for (nid, (nset, ncoords)) in result["nodes"]
add_node!(mesh, nid, ncoords)
add_node_to_node_set!(mesh, string(nset), nid)
end
mapping = Dict(
:PO1 => :Poi1,
:SE2 => :Seg2,
:TR3 => :Tri3,
:TR6 => :Tri6,
:QU4 => :Quad4,
:HE8 => :Hex8,
:TE4 => :Tet4,
:T10 => :Tet10)
for (elid, (eltype, elset, elcon)) in result["connectivity"]
haskey(mapping, eltype) || error("Code Aster .med reader: element type $eltype not found from mapping")
add_element!(mesh, elid, mapping[eltype], elcon)
add_element_to_element_set!(mesh, string(elset), elid)
end
return mesh
end
# TODO: refactor and remove obsolete stuff.
+4 -11
View File
@@ -247,21 +247,14 @@ function update_elements!{P<:BoundaryProblem}(problem::Problem{P}, u, la)
end
end
#=
function add_postprocessor!(problem::Union{FieldProblem, BoundaryProblem}, postprocessor_name::Symbol, args...; kwargs...)
push!(problem.postprocessors, (postprocessor_name, args, kwargs))
end
function add_preprocessor!(problem::Union{FieldProblem, BoundaryProblem}, preprocessor_name::Symbol, args...; kwargs...)
push!(problem.preprocessors, (preprocessor_name, args, kwargs))
end
=#
function get_elements(problem)
return problem.elements
end
function update!(problem::Problem, field_name::ASCIIString, field)
update!(problem.elements, field_name, field)
end
""" Return the dimension of the unknown field of this problem. """
function get_unknown_field_dimension(problem::Problem)
return problem.dimension
+4 -2
View File
@@ -335,9 +335,11 @@ function handle_overconstraint_error!(problem, nodes, all_dofs, C1_, C1, C2_, C2
continue
end
show_info && info("unable to resolve overconstrained situation, not continuing")
info("unable to resolve overconstrained situation, not continuing")
show_rows_in_constraint_matrix(dofs, C2, D; show_status=false)
show_rows_in_constraint_matrix(dofs, C2_, D_; show_status=false)
show_related_equations(dofs, C2, C2_, D, D_)
throw("failed to resolve overconstraint situation")
show_info && info()
end
end
+18 -2
View File
@@ -53,10 +53,23 @@ function Solver(name::ASCIIString="default solver",
return solver
end
function get_problems(solver::Solver)
return solver.problems
end
function push!(solver::Solver, problem)
push!(solver.problems, problem)
end
function getindex(solver::Solver, problem_name::ASCIIString)
for problem in get_problems(solver)
if problem.name == problem_name
return problem
end
end
throw(KeyError(problem_name))
end
# one-liner helpers to identify problem types
function is_field_problem(problem)
@@ -231,8 +244,11 @@ function create_projection(C::SparseMatrixCSC, g; S=nothing, tol=1.0e-12)
end
# FIXME: this creates dense matrices
# efficiency / memory usage is a question
P = sparse(C[S,:] \ full(C[S,:]))
h = sparse(C[S,:] \ full(g[S]))
M = get_nonzero_columns(C)
F = qrfact(C[S,:])
P = spzeros(n,m)
P[:,M] = sparse(F \ full(C[S,M]))
h = sparse(F \ full(g[S]))
resize!(P, n, m)
resize!(h, n, 1)
P = speye(n) - P