-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathIREV1.java
More file actions
2315 lines (2188 loc) · 88.1 KB
/
IREV1.java
File metadata and controls
2315 lines (2188 loc) · 88.1 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
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
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import java.io.*;
import java.util.*;
/* This is a crucial part of the agent-based simulator used in King et. al 2022
https://doi.org/10.1080/23249935.2021.2017510. More details about how the
simulator was used can be found there.
This part of the simulator defines the crowd of agents. It provides the
functions and properties of the crowd, allowing the movement and choices made
by agents to be updated. As part of the latter, the occupancies of, distances
from, and desirability of destinations are calculated as necessary.
The force-based movement model based on Helbing et. al 2000
https://www.nature.com/articles/35035023 is also implemented here.
Several functions implementing different destination choice models are provided,
but not used in the current version of the code (24/05/2022).
*/
public class IREV1 {
// Declare variable to be used in the whole class by all methods/objects.
// Initialise an array of agents.
public agent[] people;
/*
Initialise:
timestep size between updating positions,
the magnitude of random fluctuation in movement direction,
size of agent, and
set simulated time to 0.
*/
public double deltaT, noise, size, time = 0.0;
// Force-based model parameters.
public double tau, kpara, kappa;
// Destination choice model parameters.
public double [] param;
/* Initialise:
total number of agents,
the number of cells in each row/column of simulated space, and
the radius of the agents
*/
public int N, gridSize, R;
// Initialise position of exit.
public int[] exitx, exity;
// Matrix for distances of grid cells from a given destination.
public double[][] dists;
// Storage for floor field and distance values for each destination.
public double[][][] fstatic;
public double[][][] distances;
// Matrix for each grid cell specifying spatial availability for placing agents.
public double[][] grid;
// Spatial grid for floor field values.
public double[][] floorField;
// Local change to the floor field due to agents when placing new agents.
public double[][] excluFloorField;
// Coordinates of grid cell which an agent's centre of mass occupies.
public int[] gridPos;
// Gradient of floor field for calculating preferred direction for an agent.
public double[] gradient;
// Extent in x and y to which an agent overlaps with something.
public double[] entOverlap;
// Number of agents that overlap with something.
public double numEntOverlap;
// Used to calculate the total force acting directly on the body of an agent.
//public double[] bodyForces;
// Set whether to vary the mass, preferred speed and radii of the agents.
public boolean varyMass = false, varySpeed = false, varySize = false;
// Boolean for the new next destination constraint.
private boolean newNextDest;
/* Set a random number generator used to set variable mass/speed/size,
noise in agent direction and gradient perception, and assigning transition
matrix elements.*/
public Random twistor;
// Number of destination in scenario.
public int numDest;
// Transition matrix for Markov model. Used only if necessary.
public double [][] transMat;
// Number of agents at each destination.
public int[] destPopularity;
// PrintWriter object for destination sequence output file.
public PrintWriter pwout;
// Contains all the activities that each destination can have.
public char[][] destActs;
// Busyness parameter.
public double occParam;
// Distance parameter.
public double distParam;
// Desirability parameter.
public double desParam;
// Parameter for waiting time distribution.
public double expParam = -1;
// Set the radius of destinations for counting agents.
public int destSize = 20;
/* Constructor method where no model parameters are provided. Sets default
values for the destination choice model parameters as zero.
Takes the following inputs:
1. Number of agents.
2. Size of the spatial grid.
3. X-coordinates of all destinations.
4. Y-coordinates of all destinations.
5. List of all possible activities across destinations.
6. List of available activities for each destination.
7. Is the new next destination constraint being applied?
*/
public IREV1(int N, int gridSize, int[] exitx, int[] exity,
ArrayList<Character> acts, char[][] destActs, boolean newDest) {
//System.out.println("IREV1 no model parameter constructor activated.");
this.N = N; // number of agents
this.gridSize = gridSize; // number of position cells
this.exitx = exitx; // x coordinate of exit
this.exity = exity; // y coordinate of exit
this.destActs = destActs; // list of activities at each destination
numDest = exitx.length; // number of destinations
this.occParam = 0.0; // Busyness parameter
this.distParam = 0.0; // Distance parameter
this.desParam = 0.0; // Desirability parameter
this.newNextDest = newDest; // new destination constraint
// Define parameters you are unlikely to change in simulations:
deltaT = 0.05; // size of the timesteps.
R = 4; // radius of agent. Note that R is actually R/10 in metres.
noise = 0.2; // random noise for direction of movement
tau = 0.5; // velocity adaptation time (see helbing2000, moussaid2011).
kpara = 12000;
kappa = 24000; // kpara and kappa from Helbing et al. 2000.
// This is the width of one grid cell in metres.
// Numerator sets how many centimetres wide a cell is.
size = (double) gridSize / 10;
/* Set a random number generator used to set variable mass/speed/size,
noise in agent direction and gradient perception, and assigning transition
matrix elements.*/
twistor = new Random();
// Initiate the crowd of agents.
people = new agent[N];
// Initiate the body force acting on all agents.
//bodyForces = new double[N];
// For every agent in the simulation...
for (int vv = 0; vv < N; vv++) {
//...create the agent...
people[vv] = new agent(numDest, acts, acts.size());
//...and give it a first destination...
people[vv].destination = ranDest(numDest);
//...and an activity to perform at said destination.
// Defaults to first activity available.
people[vv].currAct = destActs[people[vv].destination][0];
// Add the destination to the agent's destination sequence.
// Only used if this initial choice is to be counted as a destination chosen.
//people[vv].dests.add(people[vv].destination);
// Add the first decision time.
// Only used if this initial choice is to be counted as a destination chosen.
//people[vv].decisionTimes.add(0);
// Initialise the bodily forces acting on this agent.
//bodyForces[vv] = 0.0;
}
// Matrix for distances of grid cells from a given destination.
dists = new double[N][N];
// Floor field and distance for every grid position for every destination.
fstatic = new double[numDest][gridSize][gridSize];
distances = new double[numDest][gridSize][gridSize];
// Matrix for each grid cell specifying spatial availability for placing agents.
grid = new double[gridSize][gridSize];
// Spatial grid for floor field values.
floorField = new double[gridSize][gridSize];
// Local change to the floor field due to agents when placing new agents.
excluFloorField = new double[gridSize][gridSize];
// Initialise the grid positions of an agent.
gridPos = new int[2];
gridPos[0] = 1;
gridPos[1] = 1;
// Initialise the array for storing the gradient of the floor field.
gradient = new double[2];
gradient[0] = 1.0;
gradient[1] = 1.0;
// Initialise the amount an agent overlaps in the x and y directions.
entOverlap = new double[2];
entOverlap[0] = 0.0;
entOverlap[1] = 0.0;
// Assign values to floor fields: a room without doors.
// For each destination...
for (int k = 0; k < numDest; k++) {
//...for each row of the grid...
for (int i = 0; i < gridSize; i++) {
//...for each value in the row...
for (int j = i; j < gridSize; j++) {
//...for the first/last 4 cells in the row/column of space...
if (i < 3 || i > (gridSize - 4) || j < 3 || j > (gridSize - 4)) {
//...set floor field = 0.
fstatic[k][i][j] = 0.0;
fstatic[k][j][i] = 0.0;
} else { // Otherwise...
//...set floor field = 1.
fstatic[k][i][j] = 1.0;
fstatic[k][j][i] = 1.0;
}
// Initialise distances and spatial availability for every grid cell.
distances[k][i][j] = 0;
grid[i][j] = 0.0;
grid[j][i] = 0.0;
// Set the first floor field as above.
floorField[i][j] = fstatic[0][i][j];
floorField[j][i] = fstatic[0][j][i];
}
}
}
// Transition matrix initialised with dimensions = number of destinations.
// Must initialise it despite it never being used.
transMat = new double[numDest][numDest];
}
/* Constructor method for model parameters with Markov model.
Takes the following inputs:
1. Number of agents.
2. Size of the spatial grid.
3. X-coordinates of all destinations.
4. Y-coordinates of all destinations.
5. Values of all destination choice model parameters.
6. Transition matrix for the Markov model.
7. List of all possible activities across destinations.
8. List of available activities for each destination.
9. Is the new next destination constraint being applied?
*/
public IREV1(int N, int gridSize, int[] exitx, int[] exity, double[] params,
double[][] transMat, ArrayList<Character> acts, char[][] destActs,
boolean newDest) {
//System.out.println("IREV1 Markov model constructor activated.");
this.N = N; // number of agents
this.gridSize = gridSize; // number of position cells
this.exitx = exitx; // x coordinate of exit
this.exity = exity; // y coordinate of exit
this.transMat = transMat; // Transition matrix
this.destActs = destActs; // Activities available at each destination
this.occParam = params[0]; // Busyness parameter
this.distParam = params[1]; // Distance parameter
this.desParam = params[2]; // Desirability parameter
numDest = exitx.length; // Number of destinations
this.newNextDest = newDest; // new destination constraint
// Define parameters you are unlikely to change in simulations:
deltaT = 0.05; // size of the timesteps.
R = 4; // radius of agent. Note that R is actually R/10 in units...
noise = 0.2; // random noise for direction of movement
tau = 0.5; // velocity adaptation time (see helbing2000, moussaid2011).
kpara = 12000;
kappa = 24000; // kpara and kappa from helbing2000.
// This is the width of one grid cell in metres.
// Numerator sets how many centimetres wide a cell is.
size = (double) gridSize / 10;
/* Set a random number generator used to set variable mass/speed/size,
noise in agent direction and gradient perception, and assigning transition
matrix elements.*/
twistor = new Random();
// Initiate the crowd of agents.
people = new agent[N];
// Initiate the body force acting on all agents.
//bodyForces = new double[N];
// For every agent in the simulation...
for (int vv = 0; vv < N; vv++) {
//...create the agent...
people[vv] = new agent(numDest, acts, acts.size());
//...and give it a first destination...
people[vv].destination = ranDest(numDest);
//...and an activity to perform at said destination.
// Defaults to first activity available.
people[vv].currAct = destActs[people[vv].destination][0];
// Add the destination to the agent's destination sequence.
// Only used if this initial choice is to be counted as a destination chosen.
//people[vv].dests.add(people[vv].destination);
// Add the first decision time.
// Only used if this initial choice is to be counted as a destination chosen.
//people[vv].decisionTimes.add(0);
// Initialise the bodily forces acting on this agent.
//bodyForces[vv] = 0.0;
}
// Matrix for distances of grid cells from a given destination.
dists = new double[N][N];
// Floor field and distance for every grid position for every destination.
fstatic = new double[numDest][gridSize][gridSize];
distances = new double[numDest][gridSize][gridSize];
// Matrix for each grid cell specifying spatial availability for placing agents.
grid = new double[gridSize][gridSize];
// Spatial grid for floor field values.
floorField = new double[gridSize][gridSize];
// Local change to the floor field due to agents when placing new agents.
excluFloorField = new double[gridSize][gridSize];
// Initialise the grid positions of an agent.
gridPos = new int[2];
gridPos[0] = 1;
gridPos[1] = 1;
// Initialise the array for storing the gradient of the floor field.
gradient = new double[2];
gradient[0] = 1.0;
gradient[1] = 1.0;
// Initialise the amount an agent overlaps in the x and y directions.
entOverlap = new double[2];
entOverlap[0] = 0.0;
entOverlap[1] = 0.0;
// Assign values to floor fields: a room without doors.
// For each destination...
for (int k = 0; k < numDest; k++) {
//...for each row of the grid...
for (int i = 0; i < gridSize; i++) {
//...for each value in the row...
for (int j = i; j < gridSize; j++) {
//...for the first/last 4 cells in the row/column of space...
if (i < 3 || i > (gridSize - 4) || j < 3 || j > (gridSize - 4)) {
//...set floor field = 0.
fstatic[k][i][j] = 0.0;
fstatic[k][j][i] = 0.0;
} else { // Otherwise...
//...set floor field = 1.
fstatic[k][i][j] = 1.0;
fstatic[k][j][i] = 1.0;
}
// Initialise distances and spatial availability for every grid cell.
distances[k][i][j] = 0;
grid[i][j] = 0.0;
grid[j][i] = 0.0;
// Set the first floor field as above.
floorField[i][j] = fstatic[0][i][j];
floorField[j][i] = fstatic[0][j][i];
}
}
}
}
/* Constructor method with model parameters but no Markov.
Takes the following inputs:
1. Number of agents.
2. Size of the spatial grid.
3. X-coordinates of all destinations.
4. Y-coordinates of all destinations.
5. Values of all destination choice model parameters.
6. List of all possible activities across destinations.
7. List of available activities for each destination.
8. Is the new next destination constraint being applied?
*/
public IREV1(int N, int gridSize, int[] exitx, int[] exity, double [] params,
ArrayList<Character> acts, char[][] destActs, boolean newDest) {
//System.out.println("IREV1 full model constructor activated.");
this.N = N; // number of agents
this.gridSize = gridSize; // number of position cells
this.exitx = exitx; // x coordinate of exit
this.exity = exity; // y coordinate of exit
this.destActs = destActs; // list of activities at each destination
numDest = exitx.length; // number of destinations
this.occParam = params[0]; // Busyness parameter
this.distParam = params[1]; // Distance parameter
this.desParam = params[2]; // Desirability parameter
this.newNextDest = newDest; // new destination constraint
// Define parameters you are unlikely to change in simulations:
deltaT = 0.05; // size of the timesteps.
R = 4; // radius of agent. Note that R is actually R/10 in metres.
noise = 0.2; // random noise for direction of movement
tau = 0.5; // velocity adaptation time (see helbing2000, moussaid2011).
kpara = 12000;
kappa = 24000; // kpara and kappa from helbing2000.
// This is the width of one grid cell in metres.
// Numerator sets how many centimetres wide a cell is.
size = (double) gridSize / 10;
/* Set a random number generator used to set variable mass/speed/size,
noise in agent direction and gradient perception, and assigning transition
matrix elements.*/
twistor = new Random();
// initiate the crowd of agents.
people = new agent[N];
// Initiate the body force acting on all agents.
//bodyForces = new double[N];
// For every agent in the simulation...
for (int vv = 0; vv < N; vv++) {
//...create the agent...
people[vv] = new agent(numDest, acts, acts.size());
//...and give it a first destination...
people[vv].destination = ranDest(numDest);
//...and an activity to perform at said destination.
// Defaults to first activity available.
people[vv].currAct = destActs[people[vv].destination][0];
// Add the destination to the agent's destination sequence.
// Only used if this initial choice is to be counted as a destination chosen.
//people[vv].dests.add(people[vv].destination);
// Add the first decision time.
// Only used if this initial choice is to be counted as a destination chosen.
//people[vv].decisionTimes.add(0);
// Initialise the bodily forces acting on this agent.
//bodyForces[vv] = 0.0;
}
// Matrix for distances of grid cells from a given destination.
dists = new double[N][N];
// Floor field and distance for every grid position for every destination.
fstatic = new double[numDest][gridSize][gridSize];
distances = new double[numDest][gridSize][gridSize];
// Matrix for each grid cell specifying spatial availability for placing agents.
grid = new double[gridSize][gridSize];
// Spatial grid for floor field values.
floorField = new double[gridSize][gridSize];
// Local change to the floor field due to agents when placing new agents.
excluFloorField = new double[gridSize][gridSize];
// Initialise the grid positions of an agent.
gridPos = new int[2];
gridPos[0] = 1;
gridPos[1] = 1;
// Initialise the array for storing the gradient of the floor field.
gradient = new double[2];
gradient[0] = 1.0;
gradient[1] = 1.0;
// Initialise the amount an agent overlaps in the x and y directions.
entOverlap = new double[2];
entOverlap[0] = 0.0;
entOverlap[1] = 0.0;
// Assign values to floor fields: a room without doors.
// For each destination...
for (int k = 0; k < numDest; k++) {
//...for each row of the grid...
for (int i = 0; i < gridSize; i++) {
//...for each value in the row...
for (int j = i; j < gridSize; j++) {
//...for the first/last 4 cells in the row/column of space...
if (i < 3 || i > (gridSize - 4) || j < 3 || j > (gridSize - 4)) {
//...set floor field = 0.
fstatic[k][i][j] = 0.0;
fstatic[k][j][i] = 0.0;
} else { // Otherwise...
//...set floor field = 1.
fstatic[k][i][j] = 1.0;
fstatic[k][j][i] = 1.0;
}
// Initialise distances and spatial availability for every grid cell.
distances[k][i][j] = 0;
grid[i][j] = 0.0;
grid[j][i] = 0.0;
// Set the first floor field as above.
floorField[i][j] = fstatic[0][i][j];
floorField[j][i] = fstatic[0][j][i];
}
}
}
}
// This initialises agents if positions are given. Not used.
public void setInitialPosgiven() {
//System.out.println("Setting initial agent positions from file.");
// For each agent...
for (int i = 0; i < N; i++) {
//...assign agent characteristics here:
if (varyMass) { // agent mass
people[i].mass = (double) twistor.nextInt(41) + 60;
}
if (varySize) { // agent size/radius
people[i].size = (double) twistor.nextInt(11) / 10 * 0.1 + 0.20;
}
if (varySpeed) {// agent preferred speed
people[i].speed = (double) twistor.nextInt(10) / 10 * 0.1 + 0.6;
}
// Initialises a random direction of travel for each agent.
double angle = 2 * twistor.nextDouble() * Math.PI;
// Initial speeds for each agent.
people[i].vx = 0.1 * Math.cos(angle);
people[i].vy = 0.1 * Math.sin(angle);
// Initialise the distance travelled by each agent.
// For each agent...
for (int j = i; j < N; j++) {
// Matrix is symmetric.
dists[i][j] = 0.0;
dists[j][i] = 0.0;
}
}
}
// Sets random initial positions of agents (without any overlap).
// Requires the following input:
// 1. Index of the floor field used to assess any overlaps with initial placement.
public void setInitial(int fieldIndex) {
//System.out.println("Setting random initial agent positions.");
int a1, a2, a3, a4;
// Set the initial values of the floor field in each cell.
// For each row in the grid...
for (int i = 0; i < gridSize; i++) {
//...for each value in the row...
for (int j = 0; j < gridSize; j++) {
//...if the floor field = 0 at this cell...
if (fstatic[fieldIndex][i][j] == 0) {
//...set temporary floor field to 0.
floorField[i][j] = fstatic[fieldIndex][i][j];
} else { // Otherwise set it to 1.
floorField[i][j] = 1.0;
}
}
}
/*
Now find the average floor field over cell + nearest neighbours.
This will identify the cells of the grid that are within an agent
size of walls and obstacles. They are consequently excluded from
agent placement otherwise, agents would overlap with walls/obstacles
and be repelled with huge forces!
*/
// Run on the all cells within 4 cells of the obstacle...
for (int uu = 0; uu < 4; uu++) {
//...for each row of the grid...
for (int i = 0; i < gridSize; i++) {
//...for each column of the grid...
for (int j = 0; j < gridSize; j++) {
a1 = i - 1; // neighbour on the left
// If the cell is at the left boundary already...
if (a1 < 0) {
//...then fix to 0.
a1 = 0;
}
a2 = i + 1; // neighbour on the right
// If the cell is at the right boundary already...
if (a2 > gridSize - 1) {
//...then fix to the grid size (Java is zero-indexed).
a2 = gridSize - 1;
}
a3 = j - 1; // neighbour below
// If the cell is at the bottom already...
if (a3 < 0) {
//...then fix to 0.
a3 = 0;
}
a4 = j + 1; // neighbour above
// If the cell is at the top already...
if (a4 > gridSize - 1) {
//...then fix to the grid size (Java is zero-indexed).
a4 = gridSize - 1;
}
// Find the average floor field.
double locAvFloorField = (floorField[i][j] + floorField[a1][j] + floorField[a2][j] +
floorField[i][a3] + floorField[i][a4]) / 5;
// If average is < 1, (someone is 0)...
if (locAvFloorField < 1) {
//...then set floor field = 0.
excluFloorField[i][j] = 0.0;
} else { // Otherwise, set it = 1
excluFloorField[i][j] = 1.0;
}
}
}
// Set wall/obstacle exclusion field.
// For each row in the grid...
for (int i = 0; i < gridSize; i++) {
//...for each value in the row...
for (int j = 0; j < gridSize; j++) {
//...set the floor field value.
floorField[i][j] = excluFloorField[i][j];
}
}
}
/* Assign the agents random positions and orientations (on grid for now, for
simplicity). ensure agents don't overlap initially and are not sitting on the
boundary.*/
// For each agent...
for (int i = 0; i < N; i++) {
//...initialise candidate x/y-coordinates.
int ranXCand, ranYCand;
int cc = 0; // Entity placement counter.
// Assign agent characteristics here:
if (varyMass) { // agent mass in kg
people[i].mass = (double) twistor.nextInt(41) + 60;
}
if (varySize) { // agent size/radius in metres
people[i].size = (double) twistor.nextInt(11) / 10 * 0.1 + 0.20;
}
if (varySpeed) {// agent preferred speed in m/s
people[i].speed = (double) twistor.nextInt(10) / 10 * 0.1 + 0.6;
}
// Randomly assign x and y coordinates
// Must be cells away from the boundaries (i.e. free space cells).
ranXCand = twistor.nextInt(gridSize - 10) + 5;
ranYCand = twistor.nextInt(gridSize - 10) + 5;
// Convert grid cell positions into position in metres.
people[i].x = (double) ranXCand / 10;
people[i].y = (double) ranYCand / 10;
// Convert position back to obtain grid position array.
getGridpos(i);
cc = 0;
/*
If the assigned cell is within the exclusion field of either another
agent or an obstacle...
*/
while (grid[ranXCand][ranYCand] != 0 || floorField[gridPos[0]][gridPos[1]] == 0.0) {
//...re-assign intiial position.
ranXCand = twistor.nextInt(gridSize - 10) + 5;
ranYCand = twistor.nextInt(gridSize - 10) + 5;
people[i].x = (double) ranXCand / 10; // grid cell size hard coding!
people[i].y = (double) ranYCand / 10; // grid cell size hard coding!
// Convert position back to obtain grid position array.
getGridpos(i);
// Increment placement counter
cc += 1;
// If you have tried to place agent 10000 times...
if (cc > 10000) {
//...then display this warning
System.out.println(" ");
System.out.println("***********************************");
System.out.println("Cannot assign this many agents!");
System.out.println("... shutting down ...");
System.out.println("***********************************");
System.exit(0);
}
}
// Set up temporary exclusion field around newly-placed agent.
/* For cells around the central cell of the agent (hard-coded so
that any possible agent size fits in - may want to change at some
point.)...*/
for (int uu = -4; uu < 5; uu++) {
for (int vv = -4; vv < 5; vv++) {
// Compute distance of cell from centre.
double distFromCent = Math.sqrt((uu * uu) + (vv * vv));
// If the cells or part of the cells are within the boundary...
if (distFromCent <= 5 || (Math.sqrt(((Math.abs(uu)-1) *
(Math.abs(uu) - 1)) + ((Math.abs(vv) - 1) *
(Math.abs(vv) - 1))) < 5 && (distFromCent > 5))) {
//...block them out.
grid[ranXCand + uu][ranYCand + vv] = 1.0;
}
}
}
// Initialises a random direction of travel for each agent.
double angle = 2 * twistor.nextDouble() * Math.PI;
people[i].vx = 0.1 * Math.cos(angle);
people[i].vy = 0.1 * Math.sin(angle);
// Initialise the distance between each agent.
// For each agent...
for (int j = i; j < N; j++) {
//...matrix is symmetric.
dists[i][j] = 0.0;
dists[j][i] = 0.0;
}
}
}
// Get current grid position for agent chosen.
// Requires the following input:
// 1. Index of the agent chosen.
public void getGridpos(int chosen) {
// Change agent's coordinates into corresponding grid cell.
double gridXConv = people[chosen].x / size;
double gridYConv = people[chosen].y / size;
// Each grid cell is 10x10cm, and Java counts from 0.
gridXConv = gridXConv * (size * 10 - 1); // grid cell size hard coding!
gridYConv = gridYConv * (size * 10 - 1); // grid cell size hard coding!
gridPos[0] = (int) (long) Math.round(gridXConv);
gridPos[1] = (int) (long) Math.round(gridYConv);
}
// Get new grid position for agent chosen.
// Requires the following input:
// 1. Index of the agent chosen.
public void getnewGridpos(int chosen) {
// Change agent's coordinates into corresponding grid cell.
double gridXConv = people[chosen].xnew / size;
double gridYConv = people[chosen].ynew / size;
// Each grid cell is 10x10cm, and Java counts from 0.
gridXConv = gridXConv * (size * 10 - 1); // grid cell size hard coding!
gridYConv = gridYConv * (size * 10 - 1); // grid cell size hard coding!
gridPos[0] = (int) (long) Math.round(gridXConv);
gridPos[1] = (int) (long) Math.round(gridYConv);
}
// Get grid position for given x/y coordinate pair.
// Requires the following inputs:
// 1. X-coordinate.
// 2. Y-coordinate.
public void getposGridpos(double gridXConv, double gridYConv) {
// Change coordinates into corresponding grid cell.
gridXConv = gridXConv / size;
gridYConv = gridYConv / size;
gridXConv = gridXConv * (size * 10 - 1); // grid cell size hard coding!
gridYConv = gridYConv * (size * 10 - 1); // grid cell size hard coding!
gridPos[0] = (int) (long) Math.round(gridXConv);
gridPos[1] = (int) (long) Math.round(gridYConv);
}
// Get magnitude of a 2D vector.
// Requires the following inputs:
// 1. X-component of the vector.
// 2. Y-component of the vector.
public double magnitude(double x, double y) {
double out = Math.sqrt(x * x + y * y);
return out;
}
// Implements Heaviside step function for a given value and threshold.
// =1 if >= threshold and =0 otherwise.
// Requires the following inputs:
// 1. Value to be assessed.
// 2. Value at which Heaviside function changes.
public double heaviside(double x, double threshold) {
// Initialise the output.
double out = 0.0;
// If the value is above the threshold...
if (x >= threshold) {
//...then the output is one.
out = 1.0;
}
return out;
}
// Method to check if agents stand on the boundary.
// Requires the following input:
// 1. Index of the agent chosen.
public boolean checkPos(int chosen) {
// How many cells to include up and down from central cell of agent.
int ui, uj;
// Initialise the grid cell positions of boundaries of agent and
// distance to move agents if overlap discovered.
double entBoundXPos, entBoundYPos, dd;
// Assume overlap with any other agents is false at first.
boolean out = false;
// Initialise the number of agents that overlaop and the amount by
// which they overlap as 0.
numEntOverlap = 0.0;
entOverlap[0] = 0.0;
entOverlap[1] = 0.0;
// For cells to left/right of centre...
for (ui = -1; ui < 2; ui++) {
//...for cells up/down from the current cell...
for (uj = -1; uj < 2; uj++) {
//...if the cell is diagonally adjacent to agent position...
if ((Math.abs(ui) + Math.abs(uj)) == 2) {
//...the position of boundary of agent is divided by sqrt(2) as cell
// is nearest diagonal neighbour.
entBoundXPos = people[chosen].x + ui * people[chosen].size / Math.sqrt(2);
entBoundYPos = people[chosen].y + uj * people[chosen].size / Math.sqrt(2);
} else { // Otherwise, for the cells directly adjacent...
//...the boundary is an integer multiple of the agent radius.
entBoundXPos = people[chosen].x + ui * people[chosen].size;
entBoundYPos = people[chosen].y + uj * people[chosen].size;
}
// Convert position of agent boundary to the corresponding cell.
getposGridpos(entBoundXPos, entBoundYPos);
// If the agent is overlapping with an obstacle...
try {
if (fstatic[people[chosen].destination][gridPos[0]][gridPos[1]] == 0) {
//...then out is true...
out = true;
//...Increase the number of agents currently overlapping.
numEntOverlap += 1;
/*
Calculate the x- and y-components of the amount by which
agent overlaps.
*/
entOverlap[0] += people[chosen].x - entBoundXPos;
entOverlap[1] += people[chosen].y - entBoundYPos;
}
} catch (ArrayIndexOutOfBoundsException e) {
// If agent ends up in impossible position, then let user know.
System.out.println("Entity " + chosen + " is at grid position: "
+ gridPos[0] + ", " + gridPos[1]);
System.out.println("It's proposed coordinates are: (" +
entBoundXPos + ", " + entBoundYPos + ").");
System.out.println("It's current coordinates are: (" +
people[chosen].x + ", " + people[chosen].y + ").");
System.out.println("The agent has a radius of " +
people[chosen].size);
}
}
}
// If any agent does overlap with an obstacle...
if (numEntOverlap > 0) {
//...calculate the amount by which agent overlaps.
dd = Math.sqrt(entOverlap[0] * entOverlap[0] + entOverlap[1] * entOverlap[1]);
// Normalise the amount of overlap.
entOverlap[0] = entOverlap[0] / dd;
entOverlap[1] = entOverlap[1] / dd;
}
// Returns true if any agent overlaps and false otherwise.
return out;
}
/*
Find direction of highest positive and/or lowest negative gradient of
floor field at agent's current position.
Requires the following input:
1. Index of the agent chosen.
*/
public void getGradient(int chosen) {
// Indices for agent x/y cells and x/y cells outside of agent, respectively.
int i = 0, j = 0, k = 0, l = 0;
// Distances of cells outside the agent boundary from the agent centre.
double gradVecs1 = 0.0, gradVecs2 = 0.0;
/* Entity movement direction, distance of cell from central agent cell,
net floor field gradient around agent, noise in gradient detection by
agent.*/
double entDir, cellDist, totGrad, gradNoise;
double dirx, diry;
// Unnormalised x and y directions of the agent.
dirx = people[chosen].vx;
diry = people[chosen].vy;
// Magnitude of direction vector.
entDir = Math.sqrt(dirx * dirx + diry * diry);
// Normalise directions.
dirx = dirx / entDir;
diry = diry / entDir;
// Convert agent's position into a grid cell.
getGridpos(chosen);
// Initialise x/y components of the floor field gradient.
gradient[0] = 0.0;
gradient[1] = 0.0;
/*
In a circle of grid cells of radius R, centred around the cell
of the agent, with i moving along the horizontal axis and j
moving along the vertical.
*/
for (i = 0; i < (R + R + 1); i++) {
for (j = 0; j < (R + R + 1); j++) {
/*
As long as i and j are not on the agent's cells
and are not both = R...
*/
if ((i != R && j != R) || (i == R && j != R) || (i != R && j == R)) {
// Calculate the position of the cell from the agent's cell.
k = gridPos[0] + i - R;
l = gridPos[1] + j - R;
/*
Find the x/y components of the distance of the
cell from the agent cell, find the Euclidean distance, then
normalise the x/y components with it.
*/
gradVecs1 = (double) (k - gridPos[0]);
gradVecs2 = (double) (l - gridPos[1]);
cellDist = magnitude(gradVecs1, gradVecs2);
gradVecs1 = gradVecs1 / cellDist;
gradVecs2 = gradVecs2 / cellDist;
// How to deal with boundaries. No periodic boundaries!
if (k < 0) { // Negative x-index
//...set to 0.
k = 0;
}
// x-index exceeds grid size. -1 because Java counts from 0.
if (k > (gridSize - 1)) {
//...set to grid size.
k = gridSize - 1;
}
if (l < 0) { // Negative y-index
//...set to 0.
l = 0;
}
// y-index exceeds grid size. -1 because Java counts from 0.
if (l > (gridSize - 1)) {
//...set to grid size.
l = gridSize - 1;
}
/*
Use this to implement a field of view for the gradient
detection in the dynamic floor field. Only consider values
in front of agent.
*/
double grad = fstatic[people[chosen].destination][k][l] -
fstatic[people[chosen].destination][gridPos[0]][gridPos[1]];
// Increase the x/y components of the gradient by
gradient[0] += gradVecs1 * grad;
gradient[1] += gradVecs2 * grad;
}
}
}
// Find total floor field gradient for agent.
totGrad = magnitude(gradient[0], gradient[1]);
// If total floor field gradient = 0...
if (totGrad == 0) {
//...then randomly generate a gradient
totGrad = Math.atan2(people[chosen].vy, people[chosen].vx)
+ 2 * noise * Math.PI * (2 * twistor.nextDouble() - 1);
// Set totGrad to be in the interval [0, 2*pi].
if (totGrad > 2 * Math.PI) {
totGrad -= 2 * Math.PI;
}
if (totGrad < 0) {
totGrad += 2 * Math.PI;
}
// Calculate x/y components of the gradient vector
gradient[0] = Math.cos(totGrad);
gradient[1] = Math.sin(totGrad);
} else { // Otherwise, if the total floor field gradient is not zero...
//...normalise floor field gradient.
gradient[0] = gradient[0] / totGrad;
gradient[1] = gradient[1] / totGrad;
// Add some noise to the gradient detection to avoid individual agents getting
// stuck in doors.
gradNoise = Math.atan2(gradient[1], gradient[0]) + noise * Math.PI *
(2 * twistor.nextDouble() - 1);
// Set noise to be in the interval [0, 2*pi].
if (gradNoise > 2 * Math.PI) {
gradNoise -= 2 * Math.PI;
}
if (gradNoise < 0) {
gradNoise += 2 * Math.PI;
}
gradient[0] = Math.cos(gradNoise);
gradient[1] = Math.sin(gradNoise);
}
}
// This method updates the movement of all agents.
// Requires the following input:
// 1. Number indicating the current timestep.
public void updateMovement(int timestep){
//System.out.println("Moving all agents now.");
// Initialise navigation and repulsive social forces.
double fnavx = 0.0, fnavy = 0.0, frepx = 0.0;
double frepy = 0.0, frepwx = 0.0, frepwy = 0.0;
// Initialise array for destination occupancies.
destPopularity = new int [numDest];
// Update to next timestep.
time += deltaT;
// For every agent...
for (int i = 0; i < N; i++) {
//...between every agent and this agent, including itself...
for (int j = i; j < N; j++) {
dists[i][j] = size;
//...calculate distance between agents as Euclidean distance.
dists[i][j] = magnitude(people[i].x - people[j].x, people[i].y - people[j].y);
// Distance between j and i is the same as between i and j.
dists[j][i] = dists[i][j];
}
// The distance between an agent and itself is just its size.
dists[i][i] = size;
// body interaction force (helbing2000)
frepx = 0.0;
frepy = 0.0;
// Total force acting on agent.
//bodyForces[i] = 0.0;
/* Now, to save a bit of computation time, only calculate the social forces
between this agent and other agents within 2m of it. Threshold distance
chosen arbitrarily.*/
// Initialise ArrayList to store distances between this agent and all others.
ArrayList<Integer> close = new ArrayList<Integer>();
// For each other agent...
for (int u = 0; u < dists[i].length; u++) {
//...if the distance is less than 2m...
if (dists[i][u] <= 2) {
//...then add the agent to the list.
close.add(u);
}
}
// Convert mutable ArrayList to immutable array.
Integer[] closest = close.toArray(new Integer[0]);
// For each close agent to this agent...
for (int k = 0; k < closest.length; k++) {
//...calculate the total repulsive force due to other agents.
int ent = closest[k];
// Only consider other agents...
if (ent != i) {
/*
This would be where to go to fix ridiculously high
repulsive forces between agents.
*/
//...calculate repulsive force according to Helbing et al. 2000.
double srep = people[i].A
* Math.exp(((people[i].size + people[ent].size)
- dists[i][ent]) / people[i].B);
// Initialise the components of repulsive forces due to agents touching.
double repcomp = 0.0, reptang = 0.0;
// Normalised x- and y-distances between i and k.
double xr = (people[i].x - people[ent].x)
/ magnitude((people[i].x - people[ent].x),
(people[i].y - people[ent].y));
double yr = (people[i].y - people[ent].y)
/ magnitude((people[i].x - people[ent].x),
(people[i].y - people[ent].y));
// If pedestrians touch each other...
if (dists[i][ent] < (people[i].size + people[ent].size)) {
//...then there is also sliding friction.