-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpercolation.f90
432 lines (333 loc) · 16.1 KB
/
percolation.f90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
module percolation
!! A module with procedures for calculating various properties which
!! are interesting when doing numerical percolation experiments.
use utilities
use hk
implicit none
private :: has_spanning_cluster_one_sample, spanning_density_one_sample, &
label_recursively, growcluster
real(kind=dp), parameter :: pc = 0.592746
!! Known critical probability for a two-dimensional site-percolating
!! system.
contains
function create_binary_matrix(p, L) result(binary_matrix)
!! Create a random, binary (`logical`) matrix,
!! which can be used in percolation experiments.
logical, dimension(:,:), allocatable :: binary_matrix
!! Randomly created matrix, where each element is `.true.`
!! or `.false.` if a randomly generated number is smaller
!! or greater than p.
real(kind=dp), intent(in) :: p
!! Probability for each matrix element to be `.true.`.
integer, intent(in) :: L
real(kind=dp), dimension(:,:), allocatable :: p_matrix
allocate(p_matrix(L,L))
call random_seed()
call random_number(p_matrix)
binary_matrix = p_matrix < p
end function
subroutine label(matrix, labelled_matrix, num_labels)
!! Alternative interface to the Hoshen-Kopelman algorithm
!! from the hk module, which uses a binary matrix created
!! by [[create_binary_matrix]].
logical, dimension(:,:), intent(in) :: matrix
!! Binary matrix where clusters will be identified.
integer, dimension(:,:), allocatable, intent(out) :: labelled_matrix
!! Integer matrix which will store the labels of each cluster.
!! Reallocated if necessary.
integer, intent(out) :: num_labels
!! Overwritten with the total number of disjoint clusters.
integer :: L
L = size(matrix,1)
if(.not. allocated(labelled_matrix)) then
allocate(labelled_matrix(L,L))
else if(size(labelled_matrix,1) /= L) then
deallocate(labelled_matrix)
allocate(labelled_matrix(L,L))
endif
where(matrix)
labelled_matrix = 1
else where
labelled_matrix = 0
end where
num_labels = hoshen_kopelman(labelled_matrix)
end subroutine
function find_sizes(labelled_matrix, num_labels) result(sizes)
!! Count the number of sites belonging to each cluster
!! in the labelled matrix.
integer, dimension(:), allocatable :: sizes
!! Array of cluster sizes. The i'th element is the size of
!! the cluster with label i.
integer, dimension(:,:), intent(in) :: labelled_matrix
!! Integer matrix with labelled clusters, resulting from
!! the Hoshen-Kopelman algorithm.
integer, intent(in) :: num_labels
!! The known number of clusters.
integer :: L,i,j
L = size(labelled_matrix,1)
allocate(sizes(num_labels))
sizes(:) = 0
do j=1,L
do i=1,L
associate (label => labelled_matrix(i,j))
if(label /= 0) then
sizes(label) = sizes(label) + 1
end if
end associate
end do
end do
end function
subroutine cluster_number_density(p, L, num_samples, bin_mids, results, binsize_base)
!! Calculate the number density of clusters with different sizes.
!! The cluster number density is defined as
!! \\begin{equation}
!! n(s,p) = \sum_{\text{MC-samples}}
!! \frac{\text{number of clusters with size }s}
!! {L^2\cdot\text{number of MC-samples}}.
!! \\end{equation}
!! Direct calculations will usually give bad results, as there
!! will be very few
!! clusters with large sizes compared to the numbers of clusters
!! with small sizes. This is circumvented by doing logarithmic
!! binning and averaging, i.e.
!! \\begin{equation}
!! n\left([s+\Delta s),p\right) = \sum_{\text{MC-samples}}
!! \frac{\text{number of clusters with size }s
!! \in[s,s+\Delta s)}
!! {\Delta s\cdot L^2\cdot\text{number of MC-samples}},
!! \label{eq:nsp}
!! \\end{equation}
!! where \\(\Delta s\\) are logarithmically distributed. After execution,
!! **bin_mids** will contain the centres of the bins.
integer, intent(in) :: L
!! Percolating systems will be \\(L\times L\\).
integer, intent(in) :: num_samples
!! Results will be averaged over this number of Monte Carlo-samples.
!! Sampling is parallelised.
real(kind=dp), intent(in) :: p
!! Probability for a given site to allow transport.
real(kind=dp), intent(in), optional :: binsize_base
!! The edges of the logarithmically distributed bins will be
!! integer powers of this number. Default: 1.5.
real(kind=dp), dimension(:), intent(out), allocatable :: bin_mids
!! Centres of bins.
real(kind=dp), dimension(:), intent(out), allocatable :: results
!! Cluster number density in \eqref{eq:nsp}.
integer :: num_bins, i, j, num_labels, sizeindex, spanning_label
real(kind=dp) :: a, loga
logical, dimension(:,:), allocatable :: binary_matrix
integer, dimension(:,:), allocatable :: label_matrix
integer, dimension(:), allocatable :: clustersizes, histogram
real(kind=dp), dimension(:), allocatable :: bin_edges, bin_sizes
if(present(binsize_base)) then
a = binsize_base
else
a = 1.5d0
end if
!/cndstart/!
loga = log(a)
num_bins = ceiling(log(1.0d0*L**2)/loga)
bin_edges = a**[(i, i=0 ,num_bins)]
bin_mids = 0.5*(bin_edges(1:num_bins) + bin_edges(2:num_bins+1))
bin_sizes = bin_edges(2:num_bins+1) - bin_edges(1:num_bins)
allocate(histogram(1:num_bins))
histogram = 0
!$omp parallel do &
!$omp& private(binary_matrix, label_matrix, num_labels, &
!$omp& clustersizes, spanning_label, sizeindex) &
!$omp& reduction(+:histogram)
do i = 1, num_samples
binary_matrix = create_binary_matrix(p, L)
call label(binary_matrix, label_matrix, num_labels)
clustersizes = find_sizes(label_matrix, num_labels)
spanning_label = find_spanning_cluster(label_matrix, num_labels)
do j = 1, num_labels
if(j /= spanning_label) then
sizeindex = floor(log(1.0d0*clustersizes(j))/loga) + 1
histogram(sizeindex) = histogram(sizeindex) + 1
end if
end do
end do
!$omp end parallel do
results = histogram/(L**2 * num_samples * bin_sizes)
!/cndend/!
end subroutine
function find_spanning_cluster(labelled_matrix, num_labels) result(spanning_label)
!! Find the label of the percolating cluster, i.e. the one spanning from
!! one side of the system to the opposing side.
integer :: spanning_label
!! Label of the percolating cluster. -1 if no percolating cluster is found.
integer, dimension(:,:), intent(in) :: labelled_matrix
!! Labelled matrix of clusters from [[hoshen_kopelman]]/[[label]].
integer, intent(in) :: num_labels
!! Known number of clusters.
integer :: L
L = size(labelled_matrix,1)
spanning_label = find_intersection(labelled_matrix(:,1), labelled_matrix(:,L), num_labels)
if (spanning_label == -1) then
spanning_label = find_intersection(labelled_matrix(1,:), labelled_matrix(L,:), num_labels)
end if
end function
function spanning_density_one_sample(p, L) result(spanning_density)
real(kind=dp) :: spanning_density
real(kind=dp), intent(in) :: p
integer, intent(in) :: L
logical, dimension(:,:), allocatable :: binary_matrix
integer, dimension(:,:), allocatable :: labelled_matrix
integer :: num_labels, spanning_label
binary_matrix = create_binary_matrix(p,L)
call label(binary_matrix, labelled_matrix, num_labels)
spanning_label = find_spanning_cluster(labelled_matrix, num_labels)
if (spanning_label == -1) then
spanning_density = 0
return
end if
spanning_density = count(labelled_matrix == spanning_label)/real(L**2,kind=dp)
end function
function spanning_density(p, L, num_samples)
!! Density of the spanning/percolating cluster, i.e.
!! the number of sites on the percolating cluster divided by \\(L^2\\).
!! Averaged over **num_samples** Monte Carlo samples (with OpenMP).
real(kind=dp) :: spanning_density
!! The number of sites on the spanning cluster divided by \\(L^2\\).
real(kind=dp), intent(in) :: p
!! The probability for a site to allow transport.
integer, intent(in) :: L
!! The size of the system.
integer, intent(in) :: num_samples
!! The number of Monte Carlo samples.
integer :: i
real(kind=dp), dimension(:), allocatable :: results
allocate(results(num_samples))
!$omp parallel do
do i=1,num_samples
results(i) = spanning_density_one_sample(p, L)
end do
!$omp end parallel do
spanning_density = sum(results)/num_samples
end function
function has_spanning_cluster_one_sample(p, L) result(has_spanning)
logical :: has_spanning
real(kind=dp), intent(in) :: p
integer, intent(in) :: L
logical, dimension(:,:), allocatable :: binary_matrix
integer, dimension(:,:), allocatable :: labelled_matrix
integer :: num_labels, spanning_label
binary_matrix = create_binary_matrix(p,L)
call label(binary_matrix, labelled_matrix, num_labels)
spanning_label = find_spanning_cluster(labelled_matrix, num_labels)
has_spanning = spanning_label /= -1
end function
function spanning_probability(p, L, num_samples)
!! Calculate the probability of having a spanning/percolating cluster, given
!! a system size **L** and probability for a site to have transport **p**.
real(kind=dp) :: spanning_probability
!! The probability of having a percolating cluster, calculated as the number
!! of times a percolating cluster is found, divided by the number of attempts
!! (**num_samples**).
real(kind=dp), intent(in) :: p
!! Probability for a site to allow transport.
integer, intent(in) :: L
!! Size of the system.
integer, intent(in) :: num_samples
!! Number of Monte Carlo samples.
integer :: i
logical, dimension(:), allocatable :: results
allocate(results(num_samples))
!$omp parallel do
do i=1,num_samples
results(i) = has_spanning_cluster_one_sample(p, L)
end do
!$omp end parallel do
spanning_probability = count(results)/real(num_samples,kind=dp)
end function
function spanning_probability_inverse(x, L, num_samples, tolerance) result(p_x)
!! Find the inverse of [[spanning_probability]] by use
!! of the bisection method.
real(kind=dp), intent(in) :: x
!! The value of [[spanning_probability]] for which the inverse
!! is calculated.
real(kind=dp), intent(in) :: tolerance
!! Tolerance of approximation. The return value is within
!! **tolerance**/2 of the correct (but numerical) value.
integer, intent(in) :: L
!! Size of the system.
integer, intent(in) :: num_samples
!! Number of Monte Carlo samples to use when evaluating [[spanning_probability]].
real(kind=dp) :: p_x
!! The inverse of [[spanning_probability]]. If [[spanning_probability]] is
!! denoted \\(\Pi(p,L)\\), this function returns \\(p\\) such
!! that \\(\Pi(p,L)=x\\).
real(kind=dp) :: lower, upper, lowerPI, upperPI, mid, midPI
!/invPIstart/!
lower = 0
upper = 1
lowerPI = 0
upperPI = 1
do while(upper - lower > tolerance)
mid = (lower + upper)/2
midPI = spanning_probability(mid, L, num_samples)
if(midPI > x) then
upper = mid
upperPI = midPI
else
lower = mid
lowerPI = midPI
end if
end do
p_x = mid
!/invPIend/!
end function
!/labelsubroutinestart/!
subroutine label_recursively(matrix, labelled_matrix, num_labels)
logical, dimension(:,:), allocatable, intent(in) :: matrix
integer, dimension(:,:), allocatable, intent(inout) :: labelled_matrix
integer, intent(inout) :: num_labels
integer :: L, i, j
L = size(matrix,1)
num_labels = 0
if(.not. allocated(labelled_matrix)) then
allocate(labelled_matrix(L,L))
end if
labelled_matrix = 0
do j=1,L
do i=1,L
if(matrix(i,j) .and. labelled_matrix(i,j) == 0) then
num_labels = num_labels + 1
call growcluster(matrix,labelled_matrix,i,j,num_labels)
end if
end do
end do
end subroutine
!/labelsubroutineend/!
!/growclustersubroutinestart/!
recursive subroutine growcluster(matrix, labelled_matrix, i, j, label)
logical, dimension(:,:), allocatable, intent(in) :: matrix
integer, dimension(:,:), allocatable, intent(inout) :: labelled_matrix
integer, intent(in) :: i, j, label
integer :: L
L = size(matrix,1)
labelled_matrix(i,j) = label
if(i<L) then
if(matrix(i+1,j) .and. labelled_matrix(i+1,j)==0) then
call growcluster(matrix, labelled_matrix, i+1, j, label)
end if
end if
if(j<L) then
if(matrix(i,j+1) .and. labelled_matrix(i,j+1)==0) then
call growcluster(matrix, labelled_matrix, i, j+1, label)
end if
end if
if(i>1) then
if(matrix(i-1,j) .and. labelled_matrix(i-1,j)==0) then
call growcluster(matrix, labelled_matrix, i-1, j, label)
end if
end if
if(j>1) then
if(matrix(i,j-1) .and. labelled_matrix(i,j-1)==0) then
call growcluster(matrix, labelled_matrix, i, j-1, label)
end if
end if
end subroutine
!/growclustersubroutineend/!
end module percolation