@@ -87,71 +87,59 @@ function build_assembler!(CD::CombineDofs{UT, CT}, FE::Array{<:FEVectorBlock, 1}
8787 function assemble! (A:: AbstractSparseArray{T} , b:: AbstractVector{T} , assemble_matrix:: Bool , assemble_rhs:: Bool , kwargs... ) where {T}
8888 if assemble_matrix
8989
90- # put FE adjacency information into this matrix row
91- for dof_i in 1 : size (coupling_matrix, 2 )
90+ # transpose the matrix once for efficient row access
91+ transposed_coupling_matrix = sparse (transpose (coupling_matrix))
92+
93+ # go through each coupled dof and update the FE adjacency info
94+ # from the constrained dofs here
95+
96+ for dof_i in 1 : size (transposed_coupling_matrix, 2 )
9297 # this col-view is efficient
93- coupling_i = @views coupling_matrix [:, dof_i]
94- # do nothing if no coupling for dof_i
98+ coupling_i = @views transposed_coupling_matrix [:, dof_i]
99+ # do nothing if dof_k is not coupled to any constrained dof
95100 if nnz (coupling_i) == 0
96101 continue
97102 end
98103
104+ # write the FE adjacency of the constrained dofs into this row
99105 targetrow = dof_i + offsetX
100106
107+ # extract the constrained dofs and the weights
101108 coupled_dofs_i, weights_i = findnz (coupling_i)
102109
103- for dof_j in 1 : size (coupling_matrix, 2 )
110+ # parse through all cols and update the entries
111+ for dof_j in 1 : size (transposed_coupling_matrix, 2 )
104112 # this col-view is efficient
105- coupling_j = @views coupling_matrix [:, dof_j]
113+ coupling_j = @views transposed_coupling_matrix [:, dof_j]
106114
115+ # if both dof_i and dof_j are coupled to a constrained dof, then
116+ # the FE adjacency A_ij is not updated: this is covered by the linear combinations
117+ # expressed in the rows of the constrained dofs_on_boundary
118+ # Hence, check that dof_j is not coupled to anything
107119 if nnz (coupling_j) == 0
108- # not both i and j on the boundary
109-
110120 targetcol = dof_j + offsetY
111- for (dof_k, weight_k ) in zip (coupled_dofs_i, weights_i)
121+ for (dof_k, weight_ik ) in zip (coupled_dofs_i, weights_i)
112122 sourcerow = dof_k + offsetX
113123 sourcecol = targetcol
114124 val = A[sourcerow, sourcecol]
115- _addnz (A, targetrow, targetcol, val, weight_k)
116- end
117-
118-
119- else
120- # i and j on the same boundary
121-
122- coupled_dofs_j, weights_j = findnz (coupling_j)
123- targetcol = dof_j + offsetY
124-
125- for (dof_k, weight_k) in zip (coupled_dofs_i, weights_i)
126- for (dof_l, weight_l) in zip (coupled_dofs_j, weights_j)
127-
128- sourcerow = dof_k + offsetX
129- sourcecol = dof_l + offsetY
130-
131- val = A[sourcerow, sourcecol]
132- # @show sourcerow, sourcecol, targetrow, targetcol, val, weight_k, weight_l
133- _addnz (A, targetrow, targetcol, val, weight_k * weight_l)
134-
135- end
125+ _addnz (A, targetrow, targetcol, val, weight_ik)
136126 end
137127 end
138128 end
139129 end
140130
141- # replace the geometric coupling rows
131+ # replace the geometric coupling rows based
132+ # on the original coupling matrix
142133 for dof_i in 1 : size (coupling_matrix, 2 )
143- # go through the rows of the matrix; use transposed matrix implicitly
144134
145- # this row-view is not efficient :(
146- coupling_i = coupling_matrix[dof_i, :]
135+ coupling_i = coupling_matrix[:, dof_i]
147136 # do nothing if no coupling for dof_i
148137 if nnz (coupling_i) == 0
149138 continue
150139 end
151140
152-
153141 # get the coupled dofs of dof_i and the corresponding weights
154- coupled_dofs, weights = findnz (coupling_i)
142+ coupled_dofs_i, weights_i = findnz (coupling_i)
155143
156144 sourcerow = dof_i + offsetX
157145
@@ -162,9 +150,9 @@ function build_assembler!(CD::CombineDofs{UT, CT}, FE::Array{<:FEVectorBlock, 1}
162150
163151 # replace sourcerow with coupling linear combination
164152 _addnz (A, sourcerow, sourcerow, - 1.0 , 1 )
165- for (dof_j, weight ) in zip (coupled_dofs, weights )
153+ for (dof_j, weight_ij ) in zip (coupled_dofs_i, weights_i )
166154 # weights for ∑ⱼ wⱼdofⱼ - dofᵢ = 0
167- _addnz (A, sourcerow, dof_j + offsetY, weight , 1 )
155+ _addnz (A, sourcerow, dof_j + offsetY, weight_ij , 1 )
168156 end
169157
170158 end
@@ -173,9 +161,9 @@ function build_assembler!(CD::CombineDofs{UT, CT}, FE::Array{<:FEVectorBlock, 1}
173161
174162 if assemble_rhs
175163
176- for dof_i in 1 : size (coupling_matrix , 2 )
164+ for dof_i in 1 : size (transposed_coupling_matrix , 2 )
177165 # this col-view is efficient
178- coupling_i = @views coupling_matrix [:, dof_i]
166+ coupling_i = @views transposed_coupling_matrix [:, dof_i]
179167 # do nothing if no coupling for dof_i
180168 if nnz (coupling_i) == 0
181169 continue
@@ -193,16 +181,15 @@ function build_assembler!(CD::CombineDofs{UT, CT}, FE::Array{<:FEVectorBlock, 1}
193181 end
194182
195183
184+ # now set the rows of the constrained dofs to zero to enforce the linear combination
196185 for dof_i in 1 : size (coupling_matrix, 2 )
197186 coupling_i = coupling_matrix[dof_i, :]
198187 # do nothing if no coupling for dof_i
199188 if nnz (coupling_i) == 0
200189 continue
201190 end
202191
203- sourcerow = dof_i + offsetX
204-
205- b[sourcerow] = 0.0
192+ b[dof_i + offsetX] = 0.0
206193
207194 end
208195 end
0 commit comments