Skip to content

Commit 9301319

Browse files
authored
added skalna06 algorithm for PILS (#115)
* added skalna06 algorithm for PILS * allow rhs to be non-parametric * allow apa to be called with single vectors * renamed @linvars --> @affinevars * update docstring * simplified apa structures and updated FEM docs * updated docstring and FEM example * retrigger workflow
1 parent 474a4b5 commit 9301319

File tree

10 files changed

+228
-99
lines changed

10 files changed

+228
-99
lines changed

docs/literate/applications/FEM_example.jl

Lines changed: 83 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@
5656
function unitaryStiffnessMatrix( coordFirstNode, coordSecondNode )
5757
diff = (coordSecondNode - coordFirstNode)
5858
length = sqrt( diff' * diff )
59-
c = diff[1] / length ;
60-
s = diff[2] / length ;
61-
Qloc2glo = [ c -s 0 0 ; s c 0 0 ; 0 0 c -s ; 0 0 s c ] ;
62-
Kloc = [ 1 0 -1 0 ; 0 0 0 0 ; -1 0 1 0 ; 0 0 0 0 ] ;
59+
c = diff[1] / length
60+
s = diff[2] / length
61+
Qloc2glo = [ c -s 0 0 ; s c 0 0 ; 0 0 c -s ; 0 0 s c ]
62+
Kloc = [ 1 0 -1 0 ; 0 0 0 0 ; -1 0 1 0 ; 0 0 0 0 ]
6363
Kglo = Qloc2glo * Kloc * transpose(Qloc2glo)
6464
return Kglo, length
6565
end
@@ -79,11 +79,11 @@ E = 2e11 ; # Young modulus
7979
A = 5e-3 ; # Cross-section area
8080
# The coordinate matrix is given by
8181
## coordinates x y
82-
nodesCMatrix = [ 0. 0. ;
83-
1. 1. ;
84-
2. 0. ;
85-
3. 1. ;
86-
4. 0. ];
82+
nodesCMatrix = [ 0.0 0.0 ;
83+
1.0 1.0 ;
84+
2.0 0.0 ;
85+
3.0 1.0 ;
86+
4.0 0.0 ];
8787
# the connectivity matrix is given by
8888
## connectivity start end
8989
connecMatrix = [ 1 2 ;
@@ -92,54 +92,54 @@ connecMatrix = [ 1 2 ;
9292
2 4 ;
9393
3 4 ;
9494
3 5 ;
95-
4 5 ];
95+
4 5 ]
9696
# and the fixed degrees of freedom (supports) are defined by the vector
97-
fixedDofs = [2 9 10 ];
97+
fixedDofs = [2 9 10 ]
9898
#
9999
# calculations
100-
numNodes = size( nodesCMatrix )[1]; # compute the number of nodes
101-
numElems = size( connecMatrix )[1]; # compute the number of elements
102-
freeDofs = zeros(Int8, 2*numNodes-length(fixedDofs));
103-
indDof = 1 ; counter = 0 ;
100+
numNodes = size( nodesCMatrix )[1] # compute the number of nodes
101+
numElems = size( connecMatrix )[1] # compute the number of elements
102+
freeDofs = zeros(Int8, 2*numNodes-length(fixedDofs))
103+
indDof = 1 ; counter = 0
104104
while indDof <= (2*numNodes)
105105
if !(indDof in fixedDofs)
106-
global counter = counter + 1 ;
107-
freeDofs[ counter ] = indDof ;
106+
global counter = counter + 1
107+
freeDofs[ counter ] = indDof
108108
end
109-
global indDof = indDof + 1 ;
109+
global indDof = indDof + 1
110110
end
111111
#
112112
# assembly
113-
KG = zeros( 2*numNodes, 2*numNodes );
114-
FG = zeros( 2*numNodes );
113+
KG = zeros( 2*numNodes, 2*numNodes )
114+
FG = zeros( 2*numNodes )
115115
for elem in 1:numElems
116116
print(" assembling stiffness matrix of element ", elem , "\n")
117117
indexFirstNode = connecMatrix[ elem, 1 ]
118118
indexSecondNode = connecMatrix[ elem, 2 ]
119119
dofsElem = [2*indexFirstNode-1 2*indexFirstNode 2*indexSecondNode-1 2*indexSecondNode ]
120120
KGelem, lengthElem = unitaryStiffnessMatrix( nodesCMatrix[ indexSecondNode, : ], nodesCMatrix[ indexFirstNode, : ] )
121-
stiffnessParam = E * A / lengthElem ;
121+
stiffnessParam = E * A / lengthElem
122122
for i in 1:4
123123
for j in 1:4
124124
KG[ dofsElem[i], dofsElem[j] ] = KG[ dofsElem[i], dofsElem[j] ] + stiffnessParam * KGelem[i,j]
125125
end
126126
end
127127
end
128128
FG[4] = -1e4 ;
129-
KG = KG[ freeDofs, : ] ;
130-
KG = KG[ :, freeDofs ] ;
131-
FG = FG[ freeDofs ];
129+
KG = KG[ freeDofs, : ]
130+
KG = KG[ :, freeDofs ]
131+
FG = FG[ freeDofs ]
132132

133133
u = KG \ FG
134-
UG = zeros( 2*numNodes );
135-
UG[ freeDofs ] = u ;
134+
UG = zeros( 2*numNodes )
135+
UG[ freeDofs ] = u
136136
#
137137
# #### Deformed structure
138138
#
139139
# The reference (dashed blue line) and deformed (solid red) configurations of the structure are ploted. Since the displacements are very small, a `scaleFactor` is considered to amplify the deformation and ease the visualization.
140140
#
141141
using Plots
142-
scaleFactor = 2e3 ;
142+
scaleFactor = 2e3
143143
plot();
144144
for elem in 1:numElems
145145
indexFirstNode = connecMatrix[ elem, 1 ];
@@ -165,5 +165,59 @@ savefig("deformed.png") # hide
165165
# ![](deformed.png)
166166
#
167167
# ### Problem with interval parameters
168-
#
169-
# TO DO
168+
169+
md"""
170+
Suppose now we have a 10% uncertainty for the stiffness $s_{23}$ associated with the third
171+
element. To model the problem, we introduce the symbolic variable `s23` using the IntervalLinearAlgebra macro `@linvars`.
172+
"""
173+
174+
using IntervalLinearAlgebra
175+
@affinevars s23
176+
177+
# now we can construct the matrix as before
178+
179+
KGp = zeros(AffineExpression{Float64}, 2*numNodes, 2*numNodes );
180+
for elem in 1:numElems
181+
print(" assembling stiffness matrix of element ", elem , "\n")
182+
indexFirstNode = connecMatrix[ elem, 1 ]
183+
indexSecondNode = connecMatrix[ elem, 2 ]
184+
dofsElem = [2*indexFirstNode-1 2*indexFirstNode 2*indexSecondNode-1 2*indexSecondNode ]
185+
KGelem, lengthElem = unitaryStiffnessMatrix( nodesCMatrix[ indexSecondNode, : ], nodesCMatrix[ indexFirstNode, : ] )
186+
if elem == 3
187+
stiffnessParam = s23
188+
else
189+
stiffnessParam = E * A / lengthElem
190+
end
191+
for i in 1:4
192+
for j in 1:4
193+
KGp[ dofsElem[i], dofsElem[j] ] = KGp[ dofsElem[i], dofsElem[j] ] + stiffnessParam * KGelem[i,j]
194+
end
195+
end
196+
end
197+
KGp = KGp[ freeDofs, : ]
198+
KGp = KGp[ :, freeDofs ]
199+
200+
# Now we can construct the [`AffineParametricArray`](@ref)
201+
202+
KGp = AffineParametricArray(KGp)
203+
204+
# The range of the stiffness is
205+
206+
srange = E * A / sqrt(2) ± 0.1 * E * A / sqrt(2)
207+
208+
# To solve the system, we could of course just subsitute `srange` into the parametric matrix
209+
# `KGp` and solve the "normal" interval linear system
210+
211+
usimple = solve(KGp(srange), Interval.(FG))
212+
213+
# This approach, however soffers from the [dependency problem](https://en.wikipedia.org/wiki/Interval_arithmetic#Dependency_problem)
214+
# and hence the compute displacement will be an overestimation of the true displacement.
215+
216+
# To mitigate this issue, algorithms to solve linear systems with parameters have been developed.
217+
# In this case we use the algorithm presented in [[SKA06]](@ref)
218+
219+
uparam = solve(KGp, FG, srange)
220+
221+
# We can now compare the naive and parametric solution
222+
223+
hcat(usimple, uparam)/1e-6

docs/src/api/algorithms.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,12 @@ LinearKrawczyk
2727
```@docs
2828
LinearOettliPrager
2929
NonLinearOettliPrager
30+
```
31+
32+
## Parametric Solvers
33+
34+
### Direct Methods
35+
36+
```@docs
37+
Skalna06
3038
```

src/IntervalLinearAlgebra.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export
2222
is_H_matrix, is_strongly_regular, is_strictly_diagonally_dominant, is_Z_matrix, is_M_matrix,
2323
rref,
2424
eigenbox, Rohn, Hertz, verify_eigen, bound_perron_frobenius_eigenvalue,
25-
AffineExpression, @linvars,
25+
AffineExpression, @affinevars,
2626
AffineParametricArray, AffineParametricMatrix, AffineParametricVector
2727

2828

@@ -35,8 +35,9 @@ include("multiplication.jl")
3535
include("utils.jl")
3636
include("classify.jl")
3737
include("rref.jl")
38-
include("pils/linexpr.jl")
38+
include("pils/affine_expressions.jl")
3939
include("pils/affine_parametric_array.jl")
40+
include("pils/pils_solvers.jl")
4041

4142
include("eigenvalues/interval_eigenvalues.jl")
4243
include("eigenvalues/verify_eigs.jl")
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Data structure to represent affine expressions, such as ``x+2y+z+4``.
88
### Examples
99
1010
```jldoctest
11-
julia> @linvars x y z
11+
julia> @affinevars x y z
1212
3-element Vector{AffineExpression{Int64}}:
1313
x
1414
y
@@ -45,32 +45,32 @@ function _get_vars(x...)
4545
end
4646

4747
"""
48-
@linvars(x...)
48+
@affinevars(x...)
4949
5050
Macro to construct the variables used to represent [`AffineExpression`](@ref).
5151
5252
### Examples
5353
5454
```jldoctest
55-
julia> @linvars x
55+
julia> @affinevars x
5656
1-element Vector{AffineExpression{Int64}}:
5757
x
5858
59-
julia> @linvars x y z
59+
julia> @affinevars x y z
6060
3-element Vector{AffineExpression{Int64}}:
6161
x
6262
y
6363
z
6464
65-
julia> @linvars x[1:4]
65+
julia> @affinevars x[1:4]
6666
4-element Vector{AffineExpression{Int64}}:
6767
x1
6868
x2
6969
x3
7070
x4
7171
```
7272
"""
73-
macro linvars(x...)
73+
macro affinevars(x...)
7474
vars = _get_vars(x...)
7575
_vars_dict[:vars] = vars
7676

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,99 @@
1-
struct AffineParametricArray{T, N, MT<:AbstractArray{T, N}, VAR} <: AbstractArray{T, N}
1+
struct AffineParametricArray{T, N, MT<:AbstractArray{T, N}} <: AbstractArray{T, N}
22
coeffs::Vector{MT}
3-
vars::Vector{VAR}
43
end
54

65
const AffineParametricMatrix{T, MT} = AffineParametricArray{T, 2, MT} where {T, MT<:AbstractMatrix{T}}
76

87
const AffineParametricVector{T, VT} = AffineParametricArray{T, 1, VT} where {T, VT <: AbstractVector{T}}
98

109
# apas are callable
11-
function (apa::AffineParametricArray)(p::AbstractVector)
10+
function (apa::AffineParametricArray)(p)
1211
length(p) + 1 == length(apa.coeffs) || throw(ArgumentError("dimension mismatch"))
1312
return sum(apa.coeffs[i] * p[i] for i in eachindex(p)) + apa.coeffs[end]
1413
end
1514

16-
function (apa::AffineParametricArray)()
17-
return apa.coeffs[end] + sum(apa.coeffs[i] * apa.vars[i] for i in eachindex(apa.vars))
18-
end
19-
2015
# Array interface
2116
IndexStyle(::Type{<:AffineParametricArray}) = IndexLinear()
2217
size(apa::AffineParametricArray) = size(apa.coeffs[1])
2318

24-
getindex(apa::AffineParametricArray, idx...) = getindex(apa(), idx...)
19+
function getindex(apa::AffineParametricArray, idx...)
20+
nvars = length(_vars_dict[:vars])
21+
vars = [AffineExpression(Vector(c)) for c in eachcol(Matrix(I, nvars + 1, nvars))]
22+
tmp = sum(getindex(c, idx...) * v for (v, c) in zip(vars, apa.coeffs[1:end-1]))
23+
return getindex(apa.coeffs[end], idx...) + tmp
24+
end
25+
26+
function setindex!(apa::AffineParametricArray, ae::AffineExpression, idx...)
27+
@inbounds for i in eachindex(ae.coeffs)
28+
setindex!(apa.coeffs[i], ae.coeffs[i], idx...)
29+
end
30+
end
2531

26-
# operations
27-
function ==(apa1::AffineParametricArray, apa2::AffineParametricArray)
28-
return apa1.vars == apa2.vars && apa1.coeffs == apa2.coeffs
32+
function setindex!(apa::AffineParametricArray, num::Number, idx...)
33+
setindex!(apa.coeffs[end], num, idx...)
2934
end
3035

36+
==(apa1::AffineParametricArray, apa2::AffineParametricArray) = apa1.coeffs == apa2.coeffs
37+
3138
# unary operations
3239
+(apa::AffineParametricArray) = apa
33-
-(apa::AffineParametricArray) = AffineParametricArray(-apa.coeffs, apa.vars)
40+
-(apa::AffineParametricArray) = AffineParametricArray(-apa.coeffs)
3441

3542
# addition subtraction
3643
for op in (:+, :-)
3744
@eval function $op(apa1::AffineParametricArray, apa2::AffineParametricArray)
38-
return AffineParametricArray($op(apa1.coeffs, apa2.coeffs), apa1.vars)
45+
return AffineParametricArray($op(apa1.coeffs, apa2.coeffs))
3946
end
4047

41-
@eval function $op(apa::AffineParametricArray{T, N, MT, VAR}, B::MS) where {T, N, MT, VAR, S, MS<:AbstractArray{S, N}}
48+
@eval function $op(apa::AffineParametricArray{T, N, MT}, B::MS) where {T, N, MT, S, MS<:AbstractArray{S, N}}
4249
MTS = promote_type(MT, MS)
4350
coeffs = similar(apa.coeffs, MTS)
4451
coeffs .= apa.coeffs
4552
coeffs[end] = $op(coeffs[end], B)
46-
return AffineParametricArray(coeffs, apa.vars)
53+
return AffineParametricArray(coeffs)
4754
end
4855

49-
@eval function $op(B::MS, apa::AffineParametricArray{T, N, MT, VAR}) where {T, N, MT, VAR, S, MS<:AbstractArray{S, N}}
56+
@eval function $op(B::MS, apa::AffineParametricArray{T, N, MT}) where {T, N, MT, S, MS<:AbstractArray{S, N}}
5057
MTS = promote_type(MT, MS)
5158
coeffs = similar(apa.coeffs, MTS)
5259
coeffs .= $op(apa.coeffs)
5360
coeffs[end] = $op(B, apa.coeffs[end])
54-
return AffineParametricArray(coeffs, apa.vars)
61+
return AffineParametricArray(coeffs)
5562
end
5663
end
5764

5865
# multiplication, backslash
5966
for op in (:*, :\)
6067
@eval function $op(A::AbstractMatrix, apa::AffineParametricArray)
6168
coeffs = [$op(A, coeff) for coeff in apa.coeffs]
62-
return AffineParametricArray(coeffs, apa.vars)
69+
return AffineParametricArray(coeffs)
6370
end
6471
end
6572

6673
function *(apa::AffineParametricMatrix, B::AbstractMatrix)
6774
coeffs = [coeff*B for coeff in apa.coeffs]
68-
return AffineParametricArray(coeffs, apa.vars)
75+
return AffineParametricArray(coeffs)
6976
end
7077

7178
function *(apa::AffineParametricMatrix, v::AbstractVector)
7279
coeffs = [coeff*v for coeff in apa.coeffs]
73-
return AffineParametricArray(coeffs, apa.vars)
80+
return AffineParametricArray(coeffs)
7481
end
7582

76-
*(apa::AffineParametricArray, n::Number) = AffineParametricArray(apa.coeffs * n, apa.vars)
77-
*(n::Number, apa::AffineParametricArray) = AffineParametricArray(apa.coeffs * n, apa.vars)
78-
/(apa::AffineParametricArray, n::Number) = AffineParametricArray(apa.coeffs / n, apa.vars)
83+
*(apa::AffineParametricArray, n::Number) = AffineParametricArray(apa.coeffs * n)
84+
*(n::Number, apa::AffineParametricArray) = AffineParametricArray(apa.coeffs * n)
85+
/(apa::AffineParametricArray, n::Number) = AffineParametricArray(apa.coeffs / n)
7986

8087
# with AffineExpression
81-
function AffineParametricArray(A::AbstractArray{<:AffineExpression}, vars::Vector{<:AffineExpression})
82-
ncoeffs = length(vars) + 1
88+
function AffineParametricArray(A::AbstractArray{<:AffineExpression})
89+
ncoeffs = length(first(A).coeffs)
8390
coeffs = [map(a -> a.coeffs[i], A) for i in 1:ncoeffs]
84-
return AffineParametricArray(coeffs, vars)
91+
return AffineParametricArray(coeffs)
8592
end
8693

87-
function AffineParametricArray(A::AbstractArray{<:Number}, vars::Vector{<:AffineExpression})
88-
ncoeffs = length(vars) + 1
94+
function AffineParametricArray(A::AbstractArray{<:Number})
95+
ncoeffs = length(_vars_dict[:vars]) + 1
8996
coeffs = [zero(A) for _ in 1:ncoeffs]
9097
coeffs[end] = A
91-
# vars = [AffineExpression(Vector(c)) for c in eachcol(Matrix{T}(I, ncoeffs, ncoeffs-1))]
92-
return AffineParametricArray(coeffs, vars)
93-
end
94-
95-
function setindex!(apa::AffineParametricArray, ae::AffineExpression, idx...)
96-
@inbounds for i in eachindex(ae.coeffs)
97-
setindex!(apa.coeffs[i], ae.coeffs[i], idx...)
98-
end
98+
return AffineParametricArray(coeffs)
9999
end

0 commit comments

Comments
 (0)