-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwetting.js
More file actions
302 lines (250 loc) · 11 KB
/
wetting.js
File metadata and controls
302 lines (250 loc) · 11 KB
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
/*
Copyright (c) 2019 Yunfei Dai, Martin Ma, Harry Zhou, Leonardo T. Rolla
You can use, modify and redistribute this program under the terms of the GNU Lesser General Public License, either version 3 of the License, or any later version.
*/
// Constants to match different colors with different site conditions
const wettingVacant = 1;
const wettingCeiling = 2;
const wettingOccupied = 3;
// Use an array to save the values of exp{paramBeta * Delta_negative energy}
// so that we do not have to call Math.exp() every time during update, which speeds up the code
// Note that Delta_negative energy only has values in -3,-2,-1,0,1,2,3
// So we use expValue[i] to represent exp{paramBeta * (i+3)}
var wettingExpValue = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
// Initialize matrices if model is Wetting Kawasaki
// The procedure reads the global variable colorConfiguration and changes its value
function wettingInit() {
// In wetting model, the first row is set as the interaction ceiling
for (var j = 0; j < gridSize; ++j) {
colorConfiguration[0][j] = wettingCeiling;
}
// The last row is always vacant
for (var i = 1; i < gridSize; ++i) {
for (var j = 0; j < gridSize; ++j) {
colorConfiguration[i][j] = wettingVacant;
}
}
// For the remaining sites, we organize them as a square bubble adjacent to the ceiling
// Starts with density Zeta
var volume = (gridSize - 2) * gridSize * paramZeta;
var bubbleSize = Math.round(Math.sqrt(volume));
var startingColumn = Math.floor((gridSize - bubbleSize) / 2);
var endingColumn = Math.floor((gridSize + bubbleSize) / 2);
// Put the bubble horizontally centered
for (var i = 6; i <= bubbleSize + 5 && i < gridSize - 1; ++i) {
for (var j = startingColumn; j < endingColumn; ++j) {
colorConfiguration[i][j] = wettingOccupied;
}
}
// Initialize wettingExpValue when beta is fixed
for (var i = 0; i < 7; ++i) {
wettingExpValue[i] = Math.exp(paramBeta * (i - 3));
}
}
// Ramdonly choose a site and update Mat in Wetting
// The procedure reads the global variable Mat and changes its value
// Note that the Mat IS ACTUALLY A TORUS during the simulation
function wettingUpdateSite(deltaTime) {
var iterTime = Math.ceil(deltaTime * gridSize * gridSize);
while(iterTime--) {
// Randomly choose Mat[i][j], i = 1,2,...,N-2, j = 0,1,...,N-1
var i = Math.floor(myRand() * (gridSize - 2)) + 1;
var j = Math.floor(myRand() * gridSize);
// Randomly pick a neighbor of Mat[i][j]
var temp = wettingPickNeighbor(i,j);
var newi = temp[1];
var newj = temp[2];
// Try to exchange these two sites and calculate the negative energy
var formerIJ = colorConfiguration[i][j];
var formerNewIJ = colorConfiguration[newi][newj];
var deltaNegativeEnergy = wettingCountDeltaEnergy(colorConfiguration,i,j,newi,newj);
// If the energy increases, accept the exchange
// If the negative energy drops, there's exp(Beta * Delta_negative energy) probability
// to accept the exchange.
var threshold;
if(deltaNegativeEnergy % 1 == 0) {
// If the delta energy is an integer, use calculated values to speed up
threshold = wettingExpValue[deltaNegativeEnergy + 3];
}
else {
threshold = Math.exp(paramBeta * deltaNegativeEnergy);
}
if(myRand() > threshold) {
// Reject the exchange
continue;
}
else {
// Accept the change, update the negative energy
colorConfiguration[i][j] = formerNewIJ;
colorConfiguration[newi][newj] = formerIJ;
}
}
}
// Calculate how much negative energy has changed after the [i][j]
// element is exchanged with the [newi][newj] element in
// the configuration Mat
function wettingCountDeltaEnergy(Mat,i,j,newi,newj) {
if(Mat[i][j] == Mat[newi][newj]) {
// Negative energy does not change
return 0;
}
// When [i][j] site is occupied, indicator = 1, else indicator = -1
var IJOccupiedIndicator = 2 * (Mat[i][j] == wettingOccupied) - 1;
if(j == newj) {
// Two sites are on the same column
if(i < newi) {
// The original site is higher
if(i == 1) {
// Boundary situation
return ( (Mat[newi][commonTorus(newj - 1)] == wettingOccupied) + (Mat[newi + 1][newj] == wettingOccupied) +
(Mat[newi][commonTorus(newj + 1)] == wettingOccupied) - (Mat[i][commonTorus(j - 1)] == wettingOccupied) -
paramDelta - (Mat[i][commonTorus(j + 1)] == wettingOccupied) )
* IJOccupiedIndicator;
}
else {
// Normal situation
return ( (Mat[newi][commonTorus(newj - 1)] == wettingOccupied) + (Mat[newi + 1][newj] == wettingOccupied) +
(Mat[newi][commonTorus(newj + 1)] == wettingOccupied) - (Mat[i][commonTorus(j - 1)] == wettingOccupied) -
(Mat[i - 1][j] == wettingOccupied) - (Mat[i][commonTorus(j + 1)] == wettingOccupied) )
* IJOccupiedIndicator;
}
}
else {
// The original site is lower
if(newi == 1) {
// Boundary situation
return ( (Mat[newi][commonTorus(newj - 1)] == wettingOccupied) + paramDelta +
(Mat[newi][commonTorus(newj + 1)] == wettingOccupied) - (Mat[i][commonTorus(j - 1)] == wettingOccupied) -
(Mat[i + 1][j] == wettingOccupied) - (Mat[i][commonTorus(j + 1)] == wettingOccupied) )
* IJOccupiedIndicator;
}
else {
// Normal situation
return ( (Mat[newi][commonTorus(newj - 1)] == wettingOccupied) + (Mat[newi - 1][newj] == wettingOccupied) +
(Mat[newi][commonTorus(newj + 1)] == wettingOccupied) - (Mat[i][commonTorus(j - 1)] == wettingOccupied) -
(Mat[i + 1][j] == wettingOccupied) - (Mat[i][commonTorus(j + 1)] == wettingOccupied) )
* IJOccupiedIndicator;
}
}
}
else {
// Two sites are on the same row
if(commonTorus(j + 1) == newj) {
// The original site is lefter (note that the left and right edge of Mat is connected)
if(i == 1) {
// Boundary situation
return ( (Mat[newi + 1][newj] == wettingOccupied) + (Mat[newi][commonTorus(newj + 1)] == wettingOccupied) -
(Mat[i][commonTorus(j - 1)] == wettingOccupied) - (Mat[i + 1][j] == wettingOccupied) )
* IJOccupiedIndicator;
}
else {
// Normal situation
return ( (Mat[newi + 1][newj] == wettingOccupied) + (Mat[newi][commonTorus(newj + 1)] == wettingOccupied) +
(Mat[newi - 1][newj] == wettingOccupied) - (Mat[i - 1][j] == wettingOccupied) -
(Mat[i][commonTorus(j - 1)] == wettingOccupied) - (Mat[i + 1][j] == wettingOccupied) )
* IJOccupiedIndicator;
}
}
else {
// The original site is righter
if(i == 1) {
// Boundary situation
return ( (Mat[newi + 1][newj] == wettingOccupied) + (Mat[newi][commonTorus(newj - 1)] == wettingOccupied) -
(Mat[i][commonTorus(j + 1)] == wettingOccupied) - (Mat[i + 1][j] == wettingOccupied) )
* IJOccupiedIndicator;
}
else {
// Normal situation
return ( (Mat[newi + 1][newj] == wettingOccupied) + (Mat[newi][commonTorus(newj - 1)] == wettingOccupied) +
(Mat[newi - 1][newj] == wettingOccupied) - (Mat[i - 1][j] == wettingOccupied) -
(Mat[i][commonTorus(j + 1)] == wettingOccupied) - (Mat[i + 1][j] == wettingOccupied) )
* IJOccupiedIndicator;
}
}
}
}
// Calculate the negative energy of the configurations given
function wettingCalculateEnergy(Mat) {
var negativeEnergy = 0;
// The part produced from the interaction with the ceiling
for (var j = 0; j < gridSize; ++j) {
if(Mat[1][j] == wettingOccupied) {
negativeEnergy += paramDelta;
}
}
// The part produced from the interaction between particles
for (var i = 1; i < gridSize - 1; ++i) {
for (var j = 0; j < gridSize; ++j) {
// A pair of neighboring occupied sites contributes 1 negative energy
// Search from left to right, up to down
negativeEnergy += ( (Mat[i][j] == wettingOccupied) && (Mat[i][commonTorus(j + 1)] == wettingOccupied) )+
( (Mat[i][j] == wettingOccupied) && (Mat[i + 1][j] == wettingOccupied) );
}
}
return negativeEnergy;
}
// Randomly pick up a neighbor
// Note that the Mat IS ACTUALLY A TORUS during the simulation
// Be careful to deal with 2 boundary situations
function wettingPickNeighbor(i,j) {
var result = new Array();
var r = myRand();
if(i == 1) {
// If mat[i][j] is in the second highest row
if(r < 1/3){
result[1] = i + 1;
result[2] = j;
}
else if(r < 2/3){
result[1] = i;
result[2] = commonTorus(j - 1);
}
else{
result[1] = i;
result[2] = commonTorus(j + 1);
}
}
else if(i == gridSize - 2) {
// If mat[i][j] is in the second lowest row
if(r < 1/3){
result[1] = i - 1;
result[2] = j;
}
else if(r < 2/3){
result[1] = i;
result[2] = commonTorus(j - 1);
}
else{
result[1] = i;
result[2] = commonTorus(j + 1);
}
}
else {
// If mat[i][j] is neither in the second highest row
// nor in the second lowest row, it's the normal situation
if(r < 1/4){
result[1] = i - 1;
result[2] = j;
}
else if(r < 2/4){
result[1] = i;
result[2] = commonTorus(j - 1);
}
else if(r < 3/4){
result[1] = i;
result[2] = commonTorus(j + 1);
}
else{
result[1] = i + 1;
result[2] = j;
}
}
return result;
}
// Output of Wetting Kawasaki model
function wettingOutput(){
var outstring = "Wetting Kawasaki with parameter ζ = " + paramZeta + ", parameter δ = " + paramDelta +
" and parameter β = " + paramBeta + ". In the end, the negative energy is " + wettingCalculateEnergy(colorConfiguration) + " .";
// console.log(wettingCalculateEnergy(colorConfiguration));
return outstring;
}