2
2
# ## COMBINE DOFS (e.g. for periodicity) ###
3
3
# ##########################################
4
4
5
- mutable struct CombineDofs{UT, CT} <: AbstractOperator
5
+ mutable struct CombineDofs{UT, CT, AT } <: AbstractOperator
6
6
uX:: UT # component nr for dofsX
7
7
uY:: UT # component nr for dofsY
8
8
coupling_info:: CT
9
+ fixed_dofs:: AT
9
10
FESX:: Any
10
11
FESY:: Any
11
12
assembler:: Any
12
13
parameters:: Dict{Symbol, Any}
13
14
end
14
15
16
+ fixed_dofs (O:: CombineDofs ) = O. fixed_dofs
17
+
15
18
default_combop_kwargs () = Dict {Symbol, Tuple{Any, String}} (
16
19
:name => (" CombineDofs" , " name for operator used in printouts" ),
17
20
:penalty => (1.0e30 , " penalty for fixed degrees of freedom" ),
@@ -61,7 +64,14 @@ $(_myprint(default_combop_kwargs()))
61
64
function CombineDofs (uX, uY, coupling_matrix:: AbstractMatrix ; kwargs... )
62
65
parameters = Dict {Symbol, Any} (k => v[1 ] for (k, v) in default_combop_kwargs ())
63
66
_update_params! (parameters, kwargs)
64
- return CombineDofs (uX, uY, coupling_matrix, nothing , nothing , nothing , parameters)
67
+ fixed_dofs = zeros (Int, 0 )
68
+ for dof_i in 1 : size (coupling_matrix, 2 )
69
+ coupling_i = @views coupling_matrix[:, dof_i]
70
+ if nnz (coupling_i) > 0
71
+ push! (fixed_dofs, dof_i)
72
+ end
73
+ end
74
+ return CombineDofs (uX, uY, coupling_matrix, fixed_dofs, nothing , nothing , nothing , parameters)
65
75
end
66
76
67
77
function apply_penalties! (A, b, sol, CD:: CombineDofs{UT, CT} , SC:: SolverConfiguration ; assemble_matrix = true , assemble_rhs = true , kwargs... ) where {UT, CT}
@@ -80,118 +90,81 @@ function build_assembler!(CD::CombineDofs{UT, CT}, FE::Array{<:FEVectorBlock, 1}
80
90
FESX, FESY = FE[1 ]. FES, FE[2 ]. FES
81
91
if (CD. FESX != FESX) || (CD. FESY != FESY)
82
92
coupling_matrix = CD. coupling_info
93
+ fixed_dofs = CD. fixed_dofs
83
94
offsetX = FE[1 ]. offset
84
95
offsetY = FE[2 ]. offset
85
96
if CD. parameters[:verbosity ] > 0
86
97
@info " .... coupling $(length (coupling_matrix. nzval)) dofs"
87
98
end
88
- function assemble! (A:: AbstractSparseArray{T} , b:: AbstractVector{T} , assemble_matrix:: Bool , assemble_rhs:: Bool , kwargs... ) where {T}
89
-
90
- # transpose the matrix once for efficient row access
91
- transposed_coupling_matrix = sparse (transpose (coupling_matrix))
99
+ penalty = CD. parameters[:penalty ]
92
100
101
+ function assemble! (A:: AbstractSparseArray{T} , b:: AbstractVector{T} , assemble_matrix:: Bool , assemble_rhs:: Bool , kwargs... ) where {T}
93
102
if assemble_matrix
94
- # go through each coupled dof and update the FE adjacency info
95
- # from the constrained dofs here
96
-
97
- for dof_i in 1 : size (coupling_matrix, 2 )
103
+ # go through each constrained dof and update the FE adjacency info
104
+ # of the coupled dofs
105
+ for dof_i in fixed_dofs
98
106
# this col-view is efficient
99
107
coupling_i = @views coupling_matrix[:, dof_i]
100
- # do nothing if dof_k is not coupled to any constrained dof
101
- if nnz (coupling_i) == 0
102
- continue
103
- end
104
108
105
109
# write the FE adjacency of the constrained dofs into this row
106
- targetrow = dof_i + offsetX
110
+ sourcerow = dof_i + offsetX
107
111
108
112
# extract the constrained dofs and the weights
109
113
coupled_dofs_i, weights_i = findnz (coupling_i)
110
114
111
- # parse through all cols and update the entries
112
- for dof_j in 1 : size (coupling_matrix, 2 )
113
- # this col-view is efficient
114
- coupling_j = @views coupling_matrix[:, dof_j]
115
-
116
- # if both dof_i and dof_j are coupled to a constrained dof, then
117
- # the FE adjacency A_ij is not updated: this is covered by the linear combinations
118
- # expressed in the rows of the constrained dofs_on_boundary
119
- # Hence, check that dof_j is not coupled to anything
120
- if nnz (coupling_j) == 0
121
- targetcol = dof_j + offsetY
122
- for (dof_k, weight_ik) in zip (coupled_dofs_i, weights_i)
123
- sourcerow = dof_k + offsetX
124
- sourcecol = targetcol
125
- val = A[sourcerow, sourcecol]
126
- _addnz (A, targetrow, targetcol, val, weight_ik)
115
+ # parse through sourcerow and add the contents to the coupled dofs
116
+ for col in 1 : size (A, 2 )
117
+ r = findindex (A. cscmatrix, sourcerow, col)
118
+ if r > 0
119
+ val = A. cscmatrix. nzval[r]
120
+ if abs (val) > 1.0e-15
121
+ for (dof_k, weight_ik) in zip (coupled_dofs_i, weights_i)
122
+ targetrow = dof_k + offsetX
123
+ _addnz (A, targetrow, col, val, weight_ik)
124
+ end
127
125
end
128
126
end
129
127
end
130
128
end
131
129
132
130
# replace the geometric coupling rows based
133
131
# on the original coupling matrix
134
- for dof_i in 1 : size (transposed_coupling_matrix, 2 )
135
-
136
- coupling_i = transposed_coupling_matrix[:, dof_i]
137
- # do nothing if no coupling for dof_i
138
- if nnz (coupling_i) == 0
139
- continue
140
- end
132
+ for dof_i in fixed_dofs
133
+ coupling_i = coupling_matrix[:, dof_i]
141
134
142
135
# get the coupled dofs of dof_i and the corresponding weights
143
136
coupled_dofs_i, weights_i = findnz (coupling_i)
144
-
145
137
sourcerow = dof_i + offsetX
146
138
147
- # eliminate the sourcerow
148
- for col in 1 : size (A, 2 )
149
- A[sourcerow, col] = 0
150
- end
151
-
152
139
# replace sourcerow with coupling linear combination
153
- _addnz (A, sourcerow, sourcerow, - 1.0 , 1 )
140
+ _addnz (A, sourcerow, sourcerow, - 1.0 , penalty )
154
141
for (dof_j, weight_ij) in zip (coupled_dofs_i, weights_i)
155
142
# weights for ∑ⱼ wⱼdofⱼ - dofᵢ = 0
156
- _addnz (A, sourcerow, dof_j + offsetY, weight_ij, 1 )
143
+ _addnz (A, sourcerow, dof_j + offsetY, weight_ij, penalty )
157
144
end
158
-
159
145
end
160
146
flush! (A)
161
147
end
162
148
163
149
if assemble_rhs
164
-
165
- for dof_i in 1 : size (coupling_matrix, 2 )
150
+ for dof_i in fixed_dofs
166
151
# this col-view is efficient
167
152
coupling_i = @views coupling_matrix[:, dof_i]
168
- # do nothing if no coupling for dof_i
169
- if nnz (coupling_i) == 0
170
- continue
171
- end
172
153
173
154
# get the coupled dofs of dof_i and the corresponding weights
174
155
coupled_dofs, weights = findnz (coupling_i)
175
156
176
157
# transfer all assembly information to dof_i
177
- targetrow = dof_i + offsetY
158
+ sourcerow = dof_i + offsetY
178
159
for (dof_j, weight) in zip (coupled_dofs, weights)
179
- sourcerow = dof_j + offsetY
160
+ targetrow = dof_j + offsetY
180
161
b[targetrow] += weight * b[sourcerow]
181
162
end
182
163
end
183
164
184
-
185
165
# now set the rows of the constrained dofs to zero to enforce the linear combination
186
- for dof_i in 1 : size (transposed_coupling_matrix, 2 )
187
- coupling_i = transposed_coupling_matrix[:, dof_i]
188
- # do nothing if no coupling for dof_i
189
- if nnz (coupling_i) == 0
190
- continue
191
- end
192
-
166
+ for dof_i in fixed_dofs
193
167
b[dof_i + offsetX] = 0.0
194
-
195
168
end
196
169
end
197
170
0 commit comments