model = gs or ps or gs/ps
@@ -386,6 +388,29 @@ surf_react adsorb command
+
Styles with a kk suffix are functionally the same as the
+corresponding style without the suffix. They have been optimized to
+run faster, depending on your available hardware, as discussed in the
+Accelerating SPARTA section of the manual.
+The accelerated styles take the same arguments and should produce the
+same results, except for different random number, round-off and
+precision issues.
+
+These accelerated styles are part of the KOKKOS package. They are only
+enabled if SPARTA was built with that package. See the Making
+SPARTA section for more info.
+
+You can specify the accelerated styles explicitly in your input script
+by including their suffix, or you can use the -suffix command-line
+switch when you invoke SPARTA, or you can
+use the suffix command in your input script.
+
+See the Accelerating SPARTA section of the
+manual for more instructions on how to use the accelerated styles
+effectively.
+
+
+
Restrictions:
If the following conditions are met:
diff --git a/doc/surf_react_adsorb.txt b/doc/surf_react_adsorb.txt
index a50578fe6..ee65e522d 100644
--- a/doc/surf_react_adsorb.txt
+++ b/doc/surf_react_adsorb.txt
@@ -7,13 +7,14 @@
:line
surf_react adsorb command :h3
+surf_react adsorb/kk command :h3
[Syntax:]
surf_react ID adsorb model infile(s) Nsync type temp n_sites adsp1 adsp2 ... :pre
ID = user-assigned name for the surface reaction model :ulb,l
-style = {adsorb} :l
+style = {adsorb} or {adsorb/kk} :l
model = {gs} or {ps} or {gs/ps} :l
gs = gas-surface reactions
ps = pure-surface reactions
@@ -368,6 +369,29 @@ reaction since the beginning of the current run.
:line
+Styles with a {kk} suffix are functionally the same as the
+corresponding style without the suffix. They have been optimized to
+run faster, depending on your available hardware, as discussed in the
+"Accelerating SPARTA"_Section_accelerate.html section of the manual.
+The accelerated styles take the same arguments and should produce the
+same results, except for different random number, round-off and
+precision issues.
+
+These accelerated styles are part of the KOKKOS package. They are only
+enabled if SPARTA was built with that package. See the "Making
+SPARTA"_Section_start.html#start_3 section for more info.
+
+You can specify the accelerated styles explicitly in your input script
+by including their suffix, or you can use the "-suffix command-line
+switch"_Section_start.html#start_7 when you invoke SPARTA, or you can
+use the "suffix"_suffix.html command in your input script.
+
+See the "Accelerating SPARTA"_Section_accelerate.html section of the
+manual for more instructions on how to use the accelerated styles
+effectively.
+
+:line
+
[Restrictions:]
If the following conditions are met:
diff --git a/examples/ambi/in.ambi.group b/examples/ambi/in.ambi.group
new file mode 100644
index 000000000..e69d6369f
--- /dev/null
+++ b/examples/ambi/in.ambi.group
@@ -0,0 +1,47 @@
+################################################################################
+# thermal plasma in a 2d box, non-reacting multigroup ambipolar collisions
+#
+# Exercises the ambipolar approximation with more than one collision group:
+# all heavy species (neutrals + ions) form one group and the ambipolar
+# electron species "e" is its own group, as required by collide ambipolar.
+#
+# Note:
+# - The "comm/sort" option to the "global" command is used to match MPI runs.
+# - The "twopass" option is used to match Kokkos runs.
+# The "comm/sort" and "twopass" options should not be used for production runs.
+################################################################################
+
+seed 12345
+dimension 2
+boundary rr rr p
+global gridcut 0.01 comm/sort yes
+create_box -2.0 2.0 -2.0 2.0 -0.5 0.5
+create_grid 20 20 1
+balance_grid rcb cell
+
+global fnum 2.6404E16
+global nrho 2.6404e20
+
+species air.species N2 O2 N O NO N2+ O2+ N+ O+ NO+ e
+
+# collide mixture: all species, two groups
+# the ambipolar electron species e must be in a group by itself
+
+mixture gas N2 O2 N O NO N2+ O2+ N+ O+ NO+ vstream 0 0 0 temp 5000.0 group heavy
+mixture gas e group electron
+mixture gas N2 frac 0.6
+mixture gas N2+ frac 0.4
+
+fix ambi ambipolar e N+ N2+ NO+ O+ O2+
+
+collide vss gas air.vss
+collide_modify ambipolar yes
+
+create_particles gas n 10000 twopass
+
+compute temp temp
+stats 50
+stats_style step np nattempt ncoll c_temp
+
+timestep 1.0e-8
+run 300
diff --git a/examples/ambi/log.24Sep25.mpi_1.ambi.group b/examples/ambi/log.24Sep25.mpi_1.ambi.group
new file mode 100644
index 000000000..e39e4d4b8
--- /dev/null
+++ b/examples/ambi/log.24Sep25.mpi_1.ambi.group
@@ -0,0 +1,121 @@
+SPARTA (24 Sep 2025)
+Running on 1 MPI task(s)
+################################################################################
+# thermal plasma in a 2d box, non-reacting multigroup ambipolar collisions
+#
+# Exercises the ambipolar approximation with more than one collision group:
+# all heavy species (neutrals + ions) form one group and the ambipolar
+# electron species "e" is its own group, as required by collide ambipolar.
+#
+# Note:
+# - The "comm/sort" option to the "global" command is used to match MPI runs.
+# - The "twopass" option is used to match Kokkos runs.
+# The "comm/sort" and "twopass" options should not be used for production runs.
+################################################################################
+
+seed 12345
+dimension 2
+boundary rr rr p
+global gridcut 0.01 comm/sort yes
+create_box -2.0 2.0 -2.0 2.0 -0.5 0.5
+Created orthogonal box = (-2 -2 -0.5) to (2 2 0.5)
+create_grid 20 20 1
+Created 400 child grid cells
+ CPU time = 0.00135691 secs
+ create/ghost percent = 90.3865 9.61347
+balance_grid rcb cell
+Balance grid migrated 0 cells
+ CPU time = 0.000476826 secs
+ reassign/sort/migrate/ghost percent = 82.2793 0.611334 6.06154 11.0478
+
+global fnum 2.6404E16
+global nrho 2.6404e20
+
+species air.species N2 O2 N O NO N2+ O2+ N+ O+ NO+ e
+
+# collide mixture: all species, two groups
+# the ambipolar electron species e must be in a group by itself
+
+mixture gas N2 O2 N O NO N2+ O2+ N+ O+ NO+ vstream 0 0 0 temp 5000.0 group heavy
+mixture gas e group electron
+mixture gas N2 frac 0.6
+mixture gas N2+ frac 0.4
+
+fix ambi ambipolar e N+ N2+ NO+ O+ O2+
+
+collide vss gas air.vss
+collide_modify ambipolar yes
+
+create_particles gas n 10000 twopass
+Created 10000 particles
+ CPU time = 0.00470918 secs
+
+compute temp temp
+stats 50
+stats_style step np nattempt ncoll c_temp
+
+timestep 1.0e-8
+run 300
+Memory usage per proc in Mbytes:
+ particles (ave,min,max) = 2 2 2
+ grid (ave,min,max) = 1.51379 1.51379 1.51379
+ surf (ave,min,max) = 0 0 0
+ total (ave,min,max) = 3.51379 3.51379 3.51379
+Step Np Natt Ncoll c_temp
+ 0 10000 0 0 4975.0406
+ 50 10000 77 34 4975.0911
+ 100 10000 108 38 4975.0667
+ 150 10000 134 57 4975.0591
+ 200 10000 37 16 4974.9255
+ 250 10000 70 28 4976.2863
+ 300 10000 50 18 4974.4373
+Loop time of 0.0799685 on 1 procs for 300 steps with 10000 particles
+Performance: 3751.478 timesteps/s, 37.515 Mparticle-step/s
+
+MPI task timing breakdown:
+Section | min time | avg time | max time |%varavg| %total
+---------------------------------------------------------------
+Move | 0.025585 | 0.025585 | 0.025585 | 0.0 | 31.99
+Coll | 0.042859 | 0.042859 | 0.042859 | 0.0 | 53.59
+Sort | 0.010504 | 0.010504 | 0.010504 | 0.0 | 13.13
+Comm | 8.2923e-05 | 8.2923e-05 | 8.2923e-05 | 0.0 | 0.10
+Modify | 0 | 0 | 0 | 0.0 | 0.00
+Output | 0.00084199 | 0.00084199 | 0.00084199 | 0.0 | 1.05
+Other | | 9.562e-05 | | | 0.12
+
+Particle moves = 3000000 (3M)
+Cells touched = 3000293 (3M)
+Particle comms = 0 (0K)
+Boundary collides = 20 (0.02K)
+Boundary exits = 0 (0K)
+SurfColl checks = 0 (0K)
+SurfColl occurs = 0 (0K)
+Surf reactions = 0 (0K)
+Collide attempts = 20534 (20.5K)
+Collide occurs = 8182 (8.18K)
+Reactions = 0 (0K)
+Particles stuck = 0
+Axisymm bad moves = 0
+
+Particle-moves/CPUsec/proc: 3.75148e+07
+Particle-moves/step: 10000
+Cell-touches/particle/step: 1.0001
+Particle comm iterations/step: 1
+Particle fraction communicated: 0
+Particle fraction colliding with boundary: 6.66667e-06
+Particle fraction exiting boundary: 0
+Surface-checks/particle/step: 0
+Surface-collisions/particle/step: 0
+Surf-reactions/particle/step: 0
+Collision-attempts/particle/step: 0.00684467
+Collisions/particle/step: 0.00272733
+Reactions/particle/step: 0
+
+Particles: 10000 ave 10000 max 10000 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+Cells: 400 ave 400 max 400 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+GhostCell: 0 ave 0 max 0 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+EmptyCell: 0 ave 0 max 0 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
diff --git a/examples/ambi/log.24Sep25.mpi_4.ambi.group b/examples/ambi/log.24Sep25.mpi_4.ambi.group
new file mode 100644
index 000000000..328c2c59d
--- /dev/null
+++ b/examples/ambi/log.24Sep25.mpi_4.ambi.group
@@ -0,0 +1,122 @@
+SPARTA (24 Sep 2025)
+Running on 4 MPI task(s)
+################################################################################
+# thermal plasma in a 2d box, non-reacting multigroup ambipolar collisions
+#
+# Exercises the ambipolar approximation with more than one collision group:
+# all heavy species (neutrals + ions) form one group and the ambipolar
+# electron species "e" is its own group, as required by collide ambipolar.
+#
+# Note:
+# - The "comm/sort" option to the "global" command is used to match MPI runs.
+# - The "twopass" option is used to match Kokkos runs.
+# The "comm/sort" and "twopass" options should not be used for production runs.
+################################################################################
+
+seed 12345
+dimension 2
+boundary rr rr p
+global gridcut 0.01 comm/sort yes
+create_box -2.0 2.0 -2.0 2.0 -0.5 0.5
+Created orthogonal box = (-2 -2 -0.5) to (2 2 0.5)
+create_grid 20 20 1
+WARNING: Could not acquire nearby ghost cells b/c grid partition is not clumped (/home/runner/work/sparta/sparta/src/grid.cpp:473)
+Created 400 child grid cells
+ CPU time = 0.00264936 secs
+ create/ghost percent = 94.9017 5.09833
+balance_grid rcb cell
+Balance grid migrated 280 cells
+ CPU time = 0.00113465 secs
+ reassign/sort/migrate/ghost percent = 46.3118 0.712375 19.4597 33.5161
+
+global fnum 2.6404E16
+global nrho 2.6404e20
+
+species air.species N2 O2 N O NO N2+ O2+ N+ O+ NO+ e
+
+# collide mixture: all species, two groups
+# the ambipolar electron species e must be in a group by itself
+
+mixture gas N2 O2 N O NO N2+ O2+ N+ O+ NO+ vstream 0 0 0 temp 5000.0 group heavy
+mixture gas e group electron
+mixture gas N2 frac 0.6
+mixture gas N2+ frac 0.4
+
+fix ambi ambipolar e N+ N2+ NO+ O+ O2+
+
+collide vss gas air.vss
+collide_modify ambipolar yes
+
+create_particles gas n 10000 twopass
+Created 10000 particles
+ CPU time = 0.00489833 secs
+
+compute temp temp
+stats 50
+stats_style step np nattempt ncoll c_temp
+
+timestep 1.0e-8
+run 300
+Memory usage per proc in Mbytes:
+ particles (ave,min,max) = 2 2 2
+ grid (ave,min,max) = 1.51379 1.51379 1.51379
+ surf (ave,min,max) = 0 0 0
+ total (ave,min,max) = 3.51379 3.51379 3.51379
+Step Np Natt Ncoll c_temp
+ 0 10000 0 0 4961.4899
+ 50 10000 62 18 4961.3574
+ 100 10000 96 42 4961.4647
+ 150 10000 129 56 4961.2506
+ 200 10000 40 21 4961.1044
+ 250 10000 64 24 4961.2342
+ 300 10000 44 17 4962.6572
+Loop time of 0.027287 on 4 procs for 300 steps with 10000 particles
+Performance: 10994.232 timesteps/s, 109.942 Mparticle-step/s
+
+MPI task timing breakdown:
+Section | min time | avg time | max time |%varavg| %total
+---------------------------------------------------------------
+Move | 0.0081502 | 0.0082497 | 0.0084222 | 0.1 | 30.23
+Coll | 0.012323 | 0.012456 | 0.012749 | 0.2 | 45.65
+Sort | 0.00287 | 0.0028725 | 0.0028778 | 0.0 | 10.53
+Comm | 0.0019475 | 0.0020446 | 0.0021515 | 0.2 | 7.49
+Modify | 0 | 0 | 0 | 0.0 | 0.00
+Output | 0.00013547 | 0.00016465 | 0.00023228 | 0.0 | 0.60
+Other | | 0.0015 | | | 5.50
+
+Particle moves = 3000000 (3M)
+Cells touched = 3000288 (3M)
+Particle comms = 15 (0.015K)
+Boundary collides = 20 (0.02K)
+Boundary exits = 0 (0K)
+SurfColl checks = 0 (0K)
+SurfColl occurs = 0 (0K)
+Surf reactions = 0 (0K)
+Collide attempts = 20225 (20.2K)
+Collide occurs = 8077 (8.08K)
+Reactions = 0 (0K)
+Particles stuck = 0
+Axisymm bad moves = 0
+
+Particle-moves/CPUsec/proc: 2.74856e+07
+Particle-moves/step: 10000
+Cell-touches/particle/step: 1.0001
+Particle comm iterations/step: 1
+Particle fraction communicated: 5e-06
+Particle fraction colliding with boundary: 6.66667e-06
+Particle fraction exiting boundary: 0
+Surface-checks/particle/step: 0
+Surface-collisions/particle/step: 0
+Surf-reactions/particle/step: 0
+Collision-attempts/particle/step: 0.00674167
+Collisions/particle/step: 0.00269233
+Reactions/particle/step: 0
+
+Particles: 2500 ave 2501 max 2499 min
+Histogram: 1 0 0 0 0 2 0 0 0 1
+Cells: 100 ave 100 max 100 min
+Histogram: 4 0 0 0 0 0 0 0 0 0
+GhostCell: 21 ave 21 max 21 min
+Histogram: 4 0 0 0 0 0 0 0 0 0
+EmptyCell: 0 ave 0 max 0 min
+Histogram: 4 0 0 0 0 0 0 0 0 0
diff --git a/examples/chem/in.chem.gastally b/examples/chem/in.chem.gastally
new file mode 100644
index 000000000..dfd522925
--- /dev/null
+++ b/examples/chem/in.chem.gastally
@@ -0,0 +1,51 @@
+################################################################################
+# thermal gas in a 3d box with collisions and reactions
+# tally per-grid-cell gas collisions and reactions
+#
+# Demonstrates/verifies compute gas/collision/grid and compute gas/reaction/grid
+# (the latter in all/every/select modes).
+#
+# Note:
+# - The "comm/sort" option to the "global" command is used to match MPI runs.
+# - The "twopass" option is used to match Kokkos runs.
+# The "comm/sort" and "twopass" options should not be used for production runs.
+################################################################################
+
+seed 12345
+dimension 3
+global gridcut 1.0e-5 comm/sort yes
+boundary rr rr rr
+create_box 0 0.0001 0 0.0001 0 0.0001
+create_grid 10 10 10
+balance_grid rcb part
+
+species air.species N2 N
+mixture air N2 N vstream 0.0 0.0 0.0 temp 20000.0
+mixture air N2 frac 1.0
+mixture air N frac 0.0
+
+global nrho 7.07043E22
+global fnum 7.07043E5
+
+collide vss air air.vss
+react tce air.tce
+
+create_particles air n 10000 twopass
+
+# per-grid-cell tallies of gas collisions and reactions
+
+compute cc gas/collision/grid all all
+compute cr gas/reaction/grid all all all
+compute cre gas/reaction/grid all all every
+
+# sums over all cells: c_sumcc == ncoll-nreact and c_sumcr == nreact each step
+
+compute sumcc reduce sum c_cc
+compute sumcr reduce sum c_cr
+
+stats 100
+compute temp temp
+stats_style step np nattempt ncoll nreact c_temp c_sumcc c_sumcr
+
+timestep 7.00E-9
+run 500
diff --git a/examples/chem/log.24Sep25.mpi_1.chem.gastally b/examples/chem/log.24Sep25.mpi_1.chem.gastally
new file mode 100644
index 000000000..182561784
--- /dev/null
+++ b/examples/chem/log.24Sep25.mpi_1.chem.gastally
@@ -0,0 +1,129 @@
+SPARTA (24 Sep 2025)
+Running on 1 MPI task(s)
+################################################################################
+# thermal gas in a 3d box with collisions and reactions
+# tally per-grid-cell gas collisions and reactions
+#
+# Demonstrates/verifies compute gas/collision/grid and compute gas/reaction/grid
+# (the latter in all/every/select modes).
+#
+# Note:
+# - The "comm/sort" option to the "global" command is used to match MPI runs.
+# - The "twopass" option is used to match Kokkos runs.
+# The "comm/sort" and "twopass" options should not be used for production runs.
+################################################################################
+
+seed 12345
+dimension 3
+global gridcut 1.0e-5 comm/sort yes
+boundary rr rr rr
+create_box 0 0.0001 0 0.0001 0 0.0001
+Created orthogonal box = (0 0 0) to (0.0001 0.0001 0.0001)
+create_grid 10 10 10
+Created 1000 child grid cells
+ CPU time = 0.00157439 secs
+ create/ghost percent = 84.3119 15.6881
+balance_grid rcb part
+Balance grid migrated 0 cells
+ CPU time = 0.000282464 secs
+ reassign/sort/migrate/ghost percent = 57.7153 0.322519 11.4595 30.5026
+
+species air.species N2 N
+mixture air N2 N vstream 0.0 0.0 0.0 temp 20000.0
+mixture air N2 frac 1.0
+mixture air N frac 0.0
+
+global nrho 7.07043E22
+global fnum 7.07043E5
+
+collide vss air air.vss
+react tce air.tce
+
+create_particles air n 10000 twopass
+Created 10000 particles
+ CPU time = 0.00309303 secs
+
+# per-grid-cell tallies of gas collisions and reactions
+
+compute cc gas/collision/grid all all
+compute cr gas/reaction/grid all all all
+compute cre gas/reaction/grid all all every
+
+# sums over all cells: c_sumcc == ncoll-nreact and c_sumcr == nreact each step
+
+compute sumcc reduce sum c_cc
+compute sumcr reduce sum c_cr
+
+stats 100
+compute temp temp
+stats_style step np nattempt ncoll nreact c_temp c_sumcc c_sumcr
+
+timestep 7.00E-9
+run 500
+Memory usage per proc in Mbytes:
+ particles (ave,min,max) = 1.5625 1.5625 1.5625
+ grid (ave,min,max) = 1.51379 1.51379 1.51379
+ surf (ave,min,max) = 0 0 0
+ total (ave,min,max) = 3.43488 3.43488 3.43488
+Step Np Natt Ncoll Nreact c_temp c_sumcc c_sumcr
+ 0 10000 0 0 0 19907.187 0 0
+ 100 10190 1000 262 3 18764.989 259 3
+ 200 10335 1011 269 2 18038.695 267 2
+ 300 10484 1079 255 4 17426.475 251 4
+ 400 10627 1070 238 1 16707.966 237 1
+ 500 10760 1124 289 3 16036.998 286 3
+Loop time of 0.917186 on 1 procs for 500 steps with 10760 particles
+Performance: 545.145 timesteps/s, 5.866 Mparticle-step/s
+
+MPI task timing breakdown:
+Section | min time | avg time | max time |%varavg| %total
+---------------------------------------------------------------
+Move | 0.78531 | 0.78531 | 0.78531 | 0.0 | 85.62
+Coll | 0.11393 | 0.11393 | 0.11393 | 0.0 | 12.42
+Sort | 0.016552 | 0.016552 | 0.016552 | 0.0 | 1.80
+Comm | 0.00035723 | 0.00035723 | 0.00035723 | 0.0 | 0.04
+Modify | 0 | 0 | 0 | 0.0 | 0.00
+Output | 0.00053396 | 0.00053396 | 0.00053396 | 0.0 | 0.06
+Other | | 0.0005075 | | | 0.06
+
+Particle moves = 5201491 (5.2M)
+Cells touched = 23735594 (23.7M)
+Particle comms = 0 (0K)
+Boundary collides = 2059037 (2.06M)
+Boundary exits = 0 (0K)
+SurfColl checks = 0 (0K)
+SurfColl occurs = 0 (0K)
+Surf reactions = 0 (0K)
+Collide attempts = 512014 (0.512M)
+Collide occurs = 128199 (0.128M)
+Reactions = 760 (0.76K)
+Particles stuck = 0
+Axisymm bad moves = 0
+
+Particle-moves/CPUsec/proc: 5.67114e+06
+Particle-moves/step: 10403
+Cell-touches/particle/step: 4.56323
+Particle comm iterations/step: 1
+Particle fraction communicated: 0
+Particle fraction colliding with boundary: 0.395855
+Particle fraction exiting boundary: 0
+Surface-checks/particle/step: 0
+Surface-collisions/particle/step: 0
+Surf-reactions/particle/step: 0
+Collision-attempts/particle/step: 0.098436
+Collisions/particle/step: 0.0246466
+Reactions/particle/step: 0.000146112
+
+Gas reaction tallies:
+ style tce #-of-reactions 45
+ reaction N2 + N2 --> N + N + N2: 578
+ reaction N2 + N --> N + N + N: 182
+
+Particles: 10760 ave 10760 max 10760 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+Cells: 1000 ave 1000 max 1000 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+GhostCell: 0 ave 0 max 0 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+EmptyCell: 0 ave 0 max 0 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
diff --git a/examples/chem/log.24Sep25.mpi_4.chem.gastally b/examples/chem/log.24Sep25.mpi_4.chem.gastally
new file mode 100644
index 000000000..3d2175281
--- /dev/null
+++ b/examples/chem/log.24Sep25.mpi_4.chem.gastally
@@ -0,0 +1,130 @@
+SPARTA (24 Sep 2025)
+Running on 4 MPI task(s)
+################################################################################
+# thermal gas in a 3d box with collisions and reactions
+# tally per-grid-cell gas collisions and reactions
+#
+# Demonstrates/verifies compute gas/collision/grid and compute gas/reaction/grid
+# (the latter in all/every/select modes).
+#
+# Note:
+# - The "comm/sort" option to the "global" command is used to match MPI runs.
+# - The "twopass" option is used to match Kokkos runs.
+# The "comm/sort" and "twopass" options should not be used for production runs.
+################################################################################
+
+seed 12345
+dimension 3
+global gridcut 1.0e-5 comm/sort yes
+boundary rr rr rr
+create_box 0 0.0001 0 0.0001 0 0.0001
+Created orthogonal box = (0 0 0) to (0.0001 0.0001 0.0001)
+create_grid 10 10 10
+WARNING: Could not acquire nearby ghost cells b/c grid partition is not clumped (/home/runner/work/sparta/sparta/src/grid.cpp:473)
+Created 1000 child grid cells
+ CPU time = 0.00205981 secs
+ create/ghost percent = 94.305 5.69498
+balance_grid rcb part
+Balance grid migrated 740 cells
+ CPU time = 0.00114597 secs
+ reassign/sort/migrate/ghost percent = 49.0399 0.678202 20.0193 30.2626
+
+species air.species N2 N
+mixture air N2 N vstream 0.0 0.0 0.0 temp 20000.0
+mixture air N2 frac 1.0
+mixture air N frac 0.0
+
+global nrho 7.07043E22
+global fnum 7.07043E5
+
+collide vss air air.vss
+react tce air.tce
+
+create_particles air n 10000 twopass
+Created 10000 particles
+ CPU time = 0.00177956 secs
+
+# per-grid-cell tallies of gas collisions and reactions
+
+compute cc gas/collision/grid all all
+compute cr gas/reaction/grid all all all
+compute cre gas/reaction/grid all all every
+
+# sums over all cells: c_sumcc == ncoll-nreact and c_sumcr == nreact each step
+
+compute sumcc reduce sum c_cc
+compute sumcr reduce sum c_cr
+
+stats 100
+compute temp temp
+stats_style step np nattempt ncoll nreact c_temp c_sumcc c_sumcr
+
+timestep 7.00E-9
+run 500
+Memory usage per proc in Mbytes:
+ particles (ave,min,max) = 1.5625 1.5625 1.5625
+ grid (ave,min,max) = 1.51379 1.51379 1.51379
+ surf (ave,min,max) = 0 0 0
+ total (ave,min,max) = 3.16594 3.16594 3.16594
+Step Np Natt Ncoll Nreact c_temp c_sumcc c_sumcr
+ 0 10000 0 0 0 19847.392 0 0
+ 100 10187 998 269 0 18979.069 269 0
+ 200 10362 1020 239 2 18220.89 237 2
+ 300 10529 1067 259 2 17415.447 257 2
+ 400 10658 1079 260 0 16700.368 260 0
+ 500 10778 1097 246 1 15978.454 245 1
+Loop time of 0.292439 on 4 procs for 500 steps with 10778 particles
+Performance: 1709.759 timesteps/s, 18.428 Mparticle-step/s
+
+MPI task timing breakdown:
+Section | min time | avg time | max time |%varavg| %total
+---------------------------------------------------------------
+Move | 0.20483 | 0.20772 | 0.21009 | 0.4 | 71.03
+Coll | 0.026371 | 0.02658 | 0.02671 | 0.1 | 9.09
+Sort | 0.0047512 | 0.004797 | 0.0048304 | 0.0 | 1.64
+Comm | 0.040441 | 0.041421 | 0.041872 | 0.3 | 14.16
+Modify | 0 | 0 | 0 | 0.0 | 0.00
+Output | 0.00024169 | 0.00036943 | 0.00074573 | 0.0 | 0.13
+Other | | 0.01155 | | | 3.95
+
+Particle moves = 5212700 (5.21M)
+Cells touched = 24150571 (24.2M)
+Particle comms = 1312904 (1.31M)
+Boundary collides = 2072594 (2.07M)
+Boundary exits = 0 (0K)
+SurfColl checks = 0 (0K)
+SurfColl occurs = 0 (0K)
+Surf reactions = 0 (0K)
+Collide attempts = 514360 (0.514M)
+Collide occurs = 129535 (0.13M)
+Reactions = 778 (0.778K)
+Particles stuck = 0
+Axisymm bad moves = 0
+
+Particle-moves/CPUsec/proc: 4.45623e+06
+Particle-moves/step: 10425.4
+Cell-touches/particle/step: 4.63303
+Particle comm iterations/step: 2.994
+Particle fraction communicated: 0.251866
+Particle fraction colliding with boundary: 0.397605
+Particle fraction exiting boundary: 0
+Surface-checks/particle/step: 0
+Surface-collisions/particle/step: 0
+Surf-reactions/particle/step: 0
+Collision-attempts/particle/step: 0.0986744
+Collisions/particle/step: 0.0248499
+Reactions/particle/step: 0.000149251
+
+Gas reaction tallies:
+ style tce #-of-reactions 45
+ reaction N2 + N2 --> N + N + N2: 577
+ reaction N2 + N --> N + N + N: 201
+
+Particles: 2694.5 ave 2767 max 2647 min
+Histogram: 1 1 0 0 1 0 0 0 0 1
+Cells: 250 ave 250 max 250 min
+Histogram: 4 0 0 0 0 0 0 0 0 0
+GhostCell: 172.5 ave 240 max 110 min
+Histogram: 1 0 0 0 2 0 0 0 0 1
+EmptyCell: 62.5 ave 130 max 0 min
+Histogram: 1 0 0 0 2 0 0 0 0 1
diff --git a/examples/collide/in.collide.group b/examples/collide/in.collide.group
new file mode 100644
index 000000000..c6c492177
--- /dev/null
+++ b/examples/collide/in.collide.group
@@ -0,0 +1,47 @@
+################################################################################
+# thermal gas in a 3d box with collisions, multiple collision groups
+# particles reflect off global box boundaries
+#
+# Demonstrates/verifies non-reacting multigroup (ngroup > 1) collisions.
+# The species are split into two collision groups: "heavy" and "light".
+#
+# Note:
+# - The "comm/sort" option to the "global" command is used to match MPI runs.
+# - The "twopass" option is used to match Kokkos runs.
+# The "comm/sort" and "twopass" options should not be used for production runs.
+################################################################################
+
+seed 12345
+dimension 3
+global gridcut 1.0e-5 comm/sort yes
+
+boundary rr rr rr
+
+create_box 0 0.0001 0 0.0001 0 0.0001
+create_grid 10 10 10
+
+balance_grid rcb part
+
+species 6SpeciesAir.species N2 O2 NO N O Ar
+
+mixture air O2 N2 O N vstream 0.0 0.0 0.0 temp 273.1
+mixture air O2 frac 0.21 group heavy
+mixture air N2 frac 0.78 group heavy
+mixture air NO group heavy
+mixture air Ar frac 0.009 group heavy
+mixture air O group light
+mixture air N group light
+
+global nrho 7.07043E22
+global fnum 7.07043E6
+
+collide vss air 6SpeciesAirII.vss
+
+create_particles air n 10000 twopass
+
+stats 100
+compute temp temp
+stats_style step cpu np nattempt ncoll c_temp
+
+timestep 7.00E-9
+run 1000
diff --git a/examples/collide/log.24Sep25.mpi_1.collide.group b/examples/collide/log.24Sep25.mpi_1.collide.group
new file mode 100644
index 000000000..05905d5a1
--- /dev/null
+++ b/examples/collide/log.24Sep25.mpi_1.collide.group
@@ -0,0 +1,125 @@
+SPARTA (24 Sep 2025)
+Running on 1 MPI task(s)
+################################################################################
+# thermal gas in a 3d box with collisions, multiple collision groups
+# particles reflect off global box boundaries
+#
+# Demonstrates/verifies non-reacting multigroup (ngroup > 1) collisions.
+# The species are split into two collision groups: "heavy" and "light".
+#
+# Note:
+# - The "comm/sort" option to the "global" command is used to match MPI runs.
+# - The "twopass" option is used to match Kokkos runs.
+# The "comm/sort" and "twopass" options should not be used for production runs.
+################################################################################
+
+seed 12345
+dimension 3
+global gridcut 1.0e-5 comm/sort yes
+
+boundary rr rr rr
+
+create_box 0 0.0001 0 0.0001 0 0.0001
+Created orthogonal box = (0 0 0) to (0.0001 0.0001 0.0001)
+create_grid 10 10 10
+Created 1000 child grid cells
+ CPU time = 0.00142212 secs
+ create/ghost percent = 83.9976 16.0024
+
+balance_grid rcb part
+Balance grid migrated 0 cells
+ CPU time = 0.000282915 secs
+ reassign/sort/migrate/ghost percent = 58.3313 0.385982 11.6957 29.587
+
+species 6SpeciesAir.species N2 O2 NO N O Ar
+
+mixture air O2 N2 O N vstream 0.0 0.0 0.0 temp 273.1
+mixture air O2 frac 0.21 group heavy
+mixture air N2 frac 0.78 group heavy
+mixture air NO group heavy
+mixture air Ar frac 0.009 group heavy
+mixture air O group light
+mixture air N group light
+
+global nrho 7.07043E22
+global fnum 7.07043E6
+
+collide vss air 6SpeciesAirII.vss
+
+create_particles air n 10000 twopass
+Created 10000 particles
+ CPU time = 0.00303553 secs
+
+stats 100
+compute temp temp
+stats_style step cpu np nattempt ncoll c_temp
+
+timestep 7.00E-9
+run 1000
+Memory usage per proc in Mbytes:
+ particles (ave,min,max) = 1.5625 1.5625 1.5625
+ grid (ave,min,max) = 1.51379 1.51379 1.51379
+ surf (ave,min,max) = 0 0 0
+ total (ave,min,max) = 3.07629 3.07629 3.07629
+Step CPU Np Natt Ncoll c_temp
+ 0 0 10000 0 0 277.59158
+ 100 0.074204402 10000 1314 705 273.84816
+ 200 0.15109194 10000 1368 732 276.91389
+ 300 0.2302166 10000 1387 712 277.42376
+ 400 0.3073353 10000 1441 732 276.07365
+ 500 0.3850255 10000 1465 751 275.21438
+ 600 0.46709235 10000 1410 717 275.57577
+ 700 0.54452659 10000 1456 698 275.57243
+ 800 0.62213841 10000 1456 677 275.21249
+ 900 0.70012387 10000 1486 756 277.62023
+ 1000 0.77825893 10000 1464 692 277.53426
+Loop time of 0.778362 on 1 procs for 1000 steps with 10000 particles
+Performance: 1284.750 timesteps/s, 12.847 Mparticle-step/s
+
+MPI task timing breakdown:
+Section | min time | avg time | max time |%varavg| %total
+---------------------------------------------------------------
+Move | 0.28888 | 0.28888 | 0.28888 | 0.0 | 37.11
+Coll | 0.4618 | 0.4618 | 0.4618 | 0.0 | 59.33
+Sort | 0.025941 | 0.025941 | 0.025941 | 0.0 | 3.33
+Comm | 0.00036157 | 0.00036157 | 0.00036157 | 0.0 | 0.05
+Modify | 0 | 0 | 0 | 0.0 | 0.00
+Output | 0.00094166 | 0.00094166 | 0.00094166 | 0.0 | 0.12
+Other | | 0.0004362 | | | 0.06
+
+Particle moves = 10000000 (10M)
+Cells touched = 14254925 (14.3M)
+Particle comms = 0 (0K)
+Boundary collides = 472768 (0.473M)
+Boundary exits = 0 (0K)
+SurfColl checks = 0 (0K)
+SurfColl occurs = 0 (0K)
+Surf reactions = 0 (0K)
+Collide attempts = 1408068 (1.41M)
+Collide occurs = 718153 (0.718M)
+Reactions = 0 (0K)
+Particles stuck = 0
+Axisymm bad moves = 0
+
+Particle-moves/CPUsec/proc: 1.28475e+07
+Particle-moves/step: 10000
+Cell-touches/particle/step: 1.42549
+Particle comm iterations/step: 1
+Particle fraction communicated: 0
+Particle fraction colliding with boundary: 0.0472768
+Particle fraction exiting boundary: 0
+Surface-checks/particle/step: 0
+Surface-collisions/particle/step: 0
+Surf-reactions/particle/step: 0
+Collision-attempts/particle/step: 0.140807
+Collisions/particle/step: 0.0718153
+Reactions/particle/step: 0
+
+Particles: 10000 ave 10000 max 10000 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+Cells: 1000 ave 1000 max 1000 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+GhostCell: 0 ave 0 max 0 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
+EmptyCell: 0 ave 0 max 0 min
+Histogram: 1 0 0 0 0 0 0 0 0 0
diff --git a/examples/collide/log.24Sep25.mpi_4.collide.group b/examples/collide/log.24Sep25.mpi_4.collide.group
new file mode 100644
index 000000000..0fee37ae8
--- /dev/null
+++ b/examples/collide/log.24Sep25.mpi_4.collide.group
@@ -0,0 +1,126 @@
+SPARTA (24 Sep 2025)
+Running on 4 MPI task(s)
+################################################################################
+# thermal gas in a 3d box with collisions, multiple collision groups
+# particles reflect off global box boundaries
+#
+# Demonstrates/verifies non-reacting multigroup (ngroup > 1) collisions.
+# The species are split into two collision groups: "heavy" and "light".
+#
+# Note:
+# - The "comm/sort" option to the "global" command is used to match MPI runs.
+# - The "twopass" option is used to match Kokkos runs.
+# The "comm/sort" and "twopass" options should not be used for production runs.
+################################################################################
+
+seed 12345
+dimension 3
+global gridcut 1.0e-5 comm/sort yes
+
+boundary rr rr rr
+
+create_box 0 0.0001 0 0.0001 0 0.0001
+Created orthogonal box = (0 0 0) to (0.0001 0.0001 0.0001)
+create_grid 10 10 10
+WARNING: Could not acquire nearby ghost cells b/c grid partition is not clumped (/home/runner/work/sparta/sparta/src/grid.cpp:473)
+Created 1000 child grid cells
+ CPU time = 0.00256409 secs
+ create/ghost percent = 96.1988 3.80119
+
+balance_grid rcb part
+Balance grid migrated 740 cells
+ CPU time = 0.0015163 secs
+ reassign/sort/migrate/ghost percent = 45.2081 0.572645 18.8967 35.3225
+
+species 6SpeciesAir.species N2 O2 NO N O Ar
+
+mixture air O2 N2 O N vstream 0.0 0.0 0.0 temp 273.1
+mixture air O2 frac 0.21 group heavy
+mixture air N2 frac 0.78 group heavy
+mixture air NO group heavy
+mixture air Ar frac 0.009 group heavy
+mixture air O group light
+mixture air N group light
+
+global nrho 7.07043E22
+global fnum 7.07043E6
+
+collide vss air 6SpeciesAirII.vss
+
+create_particles air n 10000 twopass
+Created 10000 particles
+ CPU time = 0.00346542 secs
+
+stats 100
+compute temp temp
+stats_style step cpu np nattempt ncoll c_temp
+
+timestep 7.00E-9
+run 1000
+Memory usage per proc in Mbytes:
+ particles (ave,min,max) = 1.5625 1.5625 1.5625
+ grid (ave,min,max) = 1.51379 1.51379 1.51379
+ surf (ave,min,max) = 0 0 0
+ total (ave,min,max) = 3.07629 3.07629 3.07629
+Step CPU Np Natt Ncoll c_temp
+ 0 0 10000 0 0 273.34821
+ 100 0.028047249 10000 1329 716 276.86384
+ 200 0.050504899 10000 1338 677 274.71485
+ 300 0.073207879 10000 1394 712 274.004
+ 400 0.095735905 10000 1424 732 273.64174
+ 500 0.1181215 10000 1408 733 272.42923
+ 600 0.14066922 10000 1464 734 273.94597
+ 700 0.16327536 10000 1457 692 276.09022
+ 800 0.1860353 10000 1502 737 274.82414
+ 900 0.20903339 10000 1474 707 274.63488
+ 1000 0.23218167 10000 1486 697 275.00172
+Loop time of 0.23223 on 4 procs for 1000 steps with 10000 particles
+Performance: 4306.081 timesteps/s, 43.061 Mparticle-step/s
+
+MPI task timing breakdown:
+Section | min time | avg time | max time |%varavg| %total
+---------------------------------------------------------------
+Move | 0.076994 | 0.077297 | 0.077817 | 0.1 | 33.28
+Coll | 0.10799 | 0.10932 | 0.11021 | 0.3 | 47.07
+Sort | 0.0082983 | 0.0083786 | 0.0084676 | 0.1 | 3.61
+Comm | 0.020273 | 0.02226 | 0.027417 | 2.0 | 9.59
+Modify | 0 | 0 | 0 | 0.0 | 0.00
+Output | 0.00034317 | 0.00047312 | 0.00079935 | 0.0 | 0.20
+Other | | 0.0145 | | | 6.24
+
+Particle moves = 10000000 (10M)
+Cells touched = 14241042 (14.2M)
+Particle comms = 311728 (0.312M)
+Boundary collides = 471432 (0.471M)
+Boundary exits = 0 (0K)
+SurfColl checks = 0 (0K)
+SurfColl occurs = 0 (0K)
+Surf reactions = 0 (0K)
+Collide attempts = 1403898 (1.4M)
+Collide occurs = 715997 (0.716M)
+Reactions = 0 (0K)
+Particles stuck = 0
+Axisymm bad moves = 0
+
+Particle-moves/CPUsec/proc: 1.07652e+07
+Particle-moves/step: 10000
+Cell-touches/particle/step: 1.4241
+Particle comm iterations/step: 1
+Particle fraction communicated: 0.0311728
+Particle fraction colliding with boundary: 0.0471432
+Particle fraction exiting boundary: 0
+Surface-checks/particle/step: 0
+Surface-collisions/particle/step: 0
+Surf-reactions/particle/step: 0
+Collision-attempts/particle/step: 0.14039
+Collisions/particle/step: 0.0715997
+Reactions/particle/step: 0
+
+Particles: 2500 ave 2586 max 2332 min
+Histogram: 1 0 0 0 0 0 0 1 0 2
+Cells: 250 ave 250 max 250 min
+Histogram: 4 0 0 0 0 0 0 0 0 0
+GhostCell: 172.5 ave 240 max 110 min
+Histogram: 1 0 0 0 2 0 0 0 0 1
+EmptyCell: 62.5 ave 130 max 0 min
+Histogram: 1 0 0 0 2 0 0 0 0 1
diff --git a/src/KOKKOS/Install.sh b/src/KOKKOS/Install.sh
index 58fc4ce43..51e33f826 100644
--- a/src/KOKKOS/Install.sh
+++ b/src/KOKKOS/Install.sh
@@ -117,12 +117,20 @@ action react_bird_kokkos.cpp
action react_bird_kokkos.h
action react_tce_kokkos.cpp
action react_tce_kokkos.h
+action surf_collide_adiabatic_kokkos.cpp
+action surf_collide_adiabatic_kokkos.h
+action surf_collide_cll_kokkos.cpp
+action surf_collide_cll_kokkos.h
action surf_collide_diffuse_kokkos.cpp
action surf_collide_diffuse_kokkos.h
+action surf_collide_impulsive_kokkos.cpp
+action surf_collide_impulsive_kokkos.h
action surf_collide_piston_kokkos.cpp
action surf_collide_piston_kokkos.h
action surf_collide_specular_kokkos.cpp
action surf_collide_specular_kokkos.h
+action surf_collide_td_kokkos.cpp
+action surf_collide_td_kokkos.h
action surf_collide_transparent_kokkos.cpp
action surf_collide_transparent_kokkos.h
action surf_collide_vanish_kokkos.cpp
diff --git a/src/KOKKOS/collide_vss_kokkos.cpp b/src/KOKKOS/collide_vss_kokkos.cpp
index aa23b63e3..b2753f658 100644
--- a/src/KOKKOS/collide_vss_kokkos.cpp
+++ b/src/KOKKOS/collide_vss_kokkos.cpp
@@ -37,6 +37,14 @@
using namespace SPARTA_NS;
using namespace MathConst;
+#define VAL_1(X) X
+#define VAL_2(X) VAL_1(X), VAL_1(X)
+#define VAL_4(X) VAL_2(X), VAL_2(X)
+
+// the glist KKCopy arrays below are brace-initialized with VAL_4 (4 elements)
+static_assert(KOKKOS_MAX_GLIST == 4,
+ "VAL_4 initializer lists assume KOKKOS_MAX_GLIST == 4");
+
enum{NONE,DISCRETE,SMOOTH}; // several files
enum{CONSTANT,VARIABLE};
@@ -44,6 +52,7 @@ enum{CONSTANT,VARIABLE};
#define DELTADELETE 1024
#define DELTAELECTRON 128
#define DELTACELLCOUNT 2
+#define MAXGROUP 16 // max # of collision groups for Kokkos group collisions
#define MAXLINE 1024
#define EPSZERO 1.0e-14
@@ -59,9 +68,18 @@ CollideVSSKokkos::CollideVSSKokkos(SPARTA *sparta, int narg, char **arg) :
#endif
),
grid_kk_copy(sparta),
- react_kk_copy(sparta)
+ react_kk_copy(sparta),
+ react_qk_kk_copy(sparta),
+ react_tceqk_kk_copy(sparta),
+ glist_collision_copy{VAL_4(KKCopy(sparta))},
+ glist_reaction_copy{VAL_4(KKCopy(sparta))},
+ tmp_compute_gas_collision_kk(sparta),
+ tmp_compute_gas_reaction_kk(sparta)
{
kokkos_flag = 1;
+ react_style = 0;
+ nglist_collision = nglist_reaction = 0;
+ egroup = -1;
// use 1D view for scalars to reduce GPU memory operations
@@ -106,6 +124,13 @@ CollideVSSKokkos::~CollideVSSKokkos()
grid_kk_copy.uncopy();
react_kk_copy.uncopy();
+ react_qk_kk_copy.uncopy();
+ react_tceqk_kk_copy.uncopy();
+
+ for (int i = 0; i < KOKKOS_MAX_GLIST; i++) {
+ glist_collision_copy[i].uncopy();
+ glist_reaction_copy[i].uncopy();
+ }
memoryKK->destroy_kokkos(k_dellist,dellist);
@@ -233,6 +258,17 @@ void CollideVSSKokkos::init()
d_vremax_initial = k_vremax_initial.view_device();
}
+ // device copy of species-to-group mapping for group collisions
+
+ if (ngroups > 1) {
+ int nspecies = particle->nspecies;
+ int *species2group = mixture->species2group;
+ d_species2group = DAT::t_int_1d("collide:species2group",nspecies);
+ auto h_species2group = Kokkos::create_mirror_view(d_species2group);
+ for (int i = 0; i < nspecies; i++) h_species2group(i) = species2group[i];
+ Kokkos::deep_copy(d_species2group,h_species2group);
+ }
+
// if recombination reactions exist, set flags per species pair
recombflag = 0;
@@ -276,7 +312,7 @@ void CollideVSSKokkos::init()
if (ambiflag && mixture->ngroup > 1) {
int *species2group = mixture->species2group;
- int egroup = species2group[ambispecies];
+ egroup = species2group[ambispecies];
if (mixture->groupsize[egroup] != 1)
error->all(FLERR,"Multigroup ambipolar collisions require "
"electrons be their own group");
@@ -401,36 +437,65 @@ void CollideVSSKokkos::collisions()
// variant for ngas_tally active or not
// variant for single group or multiple groups
- if (ngas_tally)
- error->all(FLERR,"Kokkos does not (yet) support tallying gas/gas collisions or reactions");
+ // partition active gas/gas tally computes by type into typed KKCopy lists
+ // each must be a supported Kokkos per-grid compute; call pre_gas_tally()
+ // the per-event gas/collision/tally and gas/reaction/tally are not supported
- if (ngroups != 1)
- error->all(FLERR,"Group collisions not yet supported with Kokkos");
+ if (ngas_tally) setup_gas_tally();
COLLIDE_REDUCE reduce;
- if (!ambiflag) {
- if (!nearcp) {
+ if (ngroups == 1) {
+ if (!ambiflag) {
+ if (!nearcp) {
+ if (!ngas_tally) {
+ collisions_one<0,0>(reduce);
+ } else if (ngas_tally) {
+ collisions_one<0,1>(reduce);
+ }
+ } else if (nearcp) {
+ if (!ngas_tally) {
+ collisions_one<1,0>(reduce);
+ } else if (ngas_tally) {
+ collisions_one<1,1>(reduce);
+ }
+ }
+ } else if (ambiflag) {
if (!ngas_tally) {
- collisions_one<0,0>(reduce);
+ collisions_one_ambipolar<0>(reduce);
} else if (ngas_tally) {
- collisions_one<0,1>(reduce);
+ collisions_one_ambipolar<1>(reduce);
}
- } else if (nearcp) {
+ }
+
+ // multiple groups
+ // Kokkos currently supports only non-reacting, non-near-neighbor
+ // group collisions (with or without the ambipolar approximation)
+
+ } else {
+ if (react)
+ error->all(FLERR,"Kokkos does not (yet) support reacting group collisions");
+ if (nearcp)
+ error->all(FLERR,"Kokkos does not (yet) support near-neighbor group collisions");
+ if (!ambiflag) {
if (!ngas_tally) {
- collisions_one<1,0>(reduce);
+ collisions_group<0,0>(reduce);
} else if (ngas_tally) {
- collisions_one<1,1>(reduce);
+ collisions_group<0,1>(reduce);
+ }
+ } else if (ambiflag) {
+ if (!ngas_tally) {
+ collisions_group_ambipolar<0>(reduce);
+ } else if (ngas_tally) {
+ collisions_group_ambipolar<1>(reduce);
}
- }
- } else if (ambiflag) {
- if (!ngas_tally) {
- collisions_one_ambipolar<0>(reduce);
- } else if (!ngas_tally) {
- collisions_one_ambipolar<1>(reduce);
}
}
+ // finalize active gas/gas tally computes: contribute and sync to host
+
+ if (ngas_tally) finish_gas_tally();
+
// remove any particles deleted in chemistry reactions
// if particles deleted/created by chemistry, particles are no longer sorted
@@ -467,6 +532,69 @@ void CollideVSSKokkos::collisions()
nreact_running += nreact_one;
}
+/* ----------------------------------------------------------------------
+ partition the active gas/gas tally computes (update->glist_active) into
+ typed KKCopy lists and call pre_gas_tally() on each
+ only the per-grid Kokkos computes are supported; the per-event
+ gas/collision/tally and gas/reaction/tally computes are not
+------------------------------------------------------------------------- */
+
+void CollideVSSKokkos::setup_gas_tally()
+{
+ nglist_collision = nglist_reaction = 0;
+
+ for (int i = 0; i < ngas_tally; i++) {
+ Compute *c = update->glist_active[i];
+ if (strcmp(c->style,"gas/collision/grid") == 0) {
+ ComputeGasCollisionGridKokkos *ckk =
+ dynamic_cast(c);
+ if (!ckk)
+ error->all(FLERR,"Must use Kokkos-enabled compute gas/collision/grid with Kokkos");
+ if (nglist_collision >= KOKKOS_MAX_GLIST)
+ error->all(FLERR,"Kokkos supports at most KOKKOS_MAX_GLIST instances of compute gas/collision/grid");
+ ckk->pre_gas_tally();
+ glist_collision_copy[nglist_collision].copy(ckk);
+ nglist_collision++;
+ } else if (strcmp(c->style,"gas/reaction/grid") == 0) {
+ ComputeGasReactionGridKokkos *ckk =
+ dynamic_cast(c);
+ if (!ckk)
+ error->all(FLERR,"Must use Kokkos-enabled compute gas/reaction/grid with Kokkos");
+ if (nglist_reaction >= KOKKOS_MAX_GLIST)
+ error->all(FLERR,"Kokkos supports at most KOKKOS_MAX_GLIST instances of compute gas/reaction/grid");
+ ckk->pre_gas_tally();
+ glist_reaction_copy[nglist_reaction].copy(ckk);
+ nglist_reaction++;
+ } else {
+ error->all(FLERR,"Kokkos does not (yet) support compute gas/collision/tally or compute gas/reaction/tally");
+ }
+ }
+
+ // fill unused slots of each typed copy list with the temporary
+ // to avoid the copy getting stale leading to an issue with view ref counting
+
+ for (int i = nglist_collision; i < KOKKOS_MAX_GLIST; i++)
+ glist_collision_copy[i].copy(&tmp_compute_gas_collision_kk);
+ for (int i = nglist_reaction; i < KOKKOS_MAX_GLIST; i++)
+ glist_reaction_copy[i].copy(&tmp_compute_gas_reaction_kk);
+}
+
+/* ----------------------------------------------------------------------
+ finalize the active gas/gas tally computes
+ call post_gas_tally() on the real compute objects (not the copies)
+------------------------------------------------------------------------- */
+
+void CollideVSSKokkos::finish_gas_tally()
+{
+ for (int i = 0; i < ngas_tally; i++) {
+ Compute *c = update->glist_active[i];
+ if (strcmp(c->style,"gas/collision/grid") == 0)
+ ((ComputeGasCollisionGridKokkos*)c)->post_gas_tally();
+ else if (strcmp(c->style,"gas/reaction/grid") == 0)
+ ((ComputeGasReactionGridKokkos*)c)->post_gas_tally();
+ }
+}
+
/* ----------------------------------------------------------------------
NTC algorithm for a single group
------------------------------------------------------------------------- */
@@ -562,8 +690,18 @@ template < int NEARCP, int GASTALLY > void CollideVSSKokkos::collisions_one(COLL
grid_kk_copy.copy(grid_kk);
if (react) {
- ReactTCEKokkos* react_kk = (ReactTCEKokkos*) react;
- react_kk_copy.copy(react_kk);
+ ReactQKKokkos* react_qk = dynamic_cast(react);
+ ReactTCEQKKokkos* react_tceqk = dynamic_cast(react);
+ if (react_tceqk) {
+ react_style = 2;
+ react_tceqk_kk_copy.copy(react_tceqk);
+ } else if (react_qk) {
+ react_style = 1;
+ react_qk_kk_copy.copy(react_qk);
+ } else {
+ react_style = 0;
+ react_kk_copy.copy((ReactTCEKokkos*) react);
+ }
}
if (sparta->kokkos->atomic_reduction) {
@@ -757,10 +895,12 @@ void CollideVSSKokkos::operator()(TagCollideCollisionsOne< NEARCP, GASTALLY, ATO
else
reduce.ncollide_one++;
- //if (GASTALLY)
- // for (int m = 0; m < ngas_tally; m++)
- // glist_active[m]->gas_tally(icell,reactflag,
- // &iorig,&jorig,ipart,jpart,kpart); //////
+ if (GASTALLY) {
+ for (int m = 0; m < nglist_collision; m++)
+ glist_collision_copy[m].obj.template gas_tally_kk(icell,reactflag,&iorig,&jorig,ipart,jpart,kpart);
+ for (int m = 0; m < nglist_reaction; m++)
+ glist_reaction_copy[m].obj.template gas_tally_kk(icell,reactflag,&iorig,&jorig,ipart,jpart,kpart);
+ }
if (reactflag) {
if (ATOMIC_REDUCTION == 1)
@@ -815,6 +955,484 @@ void CollideVSSKokkos::operator()(TagCollideCollisionsOne< NEARCP, GASTALLY, ATO
rand_pool.free_state(rand_gen);
}
+/* ----------------------------------------------------------------------
+ NTC algorithm for multiple groups
+ Kokkos version supports only non-reacting, non-ambipolar, non-nearcp
+ collisions, so group membership is static within the timestep
+ and no particles are created or destroyed
+------------------------------------------------------------------------- */
+
+template < int NEARCP, int GASTALLY >
+void CollideVSSKokkos::collisions_group(COLLIDE_REDUCE &reduce)
+{
+ if (ngroups > MAXGROUP)
+ error->all(FLERR,"Too many collision groups for Kokkos group collisions");
+
+ // loop over cells I own
+
+ this->sync(Device,ALL_MASK);
+
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ particle_kk->sync(Device,PARTICLE_MASK|SPECIES_MASK);
+ if (vibstyle == DISCRETE) particle_kk->sync(Device,CUSTOM_MASK);
+ d_particles = particle_kk->k_particles.view_device();
+ d_species = particle_kk->k_species.view_device();
+ d_ewhich = particle_kk->k_ewhich.view_device();
+ k_eiarray = particle_kk->k_eiarray;
+
+ GridKokkos* grid_kk = (GridKokkos*) grid;
+ grid_kk->sync(Device,CINFO_MASK);
+ d_plist = grid_kk->d_plist;
+
+ // allocate per-cell group scratch arrays
+ // d_glist holds plist indices laid out group-contiguous per cell
+ // d_nattempt_pair holds the pre-computed attempt count per group pair
+
+ if (int(d_glist.extent(0)) < nglocal ||
+ int(d_glist.extent(1)) < int(d_plist.extent(1)))
+ MemKK::realloc_kokkos(d_glist,"collide:glist",nglocal,d_plist.extent(1));
+ if (int(d_nattempt_pair.extent(0)) < nglocal ||
+ int(d_nattempt_pair.extent(1)) < ngroups)
+ MemKK::realloc_kokkos(d_nattempt_pair,"collide:nattempt_pair",nglocal,ngroups,ngroups);
+
+ copymode = 1;
+
+ // no particles are created or destroyed for non-reacting group collisions
+
+ ndelete = 0;
+
+ h_error_flag() = 0;
+ Kokkos::deep_copy(d_scalars,h_scalars);
+
+ grid_kk_copy.copy(grid_kk);
+
+ if (sparta->kokkos->atomic_reduction) {
+ if (sparta->kokkos->need_atomics)
+ Kokkos::parallel_for(Kokkos::RangePolicy >(0,nglocal),*this);
+ else
+ Kokkos::parallel_for(Kokkos::RangePolicy >(0,nglocal),*this);
+ } else
+ Kokkos::parallel_reduce(Kokkos::RangePolicy >(0,nglocal),*this,reduce);
+
+ Kokkos::deep_copy(h_scalars,d_scalars);
+
+ copymode = 0;
+
+ if (h_error_flag())
+ error->one(FLERR,"Collision cell volume is zero");
+
+ this->modified(Device,ALL_MASK);
+ particle_kk->modify(Device,PARTICLE_MASK);
+ if (vibstyle == DISCRETE) particle_kk->modify(Device,CUSTOM_MASK);
+
+ d_particles = t_particle_1d(); // destroy reference to reduce memory use
+ d_plist = {};
+}
+
+template < int NEARCP, int GASTALLY, int ATOMIC_REDUCTION >
+KOKKOS_INLINE_FUNCTION
+void CollideVSSKokkos::operator()(TagCollideCollisionsGroup< NEARCP, GASTALLY, ATOMIC_REDUCTION >, const int &icell) const {
+ COLLIDE_REDUCE reduce;
+ this->template operator()< NEARCP, GASTALLY, ATOMIC_REDUCTION >(TagCollideCollisionsGroup< NEARCP, GASTALLY, ATOMIC_REDUCTION >(), icell, reduce);
+}
+
+template < int NEARCP, int GASTALLY, int ATOMIC_REDUCTION >
+KOKKOS_INLINE_FUNCTION
+void CollideVSSKokkos::operator()(TagCollideCollisionsGroup< NEARCP, GASTALLY, ATOMIC_REDUCTION >, const int &icell, COLLIDE_REDUCE &reduce) const {
+
+ int np = grid_kk_copy.obj.d_cellcount[icell];
+ if (np <= 1) return;
+
+ const double volume = grid_kk_copy.obj.k_cinfo.view_device()[icell].volume / grid_kk_copy.obj.k_cinfo.view_device()[icell].weight;
+ if (volume == 0.0) d_error_flag() = 1;
+
+ // build per-group particle lists for this cell
+ // gcount[g] = # of particles in group g
+ // gstart[g] = offset of group g within d_glist(icell,*)
+ // d_glist(icell,k) = plist index of kth particle, laid out group-contiguous
+ // in the same per-group order as the non-Kokkos version
+
+ int gcount[MAXGROUP];
+ int gstart[MAXGROUP];
+ int gcursor[MAXGROUP];
+
+ for (int g = 0; g < ngroups; g++) gcount[g] = 0;
+ for (int n = 0; n < np; n++) {
+ const int isp = d_particles[d_plist(icell,n)].ispecies;
+ gcount[d_species2group[isp]]++;
+ }
+ int offset = 0;
+ for (int g = 0; g < ngroups; g++) {
+ gstart[g] = offset;
+ gcursor[g] = offset;
+ offset += gcount[g];
+ }
+ for (int n = 0; n < np; n++) {
+ const int isp = d_particles[d_plist(icell,n)].ispecies;
+ const int g = d_species2group[isp];
+ d_glist(icell,gcursor[g]++) = n;
+ }
+
+ struct State precoln; // state before collision
+ struct State postcoln; // state after collision
+
+ rand_type rand_gen = rand_pool.get_state();
+
+ // pre-compute # of attempts for each pair of groups
+ // double loop over N^2 / 2 pairs of groups
+ // draw RN for every pair to match non-Kokkos collision ordering
+
+ for (int ig = 0; ig < ngroups; ig++)
+ for (int jg = ig; jg < ngroups; jg++) {
+ const double attempt =
+ attempt_collision_kokkos(icell,ig,jg,gcount[ig],gcount[jg],volume,rand_gen);
+ const int nattempt = static_cast (attempt);
+ d_nattempt_pair(icell,ig,jg) = nattempt;
+ if (nattempt) {
+ if (ATOMIC_REDUCTION == 1)
+ Kokkos::atomic_add(&d_nattempt_one(),nattempt);
+ else if (ATOMIC_REDUCTION == 0)
+ d_nattempt_one() += nattempt;
+ else
+ reduce.nattempt_one += nattempt;
+ }
+ }
+
+ // perform collisions for each pair of groups
+ // select random particle in each group, cannot be same if igroup == jgroup
+ // test if collision actually occurs, then perform it
+
+ for (int ig = 0; ig < ngroups; ig++)
+ for (int jg = ig; jg < ngroups; jg++) {
+ const int nattempt = d_nattempt_pair(icell,ig,jg);
+ if (!nattempt) continue;
+ const int ni = gcount[ig];
+ const int nj = gcount[jg];
+ if (ni == 0 || nj == 0) continue;
+ if (ig == jg && ni == 1) continue;
+
+ for (int iattempt = 0; iattempt < nattempt; iattempt++) {
+ int i = ni * rand_gen.drand();
+ int j = nj * rand_gen.drand();
+ if (ig == jg)
+ while (i == j) j = nj * rand_gen.drand();
+
+ Particle::OnePart* ipart = &d_particles[d_plist(icell,d_glist(icell,gstart[ig]+i))];
+ Particle::OnePart* jpart = &d_particles[d_plist(icell,d_glist(icell,gstart[jg]+j))];
+
+ // test if collision actually occurs
+
+ if (!test_collision_kokkos(icell,ig,jg,ipart,jpart,precoln,rand_gen)) continue;
+
+ // perform collision
+ // non-reacting: no chemistry, no 3rd particle, no create/delete
+ // if GASTALLY: save iorig/jorig for tally (tally hook deferred)
+
+ Particle::OnePart iorig,jorig;
+ if (GASTALLY) {
+ iorig = *ipart;
+ jorig = *jpart;
+ }
+
+ Particle::OnePart* kpart = NULL;
+ Particle::OnePart* recomb_part3 = NULL;
+ int recomb_species = -1;
+ double recomb_density = 0.0;
+ int index_kpart = 0;
+
+ setup_collision_kokkos(ipart,jpart,precoln,postcoln);
+ const int reactflag = perform_collision_kokkos(ipart,jpart,kpart,precoln,postcoln,rand_gen,
+ recomb_part3,recomb_species,recomb_density,index_kpart);
+
+ if (ATOMIC_REDUCTION == 1)
+ Kokkos::atomic_inc(&d_ncollide_one());
+ else if (ATOMIC_REDUCTION == 0)
+ d_ncollide_one()++;
+ else
+ reduce.ncollide_one++;
+
+ if (GASTALLY) {
+ for (int m = 0; m < nglist_collision; m++)
+ glist_collision_copy[m].obj.template gas_tally_kk(icell,reactflag,&iorig,&jorig,ipart,jpart,kpart);
+ for (int m = 0; m < nglist_reaction; m++)
+ glist_reaction_copy[m].obj.template gas_tally_kk(icell,reactflag,&iorig,&jorig,ipart,jpart,kpart);
+ }
+ }
+ }
+
+ rand_pool.free_state(rand_gen);
+}
+
+/* ----------------------------------------------------------------------
+ NTC algorithm for multiple groups with ambipolar approximation
+ Kokkos version supports only the non-reacting case, so group membership
+ and the electron list are static within the timestep and no particles
+ are created or destroyed
+------------------------------------------------------------------------- */
+
+template < int GASTALLY >
+void CollideVSSKokkos::collisions_group_ambipolar(COLLIDE_REDUCE &reduce)
+{
+ if (ngroups > MAXGROUP)
+ error->all(FLERR,"Too many collision groups for Kokkos group collisions");
+
+ // ambipolar vectors
+
+ this->sync(Device,ALL_MASK);
+
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ particle_kk->sync(Device,PARTICLE_MASK|SPECIES_MASK|CUSTOM_MASK);
+ d_particles = particle_kk->k_particles.view_device();
+ d_species = particle_kk->k_species.view_device();
+ d_ewhich = particle_kk->k_ewhich.view_device();
+ auto h_ewhich = particle_kk->k_ewhich.view_host();
+ k_eivec = particle_kk->k_eivec;
+ k_eiarray = particle_kk->k_eiarray;
+ k_edarray = particle_kk->k_edarray;
+ d_ionambi = k_eivec.view_host()[h_ewhich[index_ionambi]].k_view.view_device();
+ d_velambi = k_edarray.view_host()[h_ewhich[index_velambi]].k_view.view_device();
+
+ GridKokkos* grid_kk = (GridKokkos*) grid;
+ grid_kk->sync(Device,CINFO_MASK);
+ d_plist = grid_kk->d_plist;
+
+ // allocate per-cell group scratch arrays (see collisions_group)
+
+ if (int(d_glist.extent(0)) < nglocal ||
+ int(d_glist.extent(1)) < int(d_plist.extent(1)))
+ MemKK::realloc_kokkos(d_glist,"collide:glist",nglocal,d_plist.extent(1));
+ if (int(d_nattempt_pair.extent(0)) < nglocal ||
+ int(d_nattempt_pair.extent(1)) < ngroups)
+ MemKK::realloc_kokkos(d_nattempt_pair,"collide:nattempt_pair",nglocal,ngroups,ngroups);
+
+ // per-cell electron list; non-reacting so nelectron <= cell particle count
+
+ maxcellcount = particle_kk->get_maxcellcount();
+ if (int(d_elist.extent(0)) < nglocal || int(d_elist.extent(1)) < maxcellcount) {
+ d_elist = t_particle_2d(); // reduce memory use by deallocating first
+ d_elist = t_particle_2d(Kokkos::view_alloc("collide:elist",Kokkos::WithoutInitializing),nglocal,maxcellcount);
+ }
+
+ copymode = 1;
+
+ // no particles are created or destroyed for non-reacting group collisions
+
+ ndelete = 0;
+
+ h_error_flag() = 0;
+ Kokkos::deep_copy(d_scalars,h_scalars);
+
+ grid_kk_copy.copy(grid_kk);
+
+ if (sparta->kokkos->atomic_reduction) {
+ if (sparta->kokkos->need_atomics)
+ Kokkos::parallel_for(Kokkos::RangePolicy >(0,nglocal),*this);
+ else
+ Kokkos::parallel_for(Kokkos::RangePolicy >(0,nglocal),*this);
+ } else
+ Kokkos::parallel_reduce(Kokkos::RangePolicy >(0,nglocal),*this,reduce);
+
+ Kokkos::deep_copy(h_scalars,d_scalars);
+
+ copymode = 0;
+
+ if (h_error_flag() == 1)
+ error->one(FLERR,"Collision cell volume is zero");
+ else if (h_error_flag() == 2)
+ error->one(FLERR,"Collisions in cell did not conserve electron count");
+
+ this->modified(Device,ALL_MASK);
+ particle_kk->modify(Device,PARTICLE_MASK|CUSTOM_MASK);
+
+ d_particles = t_particle_1d(); // destroy reference to reduce memory use
+ d_plist = {};
+}
+
+template < int GASTALLY, int ATOMIC_REDUCTION >
+KOKKOS_INLINE_FUNCTION
+void CollideVSSKokkos::operator()(TagCollideCollisionsGroupAmbipolar< GASTALLY, ATOMIC_REDUCTION >, const int &icell) const {
+ COLLIDE_REDUCE reduce;
+ this->template operator()< GASTALLY, ATOMIC_REDUCTION >(TagCollideCollisionsGroupAmbipolar< GASTALLY, ATOMIC_REDUCTION >(), icell, reduce);
+}
+
+template < int GASTALLY, int ATOMIC_REDUCTION >
+KOKKOS_INLINE_FUNCTION
+void CollideVSSKokkos::operator()(TagCollideCollisionsGroupAmbipolar< GASTALLY, ATOMIC_REDUCTION >, const int &icell, COLLIDE_REDUCE &reduce) const {
+
+ int np = grid_kk_copy.obj.d_cellcount[icell];
+ if (np <= 1) return;
+
+ const double volume = grid_kk_copy.obj.k_cinfo.view_device()[icell].volume / grid_kk_copy.obj.k_cinfo.view_device()[icell].weight;
+ if (volume == 0.0) d_error_flag() = 1;
+
+ // build per-group particle lists for this cell, plus the electron list
+ // gcount[g] = particle count in group g, with the electron count for egroup
+ // gstart[g] = offset of group g's real particles within d_glist(icell,*)
+ // (the electron group egroup has no real particles, so it adds no entries)
+ // electrons (one per ambipolar ion) are created in d_elist in plist order
+
+ int gcount[MAXGROUP];
+ int gstart[MAXGROUP];
+ int gcursor[MAXGROUP];
+
+ for (int g = 0; g < ngroups; g++) gcount[g] = 0;
+
+ int nelectron = 0;
+ for (int n = 0; n < np; n++) {
+ const int ip = d_plist(icell,n);
+ const int isp = d_particles[ip].ispecies;
+ gcount[d_species2group[isp]]++;
+ if (d_ionambi[ip]) nelectron++;
+ }
+ gcount[egroup] = nelectron;
+
+ int offset = 0;
+ for (int g = 0; g < ngroups; g++) {
+ gstart[g] = offset;
+ gcursor[g] = offset;
+ if (g != egroup) offset += gcount[g];
+ }
+
+ int e = 0;
+ for (int n = 0; n < np; n++) {
+ const int ip = d_plist(icell,n);
+ const int isp = d_particles[ip].ispecies;
+ const int g = d_species2group[isp];
+ d_glist(icell,gcursor[g]++) = n;
+ if (d_ionambi[ip]) {
+ Particle::OnePart* p = &d_particles[ip];
+ Particle::OnePart* ep = &d_elist(icell,e);
+ *ep = *p;
+ ep->v[0] = d_velambi(ip,0);
+ ep->v[1] = d_velambi(ip,1);
+ ep->v[2] = d_velambi(ip,2);
+ ep->ispecies = ambispecies;
+ e++;
+ }
+ }
+
+ struct State precoln; // state before collision
+ struct State postcoln; // state after collision
+
+ rand_type rand_gen = rand_pool.get_state();
+
+ // pre-compute # of attempts for each pair of groups
+ // skip electron/electron pairs (no e/e collisions in the ambipolar model)
+ // draw RN for every other pair to match non-Kokkos collision ordering
+
+ for (int ig = 0; ig < ngroups; ig++)
+ for (int jg = ig; jg < ngroups; jg++) {
+ if (ig == egroup && jg == egroup) {
+ d_nattempt_pair(icell,ig,jg) = 0;
+ continue;
+ }
+ const double attempt =
+ attempt_collision_kokkos(icell,ig,jg,gcount[ig],gcount[jg],volume,rand_gen);
+ const int nattempt = static_cast (attempt);
+ d_nattempt_pair(icell,ig,jg) = nattempt;
+ if (nattempt) {
+ if (ATOMIC_REDUCTION == 1)
+ Kokkos::atomic_add(&d_nattempt_one(),nattempt);
+ else if (ATOMIC_REDUCTION == 0)
+ d_nattempt_one() += nattempt;
+ else
+ reduce.nattempt_one += nattempt;
+ }
+ }
+
+ // perform collisions for each pair of groups
+ // electron group is always the J side, so ipart is never an electron
+ // (matches the non-Kokkos gpair igroup/jgroup flip)
+
+ for (int ig = 0; ig < ngroups; ig++)
+ for (int jg = ig; jg < ngroups; jg++) {
+ if (ig == egroup && jg == egroup) continue;
+ const int nattempt = d_nattempt_pair(icell,ig,jg);
+ if (!nattempt) continue;
+
+ int aig,ajg;
+ if (ig == egroup) { aig = jg; ajg = ig; }
+ else { aig = ig; ajg = jg; }
+
+ const int ni = gcount[aig];
+ const int nj = gcount[ajg];
+ if (ni == 0 || nj == 0) continue;
+ if (aig == ajg && ni == 1) continue;
+
+ for (int iattempt = 0; iattempt < nattempt; iattempt++) {
+ int i = ni * rand_gen.drand();
+ int j = nj * rand_gen.drand();
+ if (aig == ajg)
+ while (i == j) j = nj * rand_gen.drand();
+
+ Particle::OnePart* ipart =
+ &d_particles[d_plist(icell,d_glist(icell,gstart[aig]+i))];
+ Particle::OnePart* jpart;
+ if (ajg == egroup) jpart = &d_elist(icell,j);
+ else jpart = &d_particles[d_plist(icell,d_glist(icell,gstart[ajg]+j))];
+
+ // test if collision actually occurs
+
+ if (!test_collision_kokkos(icell,aig,ajg,ipart,jpart,precoln,rand_gen)) continue;
+
+ // perform collision (non-reacting: no chemistry, no 3rd particle)
+ // if GASTALLY: save iorig/jorig for tally
+
+ Particle::OnePart iorig,jorig;
+ if (GASTALLY) {
+ iorig = *ipart;
+ jorig = *jpart;
+ }
+
+ Particle::OnePart* kpart = NULL;
+ Particle::OnePart* recomb_part3 = NULL;
+ int recomb_species = -1;
+ double recomb_density = 0.0;
+ int index_kpart = 0;
+
+ setup_collision_kokkos(ipart,jpart,precoln,postcoln);
+ const int reactflag = perform_collision_kokkos(ipart,jpart,kpart,precoln,postcoln,rand_gen,
+ recomb_part3,recomb_species,recomb_density,index_kpart);
+
+ if (ATOMIC_REDUCTION == 1)
+ Kokkos::atomic_inc(&d_ncollide_one());
+ else if (ATOMIC_REDUCTION == 0)
+ d_ncollide_one()++;
+ else
+ reduce.ncollide_one++;
+
+ if (GASTALLY) {
+ for (int m = 0; m < nglist_collision; m++)
+ glist_collision_copy[m].obj.template gas_tally_kk(icell,reactflag,&iorig,&jorig,ipart,jpart,kpart);
+ for (int m = 0; m < nglist_reaction; m++)
+ glist_reaction_copy[m].obj.template gas_tally_kk(icell,reactflag,&iorig,&jorig,ipart,jpart,kpart);
+ }
+ }
+ }
+
+ // recombine ambipolar ions with their matching electrons
+ // by copying the (possibly scattered) electron velocity back into velambi
+ // electrons were created in plist order, so the Nth ion gets the Nth electron
+
+ int melectron = 0;
+ for (int n = 0; n < np; n++) {
+ const int i = d_plist(icell,n);
+ if (d_ionambi[i]) {
+ if (melectron < nelectron) {
+ Particle::OnePart* ep = &d_elist(icell,melectron);
+ d_velambi(i,0) = ep->v[0];
+ d_velambi(i,1) = ep->v[1];
+ d_velambi(i,2) = ep->v[2];
+ }
+ melectron++;
+ }
+ }
+ if (melectron != nelectron)
+ d_error_flag() = 2;
+
+ rand_pool.free_state(rand_gen);
+}
+
/* ----------------------------------------------------------------------
NTC algorithm for a single group with ambipolar approximation
------------------------------------------------------------------------- */
@@ -921,8 +1539,18 @@ void CollideVSSKokkos::collisions_one_ambipolar(COLLIDE_REDUCE &reduce)
grid_kk_copy.copy(grid_kk);
if (react) {
- ReactTCEKokkos* react_kk = (ReactTCEKokkos*) react;
- react_kk_copy.copy(react_kk);
+ ReactQKKokkos* react_qk = dynamic_cast(react);
+ ReactTCEQKKokkos* react_tceqk = dynamic_cast(react);
+ if (react_tceqk) {
+ react_style = 2;
+ react_tceqk_kk_copy.copy(react_tceqk);
+ } else if (react_qk) {
+ react_style = 1;
+ react_qk_kk_copy.copy(react_qk);
+ } else {
+ react_style = 0;
+ react_kk_copy.copy((ReactTCEKokkos*) react);
+ }
}
if (sparta->kokkos->atomic_reduction) {
@@ -1167,10 +1795,12 @@ void CollideVSSKokkos::operator()(TagCollideCollisionsOneAmbipolar< GASTALLY, AT
else
reduce.ncollide_one++;
- //if (GASTALLY)
- // for (int m = 0; m < ngas_tally; m++)
- // glist_active[m]->gas_tally(icell,reactflag,
- // &iorig,&jorig,ipart,jpart,kpart); //////
+ if (GASTALLY) {
+ for (int m = 0; m < nglist_collision; m++)
+ glist_collision_copy[m].obj.template gas_tally_kk(icell,reactflag,&iorig,&jorig,ipart,jpart,kpart);
+ for (int m = 0; m < nglist_reaction; m++)
+ glist_reaction_copy[m].obj.template gas_tally_kk(icell,reactflag,&iorig,&jorig,ipart,jpart,kpart);
+ }
if (reactflag) {
if (ATOMIC_REDUCTION == 1)
@@ -1375,6 +2005,34 @@ double CollideVSSKokkos::attempt_collision_kokkos(int icell, int np, double volu
return nattempt;
}
+/* ----------------------------------------------------------------------
+ attempt count for a pair of groups
+ ni,nj = particle counts in igroup,jgroup
+------------------------------------------------------------------------- */
+
+KOKKOS_INLINE_FUNCTION
+double CollideVSSKokkos::attempt_collision_kokkos(int icell, int igroup, int jgroup,
+ int ni, int nj, double volume,
+ rand_type &rand_gen) const
+{
+ double nattempt;
+
+ // return 2x the value for igroup != jgroup, since no J,I pairing
+
+ double npairs;
+ if (igroup == jgroup) npairs = 0.5 * ni * (ni-1);
+ else npairs = ni * nj;
+
+ nattempt = npairs * d_vremax(icell,igroup,jgroup) * dt * fnum / volume;
+
+ if (remainflag) {
+ nattempt += d_remain(icell,igroup,jgroup);
+ d_remain(icell,igroup,jgroup) = nattempt - static_cast (nattempt);
+ } else nattempt += rand_gen.drand();
+
+ return nattempt;
+}
+
/* ----------------------------------------------------------------------
determine if collision actually occurs
1 = yes, 0 = no
@@ -1473,12 +2131,23 @@ int CollideVSSKokkos::perform_collision_kokkos(Particle::OnePart *&ip,
// reaction = 1 to N for which reaction occurs
// reaction is returned to caller
- if (react_defined)
- reaction = react_kk_copy.obj.attempt_kk(ip,jp,
+ if (react_defined) {
+ if (react_style == 1)
+ reaction = react_qk_kk_copy.obj.attempt_kk(ip,jp,
+ precoln.etrans,precoln.erot,
+ precoln.evib,postcoln.etotal,kspecies,
+ recomb_species,recomb_density,d_species);
+ else if (react_style == 2)
+ reaction = react_tceqk_kk_copy.obj.attempt_kk(ip,jp,
+ precoln.etrans,precoln.erot,
+ precoln.evib,postcoln.etotal,kspecies,
+ recomb_species,recomb_density,d_species);
+ else
+ reaction = react_kk_copy.obj.attempt_kk(ip,jp,
precoln.etrans,precoln.erot,
precoln.evib,postcoln.etotal,kspecies,
recomb_species,recomb_density,d_species);
- else reaction = 0;
+ } else reaction = 0;
// just collision, no reaction
diff --git a/src/KOKKOS/collide_vss_kokkos.h b/src/KOKKOS/collide_vss_kokkos.h
index 00fd1148a..df1b3bb0e 100644
--- a/src/KOKKOS/collide_vss_kokkos.h
+++ b/src/KOKKOS/collide_vss_kokkos.h
@@ -26,10 +26,16 @@ CollideStyle(vss/kk,CollideVSSKokkos)
#include "particle_kokkos.h"
#include "grid_kokkos.h"
#include "react_tce_kokkos.h"
+#include "react_qk_kokkos.h"
+#include "react_tce_qk_kokkos.h"
#include "kokkos_type.h"
#include "Kokkos_Random.hpp"
#include "rand_pool_wrap.h"
#include "kokkos_copy.h"
+#include "compute_gas_collision_grid_kokkos.h"
+#include "compute_gas_reaction_grid_kokkos.h"
+
+#define KOKKOS_MAX_GLIST 4
namespace SPARTA_NS {
@@ -60,6 +66,12 @@ struct TagCollideCollisionsOne{};
template < int GASTALLY, int ATOMIC_REDUCTION >
struct TagCollideCollisionsOneAmbipolar{};
+template < int NEARCP, int GASTALLY, int ATOMIC_REDUCTION >
+struct TagCollideCollisionsGroup{};
+
+template < int GASTALLY, int ATOMIC_REDUCTION >
+struct TagCollideCollisionsGroupAmbipolar{};
+
class CollideVSSKokkos : public CollideVSS {
public:
typedef COLLIDE_REDUCE value_type;
@@ -85,6 +97,8 @@ class CollideVSSKokkos : public CollideVSS {
KOKKOS_INLINE_FUNCTION
double attempt_collision_kokkos(int, int, double, rand_type &) const;
KOKKOS_INLINE_FUNCTION
+ double attempt_collision_kokkos(int, int, int, int, int, double, rand_type &) const;
+ KOKKOS_INLINE_FUNCTION
int test_collision_kokkos(int, int, int, Particle::OnePart *, Particle::OnePart *, struct State &, rand_type &) const;
KOKKOS_INLINE_FUNCTION
void setup_collision_kokkos(Particle::OnePart *, Particle::OnePart *, struct State &, struct State &) const;
@@ -116,6 +130,22 @@ class CollideVSSKokkos : public CollideVSS {
KOKKOS_INLINE_FUNCTION
void operator()(TagCollideCollisionsOneAmbipolar< GASTALLY, ATOMIC_REDUCTION >, const int&, COLLIDE_REDUCE&) const;
+ template < int NEARCP, int GASTALLY, int ATOMIC_REDUCTION >
+ KOKKOS_INLINE_FUNCTION
+ void operator()(TagCollideCollisionsGroup< NEARCP, GASTALLY, ATOMIC_REDUCTION >, const int&) const;
+
+ template < int NEARCP, int GASTALLY, int ATOMIC_REDUCTION >
+ KOKKOS_INLINE_FUNCTION
+ void operator()(TagCollideCollisionsGroup< NEARCP, GASTALLY, ATOMIC_REDUCTION >, const int&, COLLIDE_REDUCE&) const;
+
+ template < int GASTALLY, int ATOMIC_REDUCTION >
+ KOKKOS_INLINE_FUNCTION
+ void operator()(TagCollideCollisionsGroupAmbipolar< GASTALLY, ATOMIC_REDUCTION >, const int&) const;
+
+ template < int GASTALLY, int ATOMIC_REDUCTION >
+ KOKKOS_INLINE_FUNCTION
+ void operator()(TagCollideCollisionsGroupAmbipolar< GASTALLY, ATOMIC_REDUCTION >, const int&, COLLIDE_REDUCE&) const;
+
typedef Kokkos::
DualView tdual_params_2d;
typedef tdual_params_2d::t_dev t_params_2d;
@@ -138,11 +168,28 @@ class CollideVSSKokkos : public CollideVSS {
KKCopy grid_kk_copy;
KKCopy react_kk_copy;
+ KKCopy react_qk_kk_copy;
+ KKCopy react_tceqk_kk_copy;
+ int react_style; // 0=TCE, 1=QK, 2=TCEQK (set in setup)
+
+ // active gas/gas per-grid tally computes, partitioned by type
+ KKCopy glist_collision_copy[KOKKOS_MAX_GLIST];
+ KKCopy glist_reaction_copy[KOKKOS_MAX_GLIST];
+ int nglist_collision,nglist_reaction;
+ ComputeGasCollisionGridKokkos tmp_compute_gas_collision_kk;
+ ComputeGasReactionGridKokkos tmp_compute_gas_reaction_kk;
+ void setup_gas_tally();
+ void finish_gas_tally();
t_particle_1d d_particles;
t_species_1d_const d_species;
DAT::t_int_2d d_plist;
+ // group collision scratch (ngroups > 1)
+ DAT::t_int_1d d_species2group;
+ DAT::t_int_2d d_glist;
+ Kokkos::View d_nattempt_pair;
+
DAT::t_int_1d d_ewhich;
tdual_struct_tdual_int_1d_1d k_eivec;
tdual_struct_tdual_int_2d_1d k_eiarray;
@@ -206,6 +253,9 @@ class CollideVSSKokkos : public CollideVSS {
template < int NEARCP, int GASTALLY > void collisions_one(COLLIDE_REDUCE&);
template < int GASTALLY > void collisions_one_ambipolar(COLLIDE_REDUCE&);
+ template < int NEARCP, int GASTALLY > void collisions_group(COLLIDE_REDUCE&);
+ template < int GASTALLY > void collisions_group_ambipolar(COLLIDE_REDUCE&);
+ int egroup; // mixture group containing the ambipolar electron species
// VSS specific
diff --git a/src/KOKKOS/compute_gas_collision_grid_kokkos.cpp b/src/KOKKOS/compute_gas_collision_grid_kokkos.cpp
new file mode 100644
index 000000000..9af8d22db
--- /dev/null
+++ b/src/KOKKOS/compute_gas_collision_grid_kokkos.cpp
@@ -0,0 +1,108 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "compute_gas_collision_grid_kokkos.h"
+#include "particle_kokkos.h"
+#include "grid_kokkos.h"
+#include "memory_kokkos.h"
+#include "sparta_masks.h"
+#include "kokkos.h"
+
+using namespace SPARTA_NS;
+
+/* ---------------------------------------------------------------------- */
+
+ComputeGasCollisionGridKokkos::ComputeGasCollisionGridKokkos(SPARTA *sparta, int narg, char **arg) :
+ ComputeGasCollisionGrid(sparta, narg, arg)
+{
+ kokkos_flag = 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+ComputeGasCollisionGridKokkos::ComputeGasCollisionGridKokkos(SPARTA *sparta) :
+ ComputeGasCollisionGrid(sparta)
+{
+ copy = 1;
+ uncopy = 0;
+ vector_grid = NULL;
+ nglocal = 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+ComputeGasCollisionGridKokkos::~ComputeGasCollisionGridKokkos()
+{
+ if (copy || copymode) return;
+
+ memoryKK->destroy_kokkos(k_vector_grid,vector_grid);
+ vector_grid = NULL;
+}
+
+/* ----------------------------------------------------------------------
+ zero the tally array on device
+ called by Update at beginning of timesteps gas tallying is done
+------------------------------------------------------------------------- */
+
+void ComputeGasCollisionGridKokkos::clear()
+{
+ Kokkos::deep_copy(d_vector_grid,0.0);
+}
+
+/* ----------------------------------------------------------------------
+ setup device views and scatter view before gas tallying
+ called by Collide before the collision kernel
+------------------------------------------------------------------------- */
+
+void ComputeGasCollisionGridKokkos::pre_gas_tally()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ d_s2g = particle_kk->k_species2group.view_device();
+
+ GridKokkos* grid_kk = (GridKokkos*) grid;
+ grid_kk->sync(Device,CINFO_MASK);
+ d_cinfo = grid_kk->k_cinfo.view_device();
+}
+
+/* ----------------------------------------------------------------------
+ finalize gas tallying, sync result to host
+ called by Collide after the collision kernel
+------------------------------------------------------------------------- */
+
+void ComputeGasCollisionGridKokkos::post_gas_tally()
+{
+ k_vector_grid.modify_device();
+ k_vector_grid.sync_host();
+}
+
+/* ----------------------------------------------------------------------
+ reallocate data storage if nglocal has changed
+ called by init() and whenever grid changes
+------------------------------------------------------------------------- */
+
+void ComputeGasCollisionGridKokkos::reallocate()
+{
+ if (grid->nlocal == nglocal) return;
+
+ memoryKK->destroy_kokkos(k_vector_grid,vector_grid);
+ nglocal = grid->nlocal;
+ memoryKK->create_kokkos(k_vector_grid,vector_grid,nglocal,"gas/collision/grid:vector_grid");
+ d_vector_grid = k_vector_grid.view_device();
+
+ // clear counts b/c may be accessed before tallying is done
+
+ Kokkos::deep_copy(d_vector_grid,0.0);
+ k_vector_grid.modify_device();
+ k_vector_grid.sync_host();
+}
diff --git a/src/KOKKOS/compute_gas_collision_grid_kokkos.h b/src/KOKKOS/compute_gas_collision_grid_kokkos.h
new file mode 100644
index 000000000..ce3fc1d71
--- /dev/null
+++ b/src/KOKKOS/compute_gas_collision_grid_kokkos.h
@@ -0,0 +1,90 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef COMPUTE_CLASS
+
+ComputeStyle(gas/collision/grid/kk,ComputeGasCollisionGridKokkos)
+
+#else
+
+#ifndef SPARTA_COMPUTE_GAS_COLLISION_GRID_KOKKOS_H
+#define SPARTA_COMPUTE_GAS_COLLISION_GRID_KOKKOS_H
+
+#include "compute_gas_collision_grid.h"
+#include "kokkos_base.h"
+#include "kokkos_type.h"
+#include "particle.h"
+
+namespace SPARTA_NS {
+
+class ComputeGasCollisionGridKokkos : public ComputeGasCollisionGrid, public KokkosBase {
+ public:
+ ComputeGasCollisionGridKokkos(class SPARTA *, int, char **);
+ ComputeGasCollisionGridKokkos(class SPARTA *);
+ ~ComputeGasCollisionGridKokkos();
+ void compute_per_grid_kokkos() {} // tallying happens in Collide, not here
+ void clear();
+ void pre_gas_tally();
+ void post_gas_tally();
+ void reallocate();
+
+ // tally a single gas collision in icell on device
+ // reaction = 0 for a collision that did not induce a reaction
+ // this compute only tallies non-reacting collisions
+ // Collide parallelizes over grid cells (one icell per thread), so the
+ // per-cell tally has no write contention and needs no atomics/duplication
+ // ATOMIC_REDUCTION template arg is unused, kept for a uniform call interface
+
+ template
+ KOKKOS_INLINE_FUNCTION
+ void gas_tally_kk(int icell, int reaction,
+ Particle::OnePart *iorig, Particle::OnePart *jorig,
+ Particle::OnePart * /*ip*/, Particle::OnePart * /*jp*/,
+ Particle::OnePart * /*kp*/) const
+ {
+ // skip if a reaction (reactions tallied by compute gas/reaction/grid)
+
+ if (reaction) return;
+
+ // skip if icell not in grid group
+
+ if (!(d_cinfo[icell].mask & groupbit)) return;
+
+ // skip if either particle species not in mixture group
+
+ int igroup = d_s2g(imix,iorig->ispecies);
+ int jgroup = d_s2g(imix,jorig->ispecies);
+ if (igroup < 0 || jgroup < 0) return;
+
+ // tally the collision to its grid cell
+
+ d_vector_grid(icell) += 1.0;
+ }
+
+ private:
+ DAT::tdual_float_1d k_vector_grid;
+ // d_vector_grid is inherited from KokkosBase (read by fix ave/grid/kk)
+
+ t_cinfo_1d d_cinfo;
+ DAT::t_int_2d d_s2g;
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+*/
diff --git a/src/KOKKOS/compute_gas_reaction_grid_kokkos.cpp b/src/KOKKOS/compute_gas_reaction_grid_kokkos.cpp
new file mode 100644
index 000000000..f325eba3e
--- /dev/null
+++ b/src/KOKKOS/compute_gas_reaction_grid_kokkos.cpp
@@ -0,0 +1,142 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "compute_gas_reaction_grid_kokkos.h"
+#include "particle_kokkos.h"
+#include "grid_kokkos.h"
+#include "react.h"
+#include "memory_kokkos.h"
+#include "sparta_masks.h"
+#include "kokkos.h"
+
+using namespace SPARTA_NS;
+
+/* ---------------------------------------------------------------------- */
+
+ComputeGasReactionGridKokkos::ComputeGasReactionGridKokkos(SPARTA *sparta, int narg, char **arg) :
+ ComputeGasReactionGrid(sparta, narg, arg)
+{
+ kokkos_flag = 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+ComputeGasReactionGridKokkos::ComputeGasReactionGridKokkos(SPARTA *sparta) :
+ ComputeGasReactionGrid(sparta)
+{
+ copy = 1;
+ uncopy = 0;
+ vector_grid = NULL;
+ array_grid = NULL;
+ ncol = 0;
+ nglocal = 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+ComputeGasReactionGridKokkos::~ComputeGasReactionGridKokkos()
+{
+ if (copy || copymode) return;
+
+ if (ncol == 0) memoryKK->destroy_kokkos(k_vector_grid,vector_grid);
+ else memoryKK->destroy_kokkos(k_array_grid,array_grid);
+ vector_grid = NULL;
+ array_grid = NULL;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeGasReactionGridKokkos::init()
+{
+ ComputeGasReactionGrid::init();
+
+ // device copy of reaction -> column map for SELECT mode
+
+ if (mode == SELECT) {
+ int n = react->nlist + 1;
+ d_reaction2col = DAT::t_int_1d("gas/reaction/grid:reaction2col",n);
+ auto h_reaction2col = Kokkos::create_mirror_view(d_reaction2col);
+ for (int i = 0; i < n; i++) h_reaction2col(i) = reaction2col[i];
+ Kokkos::deep_copy(d_reaction2col,h_reaction2col);
+ }
+}
+
+/* ----------------------------------------------------------------------
+ zero the tally array on device
+ called by Update at beginning of timesteps gas tallying is done
+------------------------------------------------------------------------- */
+
+void ComputeGasReactionGridKokkos::clear()
+{
+ if (ncol == 0) Kokkos::deep_copy(d_vector_grid,0.0);
+ else Kokkos::deep_copy(d_array_grid,0.0);
+}
+
+/* ----------------------------------------------------------------------
+ setup device views and scatter view before gas tallying
+ called by Collide before the collision kernel
+------------------------------------------------------------------------- */
+
+void ComputeGasReactionGridKokkos::pre_gas_tally()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ d_s2g = particle_kk->k_species2group.view_device();
+
+ GridKokkos* grid_kk = (GridKokkos*) grid;
+ grid_kk->sync(Device,CINFO_MASK);
+ d_cinfo = grid_kk->k_cinfo.view_device();
+}
+
+/* ----------------------------------------------------------------------
+ finalize gas tallying, sync result to host
+ called by Collide after the collision kernel
+------------------------------------------------------------------------- */
+
+void ComputeGasReactionGridKokkos::post_gas_tally()
+{
+ if (ncol == 0) {
+ k_vector_grid.modify_device();
+ k_vector_grid.sync_host();
+ } else {
+ k_array_grid.modify_device();
+ k_array_grid.sync_host();
+ }
+}
+
+/* ----------------------------------------------------------------------
+ reallocate data storage if nglocal has changed
+ called by init() and whenever grid changes
+------------------------------------------------------------------------- */
+
+void ComputeGasReactionGridKokkos::reallocate()
+{
+ if (grid->nlocal == nglocal) return;
+
+ if (ncol == 0) memoryKK->destroy_kokkos(k_vector_grid,vector_grid);
+ else memoryKK->destroy_kokkos(k_array_grid,array_grid);
+ nglocal = grid->nlocal;
+ if (ncol == 0) {
+ memoryKK->create_kokkos(k_vector_grid,vector_grid,nglocal,"gas/reaction/grid:vector_grid");
+ d_vector_grid = k_vector_grid.view_device();
+ Kokkos::deep_copy(d_vector_grid,0.0);
+ k_vector_grid.modify_device();
+ k_vector_grid.sync_host();
+ } else {
+ memoryKK->create_kokkos(k_array_grid,array_grid,nglocal,ncol,"gas/reaction/grid:array_grid");
+ d_array_grid = k_array_grid.view_device();
+ Kokkos::deep_copy(d_array_grid,0.0);
+ k_array_grid.modify_device();
+ k_array_grid.sync_host();
+ }
+}
diff --git a/src/KOKKOS/compute_gas_reaction_grid_kokkos.h b/src/KOKKOS/compute_gas_reaction_grid_kokkos.h
new file mode 100644
index 000000000..d0d3b9ffd
--- /dev/null
+++ b/src/KOKKOS/compute_gas_reaction_grid_kokkos.h
@@ -0,0 +1,106 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef COMPUTE_CLASS
+
+ComputeStyle(gas/reaction/grid/kk,ComputeGasReactionGridKokkos)
+
+#else
+
+#ifndef SPARTA_COMPUTE_GAS_REACTION_GRID_KOKKOS_H
+#define SPARTA_COMPUTE_GAS_REACTION_GRID_KOKKOS_H
+
+#include "compute_gas_reaction_grid.h"
+#include "kokkos_base.h"
+#include "kokkos_type.h"
+#include "particle.h"
+
+namespace SPARTA_NS {
+
+class ComputeGasReactionGridKokkos : public ComputeGasReactionGrid, public KokkosBase {
+ public:
+ enum{ALL,EVERY,SELECT}; // must match compute_gas_reaction_grid.cpp
+
+ ComputeGasReactionGridKokkos(class SPARTA *, int, char **);
+ ComputeGasReactionGridKokkos(class SPARTA *);
+ ~ComputeGasReactionGridKokkos();
+ void init();
+ void compute_per_grid_kokkos() {} // tallying happens in Collide, not here
+ void clear();
+ void pre_gas_tally();
+ void post_gas_tally();
+ void reallocate();
+
+ // tally a single gas reaction in icell on device
+ // reaction = 1 to N for which reaction, 0 = collision only (skipped)
+ // this compute only tallies reacting collisions
+ // Collide parallelizes over grid cells (one icell per thread), so the
+ // per-cell tally has no write contention and needs no atomics/duplication
+ // ATOMIC_REDUCTION template arg is unused, kept for a uniform call interface
+
+ template
+ KOKKOS_INLINE_FUNCTION
+ void gas_tally_kk(int icell, int reaction,
+ Particle::OnePart *iorig, Particle::OnePart *jorig,
+ Particle::OnePart * /*ip*/, Particle::OnePart * /*jp*/,
+ Particle::OnePart * /*kp*/) const
+ {
+ // skip if not a reaction (collisions tallied by compute gas/collision/grid)
+
+ if (!reaction) return;
+
+ // skip if icell not in grid group
+
+ if (!(d_cinfo[icell].mask & groupbit)) return;
+
+ // skip if either particle species not in mixture group
+
+ int igroup = d_s2g(imix,iorig->ispecies);
+ int jgroup = d_s2g(imix,jorig->ispecies);
+ if (igroup < 0 || jgroup < 0) return;
+
+ // tally the reaction to its grid cell
+ // for EVERY and SELECT mode, reaction index determines column of array_grid
+
+ if (mode == ALL) {
+ d_vector_grid(icell) += 1.0;
+ } else if (mode == EVERY) {
+ int icol = reaction - 1;
+ d_array_grid(icell,icol) += 1.0;
+ } else { // SELECT
+ int icol = d_reaction2col(reaction);
+ if (icol >= 0) d_array_grid(icell,icol) += 1.0;
+ }
+ }
+
+ private:
+ DAT::tdual_float_1d k_vector_grid;
+ DAT::tdual_float_2d_lr k_array_grid;
+ // d_vector_grid and d_array_grid are inherited from KokkosBase
+ // (read by fix ave/grid/kk)
+
+ DAT::t_int_1d d_reaction2col; // reaction -> column map for SELECT mode
+
+ t_cinfo_1d d_cinfo;
+ DAT::t_int_2d d_s2g;
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+*/
diff --git a/src/KOKKOS/compute_isurf_grid_kokkos.cpp b/src/KOKKOS/compute_isurf_grid_kokkos.cpp
new file mode 100644
index 000000000..97efc1717
--- /dev/null
+++ b/src/KOKKOS/compute_isurf_grid_kokkos.cpp
@@ -0,0 +1,218 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "string.h"
+#include "compute_isurf_grid_kokkos.h"
+#include "particle_kokkos.h"
+#include "mixture.h"
+#include "surf_kokkos.h"
+#include "grid.h"
+#include "update.h"
+#include "memory_kokkos.h"
+#include "error.h"
+#include "sparta_masks.h"
+#include "kokkos.h"
+
+using namespace SPARTA_NS;
+
+/* ---------------------------------------------------------------------- */
+
+ComputeISurfGridKokkos::ComputeISurfGridKokkos(SPARTA *sparta, int narg, char **arg) :
+ ComputeISurfGrid(sparta, narg, arg)
+{
+ kokkos_flag = 1;
+
+ nsurf_tally_alloc = -1;
+
+ // hash is allocated/used only on the host; not needed for device tally
+
+ d_which = DAT::t_int_1d("isurf/grid:which",nvalue);
+}
+
+ComputeISurfGridKokkos::ComputeISurfGridKokkos(SPARTA *sparta) :
+ ComputeISurfGrid(sparta)
+{
+ copy = 1;
+ uncopy = 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+ComputeISurfGridKokkos::~ComputeISurfGridKokkos()
+{
+ if (copy) return;
+
+ memoryKK->destroy_kokkos(k_tally2surf,tally2surf);
+ memoryKK->destroy_kokkos(k_array_surf_tally,array_surf_tally);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeISurfGridKokkos::init()
+{
+ ComputeISurfGrid::init();
+
+ auto h_which = Kokkos::create_mirror_view(d_which);
+ for (int n=0; nnlocal + surf->nghost;
+
+ d_normflux = DAT::t_float_1d("isurf/grid:normflux",nsurf);
+ auto h_normflux = Kokkos::create_mirror_view(d_normflux);
+ for (int n=0; ngrow_kokkos(k_tally2surf,tally2surf,nsurf,"isurf/grid:tally2surf");
+ d_tally2surf = k_tally2surf.view_device();
+ d_surf2tally = DAT::t_int_1d("isurf/grid:surf2tally",nsurf);
+ Kokkos::deep_copy(d_surf2tally,-1);
+
+ memoryKK->grow_kokkos(k_array_surf_tally,array_surf_tally,nsurf,ntotal,"isurf/grid:array_surf_tally");
+ d_array_surf_tally = k_array_surf_tally.view_device();
+
+ nsurf_tally_alloc = nsurf;
+}
+
+/* ----------------------------------------------------------------------
+ reallocate per-cell and per-surf arrays after the grid/surfs change
+ the per-surf tally arrays are indexed by isurf, so they must track the
+ surf count; ablation regenerates surfs without changing grid->nlocal, so
+ the base reallocate() (keyed on grid->nlocal) can leave them stale
+------------------------------------------------------------------------- */
+
+void ComputeISurfGridKokkos::reallocate()
+{
+ ComputeISurfGrid::reallocate();
+
+ int nsurf = surf->nlocal + surf->nghost;
+ if (nsurf != nsurf_tally_alloc) init_normflux();
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeISurfGridKokkos::clear()
+{
+ // reset all set surf2tally values to -1
+ // called by Update at beginning of timesteps surf tallying is done
+
+ Kokkos::deep_copy(d_array_surf_tally,0);
+ Kokkos::deep_copy(d_surf2tally,-1);
+
+ ntally = 0;
+ combined = 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeISurfGridKokkos::pre_surf_tally()
+{
+ mvv2e = update->mvv2e;
+
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ particle_kk->sync(Device,SPECIES_MASK);
+ d_species = particle_kk->k_species.view_device();
+ d_s2g = particle_kk->k_species2group.view_device();
+
+ SurfKokkos* surf_kk = (SurfKokkos*) surf;
+ surf_kk->sync(Device,ALL_MASK);
+ d_lines = surf_kk->k_lines.view_device();
+ d_tris = surf_kk->k_tris.view_device();
+
+ need_dup = sparta->kokkos->need_dup();
+ if (need_dup)
+ dup_array_surf_tally = Kokkos::Experimental::create_scatter_view(d_array_surf_tally);
+ else
+ ndup_array_surf_tally = Kokkos::Experimental::create_scatter_view(d_array_surf_tally);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeISurfGridKokkos::post_surf_tally()
+{
+ if (need_dup) {
+ Kokkos::Experimental::contribute(d_array_surf_tally, dup_array_surf_tally);
+ dup_array_surf_tally = {}; // free duplicated memory
+ }
+
+ k_tally2surf.modify_device();
+ k_array_surf_tally.modify_device();
+}
+
+/* ----------------------------------------------------------------------
+ sync device tallies to host and compress to dense list (ntally tallies)
+ matches ComputeSurfKokkos::tallyinfo();
+ host fix ave/grid (PERGRIDSURF) consumes array_surf_tally + tally2surf
+------------------------------------------------------------------------- */
+
+int ComputeISurfGridKokkos::tallyinfo(surfint *&ptr)
+{
+ k_tally2surf.sync_host();
+ ptr = tally2surf;
+
+ k_array_surf_tally.sync_host();
+ auto h_surf2tally = Kokkos::create_mirror_view(d_surf2tally);
+ Kokkos::deep_copy(h_surf2tally,d_surf2tally);
+
+ // compress array_surf_tally
+
+ int nsurf = surf->nlocal + surf->nghost;
+ int istart = 0;
+ int iend = nsurf-1;
+
+ while (1) {
+ while (h_surf2tally[istart] != -1 && istart < nsurf-2) istart++;
+ while (h_surf2tally[iend] == -1 && iend > 0) iend--;
+ if (istart >= iend) {
+ ntally = istart;
+ break;
+ }
+ for (int k = 0; k < ntotal; k++) {
+ array_surf_tally[istart][k] = array_surf_tally[iend][k];
+ }
+ h_surf2tally[istart] = h_surf2tally[iend];
+ h_surf2tally[iend] = -1;
+ tally2surf[istart] = tally2surf[iend];
+ }
+
+ return ntally;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeISurfGridKokkos::grow_tally()
+{
+ // Cannot realloc inside a Kokkos parallel region, so size as nsurf
+
+ int nsurf = surf->nlocal + surf->nghost;
+
+ memoryKK->grow_kokkos(k_tally2surf,tally2surf,nsurf,"isurf/grid:tally2surf");
+ d_tally2surf = k_tally2surf.view_device();
+
+ memoryKK->grow_kokkos(k_array_surf_tally,array_surf_tally,nsurf,ntotal,"isurf/grid:array_surf_tally");
+ d_array_surf_tally = k_array_surf_tally.view_device();
+}
diff --git a/src/KOKKOS/compute_isurf_grid_kokkos.h b/src/KOKKOS/compute_isurf_grid_kokkos.h
new file mode 100644
index 000000000..1878ae49f
--- /dev/null
+++ b/src/KOKKOS/compute_isurf_grid_kokkos.h
@@ -0,0 +1,290 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef COMPUTE_CLASS
+
+ComputeStyle(isurf/grid/kk,ComputeISurfGridKokkos)
+
+#else
+
+#ifndef SPARTA_COMPUTE_ISURF_GRID_KOKKOS_H
+#define SPARTA_COMPUTE_ISURF_GRID_KOKKOS_H
+
+#include "compute_isurf_grid.h"
+#include "kokkos_type.h"
+#include "math_extra_kokkos.h"
+
+namespace SPARTA_NS {
+
+class ComputeISurfGridKokkos : public ComputeISurfGrid {
+ public:
+ ComputeISurfGridKokkos(class SPARTA *, int, char **);
+ ComputeISurfGridKokkos(class SPARTA *);
+ ~ComputeISurfGridKokkos();
+ void init();
+ void init_normflux();
+ void reallocate();
+ void clear();
+ int tallyinfo(surfint *&);
+ void pre_surf_tally();
+ void post_surf_tally();
+
+ enum{NUM,NUMWT,MFLUX,FX,FY,FZ,PRESS,XPRESS,YPRESS,ZPRESS,
+ XSHEAR,YSHEAR,ZSHEAR,KE,EROT,EVIB,ETOT};
+
+/* ----------------------------------------------------------------------
+ tally values for a single particle in icell
+ colliding with surface element isurf, performing reaction (1 to N)
+ iorig = particle ip before collision
+ ip,jp = particles after collision
+ ip = NULL means no particles after collision
+ jp = NULL means one particle after collision
+ jp != NULL means two particles after collision
+ this method mirrors ComputeISurfGrid::surf_tally(), tallying per-surf;
+ post-processing (collate to per-grid) is done on the host
+------------------------------------------------------------------------- */
+
+template
+KOKKOS_INLINE_FUNCTION
+void surf_tally_kk(double /*dtremain*/, int isurf, int /*icell*/, int /*reaction*/,
+ Particle::OnePart *iorig,
+ Particle::OnePart *ip, Particle::OnePart *jp) const
+{
+ // skip if species not in mixture group
+
+ int origspecies = iorig->ispecies;
+ int igroup = d_s2g(imix,origspecies);
+ if (igroup < 0) return;
+
+ // itally = tally index of isurf (thread-safe; compressed later on host)
+
+ surfint surfID;
+ if (dim == 2) surfID = d_lines[isurf].id;
+ else surfID = d_tris[isurf].id;
+
+ int itally = isurf;
+ d_tally2surf(itally) = surfID;
+ d_surf2tally(isurf) = isurf;
+
+ double fluxscale = d_normflux(isurf);
+
+ double vsqpre,ivsqpost,jvsqpost;
+ double ierot,jerot,ievib,jevib,iother,jother,otherpre,etot;
+ double pdelta[3],pnorm[3],ptang[3],pdelta_force[3];
+
+ double *norm;
+ if (dim == 2) norm = d_lines(isurf).norm;
+ else norm = d_tris(isurf).norm;
+
+ double weight = 1.0;
+ if (weightflag) weight = iorig->weight;
+ double origmass = d_species[origspecies].mass * weight;
+ double imass = 0.0, jmass = 0.0;
+ if (ip) imass = d_species(ip->ispecies).mass * weight;
+ if (jp) jmass = d_species(jp->ispecies).mass * weight;
+
+ double *vorig = iorig->v;
+
+ auto v_array_surf_tally = ScatterViewHelper::value,decltype(dup_array_surf_tally),decltype(ndup_array_surf_tally)>::get(dup_array_surf_tally,ndup_array_surf_tally);
+ auto a_array_surf_tally = v_array_surf_tally.template access::value>();
+
+ int k = igroup*nvalue;
+ int fflag = 0;
+ int nflag = 0;
+ int tflag = 0;
+
+ for (int m = 0; m < nvalue; m++) {
+ switch (d_which(m)) {
+ case NUM:
+ a_array_surf_tally(itally,k++) += 1.0;
+ break;
+ case NUMWT:
+ a_array_surf_tally(itally,k++) += weight;
+ break;
+ case MFLUX:
+ a_array_surf_tally(itally,k) += origmass * fluxscale;
+ if (ip) a_array_surf_tally(itally,k) -= imass * fluxscale;
+ if (jp) a_array_surf_tally(itally,k) -= jmass * fluxscale;
+ k++;
+ break;
+ case FX:
+ if (!fflag) {
+ fflag = 1;
+ MathExtraKokkos::scale3(-origmass,vorig,pdelta_force);
+ if (ip) MathExtraKokkos::axpy3(imass,ip->v,pdelta_force);
+ if (jp) MathExtraKokkos::axpy3(jmass,jp->v,pdelta_force);
+ }
+ a_array_surf_tally(itally,k++) -= pdelta_force[0] * nfactor_inverse;
+ break;
+ case FY:
+ if (!fflag) {
+ fflag = 1;
+ MathExtraKokkos::scale3(-origmass,vorig,pdelta_force);
+ if (ip) MathExtraKokkos::axpy3(imass,ip->v,pdelta_force);
+ if (jp) MathExtraKokkos::axpy3(jmass,jp->v,pdelta_force);
+ }
+ a_array_surf_tally(itally,k++) -= pdelta_force[1] * nfactor_inverse;
+ break;
+ case FZ:
+ if (!fflag) {
+ fflag = 1;
+ MathExtraKokkos::scale3(-origmass,vorig,pdelta_force);
+ if (ip) MathExtraKokkos::axpy3(imass,ip->v,pdelta_force);
+ if (jp) MathExtraKokkos::axpy3(jmass,jp->v,pdelta_force);
+ }
+ a_array_surf_tally(itally,k++) -= pdelta_force[2] * nfactor_inverse;
+ break;
+ case PRESS:
+ MathExtraKokkos::scale3(-origmass,vorig,pdelta);
+ if (ip) MathExtraKokkos::axpy3(imass,ip->v,pdelta);
+ if (jp) MathExtraKokkos::axpy3(jmass,jp->v,pdelta);
+ a_array_surf_tally(itally,k++) += MathExtraKokkos::dot3(pdelta,norm) * fluxscale;
+ break;
+ case XPRESS:
+ if (!nflag) {
+ nflag = 1;
+ MathExtraKokkos::scale3(-origmass,vorig,pdelta);
+ if (ip) MathExtraKokkos::axpy3(imass,ip->v,pdelta);
+ if (jp) MathExtraKokkos::axpy3(jmass,jp->v,pdelta);
+ MathExtraKokkos::scale3(MathExtraKokkos::dot3(pdelta,norm),norm,pnorm);
+ }
+ a_array_surf_tally(itally,k++) -= pnorm[0] * fluxscale;
+ break;
+ case YPRESS:
+ if (!nflag) {
+ nflag = 1;
+ MathExtraKokkos::scale3(-origmass,vorig,pdelta);
+ if (ip) MathExtraKokkos::axpy3(imass,ip->v,pdelta);
+ if (jp) MathExtraKokkos::axpy3(jmass,jp->v,pdelta);
+ MathExtraKokkos::scale3(MathExtraKokkos::dot3(pdelta,norm),norm,pnorm);
+ }
+ a_array_surf_tally(itally,k++) -= pnorm[1] * fluxscale;
+ break;
+ case ZPRESS:
+ if (!nflag) {
+ nflag = 1;
+ MathExtraKokkos::scale3(-origmass,vorig,pdelta);
+ if (ip) MathExtraKokkos::axpy3(imass,ip->v,pdelta);
+ if (jp) MathExtraKokkos::axpy3(jmass,jp->v,pdelta);
+ MathExtraKokkos::scale3(MathExtraKokkos::dot3(pdelta,norm),norm,pnorm);
+ }
+ a_array_surf_tally(itally,k++) -= pnorm[2] * fluxscale;
+ break;
+ case XSHEAR:
+ if (!tflag) {
+ tflag = 1;
+ MathExtraKokkos::scale3(-origmass,vorig,pdelta);
+ if (ip) MathExtraKokkos::axpy3(imass,ip->v,pdelta);
+ if (jp) MathExtraKokkos::axpy3(jmass,jp->v,pdelta);
+ MathExtraKokkos::scale3(MathExtraKokkos::dot3(pdelta,norm),norm,pnorm);
+ MathExtraKokkos::sub3(pdelta,pnorm,ptang);
+ }
+ a_array_surf_tally(itally,k++) -= ptang[0] * fluxscale;
+ break;
+ case YSHEAR:
+ if (!tflag) {
+ tflag = 1;
+ MathExtraKokkos::scale3(-origmass,vorig,pdelta);
+ if (ip) MathExtraKokkos::axpy3(imass,ip->v,pdelta);
+ if (jp) MathExtraKokkos::axpy3(jmass,jp->v,pdelta);
+ MathExtraKokkos::scale3(MathExtraKokkos::dot3(pdelta,norm),norm,pnorm);
+ MathExtraKokkos::sub3(pdelta,pnorm,ptang);
+ }
+ a_array_surf_tally(itally,k++) -= ptang[1] * fluxscale;
+ break;
+ case ZSHEAR:
+ if (!tflag) {
+ tflag = 1;
+ MathExtraKokkos::scale3(-origmass,vorig,pdelta);
+ if (ip) MathExtraKokkos::axpy3(imass,ip->v,pdelta);
+ if (jp) MathExtraKokkos::axpy3(jmass,jp->v,pdelta);
+ MathExtraKokkos::scale3(MathExtraKokkos::dot3(pdelta,norm),norm,pnorm);
+ MathExtraKokkos::sub3(pdelta,pnorm,ptang);
+ }
+ a_array_surf_tally(itally,k++) -= ptang[2] * fluxscale;
+ break;
+ case KE:
+ vsqpre = origmass * MathExtraKokkos::lensq3(vorig);
+ if (ip) ivsqpost = imass * MathExtraKokkos::lensq3(ip->v);
+ else ivsqpost = 0.0;
+ if (jp) jvsqpost = jmass * MathExtraKokkos::lensq3(jp->v);
+ else jvsqpost = 0.0;
+ a_array_surf_tally(itally,k++) -= 0.5*mvv2e * (ivsqpost + jvsqpost - vsqpre) * fluxscale;
+ break;
+ case EROT:
+ if (ip) ierot = ip->erot;
+ else ierot = 0.0;
+ if (jp) jerot = jp->erot;
+ else jerot = 0.0;
+ a_array_surf_tally(itally,k++) -= weight * (ierot + jerot - iorig->erot) * fluxscale;
+ break;
+ case EVIB:
+ if (ip) ievib = ip->evib;
+ else ievib = 0.0;
+ if (jp) jevib = jp->evib;
+ else jevib = 0.0;
+ a_array_surf_tally(itally,k++) -= weight * (ievib + jevib - iorig->evib) * fluxscale;
+ break;
+ case ETOT:
+ vsqpre = origmass * MathExtraKokkos::lensq3(vorig);
+ otherpre = iorig->erot + iorig->evib;
+ if (ip) {
+ ivsqpost = imass * MathExtraKokkos::lensq3(ip->v);
+ iother = ip->erot + ip->evib;
+ } else ivsqpost = iother = 0.0;
+ if (jp) {
+ jvsqpost = jmass * MathExtraKokkos::lensq3(jp->v);
+ jother = jp->erot + jp->evib;
+ } else jvsqpost = jother = 0.0;
+ etot = 0.5*mvv2e*(ivsqpost + jvsqpost - vsqpre) +
+ weight * (iother + jother - otherpre);
+ a_array_surf_tally(itally,k++) -= etot * fluxscale;
+ break;
+ }
+ }
+}
+
+ private:
+ double mvv2e;
+
+ DAT::t_int_1d d_which;
+
+ DAT::tdual_float_2d_lr k_array_surf_tally;
+ DAT::t_float_2d_lr d_array_surf_tally; // tally values for local surfs
+
+ int need_dup;
+ Kokkos::Experimental::ScatterView dup_array_surf_tally;
+ Kokkos::Experimental::ScatterView ndup_array_surf_tally;
+
+ DAT::t_surfint_1d d_tally2surf; // tally2surf[I] = surf ID of Ith tally
+ DAT::tdual_surfint_1d k_tally2surf;
+ DAT::t_int_1d d_surf2tally;
+
+ DAT::t_float_1d d_normflux; // normalization factor for each surf element
+
+ t_species_1d d_species;
+ DAT::t_int_2d d_s2g;
+
+ t_line_1d d_lines;
+ t_tri_1d d_tris;
+
+ int nsurf_tally_alloc; // # of surfs the per-surf tally arrays are sized for
+
+ void grow_tally();
+};
+
+}
+
+#endif
+#endif
diff --git a/src/KOKKOS/compute_property_surf_kokkos.cpp b/src/KOKKOS/compute_property_surf_kokkos.cpp
new file mode 100644
index 000000000..b9eb20a26
--- /dev/null
+++ b/src/KOKKOS/compute_property_surf_kokkos.cpp
@@ -0,0 +1,143 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "string.h"
+#include "compute_property_surf_kokkos.h"
+#include "surf_kokkos.h"
+#include "domain.h"
+#include "update.h"
+#include "memory_kokkos.h"
+#include "error.h"
+#include "sparta_masks.h"
+#include "kokkos.h"
+
+using namespace SPARTA_NS;
+
+/* ---------------------------------------------------------------------- */
+
+ComputePropertySurfKokkos::ComputePropertySurfKokkos(SPARTA *sparta, int narg, char **arg) :
+ ComputePropertySurf(sparta, narg, arg)
+{
+ kokkos_flag = 1;
+
+ // map field keywords to device index enum (must match base parse order)
+
+ d_index = DAT::t_int_1d("property/surf:index",nvalues);
+ auto h_index = Kokkos::create_mirror_view(d_index);
+ for (int i = 0; i < nvalues; i++) {
+ char *a = arg[3+i];
+ int idx = -1;
+ if (strcmp(a,"id") == 0) idx = ID;
+ else if (strcmp(a,"v1x") == 0) idx = V1X;
+ else if (strcmp(a,"v1y") == 0) idx = V1Y;
+ else if (strcmp(a,"v1z") == 0) idx = V1Z;
+ else if (strcmp(a,"v2x") == 0) idx = V2X;
+ else if (strcmp(a,"v2y") == 0) idx = V2Y;
+ else if (strcmp(a,"v2z") == 0) idx = V2Z;
+ else if (strcmp(a,"v3x") == 0) idx = V3X;
+ else if (strcmp(a,"v3y") == 0) idx = V3Y;
+ else if (strcmp(a,"v3z") == 0) idx = V3Z;
+ else if (strcmp(a,"xc") == 0) idx = XC;
+ else if (strcmp(a,"yc") == 0) idx = YC;
+ else if (strcmp(a,"zc") == 0) idx = ZC;
+ else if (strcmp(a,"area") == 0) idx = AREA;
+ else if (strcmp(a,"normx") == 0) idx = NORMX;
+ else if (strcmp(a,"normy") == 0) idx = NORMY;
+ else if (strcmp(a,"normz") == 0) idx = NORMZ;
+ h_index(i) = idx;
+ }
+ Kokkos::deep_copy(d_index,h_index);
+}
+
+/* ---------------------------------------------------------------------- */
+
+ComputePropertySurfKokkos::~ComputePropertySurfKokkos()
+{
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputePropertySurfKokkos::init()
+{
+ ComputePropertySurf::init();
+
+ // copy cglobal (owned-in-group surf indices) to device
+
+ d_cglobal = DAT::t_int_1d("property/surf:cglobal",MAX(nchoose,1));
+ auto h_cglobal = Kokkos::create_mirror_view(d_cglobal);
+ for (int i = 0; i < nchoose; i++) h_cglobal(i) = cglobal[i];
+ Kokkos::deep_copy(d_cglobal,h_cglobal);
+
+ // device output storage (sized nsown to match host vector_surf/array_surf)
+
+ int n = MAX(nsown,1);
+ if (nvalues == 1) {
+ d_vector_surf = DAT::t_float_1d("property/surf:vector_surf",n);
+ k_vector_surf = DAT::tdual_float_1d("property/surf:vector_surf",n);
+ } else {
+ d_array_surf = DAT::t_float_2d_lr("property/surf:array_surf",n,nvalues);
+ k_array_surf = DAT::tdual_float_2d_lr("property/surf:array_surf",n,nvalues);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputePropertySurfKokkos::compute_per_surf()
+{
+ if (sparta->kokkos->prewrap) {
+ ComputePropertySurf::compute_per_surf();
+ } else {
+ compute_per_surf_kokkos();
+ if (nvalues == 1) {
+ Kokkos::deep_copy(k_vector_surf.view_device(),d_vector_surf);
+ k_vector_surf.modify_device();
+ k_vector_surf.sync_host();
+ auto h = k_vector_surf.view_host();
+ for (int i = 0; i < nsown; i++) vector_surf[i] = h(i);
+ } else {
+ Kokkos::deep_copy(k_array_surf.view_device(),d_array_surf);
+ k_array_surf.modify_device();
+ k_array_surf.sync_host();
+ auto h = k_array_surf.view_host();
+ for (int i = 0; i < nsown; i++)
+ for (int n = 0; n < nvalues; n++) array_surf[i][n] = h(i,n);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputePropertySurfKokkos::compute_per_surf_kokkos()
+{
+ invoked_per_surf = update->ntimestep;
+
+ dim = domain->dimension;
+
+ SurfKokkos* surf_kk = (SurfKokkos*) surf;
+ surf_kk->sync(Device,ALL_MASK);
+ if (distributed) {
+ d_lines = surf_kk->k_mylines.view_device();
+ d_tris = surf_kk->k_mytris.view_device();
+ } else {
+ d_lines = surf_kk->k_lines.view_device();
+ d_tris = surf_kk->k_tris.view_device();
+ }
+
+ if (nvalues == 1) Kokkos::deep_copy(d_vector_surf,0.0);
+ else Kokkos::deep_copy(d_array_surf,0.0);
+
+ copymode = 1;
+ Kokkos::parallel_for(Kokkos::RangePolicy(0,nchoose),*this);
+ copymode = 0;
+}
diff --git a/src/KOKKOS/compute_property_surf_kokkos.h b/src/KOKKOS/compute_property_surf_kokkos.h
new file mode 100644
index 000000000..38ebc9013
--- /dev/null
+++ b/src/KOKKOS/compute_property_surf_kokkos.h
@@ -0,0 +1,115 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef COMPUTE_CLASS
+
+ComputeStyle(property/surf/kk,ComputePropertySurfKokkos)
+
+#else
+
+#ifndef SPARTA_COMPUTE_PROPERTY_SURF_KOKKOS_H
+#define SPARTA_COMPUTE_PROPERTY_SURF_KOKKOS_H
+
+#include "compute_property_surf.h"
+#include "kokkos_base.h"
+#include "kokkos_type.h"
+#include "math_extra_kokkos.h"
+
+namespace SPARTA_NS {
+
+class ComputePropertySurfKokkos : public ComputePropertySurf, public KokkosBase {
+ public:
+ enum{ID,V1X,V1Y,V1Z,V2X,V2Y,V2Z,V3X,V3Y,V3Z,XC,YC,ZC,AREA,NORMX,NORMY,NORMZ};
+
+ ComputePropertySurfKokkos(class SPARTA *, int, char **);
+ ~ComputePropertySurfKokkos();
+ void init();
+ void compute_per_surf();
+ void compute_per_surf_kokkos();
+
+ KOKKOS_INLINE_FUNCTION
+ double pack_one(int m, int field) const
+ {
+ const double THIRD = 1.0/3.0;
+ if (dim == 2) {
+ const auto &L = d_lines[m];
+ switch (field) {
+ case ID: return (double) L.id;
+ case V1X: return L.p1[0];
+ case V1Y: return L.p1[1];
+ case V2X: return L.p2[0];
+ case V2Y: return L.p2[1];
+ case XC: return 0.5*(L.p1[0]+L.p2[0]);
+ case YC: return 0.5*(L.p1[1]+L.p2[1]);
+ case AREA: { double p12[3]; MathExtraKokkos::sub3(L.p2,L.p1,p12);
+ return MathExtraKokkos::len3(p12); }
+ case NORMX: return L.norm[0];
+ case NORMY: return L.norm[1];
+ }
+ } else {
+ const auto &T = d_tris[m];
+ switch (field) {
+ case ID: return (double) T.id;
+ case V1X: return T.p1[0];
+ case V1Y: return T.p1[1];
+ case V1Z: return T.p1[2];
+ case V2X: return T.p2[0];
+ case V2Y: return T.p2[1];
+ case V2Z: return T.p2[2];
+ case V3X: return T.p3[0];
+ case V3Y: return T.p3[1];
+ case V3Z: return T.p3[2];
+ case XC: return THIRD*(T.p1[0]+T.p2[0]+T.p3[0]);
+ case YC: return THIRD*(T.p1[1]+T.p2[1]+T.p3[1]);
+ case ZC: return THIRD*(T.p1[2]+T.p2[2]+T.p3[2]);
+ case AREA: { double p12[3],p13[3],cross[3];
+ MathExtraKokkos::sub3(T.p2,T.p1,p12);
+ MathExtraKokkos::sub3(T.p3,T.p1,p13);
+ MathExtraKokkos::cross3(p12,p13,cross);
+ return 0.5*MathExtraKokkos::len3(cross); }
+ case NORMX: return T.norm[0];
+ case NORMY: return T.norm[1];
+ case NORMZ: return T.norm[2];
+ }
+ }
+ return 0.0;
+ }
+
+ KOKKOS_INLINE_FUNCTION
+ void operator()(const int &i) const
+ {
+ int m = d_cglobal[i];
+ if (nvalues == 1) d_vector_surf[i] = pack_one(m,d_index[0]);
+ else
+ for (int n = 0; n < nvalues; n++)
+ d_array_surf(i,n) = pack_one(m,d_index[n]);
+ }
+
+ DAT::tdual_float_1d k_vector_surf;
+ DAT::tdual_float_2d_lr k_array_surf;
+
+ private:
+ int dim;
+ DAT::t_int_1d d_index;
+ DAT::t_int_1d d_cglobal;
+ DAT::t_float_1d d_vector_surf;
+ DAT::t_float_2d_lr d_array_surf;
+ t_line_1d d_lines;
+ t_tri_1d d_tris;
+};
+
+}
+
+#endif
+#endif
diff --git a/src/KOKKOS/compute_react_isurf_grid_kokkos.cpp b/src/KOKKOS/compute_react_isurf_grid_kokkos.cpp
new file mode 100644
index 000000000..a90742506
--- /dev/null
+++ b/src/KOKKOS/compute_react_isurf_grid_kokkos.cpp
@@ -0,0 +1,194 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "string.h"
+#include "compute_react_isurf_grid_kokkos.h"
+#include "surf_kokkos.h"
+#include "surf_react.h"
+#include "grid.h"
+#include "update.h"
+#include "memory_kokkos.h"
+#include "error.h"
+#include "sparta_masks.h"
+#include "kokkos.h"
+
+using namespace SPARTA_NS;
+
+/* ---------------------------------------------------------------------- */
+
+ComputeReactISurfGridKokkos::ComputeReactISurfGridKokkos(SPARTA *sparta, int narg, char **arg) :
+ ComputeReactISurfGrid(sparta, narg, arg)
+{
+ kokkos_flag = 1;
+}
+
+ComputeReactISurfGridKokkos::ComputeReactISurfGridKokkos(SPARTA *sparta) :
+ ComputeReactISurfGrid(sparta)
+{
+ copy = 1;
+ uncopy = 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+ComputeReactISurfGridKokkos::~ComputeReactISurfGridKokkos()
+{
+ if (copy) return;
+
+ memoryKK->destroy_kokkos(k_tally2surf,tally2surf);
+ memoryKK->destroy_kokkos(k_array_surf_tally,array_surf_tally);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactISurfGridKokkos::init()
+{
+ ComputeReactISurfGrid::init();
+
+ // flatten reaction2col to device (only used when rpflag)
+
+ if (rpflag) {
+ int nreaction = surf->sr[isr]->nlist;
+ d_reaction2col = DAT::t_int_2d("react/isurf/grid:reaction2col",nreaction,ntotal);
+ auto h_r2c = Kokkos::create_mirror_view(d_reaction2col);
+ for (int i = 0; i < nreaction; i++)
+ for (int j = 0; j < ntotal; j++)
+ h_r2c(i,j) = reaction2col[i][j];
+ Kokkos::deep_copy(d_reaction2col,h_r2c);
+ }
+
+ // size per-surf tally storage to nsurf (implicit-surf ablation only shrinks)
+
+ resize_device(surf->nlocal + surf->nghost);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactISurfGridKokkos::resize_device(int nsurf)
+{
+ if (nsurf < 1) nsurf = 1;
+
+ memoryKK->grow_kokkos(k_tally2surf,tally2surf,nsurf,"react/isurf/grid:tally2surf");
+ d_tally2surf = k_tally2surf.view_device();
+
+ d_surf2tally = DAT::t_int_1d("react/isurf/grid:surf2tally",nsurf);
+ Kokkos::deep_copy(d_surf2tally,-1);
+
+ memoryKK->grow_kokkos(k_array_surf_tally,array_surf_tally,nsurf,ntotal,
+ "react/isurf/grid:array_surf_tally");
+ d_array_surf_tally = k_array_surf_tally.view_device();
+
+ nsurf_tally_alloc = nsurf;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactISurfGridKokkos::clear()
+{
+ // called by Update at beginning of timesteps surf tallying is done
+
+ int nsurf = surf->nlocal + surf->nghost;
+ if (nsurf > nsurf_tally_alloc) resize_device(nsurf);
+
+ Kokkos::deep_copy(d_array_surf_tally,0);
+ Kokkos::deep_copy(d_surf2tally,-1);
+
+ ntally = 0;
+ combined = 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactISurfGridKokkos::pre_surf_tally()
+{
+ SurfKokkos* surf_kk = (SurfKokkos*) surf;
+ surf_kk->sync(Device,ALL_MASK);
+ d_lines = surf_kk->k_lines.view_device();
+ d_tris = surf_kk->k_tris.view_device();
+
+ need_dup = sparta->kokkos->need_dup();
+ if (need_dup)
+ dup_array_surf_tally = Kokkos::Experimental::create_scatter_view(d_array_surf_tally);
+ else
+ ndup_array_surf_tally = Kokkos::Experimental::create_scatter_view(d_array_surf_tally);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactISurfGridKokkos::post_surf_tally()
+{
+ if (need_dup) {
+ Kokkos::Experimental::contribute(d_array_surf_tally, dup_array_surf_tally);
+ dup_array_surf_tally = {};
+ }
+
+ k_tally2surf.modify_device();
+ k_array_surf_tally.modify_device();
+}
+
+/* ----------------------------------------------------------------------
+ sync device tallies to host and compress to dense list (ntally tallies)
+ matches ComputeISurfGridKokkos::tallyinfo()
+------------------------------------------------------------------------- */
+
+int ComputeReactISurfGridKokkos::tallyinfo(surfint *&ptr)
+{
+ k_tally2surf.sync_host();
+ ptr = tally2surf;
+
+ k_array_surf_tally.sync_host();
+ auto h_surf2tally = Kokkos::create_mirror_view(d_surf2tally);
+ Kokkos::deep_copy(h_surf2tally,d_surf2tally);
+
+ int nsurf = surf->nlocal + surf->nghost;
+ int istart = 0;
+ int iend = nsurf-1;
+
+ while (1) {
+ while (h_surf2tally[istart] != -1 && istart < nsurf-2) istart++;
+ while (h_surf2tally[iend] == -1 && iend > 0) iend--;
+ if (istart >= iend) {
+ ntally = istart;
+ break;
+ }
+ for (int k = 0; k < ntotal; k++)
+ array_surf_tally[istart][k] = array_surf_tally[iend][k];
+ h_surf2tally[istart] = h_surf2tally[iend];
+ h_surf2tally[iend] = -1;
+ tally2surf[istart] = tally2surf[iend];
+ }
+
+ return ntally;
+}
+
+/* ----------------------------------------------------------------------
+ sync the device per-surf tally to the host (tallyinfo) before the host
+ base class collates it to per-grid; consumers (e.g. fix ablate) read the
+ compute directly via post_process_isurf_grid() rather than tallyinfo()
+------------------------------------------------------------------------- */
+
+void ComputeReactISurfGridKokkos::post_process_isurf_grid()
+{
+ if (combined) return;
+ surfint *dummy;
+ tallyinfo(dummy);
+ ComputeReactISurfGrid::post_process_isurf_grid();
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactISurfGridKokkos::grow_tally()
+{
+ resize_device(surf->nlocal + surf->nghost);
+}
diff --git a/src/KOKKOS/compute_react_isurf_grid_kokkos.h b/src/KOKKOS/compute_react_isurf_grid_kokkos.h
new file mode 100644
index 000000000..e897a3a5a
--- /dev/null
+++ b/src/KOKKOS/compute_react_isurf_grid_kokkos.h
@@ -0,0 +1,110 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef COMPUTE_CLASS
+
+ComputeStyle(react/isurf/grid/kk,ComputeReactISurfGridKokkos)
+
+#else
+
+#ifndef SPARTA_COMPUTE_REACT_ISURF_GRID_KOKKOS_H
+#define SPARTA_COMPUTE_REACT_ISURF_GRID_KOKKOS_H
+
+#include "compute_react_isurf_grid.h"
+#include "kokkos_type.h"
+
+namespace SPARTA_NS {
+
+class ComputeReactISurfGridKokkos : public ComputeReactISurfGrid {
+ public:
+ ComputeReactISurfGridKokkos(class SPARTA *, int, char **);
+ ComputeReactISurfGridKokkos(class SPARTA *);
+ ~ComputeReactISurfGridKokkos();
+ void init();
+ void clear();
+ int tallyinfo(surfint *&);
+ void post_process_isurf_grid();
+ void pre_surf_tally();
+ void post_surf_tally();
+
+/* ----------------------------------------------------------------------
+ tally a surface reaction for particle colliding with surf element isurf
+ mirrors ComputeReactISurfGrid::surf_tally(); per-surf tally, compressed to
+ the host in tallyinfo(); post-processing (collate to per-grid) is on host
+------------------------------------------------------------------------- */
+
+ template
+ KOKKOS_INLINE_FUNCTION
+ void surf_tally_kk(double /*dtremain*/, int isurf, int /*icell*/, int reaction,
+ Particle::OnePart * /*iorig*/,
+ Particle::OnePart * /*ip*/, Particle::OnePart * /*jp*/) const
+ {
+ // skip if no reaction
+
+ if (reaction == 0) return;
+ reaction--;
+
+ // skip if isurf not in group or its reaction model is not a match
+
+ surfint surfID;
+ if (dim == 2) {
+ if (!(d_lines[isurf].mask & groupbit)) return;
+ if (d_lines[isurf].isr != isr) return;
+ surfID = d_lines[isurf].id;
+ } else {
+ if (!(d_tris[isurf].mask & groupbit)) return;
+ if (d_tris[isurf].isr != isr) return;
+ surfID = d_tris[isurf].id;
+ }
+
+ int itally = isurf;
+ d_tally2surf(itally) = surfID;
+ d_surf2tally(isurf) = isurf;
+
+ auto v_array_surf_tally = ScatterViewHelper::value,decltype(dup_array_surf_tally),decltype(ndup_array_surf_tally)>::get(dup_array_surf_tally,ndup_array_surf_tally);
+ auto a_array_surf_tally = v_array_surf_tally.template access::value>();
+
+ if (rpflag) {
+ for (int i = 0; i < ntotal; i++)
+ if (d_reaction2col(reaction,i)) a_array_surf_tally(itally,i) += 1.0;
+ } else a_array_surf_tally(itally,reaction) += 1.0;
+ }
+
+ private:
+ DAT::t_int_2d d_reaction2col; // [nreaction][ntotal], only if rpflag
+
+ DAT::tdual_float_2d_lr k_array_surf_tally;
+ DAT::t_float_2d_lr d_array_surf_tally;
+
+ int need_dup;
+ Kokkos::Experimental::ScatterView dup_array_surf_tally;
+ Kokkos::Experimental::ScatterView ndup_array_surf_tally;
+
+ DAT::t_surfint_1d d_tally2surf;
+ DAT::tdual_surfint_1d k_tally2surf;
+ DAT::t_int_1d d_surf2tally;
+
+ t_line_1d d_lines;
+ t_tri_1d d_tris;
+
+ int nsurf_tally_alloc; // current device tally allocation (nsurf)
+
+ void grow_tally();
+ void resize_device(int);
+};
+
+}
+
+#endif
+#endif
diff --git a/src/KOKKOS/compute_react_surf_kokkos.cpp b/src/KOKKOS/compute_react_surf_kokkos.cpp
new file mode 100644
index 000000000..9e9b7c8ff
--- /dev/null
+++ b/src/KOKKOS/compute_react_surf_kokkos.cpp
@@ -0,0 +1,186 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "string.h"
+#include "compute_react_surf_kokkos.h"
+#include "surf_kokkos.h"
+#include "surf_react.h"
+#include "grid.h"
+#include "update.h"
+#include "memory_kokkos.h"
+#include "error.h"
+#include "sparta_masks.h"
+#include "kokkos.h"
+
+using namespace SPARTA_NS;
+
+/* ---------------------------------------------------------------------- */
+
+ComputeReactSurfKokkos::ComputeReactSurfKokkos(SPARTA *sparta, int narg, char **arg) :
+ ComputeReactSurf(sparta, narg, arg)
+{
+ kokkos_flag = 1;
+}
+
+ComputeReactSurfKokkos::ComputeReactSurfKokkos(SPARTA *sparta) :
+ ComputeReactSurf(sparta)
+{
+ copy = 1;
+ uncopy = 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+ComputeReactSurfKokkos::~ComputeReactSurfKokkos()
+{
+ if (copy) return;
+
+ memoryKK->destroy_kokkos(k_tally2surf,tally2surf);
+ memoryKK->destroy_kokkos(k_array_surf_tally,array_surf_tally);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactSurfKokkos::init()
+{
+ ComputeReactSurf::init();
+
+ if (rpflag) {
+ int nreaction = surf->sr[isr]->nlist;
+ d_reaction2col = DAT::t_int_2d("react/surf:reaction2col",nreaction,ntotal);
+ auto h_r2c = Kokkos::create_mirror_view(d_reaction2col);
+ for (int i = 0; i < nreaction; i++)
+ for (int j = 0; j < ntotal; j++)
+ h_r2c(i,j) = reaction2col[i][j];
+ Kokkos::deep_copy(d_reaction2col,h_r2c);
+ }
+
+ resize_device(surf->nlocal + surf->nghost);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactSurfKokkos::resize_device(int nsurf)
+{
+ if (nsurf < 1) nsurf = 1;
+
+ memoryKK->grow_kokkos(k_tally2surf,tally2surf,nsurf,"react/surf:tally2surf");
+ d_tally2surf = k_tally2surf.view_device();
+
+ d_surf2tally = DAT::t_int_1d("react/surf:surf2tally",nsurf);
+ Kokkos::deep_copy(d_surf2tally,-1);
+
+ memoryKK->grow_kokkos(k_array_surf_tally,array_surf_tally,nsurf,ntotal,
+ "react/surf:array_surf_tally");
+ d_array_surf_tally = k_array_surf_tally.view_device();
+
+ nsurf_tally_alloc = nsurf;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactSurfKokkos::clear()
+{
+ int nsurf = surf->nlocal + surf->nghost;
+ if (nsurf > nsurf_tally_alloc) resize_device(nsurf);
+
+ Kokkos::deep_copy(d_array_surf_tally,0);
+ Kokkos::deep_copy(d_surf2tally,-1);
+
+ ntally = 0;
+ combined = 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactSurfKokkos::pre_surf_tally()
+{
+ SurfKokkos* surf_kk = (SurfKokkos*) surf;
+ surf_kk->sync(Device,ALL_MASK);
+ d_lines = surf_kk->k_lines.view_device();
+ d_tris = surf_kk->k_tris.view_device();
+
+ need_dup = sparta->kokkos->need_dup();
+ if (need_dup)
+ dup_array_surf_tally = Kokkos::Experimental::create_scatter_view(d_array_surf_tally);
+ else
+ ndup_array_surf_tally = Kokkos::Experimental::create_scatter_view(d_array_surf_tally);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactSurfKokkos::post_surf_tally()
+{
+ if (need_dup) {
+ Kokkos::Experimental::contribute(d_array_surf_tally, dup_array_surf_tally);
+ dup_array_surf_tally = {};
+ }
+
+ k_tally2surf.modify_device();
+ k_array_surf_tally.modify_device();
+}
+
+/* ----------------------------------------------------------------------
+ sync device tallies to host and compress to dense list (ntally tallies)
+------------------------------------------------------------------------- */
+
+int ComputeReactSurfKokkos::tallyinfo(surfint *&ptr)
+{
+ k_tally2surf.sync_host();
+ ptr = tally2surf;
+
+ k_array_surf_tally.sync_host();
+ auto h_surf2tally = Kokkos::create_mirror_view(d_surf2tally);
+ Kokkos::deep_copy(h_surf2tally,d_surf2tally);
+
+ int nsurf = surf->nlocal + surf->nghost;
+ int istart = 0;
+ int iend = nsurf-1;
+
+ while (1) {
+ while (h_surf2tally[istart] != -1 && istart < nsurf-2) istart++;
+ while (h_surf2tally[iend] == -1 && iend > 0) iend--;
+ if (istart >= iend) {
+ ntally = istart;
+ break;
+ }
+ for (int k = 0; k < ntotal; k++)
+ array_surf_tally[istart][k] = array_surf_tally[iend][k];
+ h_surf2tally[istart] = h_surf2tally[iend];
+ h_surf2tally[iend] = -1;
+ tally2surf[istart] = tally2surf[iend];
+ }
+
+ return ntally;
+}
+
+/* ----------------------------------------------------------------------
+ sync device tally to host before the host base collates to per-surf;
+ consumers (dump surf, compute reduce, ...) read via post_process_surf()
+------------------------------------------------------------------------- */
+
+void ComputeReactSurfKokkos::post_process_surf()
+{
+ if (combined) return;
+ surfint *dummy;
+ tallyinfo(dummy);
+ ComputeReactSurf::post_process_surf();
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputeReactSurfKokkos::grow_tally()
+{
+ resize_device(surf->nlocal + surf->nghost);
+}
diff --git a/src/KOKKOS/compute_react_surf_kokkos.h b/src/KOKKOS/compute_react_surf_kokkos.h
new file mode 100644
index 000000000..91a9a38b2
--- /dev/null
+++ b/src/KOKKOS/compute_react_surf_kokkos.h
@@ -0,0 +1,105 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef COMPUTE_CLASS
+
+ComputeStyle(react/surf/kk,ComputeReactSurfKokkos)
+
+#else
+
+#ifndef SPARTA_COMPUTE_REACT_SURF_KOKKOS_H
+#define SPARTA_COMPUTE_REACT_SURF_KOKKOS_H
+
+#include "compute_react_surf.h"
+#include "kokkos_type.h"
+
+namespace SPARTA_NS {
+
+class ComputeReactSurfKokkos : public ComputeReactSurf {
+ public:
+ ComputeReactSurfKokkos(class SPARTA *, int, char **);
+ ComputeReactSurfKokkos(class SPARTA *);
+ ~ComputeReactSurfKokkos();
+ void init();
+ void clear();
+ int tallyinfo(surfint *&);
+ void post_process_surf();
+ void pre_surf_tally();
+ void post_surf_tally();
+
+/* ----------------------------------------------------------------------
+ tally a surface reaction for particle colliding with surf element isurf
+ mirrors ComputeReactSurf::surf_tally(); per-surf tally compressed to host
+------------------------------------------------------------------------- */
+
+ template
+ KOKKOS_INLINE_FUNCTION
+ void surf_tally_kk(double /*dtremain*/, int isurf, int /*icell*/, int reaction,
+ Particle::OnePart * /*iorig*/,
+ Particle::OnePart * /*ip*/, Particle::OnePart * /*jp*/) const
+ {
+ if (reaction == 0) return;
+ reaction--;
+
+ surfint surfID;
+ if (dim == 2) {
+ if (!(d_lines[isurf].mask & groupbit)) return;
+ if (d_lines[isurf].isr != isr) return;
+ surfID = d_lines[isurf].id;
+ } else {
+ if (!(d_tris[isurf].mask & groupbit)) return;
+ if (d_tris[isurf].isr != isr) return;
+ surfID = d_tris[isurf].id;
+ }
+
+ int itally = isurf;
+ d_tally2surf(itally) = surfID;
+ d_surf2tally(isurf) = isurf;
+
+ auto v_array_surf_tally = ScatterViewHelper::value,decltype(dup_array_surf_tally),decltype(ndup_array_surf_tally)>::get(dup_array_surf_tally,ndup_array_surf_tally);
+ auto a_array_surf_tally = v_array_surf_tally.template access::value>();
+
+ if (rpflag) {
+ for (int i = 0; i < ntotal; i++)
+ if (d_reaction2col(reaction,i)) a_array_surf_tally(itally,i) += 1.0;
+ } else a_array_surf_tally(itally,reaction) += 1.0;
+ }
+
+ private:
+ DAT::t_int_2d d_reaction2col;
+
+ DAT::tdual_float_2d_lr k_array_surf_tally;
+ DAT::t_float_2d_lr d_array_surf_tally;
+
+ int need_dup;
+ Kokkos::Experimental::ScatterView dup_array_surf_tally;
+ Kokkos::Experimental::ScatterView ndup_array_surf_tally;
+
+ DAT::t_surfint_1d d_tally2surf;
+ DAT::tdual_surfint_1d k_tally2surf;
+ DAT::t_int_1d d_surf2tally;
+
+ t_line_1d d_lines;
+ t_tri_1d d_tris;
+
+ int nsurf_tally_alloc;
+
+ void grow_tally();
+ void resize_device(int);
+};
+
+}
+
+#endif
+#endif
diff --git a/src/KOKKOS/fix_ave_grid_kokkos.cpp b/src/KOKKOS/fix_ave_grid_kokkos.cpp
index 431e07609..320b12a8f 100644
--- a/src/KOKKOS/fix_ave_grid_kokkos.cpp
+++ b/src/KOKKOS/fix_ave_grid_kokkos.cpp
@@ -51,8 +51,13 @@ FixAveGridKokkos::FixAveGridKokkos(SPARTA *sparta, int narg, char **arg) :
datamask_read = EMPTY_MASK;
datamask_modify = EMPTY_MASK;
- if (flavor == PERGRIDSURF)
- error->all(FLERR,"Cannot yet use Kokkos with fix ave/grid for grid/surf inputs");
+ // PERGRIDSURF (grid/surf inputs, e.g. compute isurf/grid) runs on the host:
+ // the per-surf tally is produced on-device by the Kokkos compute and brought
+ // to the host by its tallyinfo(), then collated to per-grid by the host base
+ // class. Skip all Kokkos-specific allocation and leave the host base ctor's
+ // allocations intact; the overridden methods below delegate to FixAveGrid.
+
+ if (flavor == PERGRIDSURF) return;
nglocal = maxgrid = grid->nlocal;
@@ -109,6 +114,8 @@ FixAveGridKokkos::~FixAveGridKokkos()
{
if (copymode) return;
+ if (flavor == PERGRIDSURF) return;
+
if (nvalues == 1) memoryKK->destroy_kokkos(k_vector_grid,vector_grid);
else memoryKK->destroy_kokkos(k_array_grid,array_grid);
memoryKK->destroy_kokkos(k_tally,tally);
@@ -120,6 +127,13 @@ FixAveGridKokkos::~FixAveGridKokkos()
void FixAveGridKokkos::init()
{
+ // PERGRIDSURF path runs entirely on the host
+
+ if (flavor == PERGRIDSURF) {
+ FixAveGrid::init();
+ return;
+ }
+
// set indices and check validity of all computes,fixes,variables,custom attributes
for (int m = 0; m < nvalues; m++) {
@@ -161,6 +175,15 @@ void FixAveGridKokkos::end_of_step()
int j,n;
//int *itmp;
+ // PERGRIDSURF path runs entirely on the host: the Kokkos compute's device
+ // surf tally is brought to the host by its tallyinfo(), then the host base
+ // class collates per-surf tallies to per-grid output
+
+ if (flavor == PERGRIDSURF) {
+ FixAveGrid::end_of_step();
+ return;
+ }
+
// skip if not step which requires doing something
bigint ntimestep = update->ntimestep;
@@ -492,6 +515,15 @@ void FixAveGridKokkos::operator()(TagFixAveGrid_Norm_array_grid, const int &i) c
void FixAveGridKokkos::grow_percell(int nnew)
{
+ // PERGRIDSURF keeps its per-cell arrays in host memory (managed by the host
+ // base class); reallocating them with Kokkos memory here would make the host
+ // base destructor free a Kokkos-allocated pointer
+
+ if (flavor == PERGRIDSURF) {
+ FixAveGrid::grow_percell(nnew);
+ return;
+ }
+
if (nglocal+nnew < maxgrid) return;
maxgrid += DELTAGRID;
int n = maxgrid;
diff --git a/src/KOKKOS/fix_temp_global_rescale_kokkos.cpp b/src/KOKKOS/fix_temp_global_rescale_kokkos.cpp
new file mode 100644
index 000000000..2cc470e35
--- /dev/null
+++ b/src/KOKKOS/fix_temp_global_rescale_kokkos.cpp
@@ -0,0 +1,106 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "fix_temp_global_rescale_kokkos.h"
+#include "update.h"
+#include "particle_kokkos.h"
+#include "sparta_masks.h"
+
+using namespace SPARTA_NS;
+
+/* ---------------------------------------------------------------------- */
+
+FixTempGlobalRescaleKokkos::FixTempGlobalRescaleKokkos(SPARTA *sparta, int narg, char **arg) :
+ FixTempGlobalRescale(sparta, narg, arg)
+{
+ kokkos_flag = 1;
+ execution_space = Device;
+ datamask_read = EMPTY_MASK;
+ datamask_modify = EMPTY_MASK;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void FixTempGlobalRescaleKokkos::end_of_step()
+{
+ if (update->ntimestep % nevery) return;
+
+ // set current t_target
+
+ double delta = update->ntimestep - update->beginstep;
+ if (delta != 0.0) delta /= update->endstep - update->beginstep;
+ double t_target = tstart + delta * (tstop-tstart);
+
+ // t_current = global temperature
+ // just return if no particles or t_current = 0.0
+
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ particle_kk->sync(Device,PARTICLE_MASK|SPECIES_MASK);
+ d_particles = particle_kk->k_particles.view_device();
+ d_species = particle_kk->k_species.view_device();
+
+ int nlocal = particle->nlocal;
+
+ // 1st pass: t = sum over my particles of mass*(v.v)
+
+ double t = 0.0;
+
+ copymode = 1;
+ Kokkos::parallel_reduce(Kokkos::RangePolicy(0,nlocal),*this,t);
+ copymode = 0;
+
+ double t_current;
+ MPI_Allreduce(&t,&t_current,1,MPI_DOUBLE,MPI_SUM,world);
+
+ bigint n = particle->nlocal;
+ MPI_Allreduce(&n,&particle->nglobal,1,MPI_SPARTA_BIGINT,MPI_SUM,world);
+ if (particle->nglobal == 0 || t_current == 0.0) return;
+
+ double tscale = update->mvv2e / (3.0 * particle->nglobal * update->boltz);
+ t_current *= tscale;
+
+ // rescale all particle velocities
+
+ t_target = t_current - fraction*(t_current-t_target);
+ vscale = sqrt(t_target/t_current);
+
+ // 2nd pass: rescale velocities of all my particles
+
+ copymode = 1;
+ Kokkos::parallel_for(Kokkos::RangePolicy(0,nlocal),*this);
+ copymode = 0;
+
+ particle_kk->modify(Device,PARTICLE_MASK);
+}
+
+/* ---------------------------------------------------------------------- */
+
+KOKKOS_INLINE_FUNCTION
+void FixTempGlobalRescaleKokkos::operator()(TagFixTempGlobalRescale_reduce,
+ const int &i, double &t) const {
+ const double *v = d_particles[i].v;
+ t += (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]) *
+ d_species[d_particles[i].ispecies].mass;
+}
+
+/* ---------------------------------------------------------------------- */
+
+KOKKOS_INLINE_FUNCTION
+void FixTempGlobalRescaleKokkos::operator()(TagFixTempGlobalRescale_scale,
+ const int &i) const {
+ double *v = d_particles[i].v;
+ v[0] *= vscale;
+ v[1] *= vscale;
+ v[2] *= vscale;
+}
diff --git a/src/KOKKOS/fix_temp_global_rescale_kokkos.h b/src/KOKKOS/fix_temp_global_rescale_kokkos.h
new file mode 100644
index 000000000..3dfa260cb
--- /dev/null
+++ b/src/KOKKOS/fix_temp_global_rescale_kokkos.h
@@ -0,0 +1,64 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef FIX_CLASS
+
+FixStyle(temp/global/rescale/kk,FixTempGlobalRescaleKokkos)
+
+#else
+
+#ifndef SPARTA_FIX_TEMP_GLOBAL_RESCALE_KOKKOS_H
+#define SPARTA_FIX_TEMP_GLOBAL_RESCALE_KOKKOS_H
+
+#include "fix_temp_global_rescale.h"
+#include "kokkos_type.h"
+
+namespace SPARTA_NS {
+
+struct TagFixTempGlobalRescale_reduce{};
+struct TagFixTempGlobalRescale_scale{};
+
+class FixTempGlobalRescaleKokkos : public FixTempGlobalRescale {
+ public:
+ FixTempGlobalRescaleKokkos(class SPARTA *, int, char **);
+ virtual ~FixTempGlobalRescaleKokkos() {}
+ void end_of_step() override;
+
+ KOKKOS_INLINE_FUNCTION
+ void operator()(TagFixTempGlobalRescale_reduce, const int&, double&) const;
+
+ KOKKOS_INLINE_FUNCTION
+ void operator()(TagFixTempGlobalRescale_scale, const int&) const;
+
+ private:
+ double vscale;
+
+ t_particle_1d d_particles;
+ t_species_1d d_species;
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Illegal ... command
+
+Self-explanatory. Check the input script syntax and compare to the
+documentation for the command. You can use -echo screen as a
+command-line option when running SPARTA to see the offending line.
+
+*/
diff --git a/src/KOKKOS/react_qk_kokkos.cpp b/src/KOKKOS/react_qk_kokkos.cpp
new file mode 100644
index 000000000..1e3b276eb
--- /dev/null
+++ b/src/KOKKOS/react_qk_kokkos.cpp
@@ -0,0 +1,59 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "string.h"
+#include "react_qk_kokkos.h"
+#include "particle.h"
+#include "collide.h"
+#include "update.h"
+#include "error.h"
+
+using namespace SPARTA_NS;
+
+/* ---------------------------------------------------------------------- */
+
+ReactQKKokkos::ReactQKKokkos(SPARTA *sparta, int narg, char **arg) :
+ ReactBirdKokkos(sparta, narg, arg) {}
+
+/* ---------------------------------------------------------------------- */
+
+void ReactQKKokkos::init()
+{
+ if (!collide || (strcmp(collide->style,"vss") != 0 &&
+ strcmp(collide->style,"vss/kk") != 0))
+ error->all(FLERR,"React qk can only be used with collide vss");
+
+ ReactBirdKokkos::init();
+
+ // do not allow recombination reactions (not supported by QK)
+
+ for (int i = 0; i < nlist; i++)
+ if (rlist[i].active && rlist[i].type == RECOMBINATION)
+ error->all(FLERR,"React qk does not currently support recombination reactions");
+ if (computeChemRates)
+ error->all(FLERR,"React qk does not currently support the "
+ "'react_modify compute_chem_rates' option");
+
+ boltz = update->boltz;
+
+ // flatten VSS omega for all species pairs to device
+
+ int nspecies = particle->nspecies;
+ d_omega = DAT::t_float_2d("react/qk:omega",nspecies,nspecies);
+ auto h_omega = Kokkos::create_mirror_view(d_omega);
+ for (int i = 0; i < nspecies; i++)
+ for (int j = 0; j < nspecies; j++)
+ h_omega(i,j) = collide->extract(i,j,"omega");
+ Kokkos::deep_copy(d_omega,h_omega);
+}
diff --git a/src/KOKKOS/react_qk_kokkos.h b/src/KOKKOS/react_qk_kokkos.h
new file mode 100644
index 000000000..e2e592d72
--- /dev/null
+++ b/src/KOKKOS/react_qk_kokkos.h
@@ -0,0 +1,164 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef REACT_CLASS
+
+ReactStyle(qk/kk,ReactQKKokkos)
+
+#else
+
+#ifndef SPARTA_REACT_QK_KOKKOS_H
+#define SPARTA_REACT_QK_KOKKOS_H
+
+#include "math.h"
+#include "react_bird_kokkos.h"
+#include "kokkos_type.h"
+
+namespace SPARTA_NS {
+
+class ReactQKKokkos : public ReactBirdKokkos {
+ public:
+ ReactQKKokkos(class SPARTA *, int, char **);
+ ReactQKKokkos(class SPARTA* sparta) : ReactBirdKokkos(sparta) {copy = 1;}
+ void init();
+ int attempt(Particle::OnePart *, Particle::OnePart *,
+ double, double, double, double &, int &) {return 0;}
+
+ enum{DISSOCIATION,EXCHANGE,IONIZATION,RECOMBINATION}; // other files
+
+/* ----------------------------------------------------------------------
+ quantum-kinetic (QK) reaction attempt, device version
+ mirrors ReactQK::attempt(); supports DISSOCIATION and EXCHANGE
+ recomb args are accepted for a uniform collide dispatch but unused (QK has
+ no recombination)
+------------------------------------------------------------------------- */
+
+KOKKOS_INLINE_FUNCTION
+int attempt_kk(Particle::OnePart *ip, Particle::OnePart *jp,
+ double pre_etrans, double pre_erot, double pre_evib,
+ double &post_etotal, int &kspecies,
+ int & /*recomb_species*/, double & /*recomb_density*/,
+ const t_species_1d_const &d_species) const
+{
+ const int isp = ip->ispecies;
+ const int jsp = jp->ispecies;
+
+ const double pre_ave_rotdof = (d_species[isp].rotdof + d_species[jsp].rotdof)/2.0;
+ const double omega = d_omega(isp,jsp);
+
+ const int n = d_reactions(isp,jsp).n;
+ if (n == 0) return 0;
+ auto& d_list = d_reactions(isp,jsp).d_list;
+
+ double react_prob = 0.0;
+ rand_type rand_gen = rand_pool.get_state();
+ const double random_prob = rand_gen.drand();
+
+ for (int i = 0; i < n; i++) {
+ OneReactionKokkos *r = &d_rlist[d_list[i]];
+
+ const double pre_etotal = pre_etrans + pre_erot + pre_evib;
+
+ double ecc = pre_etrans;
+ if (pre_ave_rotdof > 0.1) ecc += pre_erot*r->d_coeff[0]/pre_ave_rotdof;
+
+ double e_excess = ecc - r->d_coeff[1];
+ if (e_excess <= 0.0) continue;
+
+ const double inverse_kT = 1.0 / (boltz * d_species[isp].vibtemp[0]);
+
+ int iv = 0,ilevel,maxlev,limlev;
+
+ switch (r->type) {
+ case DISSOCIATION:
+ {
+ ecc = pre_etrans + ip->evib;
+ maxlev = static_cast (ecc * inverse_kT);
+ limlev = static_cast (fabs(r->d_coeff[1]) * inverse_kT);
+ if (maxlev > limlev) react_prob = 1.0;
+ break;
+ }
+ case EXCHANGE:
+ {
+ if (r->d_coeff[4] < 0.0 && d_species[isp].rotdof > 0) {
+
+ // endothermic reaction
+
+ ecc = pre_etrans + ip->evib;
+ maxlev = static_cast (ecc * inverse_kT);
+ if (ecc > r->d_coeff[1]) {
+ do {
+ iv = static_cast (rand_gen.drand()*(maxlev+0.99999999));
+ double evib = static_cast (iv / inverse_kT);
+ if (evib < ecc) react_prob = pow(1.0-evib/ecc,1.5-omega);
+ } while (rand_gen.drand() < react_prob);
+
+ ilevel = static_cast (fabs(r->d_coeff[4]) * inverse_kT);
+ if (iv >= ilevel) react_prob = 1.0;
+ }
+
+ } else if (r->d_coeff[4] > 0.0 && d_species[isp].rotdof > 0) {
+
+ ecc = pre_etrans + ip->evib;
+
+ // mspec = post-collision molecular species
+
+ int mspec = r->d_products[0];
+ if (d_species[mspec].rotdof < 2.0) mspec = r->d_products[1];
+
+ ecc += r->d_coeff[4];
+ maxlev = static_cast (ecc * inverse_kT);
+ double prob = 0.0;
+ do {
+ iv = rand_gen.drand()*(maxlev+0.99999999);
+ double evib = static_cast (iv * boltz*d_species[mspec].vibtemp[0]);
+ if (evib < ecc) prob = pow(1.0-evib/ecc,1.5 - r->d_coeff[6]);
+ } while (rand_gen.drand() < prob);
+
+ ilevel = static_cast (fabs(r->d_coeff[4]/boltz/d_species[mspec].vibtemp[0]));
+ if (iv >= ilevel) react_prob = 1.0;
+ }
+
+ break;
+ }
+ default:
+ Kokkos::abort("ReactQKKokkos: Unknown outcome in reaction\n");
+ break;
+ }
+
+ if (react_prob > random_prob) {
+ Kokkos::atomic_inc(&d_tally_reactions[d_list[i]]);
+ ip->ispecies = r->d_products[0];
+ jp->ispecies = r->d_products[1];
+ post_etotal = pre_etotal + r->d_coeff[4];
+ if (r->nproduct > 2) kspecies = r->d_products[2];
+ else kspecies = -1;
+ rand_pool.free_state(rand_gen);
+ return d_list[i] + 1;
+ }
+ }
+
+ rand_pool.free_state(rand_gen);
+ return 0;
+}
+
+ protected:
+ double boltz;
+ DAT::t_float_2d d_omega; // VSS omega for each species pair
+};
+
+}
+
+#endif
+#endif
diff --git a/src/KOKKOS/react_tce_qk_kokkos.cpp b/src/KOKKOS/react_tce_qk_kokkos.cpp
new file mode 100644
index 000000000..0cc06574b
--- /dev/null
+++ b/src/KOKKOS/react_tce_qk_kokkos.cpp
@@ -0,0 +1,59 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "string.h"
+#include "react_tce_qk_kokkos.h"
+#include "particle.h"
+#include "collide.h"
+#include "update.h"
+#include "error.h"
+
+using namespace SPARTA_NS;
+
+/* ---------------------------------------------------------------------- */
+
+ReactTCEQKKokkos::ReactTCEQKKokkos(SPARTA *sparta, int narg, char **arg) :
+ ReactBirdKokkos(sparta, narg, arg) {}
+
+/* ---------------------------------------------------------------------- */
+
+void ReactTCEQKKokkos::init()
+{
+ if (!collide || (strcmp(collide->style,"vss") != 0 &&
+ strcmp(collide->style,"vss/kk") != 0))
+ error->all(FLERR,"React tce/qk can only be used with collide vss");
+
+ ReactBirdKokkos::init();
+
+ // do not allow recombination reactions (not supported)
+
+ for (int i = 0; i < nlist; i++)
+ if (rlist[i].active && rlist[i].type == RECOMBINATION)
+ error->all(FLERR,"React tce/qk does not currently support recombination reactions");
+ if (computeChemRates)
+ error->all(FLERR,"React tce/qk does not currently support the "
+ "'react_modify compute_chem_rates' option");
+
+ boltz = update->boltz;
+
+ // flatten VSS omega for all species pairs to device
+
+ int nspecies = particle->nspecies;
+ d_omega = DAT::t_float_2d("react/tce/qk:omega",nspecies,nspecies);
+ auto h_omega = Kokkos::create_mirror_view(d_omega);
+ for (int i = 0; i < nspecies; i++)
+ for (int j = 0; j < nspecies; j++)
+ h_omega(i,j) = collide->extract(i,j,"omega");
+ Kokkos::deep_copy(d_omega,h_omega);
+}
diff --git a/src/KOKKOS/react_tce_qk_kokkos.h b/src/KOKKOS/react_tce_qk_kokkos.h
new file mode 100644
index 000000000..01869f584
--- /dev/null
+++ b/src/KOKKOS/react_tce_qk_kokkos.h
@@ -0,0 +1,179 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef REACT_CLASS
+
+ReactStyle(tce/qk/kk,ReactTCEQKKokkos)
+
+#else
+
+#ifndef SPARTA_REACT_TCE_QK_KOKKOS_H
+#define SPARTA_REACT_TCE_QK_KOKKOS_H
+
+#include "math.h"
+#include "react_bird_kokkos.h"
+#include "kokkos_type.h"
+
+namespace SPARTA_NS {
+
+class ReactTCEQKKokkos : public ReactBirdKokkos {
+ public:
+ ReactTCEQKKokkos(class SPARTA *, int, char **);
+ ReactTCEQKKokkos(class SPARTA* sparta) : ReactBirdKokkos(sparta) {copy = 1;}
+ void init();
+ int attempt(Particle::OnePart *, Particle::OnePart *,
+ double, double, double, double &, int &) {return 0;}
+
+ enum{DISSOCIATION,EXCHANGE,IONIZATION,RECOMBINATION}; // reaction types
+ enum{ARRHENIUS,QUANTUM}; // reaction styles
+
+/* ----------------------------------------------------------------------
+ hybrid TCE/QK reaction attempt, device version
+ mirrors ReactTCEQK::attempt(): per reaction, ARRHENIUS style uses the simple
+ TCE probability, QUANTUM style uses the QK model; each evaluated reaction
+ draws its own random number (matching the host RNG order)
+------------------------------------------------------------------------- */
+
+KOKKOS_INLINE_FUNCTION
+int attempt_kk(Particle::OnePart *ip, Particle::OnePart *jp,
+ double pre_etrans, double pre_erot, double pre_evib,
+ double &post_etotal, int &kspecies,
+ int & /*recomb_species*/, double & /*recomb_density*/,
+ const t_species_1d_const &d_species) const
+{
+ const int isp = ip->ispecies;
+ const int jsp = jp->ispecies;
+
+ const int n = d_reactions(isp,jsp).n;
+ if (n == 0) return 0;
+ auto& d_list = d_reactions(isp,jsp).d_list;
+
+ const double pre_ave_rotdof = (d_species[isp].rotdof + d_species[jsp].rotdof)/2.0;
+ const double omega = d_omega(isp,jsp);
+
+ rand_type rand_gen = rand_pool.get_state();
+
+ for (int i = 0; i < n; i++) {
+ OneReactionKokkos *r = &d_rlist[d_list[i]];
+
+ const double pre_etotal = pre_etrans + pre_erot + pre_evib;
+
+ // top-level energetic-possibility screen (uses total energy)
+
+ double ecc = pre_etotal;
+ if (ecc - r->d_coeff[1] <= 0.0) continue;
+
+ int fired = 0;
+
+ // per-reaction probability + its own random draw (helper semantics)
+
+ const double random_prob = rand_gen.drand();
+
+ double react_prob = 0.0;
+ double ecc2 = pre_etrans;
+ if (pre_ave_rotdof > 0.1) ecc2 += pre_erot*r->d_coeff[0]/pre_ave_rotdof;
+ const double e_excess = ecc2 - r->d_coeff[1];
+
+ if (e_excess > 0.0) {
+ if (r->style == ARRHENIUS) { // attempt_tce
+ switch (r->type) {
+ case DISSOCIATION:
+ case EXCHANGE:
+ react_prob += r->d_coeff[2] *
+ pow(ecc2-r->d_coeff[1],r->d_coeff[3]) *
+ pow(1.0-r->d_coeff[1]/ecc2,r->d_coeff[5]);
+ break;
+ default:
+ Kokkos::abort("ReactTCEQKKokkos: Unknown outcome in reaction\n");
+ break;
+ }
+ if (react_prob > random_prob) fired = 1;
+
+ } else { // attempt_qk
+ const double inverse_kT = 1.0 / (boltz * d_species[isp].vibtemp[0]);
+ int iv = 0,ilevel,maxlev,limlev;
+ double eccq;
+ switch (r->type) {
+ case DISSOCIATION:
+ {
+ eccq = pre_etrans + ip->evib;
+ maxlev = static_cast (eccq * inverse_kT);
+ limlev = static_cast (fabs(r->d_coeff[1]) * inverse_kT);
+ if (maxlev > limlev) react_prob = 1.0;
+ break;
+ }
+ case EXCHANGE:
+ {
+ if (r->d_coeff[4] < 0.0 && d_species[isp].rotdof > 0) {
+ eccq = pre_etrans + ip->evib;
+ maxlev = static_cast (eccq * inverse_kT);
+ if (eccq > r->d_coeff[1]) {
+ do {
+ iv = static_cast (rand_gen.drand()*(maxlev+0.99999999));
+ double evib = static_cast (iv / inverse_kT);
+ if (evib < eccq) react_prob = pow(1.0-evib/eccq,1.5-omega);
+ } while (rand_gen.drand() < react_prob);
+ ilevel = static_cast (fabs(r->d_coeff[4]) * inverse_kT);
+ if (iv >= ilevel) react_prob = 1.0;
+ }
+ } else if (r->d_coeff[4] > 0.0 && d_species[isp].rotdof > 0) {
+ eccq = pre_etrans + ip->evib;
+ int mspec = r->d_products[0];
+ if (d_species[mspec].rotdof < 2.0) mspec = r->d_products[1];
+ eccq += r->d_coeff[4];
+ maxlev = static_cast (eccq * inverse_kT);
+ double prob = 0.0;
+ do {
+ iv = rand_gen.drand()*(maxlev+0.99999999);
+ double evib = static_cast (iv * boltz*d_species[mspec].vibtemp[0]);
+ if (evib < eccq) prob = pow(1.0-evib/eccq,1.5 - r->d_coeff[6]);
+ } while (rand_gen.drand() < prob);
+ ilevel = static_cast (fabs(r->d_coeff[4]/boltz/d_species[mspec].vibtemp[0]));
+ if (iv >= ilevel) react_prob = 1.0;
+ }
+ break;
+ }
+ default:
+ Kokkos::abort("ReactTCEQKKokkos: Unknown outcome in reaction\n");
+ break;
+ }
+ if (react_prob > random_prob) fired = 1;
+ }
+ }
+
+ if (fired) {
+ Kokkos::atomic_inc(&d_tally_reactions[d_list[i]]);
+ ip->ispecies = r->d_products[0];
+ jp->ispecies = r->d_products[1];
+ post_etotal = pre_etotal + r->d_coeff[4];
+ if (r->nproduct > 2) kspecies = r->d_products[2];
+ else kspecies = -1;
+ rand_pool.free_state(rand_gen);
+ return d_list[i] + 1;
+ }
+ }
+
+ rand_pool.free_state(rand_gen);
+ return 0;
+}
+
+ protected:
+ double boltz;
+ DAT::t_float_2d d_omega; // VSS omega for each species pair
+};
+
+}
+
+#endif
+#endif
diff --git a/src/KOKKOS/surf_collide_adiabatic_kokkos.cpp b/src/KOKKOS/surf_collide_adiabatic_kokkos.cpp
new file mode 100644
index 000000000..da274f9cc
--- /dev/null
+++ b/src/KOKKOS/surf_collide_adiabatic_kokkos.cpp
@@ -0,0 +1,271 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "math.h"
+#include "stdlib.h"
+#include "string.h"
+#include "surf_collide_adiabatic_kokkos.h"
+#include "surf_kokkos.h"
+#include "input.h"
+#include "variable.h"
+#include "particle.h"
+#include "domain.h"
+#include "update.h"
+#include "modify.h"
+#include "comm.h"
+#include "random_mars.h"
+#include "random_knuth.h"
+#include "math_const.h"
+#include "math_extra.h"
+#include "error.h"
+#include "particle_kokkos.h"
+#include "sparta_masks.h"
+#include "collide.h"
+
+using namespace SPARTA_NS;
+using namespace MathConst;
+
+#define VAL_1(X) X
+#define VAL_2(X) VAL_1(X), VAL_1(X)
+
+/* ---------------------------------------------------------------------- */
+
+SurfCollideAdiabaticKokkos::SurfCollideAdiabaticKokkos(SPARTA *sparta, int narg, char **arg) :
+ SurfCollideAdiabatic(sparta, narg, arg),
+ fix_ambi_kk_copy(sparta),
+ fix_vibmode_kk_copy(sparta),
+ sr_kk_global_copy{VAL_2(KKCopy(sparta))},
+ sr_kk_prob_copy{VAL_2(KKCopy(sparta))},
+ rand_pool(12345 + comm->me
+#ifdef SPARTA_KOKKOS_EXACT
+ , sparta
+#endif
+ )
+{
+ kokkosable = 1;
+
+ random_backup = NULL;
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.init(random);
+#endif
+
+ // use 1D view for scalars to reduce GPU memory operations
+
+ d_scalars = t_int_2("surf_collide_adiabatic:scalars");
+ d_nsingle = Kokkos::subview(d_scalars,0);
+ d_nreact_one = Kokkos::subview(d_scalars,1);
+
+ h_scalars = t_host_int_2("surf_collide_adiabatic:scalars_mirror");
+ h_nsingle = Kokkos::subview(h_scalars,0);
+ h_nreact_one = Kokkos::subview(h_scalars,1);
+}
+
+SurfCollideAdiabaticKokkos::SurfCollideAdiabaticKokkos(SPARTA *sparta) :
+ SurfCollideAdiabatic(sparta),
+ fix_ambi_kk_copy(sparta),
+ fix_vibmode_kk_copy(sparta),
+ sr_kk_global_copy{VAL_2(KKCopy(sparta))},
+ sr_kk_prob_copy{VAL_2(KKCopy(sparta))},
+ rand_pool(12345 // seed doesn't matter since it will just be copied over
+#ifdef SPARTA_KOKKOS_EXACT
+ , sparta
+#endif
+ )
+{
+ copy = 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+SurfCollideAdiabaticKokkos::~SurfCollideAdiabaticKokkos()
+{
+ if (uncopy) {
+ fix_ambi_kk_copy.uncopy();
+ fix_vibmode_kk_copy.uncopy();
+
+ for (int i = 0; i < KOKKOS_MAX_SURF_REACT_PER_TYPE; i++) {
+ sr_kk_global_copy[i].uncopy();
+ sr_kk_prob_copy[i].uncopy();
+ }
+ }
+
+ if (copy) return;
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.destroy();
+ if (random_backup)
+ delete random_backup;
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideAdiabaticKokkos::init()
+{
+ SurfCollideAdiabatic::init();
+
+ ambi_flag = vibmode_flag = 0;
+ if (modify->n_update_custom) {
+ for (int ifix = 0; ifix < modify->nfix; ifix++) {
+ if (strcmp(modify->fix[ifix]->style,"ambipolar") == 0) {
+ ambi_flag = 1;
+ FixAmbipolar *afix = (FixAmbipolar *) modify->fix[ifix];
+ if (!afix->kokkos_flag)
+ error->all(FLERR,"Must use fix ambipolar/kk when Kokkos is enabled");
+ afix_kk = (FixAmbipolarKokkos*)afix;
+ } else if (strcmp(modify->fix[ifix]->style,"vibmode") == 0) {
+ vibmode_flag = 1;
+ FixVibmode *vfix = (FixVibmode *) modify->fix[ifix];
+ if (!vfix->kokkos_flag)
+ error->all(FLERR,"Must use fix vibmode/kk when Kokkos is enabled");
+ vfix_kk = (FixVibmodeKokkos*)vfix;
+ }
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideAdiabaticKokkos::pre_collide()
+{
+ if (ambi_flag) {
+ afix_kk->pre_update_custom_kokkos();
+ fix_ambi_kk_copy.copy(afix_kk);
+ }
+
+ if (vibmode_flag) {
+ vfix_kk->pre_update_custom_kokkos();
+ fix_vibmode_kk_copy.copy(vfix_kk);
+ }
+
+ if (surf->nsr > KOKKOS_MAX_TOT_SURF_REACT)
+ error->all(FLERR,"Kokkos currently supports two instances of each surface reaction method");
+
+ if (surf->nsr > 0) {
+ int nglob,nprob;
+ nglob = nprob = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (!surf->sr[n]->kokkosable)
+ error->all(FLERR,"Must use Kokkos-enabled surface reaction method with Kokkos");
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].copy((SurfReactGlobalKokkos*)(surf->sr[n]));
+ sr_kk_global_copy[nglob].obj.pre_react();
+ sr_type_list[n] = 0;
+ sr_map[n] = nglob;
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].copy((SurfReactProbKokkos*)(surf->sr[n]));
+ sr_kk_prob_copy[nprob].obj.pre_react();
+ sr_type_list[n] = 1;
+ sr_map[n] = nprob;
+ nprob++;
+ } else {
+ error->all(FLERR,"Unknown Kokkos surface reaction method");
+ }
+ }
+
+ if (nglob > KOKKOS_MAX_SURF_REACT_PER_TYPE || nprob > KOKKOS_MAX_SURF_REACT_PER_TYPE)
+ error->all(FLERR,"Kokkos currently supports two instances of each surface reaction method");
+ }
+
+ if (random == NULL) {
+ // initialize RNG
+
+ random = new RanKnuth(update->ranmaster->uniform());
+ double seed = update->ranmaster->uniform();
+ random->reset(seed,comm->me,100);
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.init(random);
+#endif
+ }
+
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ particle_kk->sync(Device,PARTICLE_MASK|SPECIES_MASK);
+ d_particles = particle_kk->k_particles.view_device();
+ d_species = particle_kk->k_species.view_device();
+
+ Kokkos::deep_copy(d_scalars,0);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideAdiabaticKokkos::post_collide()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ if (ambi_flag || vibmode_flag) particle_kk->modify(Device,CUSTOM_MASK);
+
+ Kokkos::deep_copy(h_scalars,d_scalars);
+
+ int m = surf->find_collide(id);
+ auto sc = surf->sc[m]; // can't modify the copy directly, use the original
+ sc->nsingle += h_nsingle();
+ surf->nreact_one += h_nreact_one();
+
+ d_particles = {};
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideAdiabaticKokkos::backup()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ d_particles = particle_kk->k_particles.view_device();
+
+ if (surf->nsr > 0) {
+ int nglob,nprob;
+ nglob = nprob = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].obj.backup();
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].obj.backup();
+ nprob++;
+ }
+ }
+ }
+
+#ifdef SPARTA_KOKKOS_EXACT
+ if (!random_backup)
+ random_backup = new RanKnuth(12345 + comm->me);
+ memcpy(random_backup,random,sizeof(RanKnuth));
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideAdiabaticKokkos::restore()
+{
+ if (surf->nsr > 0) {
+ int nglob,nprob;
+ nglob = nprob = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].obj.restore();
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].obj.restore();
+ nprob++;
+ }
+ }
+ }
+
+ Kokkos::deep_copy(d_scalars,0);
+
+#ifdef SPARTA_KOKKOS_EXACT
+ memcpy(random,random_backup,sizeof(RanKnuth));
+#endif
+}
diff --git a/src/KOKKOS/surf_collide_adiabatic_kokkos.h b/src/KOKKOS/surf_collide_adiabatic_kokkos.h
new file mode 100644
index 000000000..bdfb1b1af
--- /dev/null
+++ b/src/KOKKOS/surf_collide_adiabatic_kokkos.h
@@ -0,0 +1,258 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef SURF_COLLIDE_CLASS
+
+SurfCollideStyle(adiabatic/kk,SurfCollideAdiabaticKokkos)
+
+#else
+
+#ifndef SPARTA_SURF_COLLIDE_ADIABATIC_KOKKOS_H
+#define SPARTA_SURF_COLLIDE_ADIABATIC_KOKKOS_H
+
+#include "surf_collide_adiabatic.h"
+#include "kokkos_type.h"
+#include "math_extra_kokkos.h"
+#include "Kokkos_Random.hpp"
+#include "rand_pool_wrap.h"
+#include "kokkos_copy.h"
+#include "fix_ambipolar_kokkos.h"
+#include "fix_vibmode_kokkos.h"
+#include "surf_react_global_kokkos.h"
+#include "surf_react_prob_kokkos.h"
+
+namespace SPARTA_NS {
+
+class SurfCollideAdiabaticKokkos : public SurfCollideAdiabatic {
+ public:
+
+ enum{PKEEP,PINSERT,PDONE,PDISCARD,PENTRY,PEXIT,PSURF}; // several files
+
+ SurfCollideAdiabaticKokkos(class SPARTA *, int, char **);
+ SurfCollideAdiabaticKokkos(class SPARTA *);
+ ~SurfCollideAdiabaticKokkos();
+ void init();
+ void pre_collide();
+ void post_collide();
+ void backup();
+ void restore();
+
+ private:
+
+#ifndef SPARTA_KOKKOS_EXACT
+ Kokkos::Random_XorShift64_Pool rand_pool;
+ typedef typename Kokkos::Random_XorShift64_Pool::generator_type rand_type;
+#else
+ RandPoolWrap rand_pool;
+ typedef RandWrap rand_type;
+#endif
+
+ RanKnuth* random_backup;
+
+ typedef Kokkos::DualView tdual_int_2;
+ typedef tdual_int_2::t_dev t_int_2;
+ typedef tdual_int_2::t_host t_host_int_2;
+ t_int_2 d_scalars;
+ t_host_int_2 h_scalars;
+
+ DAT::t_int_scalar d_nsingle;
+ DAT::t_int_scalar d_nreact_one;
+
+ HAT::t_int_scalar h_nsingle;
+ HAT::t_int_scalar h_nreact_one;
+
+ t_particle_1d d_particles;
+ t_species_1d d_species;
+
+ int ambi_flag,vibmode_flag;
+ FixAmbipolarKokkos* afix_kk;
+ FixVibmodeKokkos* vfix_kk;
+ KKCopy fix_ambi_kk_copy;
+ KKCopy fix_vibmode_kk_copy;
+
+ int sr_type_list[KOKKOS_MAX_TOT_SURF_REACT];
+ int sr_map[KOKKOS_MAX_TOT_SURF_REACT];
+ KKCopy sr_kk_global_copy[KOKKOS_MAX_SURF_REACT_PER_TYPE];
+ KKCopy sr_kk_prob_copy[KOKKOS_MAX_SURF_REACT_PER_TYPE];
+
+ public:
+
+ /* ----------------------------------------------------------------------
+ particle collision with surface with optional chemistry
+ ip = particle with current x = collision pt, current v = incident v
+ isurf = index of surface element
+ norm = surface normal unit vector
+ isr = index of reaction model if >= 0, -1 for no chemistry
+ ip = set to NULL if destroyed by chemistry
+ return jp = new particle if created by chemistry
+ return reaction = index of reaction (1 to N) that took place, 0 = no reaction
+ resets particle(s) to post-collision outward velocity
+
+ note that the adiabatic condition (i.e. no energy transfer of flow to
+ surf) only applies to particle collisions. Chemistry (e.g. particle
+ adsorptions) can lead to energy transfer in both directions, so the
+ velocities reset by SurfReact are kept as-is.
+ ------------------------------------------------------------------------- */
+
+ template
+ KOKKOS_INLINE_FUNCTION
+ Particle::OnePart* collide_kokkos(Particle::OnePart *&ip, double &,
+ int isurf, const double *norm, int isr, int &reaction,
+ const DAT::t_int_scalar &d_retry, const DAT::t_int_scalar &d_nlocal) const
+ {
+ if (ATOMIC_REDUCTION == 0)
+ d_nsingle()++;
+ else
+ Kokkos::atomic_inc(&d_nsingle());
+
+ // if surface chemistry defined, attempt reaction
+ // reaction = 1 to N for which reaction took place, 0 for none
+ // velreset = 1 if reaction reset post-collision velocity, else 0
+
+ Particle::OnePart iorig;
+ Particle::OnePart *jp = NULL;
+ reaction = 0;
+ int velreset = 0;
+
+ if (REACT) {
+ if (ambi_flag || vibmode_flag) memcpy(&iorig,ip,sizeof(Particle::OnePart));
+
+ int sr_type = sr_type_list[isr];
+ int m = sr_map[isr];
+
+ if (sr_type == 0) {
+ reaction = sr_kk_global_copy[m].obj.
+ react_kokkos(ip,isurf,norm,jp,velreset,d_retry,d_nlocal);
+ } else if (sr_type == 1) {
+ reaction = sr_kk_prob_copy[m].obj.
+ react_kokkos(ip,isurf,norm,jp,velreset,d_retry,d_nlocal);
+ }
+
+ if (reaction) {
+ if (ATOMIC_REDUCTION == 0)
+ d_nreact_one()++;
+ else
+ Kokkos::atomic_inc(&d_nreact_one());
+ }
+ }
+
+ // isotropic scattering conserving velocity magnitude (kinetic energy)
+ // of each particle
+ // only if SurfReact did not already reset velocities
+ // cannot trigger fixes that require temperature of particle here
+ // because temperature of wall is not known
+
+ if (ip) {
+ if (!velreset) scatter_isotropic(ip,norm);
+ }
+ if (REACT && jp) {
+ if (!velreset) scatter_isotropic(jp,norm);
+ }
+
+ // call any fixes with a surf_react() method
+ // they may reset j to -1, e.g. fix ambipolar
+ // in which case newly created j is deleted
+
+ if (REACT && reaction && ambi_flag) {
+ int i = -1;
+ if (ip) i = ip - d_particles.data();
+ int j = -1;
+ if (jp) j = jp - d_particles.data();
+ int j_orig = j;
+ fix_ambi_kk_copy.obj.surf_react_kokkos(&iorig,i,j);
+ if (jp && j < 0) {
+ d_particles[j_orig].flag = PDISCARD;
+ jp = NULL;
+ }
+ }
+
+ return jp;
+ };
+
+ private:
+
+ /* ----------------------------------------------------------------------
+ particle collision with adiabatic surface
+ p = particle with current x = collision pt, current v = incident v
+ norm = surface normal unit vector
+ particle is scattered isotropically while conserving its velocity
+ magnitude (i.e. no energy transfer to surf)
+ ------------------------------------------------------------------------- */
+
+ KOKKOS_INLINE_FUNCTION
+ void scatter_isotropic(Particle::OnePart *p, const double *norm) const
+ {
+ rand_type rand_gen = rand_pool.get_state();
+
+ double *v = p->v;
+ double dot = MathExtraKokkos::dot3(v,norm);
+
+ // tangent1/2 = surface tangential unit vectors
+
+ double tangent1[3],tangent2[3];
+ tangent1[0] = v[0] - dot*norm[0];
+ tangent1[1] = v[1] - dot*norm[1];
+ tangent1[2] = v[2] - dot*norm[2];
+
+ // if mag(tangent1) == 0, normal collision: choose a random tangent vector
+
+ if (MathExtraKokkos::lensq3(tangent1) == 0.0) {
+ tangent2[0] = rand_gen.drand();
+ tangent2[1] = rand_gen.drand();
+ tangent2[2] = rand_gen.drand();
+ MathExtraKokkos::cross3(norm,tangent2,tangent1);
+ }
+
+ MathExtraKokkos::norm3(tangent1);
+ MathExtraKokkos::cross3(norm,tangent1,tangent2);
+
+ // isotropic scattering
+ // vmag = magnitude of incident particle velocity vector
+ // vperp = velocity component perpendicular to surface along norm
+ // vtan1/2 = 2 remaining velocity components tangential to surface
+
+ double vmag = MathExtraKokkos::len3(v);
+
+ double theta = MathConst::MY_2PI * rand_gen.drand();
+ double f_phi = rand_gen.drand();
+ double sqrt_f_phi = sqrt(f_phi);
+
+ double vperp = vmag * sqrt(1.0 - f_phi);
+ double vtan1 = vmag * sqrt_f_phi * sin(theta);
+ double vtan2 = vmag * sqrt_f_phi * cos(theta);
+
+ v[0] = vperp*norm[0] + vtan1*tangent1[0] + vtan2*tangent2[0];
+ v[1] = vperp*norm[1] + vtan1*tangent1[1] + vtan2*tangent2[1];
+ v[2] = vperp*norm[2] + vtan1*tangent1[2] + vtan2*tangent2[2];
+
+ // p->erot and p->evib stay identical
+
+ rand_pool.free_state(rand_gen);
+ }
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Illegal ... command
+
+Self-explanatory. Check the input script syntax and compare to the
+documentation for the command. You can use -echo screen as a
+command-line option when running SPARTA to see the offending line.
+
+*/
diff --git a/src/KOKKOS/surf_collide_cll_kokkos.cpp b/src/KOKKOS/surf_collide_cll_kokkos.cpp
new file mode 100644
index 000000000..2cf188db6
--- /dev/null
+++ b/src/KOKKOS/surf_collide_cll_kokkos.cpp
@@ -0,0 +1,374 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "math.h"
+#include "stdlib.h"
+#include "string.h"
+#include "surf_collide_cll_kokkos.h"
+#include "surf_kokkos.h"
+#include "input.h"
+#include "variable.h"
+#include "particle.h"
+#include "domain.h"
+#include "update.h"
+#include "modify.h"
+#include "comm.h"
+#include "random_mars.h"
+#include "random_knuth.h"
+#include "math_const.h"
+#include "math_extra.h"
+#include "memory.h"
+#include "error.h"
+#include "particle_kokkos.h"
+#include "sparta_masks.h"
+#include "collide.h"
+
+using namespace SPARTA_NS;
+using namespace MathConst;
+
+enum{INT,DOUBLE}; // several files
+enum{NUMERIC,CUSTOM,VARIABLE,VAREQUAL,VARSURF}; // surf_collide classes
+
+#define VAL_1(X) X
+#define VAL_2(X) VAL_1(X), VAL_1(X)
+
+/* ---------------------------------------------------------------------- */
+
+SurfCollideCLLKokkos::SurfCollideCLLKokkos(SPARTA *sparta, int narg, char **arg) :
+ SurfCollideCLL(sparta, narg, arg),
+ fix_ambi_kk_copy(sparta),
+ fix_vibmode_kk_copy(sparta),
+ sr_kk_global_copy{VAL_2(KKCopy(sparta))},
+ sr_kk_prob_copy{VAL_2(KKCopy(sparta))},
+ sr_kk_adsorb_copy{VAL_2(KKCopy(sparta))},
+ rand_pool(12345 + comm->me
+#ifdef SPARTA_KOKKOS_EXACT
+ , sparta
+#endif
+ )
+{
+ kokkosable = 1;
+
+ random_backup = NULL;
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.init(random);
+#endif
+
+ // use 1D view for scalars to reduce GPU memory operations
+
+ d_scalars = t_int_2("surf_collide_cll:scalars");
+ d_nsingle = Kokkos::subview(d_scalars,0);
+ d_nreact_one = Kokkos::subview(d_scalars,1);
+
+ h_scalars = t_host_int_2("surf_collide_cll:scalars_mirror");
+ h_nsingle = Kokkos::subview(h_scalars,0);
+ h_nreact_one = Kokkos::subview(h_scalars,1);
+}
+
+SurfCollideCLLKokkos::SurfCollideCLLKokkos(SPARTA *sparta) :
+ SurfCollideCLL(sparta),
+ fix_ambi_kk_copy(sparta),
+ fix_vibmode_kk_copy(sparta),
+ sr_kk_global_copy{VAL_2(KKCopy(sparta))},
+ sr_kk_prob_copy{VAL_2(KKCopy(sparta))},
+ sr_kk_adsorb_copy{VAL_2(KKCopy(sparta))},
+ rand_pool(12345 // seed doesn't matter since it will just be copied over
+#ifdef SPARTA_KOKKOS_EXACT
+ , sparta
+#endif
+ )
+{
+ copy = 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+SurfCollideCLLKokkos::~SurfCollideCLLKokkos()
+{
+ if (uncopy) {
+ fix_ambi_kk_copy.uncopy();
+ fix_vibmode_kk_copy.uncopy();
+
+ for (int i = 0; i < KOKKOS_MAX_SURF_REACT_PER_TYPE; i++) {
+ sr_kk_global_copy[i].uncopy();
+ sr_kk_prob_copy[i].uncopy();
+ sr_kk_adsorb_copy[i].uncopy();
+ }
+ }
+
+ if (copy) return;
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.destroy();
+ if (random_backup)
+ delete random_backup;
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideCLLKokkos::init()
+{
+ SurfCollideCLL::init();
+
+ ambi_flag = vibmode_flag = 0;
+ if (modify->n_update_custom) {
+ for (int ifix = 0; ifix < modify->nfix; ifix++) {
+ if (strcmp(modify->fix[ifix]->style,"ambipolar") == 0) {
+ ambi_flag = 1;
+ FixAmbipolar *afix = (FixAmbipolar *) modify->fix[ifix];
+ if (!afix->kokkos_flag)
+ error->all(FLERR,"Must use fix ambipolar/kk when Kokkos is enabled");
+ afix_kk = (FixAmbipolarKokkos*)afix;
+ } else if (strcmp(modify->fix[ifix]->style,"vibmode") == 0) {
+ vibmode_flag = 1;
+ FixVibmode *vfix = (FixVibmode *) modify->fix[ifix];
+ if (!vfix->kokkos_flag)
+ error->all(FLERR,"Must use fix vibmode/kk when Kokkos is enabled");
+ vfix_kk = (FixVibmodeKokkos*)vfix;
+ }
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------
+ recalculate Tsurf values which are dynamic
+ called by Update::setup() and Update::run()
+---------------------------------------------------------------------- */
+
+void SurfCollideCLLKokkos::dynamic()
+{
+ // VAREQUAL mode
+ // equal-style variable sets single tsurf value for all surfs
+
+ if (tmode == VAREQUAL) {
+
+ // only evaluate variable if timestep is multiple of tfreq
+
+ if (update->ntimestep % tfreq) return;
+ tsurf = input->variable->compute_equal(tindex_var);
+ if (tsurf <= 0.0) error->all(FLERR,"Surf_collide tsurf <= 0.0");
+
+ // VARSURF mode
+ // surf-style variable sets new tsurf values for all surfs
+ // particle/surf collisions access t_persurf for local+ghost values
+
+ } else if (tmode == VARSURF) {
+
+ // only evaluate variable if timestep is multiple of tfreq
+
+ int spreadflag = 0;
+ if (update->ntimestep % tfreq == 0) {
+ if (n_owned != surf->nown) {
+ memory->destroy(t_owned);
+ n_owned = surf->nown;
+ memory->create(t_owned,n_owned,"surfcollide:t_owned");
+ }
+
+ input->variable->compute_surf(tindex_var,t_owned,1,0);
+ spreadflag = 1;
+ }
+
+ // spread t_owned values to t_localghost values via spread_own2local()
+ // if just re-computed variable OR surfs are
+ // distributed and load balance/adaptation took place on previous step
+
+ if (spreadflag ||
+ (surf->distributed && surf->localghost_changed_step == update->ntimestep-1)) {
+ if (n_localghost != surf->nlocal + surf->nghost) {
+ memory->destroy(t_localghost);
+ n_localghost = surf->nlocal + surf->nghost;
+ memory->create(t_localghost,n_localghost,"surfcollide:t_localghost");
+ }
+
+ surf->spread_own2local(1,DOUBLE,t_owned,t_localghost);
+ t_persurf = t_localghost;
+
+ auto h_t_persurf = HAT::t_float_1d(t_persurf,n_localghost);
+ d_t_persurf = Kokkos::create_mirror_view_and_copy(SPADeviceType(),h_t_persurf);
+ }
+
+ // CUSTOM mode
+ // ensure access to custom per-surf vec for tsurf values for all surfs
+ // particle/surf collisions access t_persurf for local+ghost values
+
+ } else if (tmode == CUSTOM) {
+ SurfKokkos* surf_kk = (SurfKokkos*) surf;
+ auto h_edvec_local = surf_kk->k_edvec_local.view_host();
+
+ // spread owned values to local+ghost values via spread_custom()
+ // estatus == 1 means owned values already spread to local+ghost values
+ // if estatus == 0: owned values are new OR
+ // surfs are distributed and load balance/adaptation took place
+
+ if (surf->estatus[tindex_custom] == 0) surf->spread_custom(tindex_custom);
+
+ h_edvec_local[tindex_custom].k_view.sync_device();
+ d_t_persurf = h_edvec_local[tindex_custom].k_view.view_device();
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideCLLKokkos::pre_collide()
+{
+ if (ambi_flag) {
+ afix_kk->pre_update_custom_kokkos();
+ fix_ambi_kk_copy.copy(afix_kk);
+ }
+
+ if (vibmode_flag) {
+ vfix_kk->pre_update_custom_kokkos();
+ fix_vibmode_kk_copy.copy(vfix_kk);
+ }
+
+ if (surf->nsr > KOKKOS_MAX_TOT_SURF_REACT)
+ error->all(FLERR,"Kokkos currently supports two instances of each surface reaction method");
+
+ if (surf->nsr > 0) {
+ int nglob,nprob,nadsorb;
+ nglob = nprob = nadsorb = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (!surf->sr[n]->kokkosable)
+ error->all(FLERR,"Must use Kokkos-enabled surface reaction method with Kokkos");
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].copy((SurfReactGlobalKokkos*)(surf->sr[n]));
+ sr_kk_global_copy[nglob].obj.pre_react();
+ sr_type_list[n] = 0;
+ sr_map[n] = nglob;
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].copy((SurfReactProbKokkos*)(surf->sr[n]));
+ sr_kk_prob_copy[nprob].obj.pre_react();
+ sr_type_list[n] = 1;
+ sr_map[n] = nprob;
+ nprob++;
+ } else if (strcmp(surf->sr[n]->style,"adsorb") == 0) {
+ sr_kk_adsorb_copy[nadsorb].copy((SurfReactAdsorbKokkos*)(surf->sr[n]));
+ sr_kk_adsorb_copy[nadsorb].obj.pre_react();
+ sr_type_list[n] = 2;
+ sr_map[n] = nadsorb;
+ nadsorb++;
+ } else {
+ error->all(FLERR,"Unknown Kokkos surface reaction method");
+ }
+ }
+
+ if (nglob > KOKKOS_MAX_SURF_REACT_PER_TYPE || nprob > KOKKOS_MAX_SURF_REACT_PER_TYPE ||
+ nadsorb > KOKKOS_MAX_SURF_REACT_PER_TYPE)
+ error->all(FLERR,"Kokkos currently supports two instances of each surface reaction method");
+ }
+
+ if (random == NULL) {
+ // initialize RNG
+
+ random = new RanKnuth(update->ranmaster->uniform());
+ double seed = update->ranmaster->uniform();
+ random->reset(seed,comm->me,100);
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.init(random);
+#endif
+ }
+
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ particle_kk->sync(Device,PARTICLE_MASK|SPECIES_MASK);
+ d_particles = particle_kk->k_particles.view_device();
+ d_species = particle_kk->k_species.view_device();
+ boltz = update->boltz;
+
+ rotstyle = NONE;
+ if (Pointers::collide) rotstyle = Pointers::collide->rotstyle;
+ vibstyle = NONE;
+ if (Pointers::collide) vibstyle = Pointers::collide->vibstyle;
+
+ Kokkos::deep_copy(d_scalars,0);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideCLLKokkos::post_collide()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ if (ambi_flag || vibmode_flag) particle_kk->modify(Device,CUSTOM_MASK);
+
+ Kokkos::deep_copy(h_scalars,d_scalars);
+
+ int m = surf->find_collide(id);
+ auto sc = surf->sc[m]; // can't modify the copy directly, use the original
+ sc->nsingle += h_nsingle();
+ surf->nreact_one += h_nreact_one();
+
+ d_particles = {};
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideCLLKokkos::backup()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ d_particles = particle_kk->k_particles.view_device();
+
+ if (surf->nsr > 0) {
+ int nglob,nprob,nadsorb;
+ nglob = nprob = nadsorb = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].obj.backup();
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].obj.backup();
+ nprob++;
+ } else if (strcmp(surf->sr[n]->style,"adsorb") == 0) {
+ sr_kk_adsorb_copy[nadsorb].obj.backup();
+ nadsorb++;
+ }
+ }
+ }
+
+#ifdef SPARTA_KOKKOS_EXACT
+ if (!random_backup)
+ random_backup = new RanKnuth(12345 + comm->me);
+ memcpy(random_backup,random,sizeof(RanKnuth));
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideCLLKokkos::restore()
+{
+ if (surf->nsr > 0) {
+ int nglob,nprob,nadsorb;
+ nglob = nprob = nadsorb = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].obj.restore();
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].obj.restore();
+ nprob++;
+ } else if (strcmp(surf->sr[n]->style,"adsorb") == 0) {
+ sr_kk_adsorb_copy[nadsorb].obj.restore();
+ nadsorb++;
+ }
+ }
+ }
+
+ Kokkos::deep_copy(d_scalars,0);
+
+#ifdef SPARTA_KOKKOS_EXACT
+ memcpy(random,random_backup,sizeof(RanKnuth));
+#endif
+}
diff --git a/src/KOKKOS/surf_collide_cll_kokkos.h b/src/KOKKOS/surf_collide_cll_kokkos.h
new file mode 100644
index 000000000..fd39787c9
--- /dev/null
+++ b/src/KOKKOS/surf_collide_cll_kokkos.h
@@ -0,0 +1,418 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef SURF_COLLIDE_CLASS
+
+SurfCollideStyle(cll/kk,SurfCollideCLLKokkos)
+
+#else
+
+#ifndef SPARTA_SURF_COLLIDE_CLL_KOKKOS_H
+#define SPARTA_SURF_COLLIDE_CLL_KOKKOS_H
+
+#include "surf_collide_cll.h"
+#include "kokkos_type.h"
+#include "math_extra_kokkos.h"
+#include "Kokkos_Random.hpp"
+#include "rand_pool_wrap.h"
+#include "kokkos_copy.h"
+#include "fix_ambipolar_kokkos.h"
+#include "fix_vibmode_kokkos.h"
+#include "surf_react_global_kokkos.h"
+#include "surf_react_prob_kokkos.h"
+#include "surf_react_adsorb_kokkos.h"
+
+namespace SPARTA_NS {
+
+class SurfCollideCLLKokkos : public SurfCollideCLL {
+ public:
+
+ enum{NONE,DISCRETE,SMOOTH}; // several files
+ enum{PKEEP,PINSERT,PDONE,PDISCARD,PENTRY,PEXIT,PSURF}; // several files
+
+ SurfCollideCLLKokkos(class SPARTA *, int, char **);
+ SurfCollideCLLKokkos(class SPARTA *);
+ ~SurfCollideCLLKokkos();
+ void init();
+ void dynamic();
+ void pre_collide();
+ void post_collide();
+ void backup();
+ void restore();
+
+ private:
+ double boltz;
+ int rotstyle, vibstyle;
+
+#ifndef SPARTA_KOKKOS_EXACT
+ Kokkos::Random_XorShift64_Pool rand_pool;
+ typedef typename Kokkos::Random_XorShift64_Pool::generator_type rand_type;
+#else
+ RandPoolWrap rand_pool;
+ typedef RandWrap rand_type;
+#endif
+
+ RanKnuth* random_backup;
+
+ DAT::t_float_1d d_t_persurf;
+
+ typedef Kokkos::DualView tdual_int_2;
+ typedef tdual_int_2::t_dev t_int_2;
+ typedef tdual_int_2::t_host t_host_int_2;
+ t_int_2 d_scalars;
+ t_host_int_2 h_scalars;
+
+ DAT::t_int_scalar d_nsingle;
+ DAT::t_int_scalar d_nreact_one;
+
+ HAT::t_int_scalar h_nsingle;
+ HAT::t_int_scalar h_nreact_one;
+
+ t_particle_1d d_particles;
+ t_species_1d d_species;
+
+ int ambi_flag,vibmode_flag;
+ FixAmbipolarKokkos* afix_kk;
+ FixVibmodeKokkos* vfix_kk;
+ KKCopy fix_ambi_kk_copy;
+ KKCopy fix_vibmode_kk_copy;
+
+ int sr_type_list[KOKKOS_MAX_TOT_SURF_REACT];
+ int sr_map[KOKKOS_MAX_TOT_SURF_REACT];
+ KKCopy sr_kk_global_copy[KOKKOS_MAX_SURF_REACT_PER_TYPE];
+ KKCopy sr_kk_prob_copy[KOKKOS_MAX_SURF_REACT_PER_TYPE];
+ KKCopy sr_kk_adsorb_copy[KOKKOS_MAX_SURF_REACT_PER_TYPE];
+
+ public:
+
+ /* ----------------------------------------------------------------------
+ particle collision with surface with optional chemistry
+ ip = particle with current x = collision pt, current v = incident v
+ isurf = index of surface element
+ norm = surface normal unit vector
+ isr = index of reaction model if >= 0, -1 for no chemistry
+ ip = set to NULL if destroyed by chemistry
+ return jp = new particle if created by chemistry
+ return reaction = index of reaction (1 to N) that took place, 0 = no reaction
+ resets particle(s) to post-collision outward velocity
+ ------------------------------------------------------------------------- */
+
+ template
+ KOKKOS_INLINE_FUNCTION
+ Particle::OnePart* collide_kokkos(Particle::OnePart *&ip, double &,
+ int isurf, const double *norm, int isr, int &reaction,
+ const DAT::t_int_scalar &d_retry, const DAT::t_int_scalar &d_nlocal) const
+ {
+ if (ATOMIC_REDUCTION == 0)
+ d_nsingle()++;
+ else
+ Kokkos::atomic_inc(&d_nsingle());
+
+ // if surface chemistry defined, attempt reaction
+ // reaction = 1 to N for which reaction took place, 0 for none
+ // velreset = 1 if reaction reset post-collision velocity, else 0
+
+ Particle::OnePart iorig;
+ Particle::OnePart *jp = NULL;
+ reaction = 0;
+ int velreset = 0;
+
+ if (REACT && isr >= 0) {
+ if (ambi_flag || vibmode_flag) memcpy(&iorig,ip,sizeof(Particle::OnePart));
+
+ int sr_type = sr_type_list[isr];
+ int m = sr_map[isr];
+
+ if (sr_type == 0) {
+ reaction = sr_kk_global_copy[m].obj.
+ react_kokkos(ip,isurf,norm,jp,velreset,d_retry,d_nlocal);
+ } else if (sr_type == 1) {
+ reaction = sr_kk_prob_copy[m].obj.
+ react_kokkos(ip,isurf,norm,jp,velreset,d_retry,d_nlocal);
+ } else if (sr_type == 2) {
+ reaction = sr_kk_adsorb_copy[m].obj.
+ react_kokkos(ip,isurf,norm,jp,velreset,d_retry,d_nlocal);
+ }
+
+ if (reaction) {
+ if (ATOMIC_REDUCTION == 0)
+ d_nreact_one()++;
+ else
+ Kokkos::atomic_inc(&d_nreact_one());
+ }
+ }
+
+ // set temperature of isurf if VARSURF or CUSTOM
+
+ double tsurf_local = tsurf;
+ if (persurf_temperature) {
+ tsurf_local = d_t_persurf[isurf];
+ if (tsurf_local <= 0.0) Kokkos::abort("Surf_collide tsurf <= 0.0");
+ }
+
+ // CLL reflection for each particle
+ // only if SurfReact did not already reset velocities
+ // also both particles need to trigger any fixes
+ // to update per-particle properties which depend on
+ // temperature of the particle, e.g. fix vibmode and fix ambipolar
+
+ if (ip) {
+ if (!velreset) cll(ip,norm,tsurf_local);
+ int i = ip - d_particles.data();
+ if (ambi_flag)
+ fix_ambi_kk_copy.obj.update_custom_kokkos(i,tsurf_local,tsurf_local,tsurf_local,vstream);
+ if (vibmode_flag)
+ fix_vibmode_kk_copy.obj.update_custom_kokkos(i,tsurf_local,tsurf_local,tsurf_local,vstream);
+ }
+ if (REACT && jp) {
+ if (!velreset) cll(jp,norm,tsurf_local);
+ int j = jp - d_particles.data();
+ if (ambi_flag)
+ fix_ambi_kk_copy.obj.update_custom_kokkos(j,tsurf_local,tsurf_local,tsurf_local,vstream);
+ if (vibmode_flag)
+ fix_vibmode_kk_copy.obj.update_custom_kokkos(j,tsurf_local,tsurf_local,tsurf_local,vstream);
+ }
+
+ // call any fixes with a surf_react() method
+ // they may reset j to -1, e.g. fix ambipolar
+ // in which case newly created j is deleted
+
+ if (REACT && reaction && ambi_flag) {
+ int i = -1;
+ if (ip) i = ip - d_particles.data();
+ int j = -1;
+ if (jp) j = jp - d_particles.data();
+ int j_orig = j;
+ fix_ambi_kk_copy.obj.surf_react_kokkos(&iorig,i,j);
+ if (jp && j < 0) {
+ d_particles[j_orig].flag = PDISCARD;
+ jp = NULL;
+ }
+ }
+
+ return jp;
+ };
+
+ private:
+
+ /* ----------------------------------------------------------------------
+ cll reflection
+ ------------------------------------------------------------------------- */
+
+ KOKKOS_INLINE_FUNCTION
+ void cll(Particle::OnePart *p, const double *norm, const double twall) const
+ {
+ rand_type rand_gen = rand_pool.get_state();
+
+ double tangent1[3],tangent2[3];
+ int ispecies = p->ispecies;
+ double beta_un,normalized_distbn_fn;
+
+ double *v = p->v;
+ double dot = MathExtraKokkos::dot3(v,norm);
+ double vrm, vperp, vtan1, vtan2;
+
+ tangent1[0] = v[0] - dot*norm[0];
+ tangent1[1] = v[1] - dot*norm[1];
+ tangent1[2] = v[2] - dot*norm[2];
+
+ if (MathExtraKokkos::lensq3(tangent1) == 0.0) {
+ tangent2[0] = rand_gen.drand();
+ tangent2[1] = rand_gen.drand();
+ tangent2[2] = rand_gen.drand();
+ MathExtraKokkos::cross3(norm,tangent2,tangent1);
+ }
+
+ MathExtraKokkos::norm3(tangent1);
+ MathExtraKokkos::cross3(norm,tangent1,tangent2);
+
+ double tan1 = MathExtraKokkos::dot3(v,tangent1);
+
+ vrm = sqrt(2.0*boltz * twall / d_species[ispecies].mass);
+
+ // CLL model normal velocity
+
+ double r_1 = sqrt(-acc_n*log(rand_gen.drand()));
+ double theta_1 = MathConst::MY_2PI * rand_gen.drand();
+ double dot_norm = dot/vrm * sqrt(1-acc_n);
+ vperp = vrm * sqrt(r_1*r_1 + dot_norm*dot_norm + 2*r_1*dot_norm*cos(theta_1));
+
+ // CLL model tangential velocities
+
+ double r_2 = sqrt(-acc_t*log(rand_gen.drand()));
+ double theta_2 = MathConst::MY_2PI * rand_gen.drand();
+ double vtangent = tan1/vrm * sqrt(1-acc_t);
+ vtan1 = vrm * (vtangent + r_2*cos(theta_2));
+ vtan2 = vrm * r_2 * sin(theta_2);
+
+ // partial keyword
+ // incomplete energy accommodation with partial/fully diffuse scattering
+ // adjust the final angle of the particle while keeping
+ // the velocity magnitude or speed according to CLL scattering
+
+ if (pflag) {
+ double tan2 = MathExtraKokkos::dot3(v,tangent2);
+ double phi_i, psi_i, theta_f, phi_f, psi_f, cos_beta;
+
+ psi_i = acos(dot*dot/MathExtraKokkos::lensq3(v));
+ phi_i = atan2(tan2,tan1);
+
+ double v_mag = sqrt(vperp*vperp + vtan1*vtan1 + vtan2*vtan2);
+
+ double P = 0;
+ while (rand_gen.drand() > P) {
+ phi_f = MathConst::MY_2PI*rand_gen.drand();
+ psi_f = acos(1-rand_gen.drand());
+ cos_beta = cos(psi_i)*cos(psi_f) +
+ sin(psi_i)*sin(psi_f)*cos(phi_i - phi_f);
+ P = (1-eccen)/(1-eccen*cos_beta);
+ }
+
+ theta_f = acos(sqrt(cos(psi_f)));
+
+ vperp = v_mag * cos(theta_f);
+ vtan1 = v_mag * sin(theta_f) * cos(phi_f);
+ vtan2 = v_mag * sin(theta_f) * sin(phi_f);
+ }
+
+ // add in translation or rotation vector if specified
+ // only keep portion of vector tangential to surface element
+
+ if (trflag) {
+ double vxdelta,vydelta,vzdelta;
+ if (tflag) {
+ vxdelta = vx; vydelta = vy; vzdelta = vz;
+ double dot = vxdelta*norm[0] + vydelta*norm[1] + vzdelta*norm[2];
+
+ if (fabs(dot) > 0.001) {
+ dot /= vrm;
+ do {
+ do {
+ beta_un = (6.0*rand_gen.normal() - 3.0);
+ } while (beta_un + dot < 0.0);
+ normalized_distbn_fn = 2.0 * (beta_un + dot) /
+ (dot + sqrt(dot*dot + 2.0)) *
+ exp(0.5 + (0.5*dot)*(dot-sqrt(dot*dot + 2.0)) - beta_un*beta_un);
+ } while (normalized_distbn_fn < rand_gen.drand());
+ vperp = beta_un*vrm;
+ }
+
+ } else {
+ double *x = p->x;
+ vxdelta = wy*(x[2]-pz) - wz*(x[1]-py);
+ vydelta = wz*(x[0]-px) - wx*(x[2]-pz);
+ vzdelta = wx*(x[1]-py) - wy*(x[0]-px);
+ double dot = vxdelta*norm[0] + vydelta*norm[1] + vzdelta*norm[2];
+ vxdelta -= dot*norm[0];
+ vydelta -= dot*norm[1];
+ vzdelta -= dot*norm[2];
+ }
+
+ v[0] = vperp*norm[0] + vtan1*tangent1[0] + vtan2*tangent2[0] + vxdelta;
+ v[1] = vperp*norm[1] + vtan1*tangent1[1] + vtan2*tangent2[1] + vydelta;
+ v[2] = vperp*norm[2] + vtan1*tangent1[2] + vtan2*tangent2[2] + vzdelta;
+
+ // no translation or rotation
+
+ } else {
+ v[0] = vperp*norm[0] + vtan1*tangent1[0] + vtan2*tangent2[0];
+ v[1] = vperp*norm[1] + vtan1*tangent1[1] + vtan2*tangent2[1];
+ v[2] = vperp*norm[2] + vtan1*tangent1[2] + vtan2*tangent2[2];
+ }
+
+ // rotational component
+
+ if (rotstyle == NONE || d_species[ispecies].rotdof < 2) p->erot = 0.0;
+
+ else {
+ double erot_mag = sqrt(p->erot*(1-acc_rot)/(boltz*twall));
+
+ double r_rot,cos_theta_rot,A_rot,X_rot;
+ if (d_species[ispecies].rotdof == 2) {
+ r_rot = sqrt(-acc_rot*log(rand_gen.drand()));
+ cos_theta_rot = cos(MathConst::MY_2PI*rand_gen.drand());
+ }
+ else if (d_species[ispecies].rotdof > 2) {
+ A_rot = 0;
+ while (A_rot < rand_gen.drand()) {
+ X_rot = 4*rand_gen.drand();
+ A_rot = 2.71828182845904523536028747*X_rot*X_rot*exp(-X_rot*X_rot);
+ }
+ r_rot = sqrt(acc_rot)*X_rot;
+ cos_theta_rot = 2*rand_gen.drand() - 1;
+ }
+
+ p->erot = boltz * twall *
+ (r_rot*r_rot + erot_mag*erot_mag + 2*r_rot*erot_mag*cos_theta_rot);
+ }
+
+ // vibrational component
+
+ int vibdof = d_species[ispecies].vibdof;
+ double r_vib, cos_theta_vib, A_vib, X_vib, evib_mag, evib_val;
+
+ if (vibstyle == NONE || vibdof < 2)
+ p->evib = 0.0;
+
+ else if (vibstyle == DISCRETE && vibdof == 2) {
+ double evib_star =
+ -log(1 - rand_gen.drand() *
+ (1 - exp(-boltz*d_species[ispecies].vibtemp[0])));
+ evib_val = p->evib + evib_star;
+ evib_mag = sqrt(evib_val*(1-acc_vib)/(boltz*twall));
+ r_vib = sqrt(-acc_vib*log(rand_gen.drand()));
+ cos_theta_vib = cos(MathConst::MY_2PI*rand_gen.drand());
+ evib_val = boltz * twall *
+ (r_vib*r_vib + evib_mag*evib_mag + 2*r_vib*evib_mag*cos_theta_vib);
+ int ivib = evib_val / (boltz*d_species[ispecies].vibtemp[0]);
+ p->evib = ivib * boltz * d_species[ispecies].vibtemp[0];
+ }
+
+ else if (vibstyle == SMOOTH || vibdof >= 2) {
+ evib_mag = sqrt(p->evib*(1-acc_vib)/(boltz*twall));
+ if (vibdof == 2) {
+ r_vib = sqrt(-acc_vib*log(rand_gen.drand()));
+ cos_theta_vib = cos(MathConst::MY_2PI*rand_gen.drand());
+ } else if (vibdof > 2) {
+ A_vib = 0;
+ while (A_vib < rand_gen.drand()) {
+ X_vib = 4*rand_gen.drand();
+ A_vib = 2.71828182845904523536028747*X_vib*X_vib*exp(-X_vib*X_vib);
+ }
+ r_vib = sqrt(acc_vib)*X_vib;
+ cos_theta_vib = 2*rand_gen.drand() - 1;
+ }
+
+ p->evib = boltz * twall *
+ (r_vib*r_vib + evib_mag*evib_mag + 2*r_vib*evib_mag*cos_theta_vib);
+ }
+
+ rand_pool.free_state(rand_gen);
+ }
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Illegal ... command
+
+Self-explanatory. Check the input script syntax and compare to the
+documentation for the command. You can use -echo screen as a
+command-line option when running SPARTA to see the offending line.
+
+*/
diff --git a/src/KOKKOS/surf_collide_diffuse_kokkos.h b/src/KOKKOS/surf_collide_diffuse_kokkos.h
index 25826164a..321ccdbb1 100644
--- a/src/KOKKOS/surf_collide_diffuse_kokkos.h
+++ b/src/KOKKOS/surf_collide_diffuse_kokkos.h
@@ -165,7 +165,7 @@ class SurfCollideDiffuseKokkos : public SurfCollideDiffuse {
// temperature of the particle, e.g. fix vibmode and fix ambipolar
if (ip) {
- if (!velreset) diffuse(ip,norm,tsurf_local);
+ if (!velreset) diffuse(ip,norm,tsurf_local,acc);
int i = ip - d_particles.data();
if (ambi_flag)
fix_ambi_kk_copy.obj.update_custom_kokkos(i,tsurf_local,tsurf_local,tsurf_local,vstream);
@@ -173,7 +173,7 @@ class SurfCollideDiffuseKokkos : public SurfCollideDiffuse {
fix_vibmode_kk_copy.obj.update_custom_kokkos(i,tsurf_local,tsurf_local,tsurf_local,vstream);
}
if (REACT && jp) {
- if (!velreset) diffuse(jp,norm,tsurf_local);
+ if (!velreset) diffuse(jp,norm,tsurf_local,acc);
int j = jp - d_particles.data();
if (ambi_flag)
fix_ambi_kk_copy.obj.update_custom_kokkos(j,tsurf_local,tsurf_local,tsurf_local,vstream);
@@ -201,10 +201,31 @@ class SurfCollideDiffuseKokkos : public SurfCollideDiffuse {
return jp;
};
+ public:
+
+ /* ----------------------------------------------------------------------
+ wrapper on diffuse() to perform a collision for a single particle
+ called on-device by SurfReactAdsorbKokkos GS chemistry
+ flags, coeffs can be NULL; matches SurfCollideDiffuse::wrapper
+ ------------------------------------------------------------------------- */
+
+ KOKKOS_INLINE_FUNCTION
+ void wrapper_kokkos(Particle::OnePart *p, const double *norm,
+ int *, double *coeffs) const
+ {
+ double twall = tsurf;
+ double acc_local = acc;
+ if (coeffs) {
+ twall = coeffs[0];
+ acc_local = coeffs[1];
+ }
+ diffuse(p,norm,twall,acc_local);
+ }
+
private:
KOKKOS_INLINE_FUNCTION
- void diffuse(Particle::OnePart *p, const double *norm, const double twall) const
+ void diffuse(Particle::OnePart *p, const double *norm, const double twall, const double acc) const
{
// specular reflection
// reflect incident v around norm
diff --git a/src/KOKKOS/surf_collide_impulsive_kokkos.cpp b/src/KOKKOS/surf_collide_impulsive_kokkos.cpp
new file mode 100644
index 000000000..e8e357e76
--- /dev/null
+++ b/src/KOKKOS/surf_collide_impulsive_kokkos.cpp
@@ -0,0 +1,358 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "math.h"
+#include "stdlib.h"
+#include "string.h"
+#include "surf_collide_impulsive_kokkos.h"
+#include "surf_kokkos.h"
+#include "input.h"
+#include "variable.h"
+#include "particle.h"
+#include "domain.h"
+#include "update.h"
+#include "modify.h"
+#include "comm.h"
+#include "random_mars.h"
+#include "random_knuth.h"
+#include "math_const.h"
+#include "math_extra.h"
+#include "memory.h"
+#include "error.h"
+#include "particle_kokkos.h"
+#include "sparta_masks.h"
+#include "collide.h"
+
+using namespace SPARTA_NS;
+using namespace MathConst;
+
+enum{INT,DOUBLE}; // several files
+enum{NUMERIC,CUSTOM,VARIABLE,VAREQUAL,VARSURF}; // surf_collide classes
+
+#define VAL_1(X) X
+#define VAL_2(X) VAL_1(X), VAL_1(X)
+
+/* ---------------------------------------------------------------------- */
+
+SurfCollideImpulsiveKokkos::SurfCollideImpulsiveKokkos(SPARTA *sparta, int narg, char **arg) :
+ SurfCollideImpulsive(sparta, narg, arg),
+ fix_ambi_kk_copy(sparta),
+ fix_vibmode_kk_copy(sparta),
+ sr_kk_global_copy{VAL_2(KKCopy(sparta))},
+ sr_kk_prob_copy{VAL_2(KKCopy(sparta))},
+ rand_pool(12345 + comm->me
+#ifdef SPARTA_KOKKOS_EXACT
+ , sparta
+#endif
+ )
+{
+ kokkosable = 1;
+
+ random_backup = NULL;
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.init(random);
+#endif
+
+ // use 1D view for scalars to reduce GPU memory operations
+
+ d_scalars = t_int_2("surf_collide_impulsive:scalars");
+ d_nsingle = Kokkos::subview(d_scalars,0);
+ d_nreact_one = Kokkos::subview(d_scalars,1);
+
+ h_scalars = t_host_int_2("surf_collide_impulsive:scalars_mirror");
+ h_nsingle = Kokkos::subview(h_scalars,0);
+ h_nreact_one = Kokkos::subview(h_scalars,1);
+}
+
+SurfCollideImpulsiveKokkos::SurfCollideImpulsiveKokkos(SPARTA *sparta) :
+ SurfCollideImpulsive(sparta),
+ fix_ambi_kk_copy(sparta),
+ fix_vibmode_kk_copy(sparta),
+ sr_kk_global_copy{VAL_2(KKCopy(sparta))},
+ sr_kk_prob_copy{VAL_2(KKCopy(sparta))},
+ rand_pool(12345 // seed doesn't matter since it will just be copied over
+#ifdef SPARTA_KOKKOS_EXACT
+ , sparta
+#endif
+ )
+{
+ copy = 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+SurfCollideImpulsiveKokkos::~SurfCollideImpulsiveKokkos()
+{
+ if (uncopy) {
+ fix_ambi_kk_copy.uncopy();
+ fix_vibmode_kk_copy.uncopy();
+
+ for (int i = 0; i < KOKKOS_MAX_SURF_REACT_PER_TYPE; i++) {
+ sr_kk_global_copy[i].uncopy();
+ sr_kk_prob_copy[i].uncopy();
+ }
+ }
+
+ if (copy) return;
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.destroy();
+ if (random_backup)
+ delete random_backup;
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideImpulsiveKokkos::init()
+{
+ SurfCollideImpulsive::init();
+
+ ambi_flag = vibmode_flag = 0;
+ if (modify->n_update_custom) {
+ for (int ifix = 0; ifix < modify->nfix; ifix++) {
+ if (strcmp(modify->fix[ifix]->style,"ambipolar") == 0) {
+ ambi_flag = 1;
+ FixAmbipolar *afix = (FixAmbipolar *) modify->fix[ifix];
+ if (!afix->kokkos_flag)
+ error->all(FLERR,"Must use fix ambipolar/kk when Kokkos is enabled");
+ afix_kk = (FixAmbipolarKokkos*)afix;
+ } else if (strcmp(modify->fix[ifix]->style,"vibmode") == 0) {
+ vibmode_flag = 1;
+ FixVibmode *vfix = (FixVibmode *) modify->fix[ifix];
+ if (!vfix->kokkos_flag)
+ error->all(FLERR,"Must use fix vibmode/kk when Kokkos is enabled");
+ vfix_kk = (FixVibmodeKokkos*)vfix;
+ }
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------
+ recalculate Tsurf values which are dynamic
+ called by Update::setup() and Update::run()
+---------------------------------------------------------------------- */
+
+void SurfCollideImpulsiveKokkos::dynamic()
+{
+ // VAREQUAL mode
+ // equal-style variable sets single tsurf value for all surfs
+
+ if (tmode == VAREQUAL) {
+
+ // only evaluate variable if timestep is multiple of tfreq
+
+ if (update->ntimestep % tfreq) return;
+ tsurf = input->variable->compute_equal(tindex_var);
+ if (tsurf <= 0.0) error->all(FLERR,"Surf_collide tsurf <= 0.0");
+
+ // VARSURF mode
+ // surf-style variable sets new tsurf values for all surfs
+ // particle/surf collisions access t_persurf for local+ghost values
+
+ } else if (tmode == VARSURF) {
+
+ // only evaluate variable if timestep is multiple of tfreq
+
+ int spreadflag = 0;
+ if (update->ntimestep % tfreq == 0) {
+ if (n_owned != surf->nown) {
+ memory->destroy(t_owned);
+ n_owned = surf->nown;
+ memory->create(t_owned,n_owned,"surfcollide:t_owned");
+ }
+
+ input->variable->compute_surf(tindex_var,t_owned,1,0);
+ spreadflag = 1;
+ }
+
+ // spread t_owned values to t_localghost values via spread_own2local()
+ // if just re-computed variable OR surfs are
+ // distributed and load balance/adaptation took place on previous step
+
+ if (spreadflag ||
+ (surf->distributed && surf->localghost_changed_step == update->ntimestep-1)) {
+ if (n_localghost != surf->nlocal + surf->nghost) {
+ memory->destroy(t_localghost);
+ n_localghost = surf->nlocal + surf->nghost;
+ memory->create(t_localghost,n_localghost,"surfcollide:t_localghost");
+ }
+
+ surf->spread_own2local(1,DOUBLE,t_owned,t_localghost);
+ t_persurf = t_localghost;
+
+ auto h_t_persurf = HAT::t_float_1d(t_persurf,n_localghost);
+ d_t_persurf = Kokkos::create_mirror_view_and_copy(SPADeviceType(),h_t_persurf);
+ }
+
+ // CUSTOM mode
+ // ensure access to custom per-surf vec for tsurf values for all surfs
+ // particle/surf collisions access t_persurf for local+ghost values
+
+ } else if (tmode == CUSTOM) {
+ SurfKokkos* surf_kk = (SurfKokkos*) surf;
+ auto h_edvec_local = surf_kk->k_edvec_local.view_host();
+
+ // spread owned values to local+ghost values via spread_custom()
+ // estatus == 1 means owned values already spread to local+ghost values
+ // if estatus == 0: owned values are new OR
+ // surfs are distributed and load balance/adaptation took place
+
+ if (surf->estatus[tindex_custom] == 0) surf->spread_custom(tindex_custom);
+
+ h_edvec_local[tindex_custom].k_view.sync_device();
+ d_t_persurf = h_edvec_local[tindex_custom].k_view.view_device();
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideImpulsiveKokkos::pre_collide()
+{
+ if (ambi_flag) {
+ afix_kk->pre_update_custom_kokkos();
+ fix_ambi_kk_copy.copy(afix_kk);
+ }
+
+ if (vibmode_flag) {
+ vfix_kk->pre_update_custom_kokkos();
+ fix_vibmode_kk_copy.copy(vfix_kk);
+ }
+
+ if (surf->nsr > KOKKOS_MAX_TOT_SURF_REACT)
+ error->all(FLERR,"Kokkos currently supports two instances of each surface reaction method");
+
+ if (surf->nsr > 0) {
+ int nglob,nprob;
+ nglob = nprob = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (!surf->sr[n]->kokkosable)
+ error->all(FLERR,"Must use Kokkos-enabled surface reaction method with Kokkos");
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].copy((SurfReactGlobalKokkos*)(surf->sr[n]));
+ sr_kk_global_copy[nglob].obj.pre_react();
+ sr_type_list[n] = 0;
+ sr_map[n] = nglob;
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].copy((SurfReactProbKokkos*)(surf->sr[n]));
+ sr_kk_prob_copy[nprob].obj.pre_react();
+ sr_type_list[n] = 1;
+ sr_map[n] = nprob;
+ nprob++;
+ } else {
+ error->all(FLERR,"Unknown Kokkos surface reaction method");
+ }
+ }
+
+ if (nglob > KOKKOS_MAX_SURF_REACT_PER_TYPE || nprob > KOKKOS_MAX_SURF_REACT_PER_TYPE)
+ error->all(FLERR,"Kokkos currently supports two instances of each surface reaction method");
+ }
+
+ if (random == NULL) {
+ // initialize RNG
+
+ random = new RanKnuth(update->ranmaster->uniform());
+ double seed = update->ranmaster->uniform();
+ random->reset(seed,comm->me,100);
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.init(random);
+#endif
+ }
+
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ particle_kk->sync(Device,PARTICLE_MASK|SPECIES_MASK);
+ d_particles = particle_kk->k_particles.view_device();
+ d_species = particle_kk->k_species.view_device();
+ boltz = update->boltz;
+
+ rotstyle = NONE;
+ if (Pointers::collide) rotstyle = Pointers::collide->rotstyle;
+ vibstyle = NONE;
+ if (Pointers::collide) vibstyle = Pointers::collide->vibstyle;
+
+ Kokkos::deep_copy(d_scalars,0);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideImpulsiveKokkos::post_collide()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ if (ambi_flag || vibmode_flag) particle_kk->modify(Device,CUSTOM_MASK);
+
+ Kokkos::deep_copy(h_scalars,d_scalars);
+
+ int m = surf->find_collide(id);
+ auto sc = surf->sc[m]; // can't modify the copy directly, use the original
+ sc->nsingle += h_nsingle();
+ surf->nreact_one += h_nreact_one();
+
+ d_particles = {};
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideImpulsiveKokkos::backup()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ d_particles = particle_kk->k_particles.view_device();
+
+ if (surf->nsr > 0) {
+ int nglob,nprob;
+ nglob = nprob = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].obj.backup();
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].obj.backup();
+ nprob++;
+ }
+ }
+ }
+
+#ifdef SPARTA_KOKKOS_EXACT
+ if (!random_backup)
+ random_backup = new RanKnuth(12345 + comm->me);
+ memcpy(random_backup,random,sizeof(RanKnuth));
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideImpulsiveKokkos::restore()
+{
+ if (surf->nsr > 0) {
+ int nglob,nprob;
+ nglob = nprob = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].obj.restore();
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].obj.restore();
+ nprob++;
+ }
+ }
+ }
+
+ Kokkos::deep_copy(d_scalars,0);
+
+#ifdef SPARTA_KOKKOS_EXACT
+ memcpy(random,random_backup,sizeof(RanKnuth));
+#endif
+}
diff --git a/src/KOKKOS/surf_collide_impulsive_kokkos.h b/src/KOKKOS/surf_collide_impulsive_kokkos.h
new file mode 100644
index 000000000..9f909e3fc
--- /dev/null
+++ b/src/KOKKOS/surf_collide_impulsive_kokkos.h
@@ -0,0 +1,376 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef SURF_COLLIDE_CLASS
+
+SurfCollideStyle(impulsive/kk,SurfCollideImpulsiveKokkos)
+
+#else
+
+#ifndef SPARTA_SURF_COLLIDE_IMPULSIVE_KOKKOS_H
+#define SPARTA_SURF_COLLIDE_IMPULSIVE_KOKKOS_H
+
+#include "surf_collide_impulsive.h"
+#include "kokkos_type.h"
+#include "math_extra_kokkos.h"
+#include "Kokkos_Random.hpp"
+#include "rand_pool_wrap.h"
+#include "kokkos_copy.h"
+#include "fix_ambipolar_kokkos.h"
+#include "fix_vibmode_kokkos.h"
+#include "surf_react_global_kokkos.h"
+#include "surf_react_prob_kokkos.h"
+
+namespace SPARTA_NS {
+
+class SurfCollideImpulsiveKokkos : public SurfCollideImpulsive {
+ public:
+
+ enum{NONE,DISCRETE,SMOOTH}; // several files
+ enum{PKEEP,PINSERT,PDONE,PDISCARD,PENTRY,PEXIT,PSURF}; // several files
+
+ SurfCollideImpulsiveKokkos(class SPARTA *, int, char **);
+ SurfCollideImpulsiveKokkos(class SPARTA *);
+ ~SurfCollideImpulsiveKokkos();
+ void init();
+ void dynamic();
+ void pre_collide();
+ void post_collide();
+ void backup();
+ void restore();
+
+ private:
+ double boltz;
+ int rotstyle, vibstyle;
+
+#ifndef SPARTA_KOKKOS_EXACT
+ Kokkos::Random_XorShift64_Pool rand_pool;
+ typedef typename Kokkos::Random_XorShift64_Pool::generator_type rand_type;
+#else
+ RandPoolWrap rand_pool;
+ typedef RandWrap rand_type;
+#endif
+
+ RanKnuth* random_backup;
+
+ DAT::t_float_1d d_t_persurf;
+
+ typedef Kokkos::DualView tdual_int_2;
+ typedef tdual_int_2::t_dev t_int_2;
+ typedef tdual_int_2::t_host t_host_int_2;
+ t_int_2 d_scalars;
+ t_host_int_2 h_scalars;
+
+ DAT::t_int_scalar d_nsingle;
+ DAT::t_int_scalar d_nreact_one;
+
+ HAT::t_int_scalar h_nsingle;
+ HAT::t_int_scalar h_nreact_one;
+
+ t_particle_1d d_particles;
+ t_species_1d d_species;
+
+ int ambi_flag,vibmode_flag;
+ FixAmbipolarKokkos* afix_kk;
+ FixVibmodeKokkos* vfix_kk;
+ KKCopy fix_ambi_kk_copy;
+ KKCopy fix_vibmode_kk_copy;
+
+ int sr_type_list[KOKKOS_MAX_TOT_SURF_REACT];
+ int sr_map[KOKKOS_MAX_TOT_SURF_REACT];
+ KKCopy sr_kk_global_copy[KOKKOS_MAX_SURF_REACT_PER_TYPE];
+ KKCopy sr_kk_prob_copy[KOKKOS_MAX_SURF_REACT_PER_TYPE];
+
+ public:
+
+ /* ----------------------------------------------------------------------
+ particle collision with surface with optional chemistry
+ ip = particle with current x = collision pt, current v = incident v
+ isurf = index of surface element
+ norm = surface normal unit vector
+ isr = index of reaction model if >= 0, -1 for no chemistry
+ ip = set to NULL if destroyed by chemistry
+ return jp = new particle if created by chemistry
+ return reaction = index of reaction (1 to N) that took place, 0 = no reaction
+ resets particle(s) to post-collision outward velocity
+ ------------------------------------------------------------------------- */
+
+ template
+ KOKKOS_INLINE_FUNCTION
+ Particle::OnePart* collide_kokkos(Particle::OnePart *&ip, double &,
+ int isurf, const double *norm, int isr, int &reaction,
+ const DAT::t_int_scalar &d_retry, const DAT::t_int_scalar &d_nlocal) const
+ {
+ if (ATOMIC_REDUCTION == 0)
+ d_nsingle()++;
+ else
+ Kokkos::atomic_inc(&d_nsingle());
+
+ // if surface chemistry defined, attempt reaction
+ // reaction = 1 to N for which reaction took place, 0 for none
+ // velreset = 1 if reaction reset post-collision velocity, else 0
+
+ Particle::OnePart iorig;
+ Particle::OnePart *jp = NULL;
+ reaction = 0;
+ int velreset = 0;
+
+ if (REACT) {
+ if (ambi_flag || vibmode_flag) memcpy(&iorig,ip,sizeof(Particle::OnePart));
+
+ int sr_type = sr_type_list[isr];
+ int m = sr_map[isr];
+
+ if (sr_type == 0) {
+ reaction = sr_kk_global_copy[m].obj.
+ react_kokkos(ip,isurf,norm,jp,velreset,d_retry,d_nlocal);
+ } else if (sr_type == 1) {
+ reaction = sr_kk_prob_copy[m].obj.
+ react_kokkos(ip,isurf,norm,jp,velreset,d_retry,d_nlocal);
+ }
+
+ if (reaction) {
+ if (ATOMIC_REDUCTION == 0)
+ d_nreact_one()++;
+ else
+ Kokkos::atomic_inc(&d_nreact_one());
+ }
+ }
+
+ // set temperature of isurf if VARSURF or CUSTOM
+
+ double tsurf_local = tsurf;
+ if (persurf_temperature) {
+ tsurf_local = d_t_persurf[isurf];
+ if (tsurf_local <= 0.0) Kokkos::abort("Surf_collide tsurf <= 0.0");
+ }
+
+ // impulsive reflection for each particle
+ // only if SurfReact did not already reset velocities
+ // also both particles need to trigger any fixes
+ // to update per-particle properties which depend on
+ // temperature of the particle, e.g. fix vibmode and fix ambipolar
+
+ if (ip) {
+ if (!velreset) impulsive(ip,norm,tsurf_local);
+ int i = ip - d_particles.data();
+ if (ambi_flag)
+ fix_ambi_kk_copy.obj.update_custom_kokkos(i,tsurf_local,tsurf_local,tsurf_local,vstream);
+ if (vibmode_flag)
+ fix_vibmode_kk_copy.obj.update_custom_kokkos(i,tsurf_local,tsurf_local,tsurf_local,vstream);
+ }
+ if (REACT && jp) {
+ if (!velreset) impulsive(jp,norm,tsurf_local);
+ int j = jp - d_particles.data();
+ if (ambi_flag)
+ fix_ambi_kk_copy.obj.update_custom_kokkos(j,tsurf_local,tsurf_local,tsurf_local,vstream);
+ if (vibmode_flag)
+ fix_vibmode_kk_copy.obj.update_custom_kokkos(j,tsurf_local,tsurf_local,tsurf_local,vstream);
+ }
+
+ // call any fixes with a surf_react() method
+ // they may reset j to -1, e.g. fix ambipolar
+ // in which case newly created j is deleted
+
+ if (REACT && reaction && ambi_flag) {
+ int i = -1;
+ if (ip) i = ip - d_particles.data();
+ int j = -1;
+ if (jp) j = jp - d_particles.data();
+ int j_orig = j;
+ fix_ambi_kk_copy.obj.surf_react_kokkos(&iorig,i,j);
+ if (jp && j < 0) {
+ d_particles[j_orig].flag = PDISCARD;
+ jp = NULL;
+ }
+ }
+
+ return jp;
+ };
+
+ private:
+
+ /* ----------------------------------------------------------------------
+ impulsive reflection
+ ------------------------------------------------------------------------- */
+
+ KOKKOS_INLINE_FUNCTION
+ void impulsive(Particle::OnePart *p, const double *norm, const double twall) const
+ {
+ rand_type rand_gen = rand_pool.get_state();
+
+ double tangent1[3],tangent2[3];
+ int ispecies = p->ispecies;
+
+ double vperp, vtan1, vtan2;
+ double mass = d_species[ispecies].mass;
+
+ double *v = p->v;
+ double dot = MathExtraKokkos::dot3(v,norm);
+
+ tangent1[0] = v[0] - dot*norm[0];
+ tangent1[1] = v[1] - dot*norm[1];
+ tangent1[2] = v[2] - dot*norm[2];
+
+ if (MathExtraKokkos::lensq3(tangent1) == 0.0) {
+ tangent2[0] = rand_gen.drand();
+ tangent2[1] = rand_gen.drand();
+ tangent2[2] = rand_gen.drand();
+ MathExtraKokkos::cross3(norm,tangent2,tangent1);
+ }
+
+ MathExtraKokkos::norm3(tangent1);
+ MathExtraKokkos::cross3(norm,tangent1,tangent2);
+
+ // compute final polar (theta) and azimuthal (phi) angles
+
+ double tan1 = MathExtraKokkos::dot3(v,tangent1);
+ double tan2 = MathExtraKokkos::dot3(v,tangent2);
+
+ double v_i_mag_sq = MathExtraKokkos::lensq3(v);
+ double E_i = 0.5 * mass * v_i_mag_sq;
+ double theta_i = acos(-dot/sqrt(v_i_mag_sq));
+ double phi_i = atan2(tan2,tan1);
+ double phi_peak = MathConst::MY_2PI - phi_i;
+
+ double theta_f, phi_f;
+ double P = 0.0;
+
+ // theta_f calculation
+
+ while (rand_gen.drand() > P) {
+ theta_f = MathConst::MY_PI2 * rand_gen.drand();
+ P = pow(cos( theta_f - theta_peak ),cos_theta_pow) * sin(theta_f);
+ if (double_flag) {
+ if (theta_f > theta_peak)
+ P = pow(cos( theta_f - theta_peak ),cos_theta_pow_2) * sin(theta_f);
+ }
+
+ if (step_flag) {
+ double func_step = 0.0;
+ double tan_theta = tan(theta_f);
+ double cotangent = 1.0/tan_theta;
+ if (cotangent > step_size) func_step = 1 - step_size*tan_theta;
+ P *= func_step;
+ }
+ }
+
+ // phi_f calculations
+
+ P = 0.0;
+ while (rand_gen.drand() > P) {
+ phi_f = phi_peak + MathConst::MY_PI * (2*rand_gen.drand() - 1);
+ P = pow(cos( 0.5*(phi_f - phi_peak) ),cos_phi_pow);
+ }
+
+ if (phi_f > MathConst::MY_PI) phi_f -= MathConst::MY_2PI;
+ else if (phi_f < -MathConst::MY_PI) phi_f += MathConst::MY_2PI;
+
+ double v_f_avg = 0.0;
+ if (softsphere_flag) {
+ double mu = d_species[ispecies].molwt/eff_mass;
+ double cos_khi = cos(MathConst::MY_PI - theta_i - theta_f);
+ double sin_khi_sq = 1 - cos_khi*cos_khi;
+ double dE, E_f_avg;
+
+ dE = 2*mu/((mu+1)*(mu+1)) *
+ (1 + mu*sin_khi_sq + eng_ratio*(mu+1)/(2*mu) -
+ cos_khi*sqrt(1 - mu*mu*sin_khi_sq - eng_ratio*(mu + 1)));
+ E_f_avg = E_i * (1 - dE);
+ v_f_avg = var_alpha_sq * sqrt(mass/(2*E_f_avg)) *
+ (2*E_f_avg/(mass*var_alpha_sq) - 1);
+ } else {
+ v_f_avg = u0_a*twall + u0_b;
+ }
+
+ double v_f_max = 0.5 * (v_f_avg + sqrt(v_f_avg*v_f_avg + 6*var_alpha_sq));
+ double f_max = v_f_max*v_f_max*v_f_max *
+ exp(-(v_f_max - v_f_avg) * (v_f_max - v_f_avg)/(var_alpha_sq));
+
+ double v_f_mag;
+ P = 0.0;
+ while (rand_gen.drand() > P) {
+ v_f_mag = v_f_max + 3 * var_alpha * ( 2 * rand_gen.drand() - 1 );
+ P = v_f_mag*v_f_mag*v_f_mag/(f_max) *
+ exp(-(v_f_mag - v_f_avg)*(v_f_mag - v_f_avg)/(var_alpha_sq));
+ }
+
+ vperp = v_f_mag * cos(theta_f);
+ vtan1 = v_f_mag * sin(theta_f) * cos(phi_f);
+ vtan2 = v_f_mag * sin(theta_f) * sin(phi_f);
+
+ v[0] = vperp*norm[0] + vtan1*tangent1[0] + vtan2*tangent2[0];
+ v[1] = vperp*norm[1] + vtan1*tangent1[1] + vtan2*tangent2[1];
+ v[2] = vperp*norm[2] + vtan1*tangent1[2] + vtan2*tangent2[2];
+
+ if (intenergy_flag) {
+ double E_f = 0.5 * mass * v_f_mag * v_f_mag;
+ double extra_energy = E_i - E_f;
+
+ // rotational component
+
+ if (rotstyle == NONE || d_species[ispecies].rotdof < 2) p->erot = 0.0;
+ else p->erot += rot_frac*extra_energy;
+
+ // vibrational component
+
+ int vibdof = d_species[ispecies].vibdof;
+
+ if (vibstyle == NONE || vibdof < 2) {
+ p->evib = 0.0;
+ } else {
+ double *vibtemp = d_species[ispecies].vibtemp;
+ double evib_val = p->evib + vib_frac*extra_energy;
+
+ if (vibstyle == SMOOTH) p->evib = evib_val;
+ if (vibstyle == DISCRETE && vibdof==2) {
+ int ivib = evib_val / (boltz*vibtemp[0]);
+ p->evib = ivib * boltz * vibtemp[0];
+ } else {
+ int nvibmode = d_species[ispecies].nvibmode;
+ int *vibdegen = d_species[ispecies].vibdegen;
+ double tot_temp=0.0;
+ double evib_sum = 0.0;
+
+ for (int imode=0; imodeevib = evib_sum;
+ }
+ }
+ }
+
+ rand_pool.free_state(rand_gen);
+ }
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Illegal ... command
+
+Self-explanatory. Check the input script syntax and compare to the
+documentation for the command. You can use -echo screen as a
+command-line option when running SPARTA to see the offending line.
+
+*/
diff --git a/src/KOKKOS/surf_collide_specular_kokkos.h b/src/KOKKOS/surf_collide_specular_kokkos.h
index d225b150c..d36c0eb70 100644
--- a/src/KOKKOS/surf_collide_specular_kokkos.h
+++ b/src/KOKKOS/surf_collide_specular_kokkos.h
@@ -202,6 +202,19 @@ class SurfCollideSpecularKokkos : public SurfCollideSpecular {
return jp;
};
+
+ /* ----------------------------------------------------------------------
+ wrapper on specular reflection to perform a collision for a single particle
+ called on-device by SurfReactAdsorbKokkos GS chemistry
+ flags, coeffs can be NULL; matches SurfCollideSpecular::wrapper
+ ------------------------------------------------------------------------- */
+
+ KOKKOS_INLINE_FUNCTION
+ void wrapper_kokkos(Particle::OnePart *p, const double *norm,
+ int *, double *) const
+ {
+ MathExtraKokkos::reflect3(p->v,norm);
+ }
};
}
diff --git a/src/KOKKOS/surf_collide_td_kokkos.cpp b/src/KOKKOS/surf_collide_td_kokkos.cpp
new file mode 100644
index 000000000..7a49d7443
--- /dev/null
+++ b/src/KOKKOS/surf_collide_td_kokkos.cpp
@@ -0,0 +1,358 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "math.h"
+#include "stdlib.h"
+#include "string.h"
+#include "surf_collide_td_kokkos.h"
+#include "surf_kokkos.h"
+#include "input.h"
+#include "variable.h"
+#include "particle.h"
+#include "domain.h"
+#include "update.h"
+#include "modify.h"
+#include "comm.h"
+#include "random_mars.h"
+#include "random_knuth.h"
+#include "math_const.h"
+#include "math_extra.h"
+#include "memory.h"
+#include "error.h"
+#include "particle_kokkos.h"
+#include "sparta_masks.h"
+#include "collide.h"
+
+using namespace SPARTA_NS;
+using namespace MathConst;
+
+enum{INT,DOUBLE}; // several files
+enum{NUMERIC,CUSTOM,VARIABLE,VAREQUAL,VARSURF}; // surf_collide classes
+
+#define VAL_1(X) X
+#define VAL_2(X) VAL_1(X), VAL_1(X)
+
+/* ---------------------------------------------------------------------- */
+
+SurfCollideTDKokkos::SurfCollideTDKokkos(SPARTA *sparta, int narg, char **arg) :
+ SurfCollideTD(sparta, narg, arg),
+ fix_ambi_kk_copy(sparta),
+ fix_vibmode_kk_copy(sparta),
+ sr_kk_global_copy{VAL_2(KKCopy(sparta))},
+ sr_kk_prob_copy{VAL_2(KKCopy(sparta))},
+ rand_pool(12345 + comm->me
+#ifdef SPARTA_KOKKOS_EXACT
+ , sparta
+#endif
+ )
+{
+ kokkosable = 1;
+
+ random_backup = NULL;
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.init(random);
+#endif
+
+ // use 1D view for scalars to reduce GPU memory operations
+
+ d_scalars = t_int_2("surf_collide_td:scalars");
+ d_nsingle = Kokkos::subview(d_scalars,0);
+ d_nreact_one = Kokkos::subview(d_scalars,1);
+
+ h_scalars = t_host_int_2("surf_collide_td:scalars_mirror");
+ h_nsingle = Kokkos::subview(h_scalars,0);
+ h_nreact_one = Kokkos::subview(h_scalars,1);
+}
+
+SurfCollideTDKokkos::SurfCollideTDKokkos(SPARTA *sparta) :
+ SurfCollideTD(sparta),
+ fix_ambi_kk_copy(sparta),
+ fix_vibmode_kk_copy(sparta),
+ sr_kk_global_copy{VAL_2(KKCopy(sparta))},
+ sr_kk_prob_copy{VAL_2(KKCopy(sparta))},
+ rand_pool(12345 // seed doesn't matter since it will just be copied over
+#ifdef SPARTA_KOKKOS_EXACT
+ , sparta
+#endif
+ )
+{
+ copy = 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+SurfCollideTDKokkos::~SurfCollideTDKokkos()
+{
+ if (uncopy) {
+ fix_ambi_kk_copy.uncopy();
+ fix_vibmode_kk_copy.uncopy();
+
+ for (int i = 0; i < KOKKOS_MAX_SURF_REACT_PER_TYPE; i++) {
+ sr_kk_global_copy[i].uncopy();
+ sr_kk_prob_copy[i].uncopy();
+ }
+ }
+
+ if (copy) return;
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.destroy();
+ if (random_backup)
+ delete random_backup;
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideTDKokkos::init()
+{
+ SurfCollideTD::init();
+
+ ambi_flag = vibmode_flag = 0;
+ if (modify->n_update_custom) {
+ for (int ifix = 0; ifix < modify->nfix; ifix++) {
+ if (strcmp(modify->fix[ifix]->style,"ambipolar") == 0) {
+ ambi_flag = 1;
+ FixAmbipolar *afix = (FixAmbipolar *) modify->fix[ifix];
+ if (!afix->kokkos_flag)
+ error->all(FLERR,"Must use fix ambipolar/kk when Kokkos is enabled");
+ afix_kk = (FixAmbipolarKokkos*)afix;
+ } else if (strcmp(modify->fix[ifix]->style,"vibmode") == 0) {
+ vibmode_flag = 1;
+ FixVibmode *vfix = (FixVibmode *) modify->fix[ifix];
+ if (!vfix->kokkos_flag)
+ error->all(FLERR,"Must use fix vibmode/kk when Kokkos is enabled");
+ vfix_kk = (FixVibmodeKokkos*)vfix;
+ }
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------
+ recalculate Tsurf values which are dynamic
+ called by Update::setup() and Update::run()
+---------------------------------------------------------------------- */
+
+void SurfCollideTDKokkos::dynamic()
+{
+ // VAREQUAL mode
+ // equal-style variable sets single tsurf value for all surfs
+
+ if (tmode == VAREQUAL) {
+
+ // only evaluate variable if timestep is multiple of tfreq
+
+ if (update->ntimestep % tfreq) return;
+ tsurf = input->variable->compute_equal(tindex_var);
+ if (tsurf <= 0.0) error->all(FLERR,"Surf_collide tsurf <= 0.0");
+
+ // VARSURF mode
+ // surf-style variable sets new tsurf values for all surfs
+ // particle/surf collisions access t_persurf for local+ghost values
+
+ } else if (tmode == VARSURF) {
+
+ // only evaluate variable if timestep is multiple of tfreq
+
+ int spreadflag = 0;
+ if (update->ntimestep % tfreq == 0) {
+ if (n_owned != surf->nown) {
+ memory->destroy(t_owned);
+ n_owned = surf->nown;
+ memory->create(t_owned,n_owned,"surfcollide:t_owned");
+ }
+
+ input->variable->compute_surf(tindex_var,t_owned,1,0);
+ spreadflag = 1;
+ }
+
+ // spread t_owned values to t_localghost values via spread_own2local()
+ // if just re-computed variable OR surfs are
+ // distributed and load balance/adaptation took place on previous step
+
+ if (spreadflag ||
+ (surf->distributed && surf->localghost_changed_step == update->ntimestep-1)) {
+ if (n_localghost != surf->nlocal + surf->nghost) {
+ memory->destroy(t_localghost);
+ n_localghost = surf->nlocal + surf->nghost;
+ memory->create(t_localghost,n_localghost,"surfcollide:t_localghost");
+ }
+
+ surf->spread_own2local(1,DOUBLE,t_owned,t_localghost);
+ t_persurf = t_localghost;
+
+ auto h_t_persurf = HAT::t_float_1d(t_persurf,n_localghost);
+ d_t_persurf = Kokkos::create_mirror_view_and_copy(SPADeviceType(),h_t_persurf);
+ }
+
+ // CUSTOM mode
+ // ensure access to custom per-surf vec for tsurf values for all surfs
+ // particle/surf collisions access t_persurf for local+ghost values
+
+ } else if (tmode == CUSTOM) {
+ SurfKokkos* surf_kk = (SurfKokkos*) surf;
+ auto h_edvec_local = surf_kk->k_edvec_local.view_host();
+
+ // spread owned values to local+ghost values via spread_custom()
+ // estatus == 1 means owned values already spread to local+ghost values
+ // if estatus == 0: owned values are new OR
+ // surfs are distributed and load balance/adaptation took place
+
+ if (surf->estatus[tindex_custom] == 0) surf->spread_custom(tindex_custom);
+
+ h_edvec_local[tindex_custom].k_view.sync_device();
+ d_t_persurf = h_edvec_local[tindex_custom].k_view.view_device();
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideTDKokkos::pre_collide()
+{
+ if (ambi_flag) {
+ afix_kk->pre_update_custom_kokkos();
+ fix_ambi_kk_copy.copy(afix_kk);
+ }
+
+ if (vibmode_flag) {
+ vfix_kk->pre_update_custom_kokkos();
+ fix_vibmode_kk_copy.copy(vfix_kk);
+ }
+
+ if (surf->nsr > KOKKOS_MAX_TOT_SURF_REACT)
+ error->all(FLERR,"Kokkos currently supports two instances of each surface reaction method");
+
+ if (surf->nsr > 0) {
+ int nglob,nprob;
+ nglob = nprob = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (!surf->sr[n]->kokkosable)
+ error->all(FLERR,"Must use Kokkos-enabled surface reaction method with Kokkos");
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].copy((SurfReactGlobalKokkos*)(surf->sr[n]));
+ sr_kk_global_copy[nglob].obj.pre_react();
+ sr_type_list[n] = 0;
+ sr_map[n] = nglob;
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].copy((SurfReactProbKokkos*)(surf->sr[n]));
+ sr_kk_prob_copy[nprob].obj.pre_react();
+ sr_type_list[n] = 1;
+ sr_map[n] = nprob;
+ nprob++;
+ } else {
+ error->all(FLERR,"Unknown Kokkos surface reaction method");
+ }
+ }
+
+ if (nglob > KOKKOS_MAX_SURF_REACT_PER_TYPE || nprob > KOKKOS_MAX_SURF_REACT_PER_TYPE)
+ error->all(FLERR,"Kokkos currently supports two instances of each surface reaction method");
+ }
+
+ if (random == NULL) {
+ // initialize RNG
+
+ random = new RanKnuth(update->ranmaster->uniform());
+ double seed = update->ranmaster->uniform();
+ random->reset(seed,comm->me,100);
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.init(random);
+#endif
+ }
+
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ particle_kk->sync(Device,PARTICLE_MASK|SPECIES_MASK);
+ d_particles = particle_kk->k_particles.view_device();
+ d_species = particle_kk->k_species.view_device();
+ boltz = update->boltz;
+
+ rotstyle = NONE;
+ if (Pointers::collide) rotstyle = Pointers::collide->rotstyle;
+ vibstyle = NONE;
+ if (Pointers::collide) vibstyle = Pointers::collide->vibstyle;
+
+ Kokkos::deep_copy(d_scalars,0);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideTDKokkos::post_collide()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ if (ambi_flag || vibmode_flag) particle_kk->modify(Device,CUSTOM_MASK);
+
+ Kokkos::deep_copy(h_scalars,d_scalars);
+
+ int m = surf->find_collide(id);
+ auto sc = surf->sc[m]; // can't modify the copy directly, use the original
+ sc->nsingle += h_nsingle();
+ surf->nreact_one += h_nreact_one();
+
+ d_particles = {};
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideTDKokkos::backup()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ d_particles = particle_kk->k_particles.view_device();
+
+ if (surf->nsr > 0) {
+ int nglob,nprob;
+ nglob = nprob = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].obj.backup();
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].obj.backup();
+ nprob++;
+ }
+ }
+ }
+
+#ifdef SPARTA_KOKKOS_EXACT
+ if (!random_backup)
+ random_backup = new RanKnuth(12345 + comm->me);
+ memcpy(random_backup,random,sizeof(RanKnuth));
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfCollideTDKokkos::restore()
+{
+ if (surf->nsr > 0) {
+ int nglob,nprob;
+ nglob = nprob = 0;
+ for (int n = 0; n < surf->nsr; n++) {
+ if (strcmp(surf->sr[n]->style,"global") == 0) {
+ sr_kk_global_copy[nglob].obj.restore();
+ nglob++;
+ } else if (strcmp(surf->sr[n]->style,"prob") == 0) {
+ sr_kk_prob_copy[nprob].obj.restore();
+ nprob++;
+ }
+ }
+ }
+
+ Kokkos::deep_copy(d_scalars,0);
+
+#ifdef SPARTA_KOKKOS_EXACT
+ memcpy(random,random_backup,sizeof(RanKnuth));
+#endif
+}
diff --git a/src/KOKKOS/surf_collide_td_kokkos.h b/src/KOKKOS/surf_collide_td_kokkos.h
new file mode 100644
index 000000000..5df3b9102
--- /dev/null
+++ b/src/KOKKOS/surf_collide_td_kokkos.h
@@ -0,0 +1,363 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef SURF_COLLIDE_CLASS
+
+SurfCollideStyle(td/kk,SurfCollideTDKokkos)
+
+#else
+
+#ifndef SPARTA_SURF_COLLIDE_TD_KOKKOS_H
+#define SPARTA_SURF_COLLIDE_TD_KOKKOS_H
+
+#include "surf_collide_td.h"
+#include "kokkos_type.h"
+#include "math_extra_kokkos.h"
+#include "Kokkos_Random.hpp"
+#include "rand_pool_wrap.h"
+#include "kokkos_copy.h"
+#include "fix_ambipolar_kokkos.h"
+#include "fix_vibmode_kokkos.h"
+#include "surf_react_global_kokkos.h"
+#include "surf_react_prob_kokkos.h"
+
+namespace SPARTA_NS {
+
+class SurfCollideTDKokkos : public SurfCollideTD {
+ public:
+
+ enum{NONE,DISCRETE,SMOOTH}; // several files
+ enum{PKEEP,PINSERT,PDONE,PDISCARD,PENTRY,PEXIT,PSURF}; // several files
+
+ SurfCollideTDKokkos(class SPARTA *, int, char **);
+ SurfCollideTDKokkos(class SPARTA *);
+ ~SurfCollideTDKokkos();
+ void init();
+ void dynamic();
+ void pre_collide();
+ void post_collide();
+ void backup();
+ void restore();
+
+ private:
+ double boltz;
+ int rotstyle, vibstyle;
+
+#ifndef SPARTA_KOKKOS_EXACT
+ Kokkos::Random_XorShift64_Pool rand_pool;
+ typedef typename Kokkos::Random_XorShift64_Pool::generator_type rand_type;
+#else
+ RandPoolWrap rand_pool;
+ typedef RandWrap rand_type;
+#endif
+
+ RanKnuth* random_backup;
+
+ DAT::t_float_1d d_t_persurf;
+
+ typedef Kokkos::DualView tdual_int_2;
+ typedef tdual_int_2::t_dev t_int_2;
+ typedef tdual_int_2::t_host t_host_int_2;
+ t_int_2 d_scalars;
+ t_host_int_2 h_scalars;
+
+ DAT::t_int_scalar d_nsingle;
+ DAT::t_int_scalar d_nreact_one;
+
+ HAT::t_int_scalar h_nsingle;
+ HAT::t_int_scalar h_nreact_one;
+
+ t_particle_1d d_particles;
+ t_species_1d d_species;
+
+ int ambi_flag,vibmode_flag;
+ FixAmbipolarKokkos* afix_kk;
+ FixVibmodeKokkos* vfix_kk;
+ KKCopy fix_ambi_kk_copy;
+ KKCopy fix_vibmode_kk_copy;
+
+ int sr_type_list[KOKKOS_MAX_TOT_SURF_REACT];
+ int sr_map[KOKKOS_MAX_TOT_SURF_REACT];
+ KKCopy sr_kk_global_copy[KOKKOS_MAX_SURF_REACT_PER_TYPE];
+ KKCopy sr_kk_prob_copy[KOKKOS_MAX_SURF_REACT_PER_TYPE];
+
+ public:
+
+ /* ----------------------------------------------------------------------
+ particle collision with surface with optional chemistry
+ ip = particle with current x = collision pt, current v = incident v
+ isurf = index of surface element
+ norm = surface normal unit vector
+ isr = index of reaction model if >= 0, -1 for no chemistry
+ ip = set to NULL if destroyed by chemistry
+ return jp = new particle if created by chemistry
+ return reaction = index of reaction (1 to N) that took place, 0 = no reaction
+ resets particle(s) to post-collision outward velocity
+ ------------------------------------------------------------------------- */
+
+ template
+ KOKKOS_INLINE_FUNCTION
+ Particle::OnePart* collide_kokkos(Particle::OnePart *&ip, double &,
+ int isurf, const double *norm, int isr, int &reaction,
+ const DAT::t_int_scalar &d_retry, const DAT::t_int_scalar &d_nlocal) const
+ {
+ if (ATOMIC_REDUCTION == 0)
+ d_nsingle()++;
+ else
+ Kokkos::atomic_inc(&d_nsingle());
+
+ // if surface chemistry defined, attempt reaction
+ // reaction = 1 to N for which reaction took place, 0 for none
+ // velreset = 1 if reaction reset post-collision velocity, else 0
+
+ Particle::OnePart iorig;
+ Particle::OnePart *jp = NULL;
+ reaction = 0;
+ int velreset = 0;
+
+ if (REACT) {
+ if (ambi_flag || vibmode_flag) memcpy(&iorig,ip,sizeof(Particle::OnePart));
+
+ int sr_type = sr_type_list[isr];
+ int m = sr_map[isr];
+
+ if (sr_type == 0) {
+ reaction = sr_kk_global_copy[m].obj.
+ react_kokkos(ip,isurf,norm,jp,velreset,d_retry,d_nlocal);
+ } else if (sr_type == 1) {
+ reaction = sr_kk_prob_copy[m].obj.
+ react_kokkos(ip,isurf,norm,jp,velreset,d_retry,d_nlocal);
+ }
+
+ if (reaction) {
+ if (ATOMIC_REDUCTION == 0)
+ d_nreact_one()++;
+ else
+ Kokkos::atomic_inc(&d_nreact_one());
+ }
+ }
+
+ // set temperature of isurf if VARSURF or CUSTOM
+
+ double tsurf_local = tsurf;
+ if (persurf_temperature) {
+ tsurf_local = d_t_persurf[isurf];
+ if (tsurf_local <= 0.0) Kokkos::abort("Surf_collide tsurf <= 0.0");
+ }
+
+ // TD reflection for each particle
+ // only if SurfReact did not already reset velocities
+ // also both particles need to trigger any fixes
+ // to update per-particle properties which depend on
+ // temperature of the particle, e.g. fix vibmode and fix ambipolar
+
+ if (ip) {
+ if (!velreset) td(ip,norm,tsurf_local);
+ int i = ip - d_particles.data();
+ if (ambi_flag)
+ fix_ambi_kk_copy.obj.update_custom_kokkos(i,tsurf_local,tsurf_local,tsurf_local,vstream);
+ if (vibmode_flag)
+ fix_vibmode_kk_copy.obj.update_custom_kokkos(i,tsurf_local,tsurf_local,tsurf_local,vstream);
+ }
+ if (REACT && jp) {
+ if (!velreset) td(jp,norm,tsurf_local);
+ int j = jp - d_particles.data();
+ if (ambi_flag)
+ fix_ambi_kk_copy.obj.update_custom_kokkos(j,tsurf_local,tsurf_local,tsurf_local,vstream);
+ if (vibmode_flag)
+ fix_vibmode_kk_copy.obj.update_custom_kokkos(j,tsurf_local,tsurf_local,tsurf_local,vstream);
+ }
+
+ // call any fixes with a surf_react() method
+ // they may reset j to -1, e.g. fix ambipolar
+ // in which case newly created j is deleted
+
+ if (REACT && reaction && ambi_flag) {
+ int i = -1;
+ if (ip) i = ip - d_particles.data();
+ int j = -1;
+ if (jp) j = jp - d_particles.data();
+ int j_orig = j;
+ fix_ambi_kk_copy.obj.surf_react_kokkos(&iorig,i,j);
+ if (jp && j < 0) {
+ d_particles[j_orig].flag = PDISCARD;
+ jp = NULL;
+ }
+ }
+
+ return jp;
+ };
+
+ private:
+
+ /* ----------------------------------------------------------------------
+ TD (thermal desorption) reflection
+ ------------------------------------------------------------------------- */
+
+ KOKKOS_INLINE_FUNCTION
+ void td(Particle::OnePart *p, const double *norm, const double twall) const
+ {
+ rand_type rand_gen = rand_pool.get_state();
+
+ double tangent1[3],tangent2[3];
+ int ispecies = p->ispecies;
+
+ double *v = p->v;
+ double dot = MathExtraKokkos::dot3(v,norm);
+
+ tangent1[0] = v[0] - dot*norm[0];
+ tangent1[1] = v[1] - dot*norm[1];
+ tangent1[2] = v[2] - dot*norm[2];
+
+ if (MathExtraKokkos::lensq3(tangent1) == 0.0) {
+ tangent2[0] = rand_gen.drand();
+ tangent2[1] = rand_gen.drand();
+ tangent2[2] = rand_gen.drand();
+ MathExtraKokkos::cross3(norm,tangent2,tangent1);
+ }
+
+ MathExtraKokkos::norm3(tangent1);
+ MathExtraKokkos::cross3(norm,tangent1,tangent2);
+
+ double mass = d_species[ispecies].mass;
+ double E_i = 0.5 * mass * MathExtraKokkos::lensq3(v);
+
+ double E_t = boltz * twall;
+ if (bond_flag) E_t += boltz*bond_trans;
+ if (initen_flag) E_t += E_i*initen_trans;
+
+ double E_n = E_t;
+ if (barrier_flag) E_n += boltz*barrier_val;
+
+ double vrm_n = sqrt(2.0*E_n / mass);
+ double vrm_t = sqrt(2.0*E_t / mass);
+ double vperp = vrm_n * sqrt(-log(rand_gen.drand()));
+
+ double theta = MathConst::MY_2PI * rand_gen.drand();
+ double vtangent = vrm_t * sqrt(-log(rand_gen.drand()));
+ double vtan1 = vtangent * sin(theta);
+ double vtan2 = vtangent * cos(theta);
+
+ v[0] = vperp*norm[0] + vtan1*tangent1[0] + vtan2*tangent2[0];
+ v[1] = vperp*norm[1] + vtan1*tangent1[1] + vtan2*tangent2[1];
+ v[2] = vperp*norm[2] + vtan1*tangent1[2] + vtan2*tangent2[2];
+
+ double twall_rot = twall;
+ double twall_vib = twall;
+
+ if (bond_flag) {
+ twall_rot += bond_rot;
+ twall_vib += bond_vib;
+ }
+
+ if (initen_flag) {
+ twall_rot += E_i*initen_rot/boltz;
+ twall_vib += E_i*initen_vib/boltz;
+ }
+
+ p->erot = erot(ispecies,twall_rot,rand_gen,boltz);
+ p->evib = evib(ispecies,twall_vib,rand_gen,boltz);
+
+ rand_pool.free_state(rand_gen);
+ }
+
+ /* ----------------------------------------------------------------------
+ generate random rotational energy for a particle
+ only a function of species index and species properties
+ ------------------------------------------------------------------------- */
+
+ KOKKOS_INLINE_FUNCTION
+ double erot(int isp, double temp_thermal, rand_type &rand_gen, double boltz) const
+ {
+ double eng,a,erm,b;
+
+ if (rotstyle == NONE) return 0.0;
+ if (d_species[isp].rotdof < 2) return 0.0;
+
+ if (rotstyle == DISCRETE && d_species[isp].rotdof == 2) {
+ int irot = -log(rand_gen.drand()) * temp_thermal /
+ d_species[isp].rottemp[0];
+ eng = irot * boltz * d_species[isp].rottemp[0];
+ } else if (rotstyle == SMOOTH && d_species[isp].rotdof == 2) {
+ eng = -log(rand_gen.drand()) * boltz * temp_thermal;
+ } else {
+ a = 0.5*d_species[isp].rotdof-1.0;
+ while (1) {
+ // energy cut-off at 10 kT
+ erm = 10.0*rand_gen.drand();
+ b = pow(erm/a,a) * exp(a-erm);
+ if (b > rand_gen.drand()) break;
+ }
+ eng = erm * boltz * temp_thermal;
+ }
+
+ return eng;
+ }
+
+ /* ----------------------------------------------------------------------
+ generate random vibrational energy for a particle
+ only a function of species index and species properties
+ index_vibmode = index of extra per-particle vibrational mode storage
+ -1 if not defined for this model
+ ------------------------------------------------------------------------- */
+
+ KOKKOS_INLINE_FUNCTION
+ double evib(int isp, double temp_thermal, rand_type &rand_gen, double boltz) const
+ {
+ double eng,a,erm,b;
+
+ if (vibstyle == NONE || d_species[isp].vibdof < 2) return 0.0;
+
+ // for DISCRETE, only need set evib for vibdof = 2
+ // mode levels and evib will be set by FixVibmode::update_custom()
+
+ eng = 0.0;
+
+ if (vibstyle == DISCRETE && d_species[isp].vibdof == 2) {
+ int ivib = -log(rand_gen.drand()) * temp_thermal /
+ d_species[isp].vibtemp[0];
+ eng = ivib * boltz * d_species[isp].vibtemp[0];
+ } else if (vibstyle == SMOOTH || d_species[isp].vibdof >= 2) {
+ if (d_species[isp].vibdof == 2)
+ eng = -log(rand_gen.drand()) * boltz * temp_thermal;
+ else if (d_species[isp].vibdof > 2) {
+ a = 0.5*d_species[isp].vibdof-1.;
+ while (1) {
+ // energy cut-off at 10 kT
+ erm = 10.0*rand_gen.drand();
+ b = pow(erm/a,a) * exp(a-erm);
+ if (b > rand_gen.drand()) break;
+ }
+ eng = erm * boltz * temp_thermal;
+ }
+ }
+
+ return eng;
+ }
+};
+
+}
+
+#endif
+#endif
+
+/* ERROR/WARNING messages:
+
+E: Illegal ... command
+
+Self-explanatory. Check the input script syntax and compare to the
+documentation for the command. You can use -echo screen as a
+command-line option when running SPARTA to see the offending line.
+
+*/
diff --git a/src/KOKKOS/surf_react_adsorb_kokkos.cpp b/src/KOKKOS/surf_react_adsorb_kokkos.cpp
new file mode 100644
index 000000000..3607384c9
--- /dev/null
+++ b/src/KOKKOS/surf_react_adsorb_kokkos.cpp
@@ -0,0 +1,484 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#include "math.h"
+#include "string.h"
+#include "surf_react_adsorb_kokkos.h"
+#include "input.h"
+#include "update.h"
+#include "collide.h"
+#include "surf.h"
+#include "surf_collide.h"
+#include "random_knuth.h"
+#include "comm.h"
+#include "domain.h"
+#include "particle.h"
+#include "error.h"
+#include "particle_kokkos.h"
+#include "sparta_masks.h"
+
+using namespace SPARTA_NS;
+
+// cmodel coeff/flag counts (must match SurfReactAdsorb::readfile_gs)
+
+static void cmodel_sizes(int model, int &nc, int &nf)
+{
+ nc = nf = 0;
+ switch (model) {
+ case SRA_KK::SPECULAR: nf = 1; break;
+ case SRA_KK::DIFFUSE: nc = 2; break;
+ case SRA_KK::CLL: nc = 5; nf = 1; break;
+ case SRA_KK::TD: nc = 8; nf = 3; break;
+ case SRA_KK::IMPULSIVE: nc = 11; nf = 4; break;
+ }
+}
+
+static bool cmodel_unsupported(int m)
+{
+ return (m == SRA_KK::ADIABATIC || m == SRA_KK::IMPULSIVE);
+}
+
+/* ---------------------------------------------------------------------- */
+
+SurfReactAdsorbKokkos::SurfReactAdsorbKokkos(SPARTA *sparta, int narg, char **arg) :
+ SurfReactAdsorb(sparta,narg,arg),
+ rand_pool(12345 + comm->me
+#ifdef SPARTA_KOKKOS_EXACT
+ , sparta
+#endif
+ )
+{
+ kokkosable = 1;
+
+ d_scalars = DAT::t_int_1d("surf_react_adsorb:scalars",nlist_gs+1);
+ d_nsingle = Kokkos::subview(d_scalars,0);
+ d_tally_single = Kokkos::subview(d_scalars,std::make_pair(1,nlist_gs+1));
+
+ h_scalars = HAT::t_int_1d("surf_react_adsorb:scalars_mirror",nlist_gs+1);
+ h_nsingle = Kokkos::subview(h_scalars,0);
+ h_tally_single = Kokkos::subview(h_scalars,std::make_pair(1,nlist_gs+1));
+
+ random_backup = NULL;
+
+ for (int i = 0; i < SRA_KK_MAXMODELS; i++) cmodel_pool[i] = NULL;
+}
+
+SurfReactAdsorbKokkos::SurfReactAdsorbKokkos(SPARTA *sparta) :
+ SurfReactAdsorb(sparta),
+ rand_pool(12345
+#ifdef SPARTA_KOKKOS_EXACT
+ , sparta
+#endif
+ )
+{
+ copy = 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+SurfReactAdsorbKokkos::~SurfReactAdsorbKokkos()
+{
+ if (copy) return;
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.destroy();
+ if (random_backup) delete random_backup;
+ for (int i = 0; i < SRA_KK_MAXMODELS; i++)
+ if (cmodel_pool[i]) { cmodel_pool[i]->destroy(); delete cmodel_pool[i]; }
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfReactAdsorbKokkos::init()
+{
+ SurfReactAdsorb::init();
+
+ // Kokkos GS adsorb currently supports a restricted feature set;
+ // error clearly at init rather than silently producing wrong results
+
+
+ for (int i = 0; i < nlist_gs; i++) {
+ OneReaction_GS *r = &rlist_gs[i];
+ if (!r->active) continue;
+ // post-reaction collision model (cmodel) scatter on device supports
+ // NOMODEL/SPECULAR/DIFFUSE/CLL/TD; adiabatic/impulsive deferred
+
+ if (cmodel_unsupported(r->cmodel_ip) || cmodel_unsupported(r->cmodel_jp))
+ error->all(FLERR,"Kokkos surf_react adsorb does not yet support reactions with "
+ "an adiabatic or impulsive post-reaction collision model");
+ }
+
+ Kokkos::deep_copy(d_scalars,0);
+
+ init_reactions_gs_kokkos();
+ init_cmodels_kokkos();
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_pool.init(random);
+#endif
+}
+
+/* ----------------------------------------------------------------------
+ flatten per-reaction cmodel coeffs/flags and build one RNG pool per
+ cmodel type, each wrapping that cmodel's RanKnuth so the device scatter
+ matches the host SurfCollide::wrapper bit-for-bit (EXACT serial)
+------------------------------------------------------------------------- */
+
+void SurfReactAdsorbKokkos::init_cmodels_kokkos()
+{
+ int nr = MAX(nlist_gs,1);
+ d_cmip_coeffs = DAT::t_float_2d("sra:cmip_coeffs",nr,SRA_KK_MAXCMCOEFF);
+ d_cmjp_coeffs = DAT::t_float_2d("sra:cmjp_coeffs",nr,SRA_KK_MAXCMCOEFF);
+ d_cmip_flags = DAT::t_int_2d("sra:cmip_flags",nr,SRA_KK_MAXCMFLAG);
+ d_cmjp_flags = DAT::t_int_2d("sra:cmjp_flags",nr,SRA_KK_MAXCMFLAG);
+
+ auto h_cmip_coeffs = Kokkos::create_mirror_view(d_cmip_coeffs);
+ auto h_cmjp_coeffs = Kokkos::create_mirror_view(d_cmjp_coeffs);
+ auto h_cmip_flags = Kokkos::create_mirror_view(d_cmip_flags);
+ auto h_cmjp_flags = Kokkos::create_mirror_view(d_cmjp_flags);
+
+ for (int i = 0; i < nlist_gs; i++) {
+ OneReaction_GS *r = &rlist_gs[i];
+ int nc,nf;
+ cmodel_sizes(r->cmodel_ip,nc,nf);
+ for (int k = 0; k < nc; k++) h_cmip_coeffs(i,k) = r->cmodel_ip_coeffs[k];
+ for (int k = 0; k < nf; k++) h_cmip_flags(i,k) = r->cmodel_ip_flags[k];
+ cmodel_sizes(r->cmodel_jp,nc,nf);
+ for (int k = 0; k < nc; k++) h_cmjp_coeffs(i,k) = r->cmodel_jp_coeffs[k];
+ for (int k = 0; k < nf; k++) h_cmjp_flags(i,k) = r->cmodel_jp_flags[k];
+ }
+
+ Kokkos::deep_copy(d_cmip_coeffs,h_cmip_coeffs);
+ Kokkos::deep_copy(d_cmjp_coeffs,h_cmjp_coeffs);
+ Kokkos::deep_copy(d_cmip_flags,h_cmip_flags);
+ Kokkos::deep_copy(d_cmjp_flags,h_cmjp_flags);
+
+#ifdef SPARTA_KOKKOS_EXACT
+ for (int idx = 0; idx < SRA_KK_MAXMODELS; idx++) {
+ if (cmodel_pool[idx]) continue;
+ if (!cmodels[idx]) continue;
+ RanKnuth *cmrand = cmodels[idx]->kokkos_random();
+ if (!cmrand) continue; // e.g. specular has no RNG
+ cmodel_pool[idx] = new RandPoolWrap(12345,sparta);
+ cmodel_pool[idx]->init(cmrand);
+ }
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfReactAdsorbKokkos::init_reactions_gs_kokkos()
+{
+ int nspecies = particle->nspecies;
+
+ // per-species reaction lists
+
+ int nmax = 0;
+ d_reactions_n = DAT::t_int_1d("surf_react_adsorb:reactions_n",nspecies);
+ auto h_reactions_n = Kokkos::create_mirror_view(d_reactions_n);
+ for (int i = 0; i < nspecies; i++) {
+ int n = gsflag ? reactions_gs[i].n : 0; // PS-only: no GS reactions
+ h_reactions_n(i) = n;
+ nmax = MAX(nmax,n);
+ }
+ if (nmax > SRA_KK_MAXPERSPECIES)
+ error->all(FLERR,"Too many Kokkos surf_react adsorb reactions per species");
+
+ d_list = DAT::t_int_2d("surf_react_adsorb:list",nspecies,MAX(nmax,1));
+ auto h_list = Kokkos::create_mirror_view(d_list);
+ if (gsflag)
+ for (int i = 0; i < nspecies; i++)
+ for (int j = 0; j < reactions_gs[i].n; j++)
+ h_list(i,j) = reactions_gs[i].list[j];
+
+ // flattened per-reaction tables
+
+ int nr = MAX(nlist_gs,1);
+ d_type = DAT::t_int_1d("sra:type",nr);
+ d_style = DAT::t_int_1d("sra:style",nr);
+ d_kreact = DAT::t_float_1d("sra:kreact",nr);
+ d_kisliuk_flag = DAT::t_int_1d("sra:kflag",nr);
+ d_kisliuk = DAT::t_float_2d("sra:kisliuk",nr,3);
+ d_energy_flag = DAT::t_int_1d("sra:eflag",nr);
+ d_energy = DAT::t_float_2d("sra:energy",nr,2);
+ d_coeff = DAT::t_float_2d("sra:coeff",nr,SRA_KK_MAXCOEFF);
+ d_nreactant = DAT::t_int_1d("sra:nreactant",nr);
+ d_nproduct = DAT::t_int_1d("sra:nproduct",nr);
+ d_nprod_g = DAT::t_int_1d("sra:nprod_g",nr);
+ d_nprod_g_tot = DAT::t_int_1d("sra:nprod_g_tot",nr);
+ d_cmodel_ip = DAT::t_int_1d("sra:cmodel_ip",nr);
+ d_cmodel_jp = DAT::t_int_1d("sra:cmodel_jp",nr);
+ d_rstate = DAT::t_int_2d("sra:rstate",nr,SRA_KK_MAXREACTANT);
+ d_rpart = DAT::t_int_2d("sra:rpart",nr,SRA_KK_MAXREACTANT);
+ d_rstoich = DAT::t_int_2d("sra:rstoich",nr,SRA_KK_MAXREACTANT);
+ d_rad = DAT::t_int_2d("sra:rad",nr,SRA_KK_MAXREACTANT);
+ d_pstate = DAT::t_int_2d("sra:pstate",nr,SRA_KK_MAXPRODUCT);
+ d_ppart = DAT::t_int_2d("sra:ppart",nr,SRA_KK_MAXPRODUCT);
+ d_pstoich = DAT::t_int_2d("sra:pstoich",nr,SRA_KK_MAXPRODUCT);
+ d_pad = DAT::t_int_2d("sra:pad",nr,SRA_KK_MAXPRODUCT);
+ d_products = DAT::t_int_2d("sra:products",nr,SRA_KK_MAXPRODUCT);
+
+ auto h_type = Kokkos::create_mirror_view(d_type);
+ auto h_style = Kokkos::create_mirror_view(d_style);
+ auto h_kreact = Kokkos::create_mirror_view(d_kreact);
+ auto h_kflag = Kokkos::create_mirror_view(d_kisliuk_flag);
+ auto h_kisliuk = Kokkos::create_mirror_view(d_kisliuk);
+ auto h_eflag = Kokkos::create_mirror_view(d_energy_flag);
+ auto h_energy = Kokkos::create_mirror_view(d_energy);
+ auto h_coeff = Kokkos::create_mirror_view(d_coeff);
+ auto h_nreactant = Kokkos::create_mirror_view(d_nreactant);
+ auto h_nproduct = Kokkos::create_mirror_view(d_nproduct);
+ auto h_nprod_g = Kokkos::create_mirror_view(d_nprod_g);
+ auto h_nprod_g_tot = Kokkos::create_mirror_view(d_nprod_g_tot);
+ auto h_cmodel_ip = Kokkos::create_mirror_view(d_cmodel_ip);
+ auto h_cmodel_jp = Kokkos::create_mirror_view(d_cmodel_jp);
+ auto h_rstate = Kokkos::create_mirror_view(d_rstate);
+ auto h_rpart = Kokkos::create_mirror_view(d_rpart);
+ auto h_rstoich = Kokkos::create_mirror_view(d_rstoich);
+ auto h_rad = Kokkos::create_mirror_view(d_rad);
+ auto h_pstate = Kokkos::create_mirror_view(d_pstate);
+ auto h_ppart = Kokkos::create_mirror_view(d_ppart);
+ auto h_pstoich = Kokkos::create_mirror_view(d_pstoich);
+ auto h_pad = Kokkos::create_mirror_view(d_pad);
+ auto h_products = Kokkos::create_mirror_view(d_products);
+
+ for (int i = 0; i < nlist_gs; i++) {
+ OneReaction_GS *r = &rlist_gs[i];
+ h_type(i) = r->type;
+ h_style(i) = r->style;
+ h_kreact(i) = r->k_react;
+ h_kflag(i) = r->kisliuk_flag;
+ for (int k = 0; k < 3; k++) h_kisliuk(i,k) = r->kisliuk_coeff[k];
+ h_eflag(i) = r->energy_flag;
+ for (int k = 0; k < 2; k++) h_energy(i,k) = r->energy_coeff[k];
+ for (int k = 0; k < SRA_KK_MAXCOEFF; k++)
+ h_coeff(i,k) = (k < r->ncoeff) ? r->coeff[k] : 0.0;
+ h_nreactant(i) = r->nreactant;
+ h_nproduct(i) = r->nproduct;
+ h_nprod_g(i) = r->nprod_g;
+ h_nprod_g_tot(i) = r->nprod_g_tot;
+ h_cmodel_ip(i) = r->cmodel_ip;
+ h_cmodel_jp(i) = r->cmodel_jp;
+ for (int k = 0; k < r->nreactant && k < SRA_KK_MAXREACTANT; k++) {
+ h_rstate(i,k) = r->state_reactants[k][0];
+ h_rpart(i,k) = r->part_reactants[k];
+ h_rstoich(i,k) = r->stoich_reactants[k];
+ h_rad(i,k) = r->reactants_ad_index[k];
+ }
+ for (int k = 0; k < r->nproduct && k < SRA_KK_MAXPRODUCT; k++) {
+ h_pstate(i,k) = r->state_products[k][0];
+ h_ppart(i,k) = r->part_products[k];
+ h_pstoich(i,k) = r->stoich_products[k];
+ h_pad(i,k) = r->products_ad_index[k];
+ h_products(i,k) = r->products[k];
+ }
+ }
+
+ Kokkos::deep_copy(d_reactions_n,h_reactions_n);
+ Kokkos::deep_copy(d_list,h_list);
+ Kokkos::deep_copy(d_type,h_type);
+ Kokkos::deep_copy(d_style,h_style);
+ Kokkos::deep_copy(d_kreact,h_kreact);
+ Kokkos::deep_copy(d_kisliuk_flag,h_kflag);
+ Kokkos::deep_copy(d_kisliuk,h_kisliuk);
+ Kokkos::deep_copy(d_energy_flag,h_eflag);
+ Kokkos::deep_copy(d_energy,h_energy);
+ Kokkos::deep_copy(d_coeff,h_coeff);
+ Kokkos::deep_copy(d_nreactant,h_nreactant);
+ Kokkos::deep_copy(d_nproduct,h_nproduct);
+ Kokkos::deep_copy(d_nprod_g,h_nprod_g);
+ Kokkos::deep_copy(d_nprod_g_tot,h_nprod_g_tot);
+ Kokkos::deep_copy(d_cmodel_ip,h_cmodel_ip);
+ Kokkos::deep_copy(d_cmodel_jp,h_cmodel_jp);
+ Kokkos::deep_copy(d_rstate,h_rstate);
+ Kokkos::deep_copy(d_rpart,h_rpart);
+ Kokkos::deep_copy(d_rstoich,h_rstoich);
+ Kokkos::deep_copy(d_rad,h_rad);
+ Kokkos::deep_copy(d_pstate,h_pstate);
+ Kokkos::deep_copy(d_ppart,h_ppart);
+ Kokkos::deep_copy(d_pstoich,h_pstoich);
+ Kokkos::deep_copy(d_pad,h_pad);
+ Kokkos::deep_copy(d_products,h_products);
+
+ // per-state-slot device storage: FACE => 6 box faces, SURF => nlocal+nghost
+
+ nstate_ = (mode == SRA_KK::FACE) ? nface : (surf->nlocal + surf->nghost);
+ int ns = MAX(nstate_,1);
+
+ d_total_state = DAT::t_int_1d("sra:total_state",ns);
+ d_area = DAT::t_float_1d("sra:area",ns);
+ d_weight = DAT::t_float_1d("sra:weight",ns);
+ d_species_state = DAT::t_int_2d("sra:species_state",ns,nspecies_surf);
+
+ k_species_delta = DAT::tdual_int_2d("sra:species_delta",ns,nspecies_surf);
+ d_species_delta = k_species_delta.view_device();
+ Kokkos::deep_copy(d_species_delta,0);
+
+ k_mark = DAT::tdual_int_1d("sra:mark",ns);
+ d_mark = k_mark.view_device();
+ Kokkos::deep_copy(d_mark,0);
+}
+
+/* ----------------------------------------------------------------------
+ sync per-face state host->device and refresh particle/scalar views
+ called each step from the surf collide pre_collide
+------------------------------------------------------------------------- */
+
+void SurfReactAdsorbKokkos::pre_react()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ particle_kk->sync(Device,PARTICLE_MASK|SPECIES_MASK);
+ d_particles = particle_kk->k_particles.view_device();
+ d_species = particle_kk->k_species.view_device();
+
+ fnum_ = update->fnum;
+
+ // cmodel scatter state: boltz, collide rot/vib styles, per-cmodel RNG
+
+ boltz_ = update->boltz;
+ rotstyle_ = SRA_KK::NONE;
+ if (Pointers::collide) rotstyle_ = Pointers::collide->rotstyle;
+ vibstyle_ = SRA_KK::NONE;
+ if (Pointers::collide) vibstyle_ = Pointers::collide->vibstyle;
+
+#ifdef SPARTA_KOKKOS_EXACT
+ for (int idx = 0; idx < SRA_KK_MAXMODELS; idx++)
+ if (cmodel_pool[idx]) d_cmodel_rand[idx] = cmodel_pool[idx]->get_state();
+#endif
+
+ // SURF mode: refresh host state pointers to the current local custom arrays
+ // (they may have been reallocated/spread since last step)
+
+ if (mode == SRA_KK::SURF) {
+ total_state = surf->eivec_local[surf->ewhich[total_state_index]];
+ species_state = surf->eiarray_local[surf->ewhich[species_state_index]];
+ area = surf->edvec_local[surf->ewhich[area_index]];
+ weight = surf->edvec_local[surf->ewhich[weight_index]];
+ }
+
+ // copy current per-slot state (changes only at sync) host->device
+
+ auto h_total = Kokkos::create_mirror_view(d_total_state);
+ auto h_area = Kokkos::create_mirror_view(d_area);
+ auto h_weight = Kokkos::create_mirror_view(d_weight);
+ auto h_sstate = Kokkos::create_mirror_view(d_species_state);
+ for (int i = 0; i < nstate_; i++) {
+ h_total(i) = total_state[i];
+ h_area(i) = area[i];
+ h_weight(i) = weight[i];
+ for (int j = 0; j < nspecies_surf; j++)
+ h_sstate(i,j) = species_state[i][j];
+ }
+ Kokkos::deep_copy(d_total_state,h_total);
+ Kokkos::deep_copy(d_area,h_area);
+ Kokkos::deep_copy(d_weight,h_weight);
+ Kokkos::deep_copy(d_species_state,h_sstate);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfReactAdsorbKokkos::tally_reset()
+{
+ SurfReact::tally_reset();
+ Kokkos::deep_copy(d_scalars,0);
+}
+
+/* ----------------------------------------------------------------------
+ bring device tallies + per-face deltas to host, then run the host
+ state-sync logic (MPI reduce + per-face state update), then re-zero
+------------------------------------------------------------------------- */
+
+void SurfReactAdsorbKokkos::tally_update()
+{
+ // PS (on-surface) chemistry desorbs/inserts particles on the host inside the
+ // base tally_update(); make the host particle list current first so
+ // add_particle() appends to up-to-date data, and mark host-modified after
+ // so the device picks up the new particles on the next sync
+
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ if (psflag) particle_kk->sync(Host,PARTICLE_MASK);
+
+ // device -> host: reaction counts
+
+ Kokkos::deep_copy(h_scalars,d_scalars);
+ nsingle = h_nsingle();
+ for (int i = 0; i < nlist_gs; i++) tally_single[i] = h_tally_single[i];
+
+ // device -> host: perspecies deltas (+ mark for SURF) accumulated since sync
+
+ k_species_delta.modify_device();
+ k_species_delta.sync_host();
+ auto h_delta = k_species_delta.view_host();
+ for (int i = 0; i < nstate_; i++)
+ for (int j = 0; j < nspecies_surf; j++)
+ species_delta[i][j] = h_delta(i,j);
+
+ if (mode == SRA_KK::SURF) {
+ k_mark.modify_device();
+ k_mark.sync_host();
+ auto h_m = k_mark.view_host();
+ for (int i = 0; i < nstate_; i++) mark[i] = h_m(i);
+ }
+
+ // host logic: accumulate tallies and (every nsync) sync per-slot state;
+ // update_state_face()/update_state_surf() re-zero host species_delta
+ // (and update_state_surf clears mark)
+
+ SurfReactAdsorb::tally_update();
+
+ // PS chemistry may have appended particles on the host
+
+ if (psflag) particle_kk->modify(Host,PARTICLE_MASK);
+
+ // mirror re-zeroed host deltas (+ mark) back to device (only on a sync step)
+
+ if (update->ntimestep % nsync == 0) {
+ for (int i = 0; i < nstate_; i++)
+ for (int j = 0; j < nspecies_surf; j++)
+ h_delta(i,j) = species_delta[i][j];
+ k_species_delta.modify_host();
+ k_species_delta.sync_device();
+ if (mode == SRA_KK::SURF) {
+ auto h_m = k_mark.view_host();
+ for (int i = 0; i < nstate_; i++) h_m(i) = mark[i];
+ k_mark.modify_host();
+ k_mark.sync_device();
+ }
+ Kokkos::deep_copy(d_scalars,0);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfReactAdsorbKokkos::backup()
+{
+ ParticleKokkos* particle_kk = (ParticleKokkos*) particle;
+ d_particles = particle_kk->k_particles.view_device();
+
+#ifdef SPARTA_KOKKOS_EXACT
+ if (!random_backup)
+ random_backup = new RanKnuth(12345 + comm->me);
+ memcpy(random_backup,random,sizeof(RanKnuth));
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+void SurfReactAdsorbKokkos::restore()
+{
+#ifdef SPARTA_KOKKOS_EXACT
+ memcpy(random,random_backup,sizeof(RanKnuth));
+#endif
+}
diff --git a/src/KOKKOS/surf_react_adsorb_kokkos.h b/src/KOKKOS/surf_react_adsorb_kokkos.h
new file mode 100644
index 000000000..51a92b23b
--- /dev/null
+++ b/src/KOKKOS/surf_react_adsorb_kokkos.h
@@ -0,0 +1,761 @@
+/* ----------------------------------------------------------------------
+ SPARTA - Stochastic PArallel Rarefied-gas Time-accurate Analyzer
+ http://sparta.github.io
+ Steve Plimpton, sjplimp@gmail.com, Michael Gallis, magalli@sandia.gov
+ Sandia National Laboratories
+
+ Copyright (2014) Sandia Corporation. Under the terms of Contract
+ DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+ certain rights in this software. This software is distributed under
+ the GNU General Public License.
+
+ See the README file in the top-level SPARTA directory.
+------------------------------------------------------------------------- */
+
+#ifdef SURF_REACT_CLASS
+
+SurfReactStyle(adsorb/kk,SurfReactAdsorbKokkos)
+
+#else
+
+#ifndef SPARTA_SURF_REACT_ADSORB_KOKKOS_H
+#define SPARTA_SURF_REACT_ADSORB_KOKKOS_H
+
+#include "surf_react_adsorb.h"
+#include "kokkos_type.h"
+#include "math_extra_kokkos.h"
+#include "math_const.h"
+#include "rand_pool_wrap.h"
+#include "Kokkos_Random.hpp"
+#include "particle_kokkos.h"
+
+namespace SPARTA_NS {
+
+// must match enums in surf_react_adsorb.cpp
+
+namespace SRA_KK {
+ enum{DISSOCIATION,EXCHANGE,RECOMBINATION,AA,DA,LH1,LH3,CD,ER,CI};
+ enum{FACE,SURF};
+ enum{NOMODEL,SPECULAR,DIFFUSE,ADIABATIC,CLL,TD,IMPULSIVE,MAXMODELS};
+ enum{SIMPLE,ARRHENIUS};
+ enum{NONE,DISCRETE,SMOOTH}; // rotstyle/vibstyle, must match collide.h
+}
+
+#define SRA_KK_MAXREACTANT 5
+#define SRA_KK_MAXPRODUCT 5
+#define SRA_KK_MAXCOEFF 4
+#define SRA_KK_MAXPERSPECIES 16 // max GS reactions a single species can be in
+#define SRA_KK_MAXMODELS 7 // = MAXMODELS
+#define SRA_KK_MAXCMCOEFF 11 // max cmodel coeffs (impulsive)
+#define SRA_KK_MAXCMFLAG 4 // max cmodel flags (impulsive)
+
+class SurfReactAdsorbKokkos : public SurfReactAdsorb {
+ public:
+ SurfReactAdsorbKokkos(class SPARTA *, int, char **);
+ SurfReactAdsorbKokkos(class SPARTA *);
+ ~SurfReactAdsorbKokkos();
+ void init();
+ void tally_reset();
+ void tally_update();
+
+ void pre_react();
+ void backup();
+ void restore();
+
+ private:
+ // flattened GS reaction tables (indexed by reaction j in 0..nlist_gs)
+
+ DAT::t_int_1d d_reactions_n; // # of GS reactions for each species
+ DAT::t_int_2d d_list; // per-species list of reaction indices
+
+ DAT::t_int_1d d_type; // reaction type (DISSOCIATION,...)
+ DAT::t_int_1d d_style; // SIMPLE or ARRHENIUS
+ DAT::t_float_1d d_kreact; // precomputed rate coefficient
+ DAT::t_int_1d d_kisliuk_flag;
+ DAT::t_float_2d d_kisliuk; // [j][3]
+ DAT::t_int_1d d_energy_flag;
+ DAT::t_float_2d d_energy; // [j][2]
+ DAT::t_float_2d d_coeff; // [j][MAXCOEFF]
+ DAT::t_int_1d d_nreactant,d_nproduct;
+ DAT::t_int_1d d_nprod_g,d_nprod_g_tot;
+ DAT::t_int_1d d_cmodel_ip,d_cmodel_jp;
+
+ DAT::t_int_2d d_rstate,d_rpart,d_rstoich,d_rad; // reactant slots [j][MAXREACTANT]
+ DAT::t_int_2d d_pstate,d_ppart,d_pstoich,d_pad; // product slots [j][MAXPRODUCT]
+ DAT::t_int_2d d_products; // product species indices
+
+ // per-state-slot data: FACE mode => 6 box faces; SURF mode => nlocal+nghost surfs
+
+ int nstate_; // # of state slots (nface or nall)
+ DAT::t_int_1d d_total_state; // [nstate]
+ DAT::t_float_1d d_area,d_weight; // [nstate]
+ DAT::t_int_2d d_species_state; // [nstate][nspecies_surf]
+ DAT::t_int_2d d_species_delta; // [nstate][nspecies_surf] (atomic)
+
+ DAT::tdual_int_2d k_species_delta;
+
+ DAT::tdual_int_1d k_mark; // [nstate] reacted-surf mark (SURF)
+ DAT::t_int_1d d_mark;
+
+ double fnum_; // update->fnum, set in pre_react
+
+ // post-reaction collision model (cmodel) state for bit-exact device scatter
+ // per-reaction flattened cmodel coeffs/flags (ip and jp), plus collide
+ // rot/vib styles and boltz captured in pre_react
+
+ DAT::t_int_2d d_cmip_flags,d_cmjp_flags; // [nr][MAXCMFLAG]
+ DAT::t_float_2d d_cmip_coeffs,d_cmjp_coeffs; // [nr][MAXCMCOEFF]
+ double boltz_;
+ int rotstyle_,vibstyle_;
+
+ // one RNG per cmodel type, wrapping that cmodel's RanKnuth, so device
+ // scatter draws match the host wrapper bit-for-bit (EXACT serial)
+
+ RandPoolWrap *cmodel_pool[SRA_KK_MAXMODELS];
+#ifdef SPARTA_KOKKOS_EXACT
+ RandWrap d_cmodel_rand[SRA_KK_MAXMODELS];
+#endif
+
+ void init_reactions_gs_kokkos();
+ void init_cmodels_kokkos();
+
+#ifndef SPARTA_KOKKOS_EXACT
+ Kokkos::Random_XorShift64_Pool rand_pool;
+ typedef typename Kokkos::Random_XorShift64_Pool::generator_type rand_type;
+#else
+ RandPoolWrap rand_pool;
+ typedef RandWrap rand_type;
+#endif
+
+ RanKnuth* random_backup;
+
+ DAT::t_int_1d d_scalars;
+ HAT::t_int_1d h_scalars;
+ DAT::t_int_scalar d_nsingle;
+ DAT::t_int_1d d_tally_single;
+ HAT::t_int_scalar h_nsingle;
+ HAT::t_int_1d h_tally_single;
+
+ t_particle_1d d_particles;
+ t_species_1d d_species;
+
+ public:
+
+ /* ----------------------------------------------------------------------
+ select GS surface reaction to perform for particle IP on box face
+ mirrors SurfReactAdsorb::react() for mode == FACE, gsflag == 1
+ return reaction 1 to N, 0 = no reaction
+ only reaction types/cmodels validated at init are reachable here
+ ------------------------------------------------------------------------- */
+
+ template
+ KOKKOS_INLINE_FUNCTION
+ int react_kokkos(Particle::OnePart *&ip, int isurf, const double *norm,
+ Particle::OnePart *&jp, int &velreset,
+ const DAT::t_int_scalar &d_retry,
+ const DAT::t_int_scalar &d_nlocal) const
+ {
+ // FACE: convert negative face code to 0..5; SURF: use local surf index
+
+ int idx;
+ if (mode == SRA_KK::FACE) idx = -(isurf+1);
+ else idx = isurf;
+
+ int n = d_reactions_n[ip->ispecies];
+ if (n == 0) return 0;
+
+ double fnum = fnum_;
+ long int maxstick = ceil(max_cover*d_area[idx] / (fnum*d_weight[idx]));
+ double factor = fnum * d_weight[idx] / d_area[idx];
+ double ms_inv = factor / max_cover;
+
+ double prob_value[SRA_KK_MAXPERSPECIES];
+ double sum_prob = 0.0;
+ double scatter_prob = 0.0, correction = 1.0;
+ int coeff_val = 1;
+
+ rand_type rand_gen = rand_pool.get_state();
+
+ for (int i = 0; i < n; i++) {
+ int j = d_list(ip->ispecies,i);
+
+ if (d_style(j) == SRA_KK::ARRHENIUS) coeff_val = 3;
+
+ double surf_cover,S_theta,K_ads;
+
+ switch (d_type(j)) {
+ case SRA_KK::DISSOCIATION:
+ case SRA_KK::EXCHANGE:
+ case SRA_KK::RECOMBINATION:
+ prob_value[i] = d_kreact(j);
+ break;
+
+ case SRA_KK::AA:
+ case SRA_KK::DA:
+ case SRA_KK::LH1:
+ case SRA_KK::LH3:
+ case SRA_KK::CD:
+ surf_cover = d_total_state[idx] * ms_inv;
+ S_theta = 0.0;
+ if (d_kisliuk_flag(j)) {
+ K_ads = d_kisliuk(j,0) * pow(twall,d_kisliuk(j,1)) *
+ exp(-d_kisliuk(j,2)/twall);
+ if (surf_cover < 1)
+ S_theta = pow((1 - surf_cover) /
+ (1 - surf_cover + K_ads*surf_cover),d_coeff(j,coeff_val));
+ } else {
+ S_theta = pow((1-surf_cover),d_coeff(j,coeff_val));
+ }
+ prob_value[i] = d_kreact(j)*S_theta;
+ break;
+
+ case SRA_KK::ER:
+ {
+ double dot = 2.0;
+ if (d_nreactant(j) == 1)
+ prob_value[i] = 2.0 * d_kreact(j) *
+ (maxstick - d_total_state[idx]) * ms_inv / fabs(dot);
+ else
+ prob_value[i] = 2.0 * d_kreact(j) / fabs(dot);
+ break;
+ }
+
+ case SRA_KK::CI:
+ prob_value[i] = d_kreact(j);
+ if (d_energy_flag(j)) {
+ double *v = ip->v;
+ double dot = v[0]*norm[0]+v[1]*norm[1]+v[2]*norm[2];
+ double vmag_sq = v[0]*v[0]+v[1]*v[1]+v[2]*v[2];
+ double E_i = 0.5 * d_species[ip->ispecies].mass * vmag_sq;
+ double cos_theta = fabs(dot) / sqrt(vmag_sq);
+ prob_value[i] *= pow(E_i,d_energy(j,0)) * pow(cos_theta,d_energy(j,1));
+ }
+ break;
+ }
+
+ for (int k = 1; k < d_nreactant(j); k++) {
+ if (d_rstate(j,k) == 's') {
+ if (d_rpart(j,k) == 0)
+ prob_value[i] *= stoich_pow_kk(d_total_state[idx],d_rstoich(j,k)) *
+ pow(ms_inv,d_rstoich(j,k));
+ else
+ prob_value[i] *= stoich_pow_kk(d_species_state(idx,d_rad(j,k)),
+ d_rstoich(j,k)) *
+ pow(ms_inv,d_rstoich(j,k));
+ }
+ }
+
+ sum_prob += prob_value[i];
+ }
+
+ if (sum_prob > 1.0) correction = 1.0/sum_prob;
+ else scatter_prob = 1.0 - sum_prob;
+
+ double react_prob = scatter_prob;
+ double random_prob = rand_gen.drand();
+
+ if (react_prob > random_prob) {
+ rand_pool.free_state(rand_gen);
+ return 0;
+ }
+
+ for (int i = 0; i < n; i++) {
+ int j = d_list(ip->ispecies,i);
+ react_prob += prob_value[i] * correction;
+ if (react_prob <= random_prob) continue;
+
+ // reaction j fires
+
+ if (ATOMIC_REDUCTION == 0) {
+ d_nsingle()++;
+ d_tally_single(j)++;
+ } else {
+ Kokkos::atomic_inc(&d_nsingle());
+ Kokkos::atomic_inc(&d_tally_single(j));
+ }
+
+ // SURF mode: mark this surf element for the periodic state collate
+
+ if (mode == SRA_KK::SURF) d_mark(idx) = 1;
+
+ // update perspecies deltas for participating surf reactants/products
+
+ auto a_species_delta = d_species_delta;
+ for (int k = 0; k < d_nreactant(j); k++)
+ if (d_rpart(j,k) == 1 && d_rstate(j,k) == 's')
+ Kokkos::atomic_add(&a_species_delta(idx,d_rad(j,k)),-d_rstoich(j,k));
+ for (int k = 0; k < d_nproduct(j); k++)
+ if (d_ppart(j,k) == 1 && d_pstate(j,k) == 's')
+ Kokkos::atomic_add(&a_species_delta(idx,d_pad(j,k)),d_pstoich(j,k));
+
+ // post-reaction particle handling, mirrors SurfReactAdsorb::react()
+ // cmodel post-reaction scatter currently supports NOMODEL and SPECULAR
+ // (validated at init); RNG-based cmodels (diffuse/cll/td/...) deferred
+
+ switch (d_type(j)) {
+
+ case SRA_KK::DISSOCIATION:
+ {
+ double x[3],v[3];
+ ip->ispecies = d_products(j,0);
+ int id = MAXSMALLINT*rand_gen.drand();
+ memcpy(x,ip->x,3*sizeof(double));
+ memcpy(v,ip->v,3*sizeof(double));
+ int jp_species;
+ if (d_pstoich(j,0) == 2) jp_species = d_products(j,0);
+ else jp_species = d_products(j,1);
+
+ int index;
+ if (ATOMIC_REDUCTION == 0) { index = d_nlocal(); d_nlocal()++; }
+ else index = Kokkos::atomic_fetch_add(&d_nlocal(),1);
+
+ int reallocflag = ParticleKokkos::add_particle_kokkos(d_particles,index,
+ id,jp_species,ip->icell,x,v,0.0,0.0);
+ if (reallocflag) {
+ d_retry() = 1;
+ rand_pool.free_state(rand_gen);
+ return 0;
+ }
+ jp = &d_particles[index];
+ rand_pool.free_state(rand_gen);
+ return (j + 1);
+ }
+
+ case SRA_KK::EXCHANGE:
+ ip->ispecies = d_products(j,0);
+ rand_pool.free_state(rand_gen);
+ return (j + 1);
+
+ case SRA_KK::RECOMBINATION:
+ case SRA_KK::AA:
+ case SRA_KK::LH3:
+ case SRA_KK::CD:
+ ip = NULL;
+ rand_pool.free_state(rand_gen);
+ return (j + 1);
+
+ case SRA_KK::DA:
+ {
+ if (d_nprod_g(j) == 0) ip = NULL;
+ else {
+ int nn = 1;
+ for (int pj = 1; pj < d_nproduct(j); pj++) {
+ if (d_pstate(j,pj) == 'g') {
+ if (nn == 1) {
+ nn++;
+ ip->ispecies = d_products(j,pj);
+ scatter_cmodel(ip,norm,d_cmodel_ip(j),j,0,rand_gen);
+ if (d_pstoich(j,pj) == 2) {
+ jp = create_particle(ip,d_products(j,pj),rand_gen,d_nlocal,d_retry);
+ if (!jp) { rand_pool.free_state(rand_gen); return 0; }
+ scatter_cmodel(jp,norm,d_cmodel_ip(j),j,0,rand_gen);
+ }
+ } else {
+ jp = create_particle(ip,d_products(j,pj),rand_gen,d_nlocal,d_retry);
+ if (!jp) { rand_pool.free_state(rand_gen); return 0; }
+ scatter_cmodel(jp,norm,d_cmodel_jp(j),j,1,rand_gen);
+ }
+ }
+ }
+ }
+ if (d_cmodel_ip(j) != SRA_KK::NOMODEL) velreset = 1;
+ rand_pool.free_state(rand_gen);
+ return (j + 1);
+ }
+
+ case SRA_KK::LH1:
+ case SRA_KK::ER:
+ ip->ispecies = d_products(j,0);
+ scatter_cmodel(ip,norm,d_cmodel_ip(j),j,0,rand_gen);
+ if (d_cmodel_ip(j) != SRA_KK::NOMODEL) velreset = 1;
+ rand_pool.free_state(rand_gen);
+ return (j + 1);
+
+ case SRA_KK::CI:
+ {
+ ip->ispecies = d_products(j,0);
+ scatter_cmodel(ip,norm,d_cmodel_ip(j),j,0,rand_gen);
+ if (d_nprod_g_tot(j) == 2) {
+ if (d_pstoich(j,0) == 2) {
+ jp = create_particle(ip,d_products(j,0),rand_gen,d_nlocal,d_retry);
+ if (!jp) { rand_pool.free_state(rand_gen); return 0; }
+ scatter_cmodel(jp,norm,d_cmodel_ip(j),j,0,rand_gen);
+ } else {
+ jp = create_particle(ip,d_products(j,1),rand_gen,d_nlocal,d_retry);
+ if (!jp) { rand_pool.free_state(rand_gen); return 0; }
+ scatter_cmodel(jp,norm,d_cmodel_jp(j),j,1,rand_gen);
+ }
+ }
+ if (d_cmodel_ip(j) != SRA_KK::NOMODEL) velreset = 1;
+ rand_pool.free_state(rand_gen);
+ return (j + 1);
+ }
+ }
+ }
+
+ rand_pool.free_state(rand_gen);
+ return 0;
+ }
+
+ /* ----------------------------------------------------------------------
+ apply a post-reaction collision model (cmodel) scatter to particle p
+ SPECULAR mirrors SurfCollideSpecular::wrapper() (reflect, no RNG)
+ NOMODEL is a no-op; RNG-based cmodels are rejected at init
+ ------------------------------------------------------------------------- */
+
+ KOKKOS_INLINE_FUNCTION
+ void scatter_cmodel(Particle::OnePart *p, const double *norm, int cmodel,
+ int j, int useJp, rand_type &adsorb_rand) const
+ {
+ if (cmodel == SRA_KK::NOMODEL) return;
+ if (cmodel == SRA_KK::SPECULAR) { // SurfCollideSpecular::wrapper
+ MathExtraKokkos::reflect3(p->v,norm);
+ return;
+ }
+
+ // RNG: bit-exact uses the cmodel's own RanKnuth (EXACT serial); on GPU
+ // builds fall back to the surf-react RNG (not bit-exact, not gated here)
+
+#ifdef SPARTA_KOKKOS_EXACT
+ rand_type rg = d_cmodel_rand[cmodel];
+#else
+ rand_type &rg = adsorb_rand;
+#endif
+
+ // gather this reaction's cmodel coeffs/flags (ip or jp slot)
+
+ double cf[SRA_KK_MAXCMCOEFF];
+ int fl[SRA_KK_MAXCMFLAG];
+ for (int k = 0; k < SRA_KK_MAXCMCOEFF; k++)
+ cf[k] = useJp ? d_cmjp_coeffs(j,k) : d_cmip_coeffs(j,k);
+ for (int k = 0; k < SRA_KK_MAXCMFLAG; k++)
+ fl[k] = useJp ? d_cmjp_flags(j,k) : d_cmip_flags(j,k);
+
+ if (cmodel == SRA_KK::DIFFUSE) {
+ diffuse_scatter(p,norm,cf[0],cf[1],rg); // tsurf, acc
+ } else if (cmodel == SRA_KK::CLL) {
+ double eccen = fl[0] ? cf[5] : 0.0;
+ cll_scatter(p,norm,cf[0],cf[1],cf[2],cf[3],cf[4],fl[0],eccen,rg);
+ } else if (cmodel == SRA_KK::TD) {
+ double tsurf = cf[0];
+ int barrier_flag = fl[0], initen_flag = fl[1], bond_flag = fl[2];
+ int m = 1;
+ double barrier_val = 0.0;
+ double initen_trans = 0.0,initen_rot = 0.0,initen_vib = 0.0;
+ double bond_trans = 0.0,bond_rot = 0.0,bond_vib = 0.0;
+ if (barrier_flag) barrier_val = cf[m++];
+ if (initen_flag) { initen_trans = cf[m++]; initen_rot = cf[m++]; initen_vib = cf[m++]; }
+ if (bond_flag) { bond_trans = cf[m++]; bond_rot = cf[m++]; bond_vib = cf[m++]; }
+ td_scatter(p,norm,tsurf,barrier_flag,barrier_val,initen_flag,initen_trans,
+ initen_rot,initen_vib,bond_flag,bond_trans,bond_rot,bond_vib,rg);
+ }
+ }
+
+ /* ----------------------------------------------------------------------
+ replicas of the Kokkos surf-collide scatter device functions, drawing
+ from the cmodel's RNG so they match the host wrapper bit-for-bit;
+ cmodels never translate/rotate (trflag off)
+ ------------------------------------------------------------------------- */
+
+ KOKKOS_INLINE_FUNCTION
+ double erot_kk(int isp, double temp, rand_type &rg) const
+ {
+ double eng,a,erm,b;
+ if (rotstyle_ == SRA_KK::NONE) return 0.0;
+ if (d_species[isp].rotdof < 2) return 0.0;
+ if (rotstyle_ == SRA_KK::DISCRETE && d_species[isp].rotdof == 2) {
+ int irot = -log(rg.drand()) * temp / d_species[isp].rottemp[0];
+ eng = irot * boltz_ * d_species[isp].rottemp[0];
+ } else if (rotstyle_ == SRA_KK::SMOOTH && d_species[isp].rotdof == 2) {
+ eng = -log(rg.drand()) * boltz_ * temp;
+ } else {
+ a = 0.5*d_species[isp].rotdof - 1.0;
+ while (1) {
+ erm = 10.0*rg.drand();
+ b = pow(erm/a,a) * exp(a-erm);
+ if (b > rg.drand()) break;
+ }
+ eng = erm * boltz_ * temp;
+ }
+ return eng;
+ }
+
+ KOKKOS_INLINE_FUNCTION
+ double evib_kk(int isp, double temp, rand_type &rg) const
+ {
+ double eng,a,erm,b;
+ if (vibstyle_ == SRA_KK::NONE || d_species[isp].vibdof < 2) return 0.0;
+ eng = 0.0;
+ if (vibstyle_ == SRA_KK::DISCRETE && d_species[isp].vibdof == 2) {
+ int ivib = -log(rg.drand()) * temp / d_species[isp].vibtemp[0];
+ eng = ivib * boltz_ * d_species[isp].vibtemp[0];
+ } else if (vibstyle_ == SRA_KK::SMOOTH || d_species[isp].vibdof >= 2) {
+ if (d_species[isp].vibdof == 2)
+ eng = -log(rg.drand()) * boltz_ * temp;
+ else if (d_species[isp].vibdof > 2) {
+ a = 0.5*d_species[isp].vibdof - 1.0;
+ while (1) {
+ erm = 10.0*rg.drand();
+ b = pow(erm/a,a) * exp(a-erm);
+ if (b > rg.drand()) break;
+ }
+ eng = erm * boltz_ * temp;
+ }
+ }
+ return eng;
+ }
+
+ KOKKOS_INLINE_FUNCTION
+ void diffuse_scatter(Particle::OnePart *p, const double *norm,
+ double twall, double acc, rand_type &rg) const
+ {
+ if (rg.drand() > acc) {
+ MathExtraKokkos::reflect3(p->v,norm);
+ } else {
+ double tangent1[3],tangent2[3];
+ int isp = p->ispecies;
+ double vrm = sqrt(2.0*boltz_*twall / d_species[isp].mass);
+ double vperp = vrm * sqrt(-log(rg.drand()));
+ double theta = MathConst::MY_2PI * rg.drand();
+ double vtangent = vrm * sqrt(-log(rg.drand()));
+ double vtan1 = vtangent * sin(theta);
+ double vtan2 = vtangent * cos(theta);
+ double *v = p->v;
+ double dot = MathExtraKokkos::dot3(v,norm);
+ tangent1[0] = v[0] - dot*norm[0];
+ tangent1[1] = v[1] - dot*norm[1];
+ tangent1[2] = v[2] - dot*norm[2];
+ if (MathExtraKokkos::lensq3(tangent1) == 0.0) {
+ tangent2[0] = rg.drand();
+ tangent2[1] = rg.drand();
+ tangent2[2] = rg.drand();
+ MathExtraKokkos::cross3(norm,tangent2,tangent1);
+ }
+ MathExtraKokkos::norm3(tangent1);
+ MathExtraKokkos::cross3(norm,tangent1,tangent2);
+ v[0] = vperp*norm[0] + vtan1*tangent1[0] + vtan2*tangent2[0];
+ v[1] = vperp*norm[1] + vtan1*tangent1[1] + vtan2*tangent2[1];
+ v[2] = vperp*norm[2] + vtan1*tangent1[2] + vtan2*tangent2[2];
+ p->erot = erot_kk(isp,twall,rg);
+ p->evib = evib_kk(isp,twall,rg);
+ }
+ }
+
+ KOKKOS_INLINE_FUNCTION
+ void cll_scatter(Particle::OnePart *p, const double *norm, double twall,
+ double acc_n, double acc_t, double acc_rot, double acc_vib,
+ int pflag, double eccen, rand_type &rg) const
+ {
+ double tangent1[3],tangent2[3];
+ int ispecies = p->ispecies;
+ double *v = p->v;
+ double dot = MathExtraKokkos::dot3(v,norm);
+ double vrm,vperp,vtan1,vtan2;
+
+ tangent1[0] = v[0] - dot*norm[0];
+ tangent1[1] = v[1] - dot*norm[1];
+ tangent1[2] = v[2] - dot*norm[2];
+ if (MathExtraKokkos::lensq3(tangent1) == 0.0) {
+ tangent2[0] = rg.drand();
+ tangent2[1] = rg.drand();
+ tangent2[2] = rg.drand();
+ MathExtraKokkos::cross3(norm,tangent2,tangent1);
+ }
+ MathExtraKokkos::norm3(tangent1);
+ MathExtraKokkos::cross3(norm,tangent1,tangent2);
+ double tan1 = MathExtraKokkos::dot3(v,tangent1);
+
+ vrm = sqrt(2.0*boltz_ * twall / d_species[ispecies].mass);
+
+ double r_1 = sqrt(-acc_n*log(rg.drand()));
+ double theta_1 = MathConst::MY_2PI * rg.drand();
+ double dot_norm = dot/vrm * sqrt(1-acc_n);
+ vperp = vrm * sqrt(r_1*r_1 + dot_norm*dot_norm + 2*r_1*dot_norm*cos(theta_1));
+
+ double r_2 = sqrt(-acc_t*log(rg.drand()));
+ double theta_2 = MathConst::MY_2PI * rg.drand();
+ double vtangent = tan1/vrm * sqrt(1-acc_t);
+ vtan1 = vrm * (vtangent + r_2*cos(theta_2));
+ vtan2 = vrm * r_2 * sin(theta_2);
+
+ if (pflag) {
+ double tan2 = MathExtraKokkos::dot3(v,tangent2);
+ double phi_i,psi_i,theta_f,phi_f,psi_f,cos_beta;
+ psi_i = acos(dot*dot/MathExtraKokkos::lensq3(v));
+ phi_i = atan2(tan2,tan1);
+ double v_mag = sqrt(vperp*vperp + vtan1*vtan1 + vtan2*vtan2);
+ double P = 0;
+ while (rg.drand() > P) {
+ phi_f = MathConst::MY_2PI*rg.drand();
+ psi_f = acos(1-rg.drand());
+ cos_beta = cos(psi_i)*cos(psi_f) + sin(psi_i)*sin(psi_f)*cos(phi_i - phi_f);
+ P = (1-eccen)/(1-eccen*cos_beta);
+ }
+ theta_f = acos(sqrt(cos(psi_f)));
+ vperp = v_mag * cos(theta_f);
+ vtan1 = v_mag * sin(theta_f) * cos(phi_f);
+ vtan2 = v_mag * sin(theta_f) * sin(phi_f);
+ }
+
+ v[0] = vperp*norm[0] + vtan1*tangent1[0] + vtan2*tangent2[0];
+ v[1] = vperp*norm[1] + vtan1*tangent1[1] + vtan2*tangent2[1];
+ v[2] = vperp*norm[2] + vtan1*tangent1[2] + vtan2*tangent2[2];
+
+ // rotational component (CLL partial accommodation)
+
+ if (rotstyle_ == SRA_KK::NONE || d_species[ispecies].rotdof < 2) p->erot = 0.0;
+ else {
+ double erot_mag = sqrt(p->erot*(1-acc_rot)/(boltz_*twall));
+ double r_rot,cos_theta_rot,A_rot,X_rot;
+ if (d_species[ispecies].rotdof == 2) {
+ r_rot = sqrt(-acc_rot*log(rg.drand()));
+ cos_theta_rot = cos(MathConst::MY_2PI*rg.drand());
+ } else {
+ A_rot = 0;
+ while (A_rot < rg.drand()) {
+ X_rot = 4*rg.drand();
+ A_rot = 2.71828182845904523536028747*X_rot*X_rot*exp(-X_rot*X_rot);
+ }
+ r_rot = sqrt(acc_rot)*X_rot;
+ cos_theta_rot = 2*rg.drand() - 1;
+ }
+ p->erot = boltz_ * twall *
+ (r_rot*r_rot + erot_mag*erot_mag + 2*r_rot*erot_mag*cos_theta_rot);
+ }
+
+ // vibrational component
+
+ int vibdof = d_species[ispecies].vibdof;
+ double r_vib,cos_theta_vib,A_vib,X_vib,evib_mag,evib_val;
+ if (vibstyle_ == SRA_KK::NONE || vibdof < 2) p->evib = 0.0;
+ else if (vibstyle_ == SRA_KK::DISCRETE && vibdof == 2) {
+ double evib_star = -log(1 - rg.drand() *
+ (1 - exp(-boltz_*d_species[ispecies].vibtemp[0])));
+ evib_val = p->evib + evib_star;
+ evib_mag = sqrt(evib_val*(1-acc_vib)/(boltz_*twall));
+ r_vib = sqrt(-acc_vib*log(rg.drand()));
+ cos_theta_vib = cos(MathConst::MY_2PI*rg.drand());
+ evib_val = boltz_ * twall *
+ (r_vib*r_vib + evib_mag*evib_mag + 2*r_vib*evib_mag*cos_theta_vib);
+ int ivib = evib_val / (boltz_*d_species[ispecies].vibtemp[0]);
+ p->evib = ivib * boltz_ * d_species[ispecies].vibtemp[0];
+ }
+ else if (vibstyle_ == SRA_KK::SMOOTH || vibdof >= 2) {
+ evib_mag = sqrt(p->evib*(1-acc_vib)/(boltz_*twall));
+ if (vibdof == 2) {
+ r_vib = sqrt(-acc_vib*log(rg.drand()));
+ cos_theta_vib = cos(MathConst::MY_2PI*rg.drand());
+ } else {
+ A_vib = 0;
+ while (A_vib < rg.drand()) {
+ X_vib = 4*rg.drand();
+ A_vib = 2.71828182845904523536028747*X_vib*X_vib*exp(-X_vib*X_vib);
+ }
+ r_vib = sqrt(acc_vib)*X_vib;
+ cos_theta_vib = 2*rg.drand() - 1;
+ }
+ p->evib = boltz_ * twall *
+ (r_vib*r_vib + evib_mag*evib_mag + 2*r_vib*evib_mag*cos_theta_vib);
+ }
+ }
+
+ KOKKOS_INLINE_FUNCTION
+ void td_scatter(Particle::OnePart *p, const double *norm, double twall,
+ int barrier_flag, double barrier_val,
+ int initen_flag, double initen_trans, double initen_rot, double initen_vib,
+ int bond_flag, double bond_trans, double bond_rot, double bond_vib,
+ rand_type &rg) const
+ {
+ double tangent1[3],tangent2[3];
+ int ispecies = p->ispecies;
+ double *v = p->v;
+ double dot = MathExtraKokkos::dot3(v,norm);
+
+ tangent1[0] = v[0] - dot*norm[0];
+ tangent1[1] = v[1] - dot*norm[1];
+ tangent1[2] = v[2] - dot*norm[2];
+ if (MathExtraKokkos::lensq3(tangent1) == 0.0) {
+ tangent2[0] = rg.drand();
+ tangent2[1] = rg.drand();
+ tangent2[2] = rg.drand();
+ MathExtraKokkos::cross3(norm,tangent2,tangent1);
+ }
+ MathExtraKokkos::norm3(tangent1);
+ MathExtraKokkos::cross3(norm,tangent1,tangent2);
+
+ double mass = d_species[ispecies].mass;
+ double E_i = 0.5 * mass * MathExtraKokkos::lensq3(v);
+ double E_t = boltz_ * twall;
+ if (bond_flag) E_t += boltz_*bond_trans;
+ if (initen_flag) E_t += E_i*initen_trans;
+ double E_n = E_t;
+ if (barrier_flag) E_n += boltz_*barrier_val;
+
+ double vrm_n = sqrt(2.0*E_n / mass);
+ double vrm_t = sqrt(2.0*E_t / mass);
+ double vperp = vrm_n * sqrt(-log(rg.drand()));
+ double theta = MathConst::MY_2PI * rg.drand();
+ double vtangent = vrm_t * sqrt(-log(rg.drand()));
+ double vtan1 = vtangent * sin(theta);
+ double vtan2 = vtangent * cos(theta);
+
+ v[0] = vperp*norm[0] + vtan1*tangent1[0] + vtan2*tangent2[0];
+ v[1] = vperp*norm[1] + vtan1*tangent1[1] + vtan2*tangent2[1];
+ v[2] = vperp*norm[2] + vtan1*tangent1[2] + vtan2*tangent2[2];
+
+ double twall_rot = twall, twall_vib = twall;
+ if (bond_flag) { twall_rot += bond_rot; twall_vib += bond_vib; }
+ if (initen_flag) { twall_rot += E_i*initen_rot/boltz_; twall_vib += E_i*initen_vib/boltz_; }
+
+ p->erot = erot_kk(ispecies,twall_rot,rg);
+ p->evib = evib_kk(ispecies,twall_vib,rg);
+ }
+
+ /* ----------------------------------------------------------------------
+ create a new particle (copy of ip's x,v) of species sp, mirrors the
+ add_particle path in SurfReactAdsorb::react(); returns ptr or NULL on
+ realloc (caller retries the whole move)
+ ------------------------------------------------------------------------- */
+
+ KOKKOS_INLINE_FUNCTION
+ Particle::OnePart *create_particle(Particle::OnePart *ip, int sp,
+ rand_type &rand_gen,
+ const DAT::t_int_scalar &d_nlocal,
+ const DAT::t_int_scalar &d_retry) const
+ {
+ double x[3],v[3];
+ memcpy(x,ip->x,3*sizeof(double));
+ memcpy(v,ip->v,3*sizeof(double));
+ int id = MAXSMALLINT*rand_gen.drand();
+
+ int index = Kokkos::atomic_fetch_add(&d_nlocal(),1);
+ int reallocflag = ParticleKokkos::add_particle_kokkos(d_particles,index,
+ id,sp,ip->icell,x,v,0.0,0.0);
+ if (reallocflag) {
+ d_retry() = 1;
+ return NULL;
+ }
+ return &d_particles[index];
+ }
+
+ KOKKOS_INLINE_FUNCTION
+ double stoich_pow_kk(int base, int p) const
+ {
+ const double THIRD = 1.0/3.0;
+ switch (p) {
+ case 0: return 1.0;
+ case 1: return (base >= p) ? double(base) : 0.0;
+ case 2: return (base >= p) ? 0.5*base*(base-1) : 0.0;
+ case 3: return (base >= p) ? 0.5*THIRD*base*(base-1)*(base-2) : 0.0;
+ case 4: return (base >= p) ? 0.125*THIRD*base*(base-1)*(base-2)*(base-3) : 0.0;
+ case 5: return (base >= p) ? 0.025*THIRD*base*(base-1)*(base-2)*(base-3)*(base-4) : 0.0;
+ case 6: return (base >= p) ? 0.0125*THIRD*THIRD*base*(base-1)*(base-2)*(base-3)*(base-4)*(base-5) : 0.0;
+ }
+ return 0.0;
+ }
+};
+
+}
+
+#endif
+#endif
diff --git a/src/KOKKOS/update_kokkos.cpp b/src/KOKKOS/update_kokkos.cpp
index 5f82f5a0f..98e512c3f 100644
--- a/src/KOKKOS/update_kokkos.cpp
+++ b/src/KOKKOS/update_kokkos.cpp
@@ -80,11 +80,23 @@ UpdateKokkos::UpdateKokkos(SPARTA *sparta) : Update(sparta),
sc_kk_vanish_copy{VAL_2(KKCopy(sparta))},
sc_kk_piston_copy{VAL_2(KKCopy(sparta))},
sc_kk_transparent_copy{VAL_2(KKCopy(sparta))},
+ sc_kk_adiabatic_copy{VAL_2(KKCopy(sparta))},
+ sc_kk_impulsive_copy{VAL_2(KKCopy(sparta))},
+ sc_kk_td_copy{VAL_2(KKCopy(sparta))},
+ sc_kk_cll_copy{VAL_2(KKCopy(sparta))},
blist_active_copy{VAL_2(KKCopy(sparta))},
slist_active_copy{VAL_2(KKCopy(sparta))},
+ slist_active_isurf_copy{VAL_2(KKCopy(sparta))},
+ slist_active_react_isurf_copy{VAL_2(KKCopy(sparta))},
+ slist_active_react_surf_copy{VAL_2(KKCopy(sparta))},
tmp_compute_boundary_kk(sparta),
- tmp_compute_surf_kk(sparta)
+ tmp_compute_surf_kk(sparta),
+ tmp_compute_isurf_grid_kk(sparta),
+ tmp_compute_react_isurf_grid_kk(sparta),
+ tmp_compute_react_surf_kk(sparta)
{
+ nslist_surf = nslist_isurf = nslist_react_isurf = nslist_react_surf = 0;
+
// use 1D view for scalars to reduce GPU memory operations
@@ -138,6 +150,9 @@ UpdateKokkos::~UpdateKokkos()
tmp_compute_boundary_kk.uncopy = 1;
tmp_compute_surf_kk.uncopy = 1;
+ tmp_compute_isurf_grid_kk.uncopy = 1;
+ tmp_compute_react_isurf_grid_kk.uncopy = 1;
+ tmp_compute_react_surf_kk.uncopy = 1;
for (int i=0; idimension == 3) {
if (surf->exist) {
if (surf->nsr) moveptr = &UpdateKokkos::move<3,1,1,0>;
else moveptr = &UpdateKokkos::move<3,1,0,0>;
} else {
- if (optmove_flag) moveptr = &UpdateKokkos::move<3,0,0,1>;
+ if (surf->nsr) moveptr = &UpdateKokkos::move<3,0,1,0>;
+ else if (optmove_flag) moveptr = &UpdateKokkos::move<3,0,0,1>;
else moveptr = &UpdateKokkos::move<3,0,0,0>;
}
} else if (domain->axisymmetric) {
@@ -194,7 +220,8 @@ void UpdateKokkos::init()
if (surf->nsr) moveptr = &UpdateKokkos::move<1,1,1,0>;
else moveptr = &UpdateKokkos::move<1,1,0,0>;
} else {
- if (optmove_flag) moveptr = &UpdateKokkos::move<1,0,0,1>;
+ if (surf->nsr) moveptr = &UpdateKokkos::move<1,0,1,0>;
+ else if (optmove_flag) moveptr = &UpdateKokkos::move<1,0,0,1>;
else moveptr = &UpdateKokkos::move<1,0,0,0>;
}
} else if (domain->dimension == 2) {
@@ -202,7 +229,8 @@ void UpdateKokkos::init()
if (surf->nsr) moveptr = &UpdateKokkos::move<2,1,1,0>;
else moveptr = &UpdateKokkos::move<2,1,0,0>;
} else {
- if (optmove_flag) moveptr = &UpdateKokkos::move<2,0,0,1>;
+ if (surf->nsr) moveptr = &UpdateKokkos::move<2,0,1,0>;
+ else if (optmove_flag) moveptr = &UpdateKokkos::move<2,0,0,1>;
else moveptr = &UpdateKokkos::move<2,0,0,0>;
}
}
@@ -301,6 +329,10 @@ void UpdateKokkos::setup()
}
hash_kk = grid_kk->hash_kk;
+ // device grid/surf graphs are now current; clear any pending change flag so
+ // the run loop does not do a redundant resync on the first step
+ grid->changed = 0;
+
Update::setup(); // must come after prewrap since computes are called by setup()
// For MPI debugging
@@ -403,6 +435,24 @@ void UpdateKokkos::run(int nsteps)
timer->stamp(TIME_MODIFY);
}
+ // if an end-of-step fix changed the grid/surf topology (e.g. fix ablate
+ // regenerated implicit surfaces), the host grid is now authoritative but
+ // the device per-cell surf graphs (d_csurfs/d_csplits/d_csubs) are stale.
+ // Resync them to the device before the next move, mirroring setup().
+ // Safe here: grid_kk_copy from this step's move is no longer in use and is
+ // refreshed at the start of the next move.
+
+ if (grid->changed) {
+ GridKokkos* grid_kk = (GridKokkos*) grid;
+ grid_kk->modify(Host,ALL_MASK);
+ grid_kk->update_hash();
+ if (surf->exist) {
+ ((SurfKokkos*)surf)->modify(Host,ALL_MASK);
+ grid_kk->wrap_kokkos_graphs();
+ }
+ grid->changed = 0;
+ }
+
// all output
if (ntimestep == output->next) {
@@ -531,8 +581,8 @@ template < int DIM, int SURF, int REACT, int OPT > void UpdateKokkos::move()
error->all(FLERR,"Kokkos currently supports two instances of each surface collide method");
if (surf->nsc > 0) {
- int nspec,ndiff,nvan,npist,ntrans;
- nspec = ndiff = nvan = npist = ntrans = 0;
+ int nspec,ndiff,nvan,npist,ntrans,nadia,nimpul,ntd,ncll;
+ nspec = ndiff = nvan = npist = ntrans = nadia = nimpul = ntd = ncll = 0;
for (int n = 0; n < surf->nsc; n++) {
if (!surf->sc[n]->kokkosable)
error->all(FLERR,"Must use Kokkos-enabled surface collide method with Kokkos");
@@ -566,13 +616,39 @@ template < int DIM, int SURF, int REACT, int OPT > void UpdateKokkos::move()
sc_type_list[n] = 4;
sc_map[n] = ntrans;
ntrans++;
+ } else if (strcmp(surf->sc[n]->style,"adiabatic") == 0) {
+ sc_kk_adiabatic_copy[nadia].copy((SurfCollideAdiabaticKokkos*)(surf->sc[n]));
+ sc_kk_adiabatic_copy[nadia].obj.pre_collide();
+ sc_type_list[n] = 5;
+ sc_map[n] = nadia;
+ nadia++;
+ } else if (strcmp(surf->sc[n]->style,"impulsive") == 0) {
+ sc_kk_impulsive_copy[nimpul].copy((SurfCollideImpulsiveKokkos*)(surf->sc[n]));
+ sc_kk_impulsive_copy[nimpul].obj.pre_collide();
+ sc_type_list[n] = 6;
+ sc_map[n] = nimpul;
+ nimpul++;
+ } else if (strcmp(surf->sc[n]->style,"td") == 0) {
+ sc_kk_td_copy[ntd].copy((SurfCollideTDKokkos*)(surf->sc[n]));
+ sc_kk_td_copy[ntd].obj.pre_collide();
+ sc_type_list[n] = 7;
+ sc_map[n] = ntd;
+ ntd++;
+ } else if (strcmp(surf->sc[n]->style,"cll") == 0) {
+ sc_kk_cll_copy[ncll].copy((SurfCollideCLLKokkos*)(surf->sc[n]));
+ sc_kk_cll_copy[ncll].obj.pre_collide();
+ sc_type_list[n] = 8;
+ sc_map[n] = ncll;
+ ncll++;
} else {
error->all(FLERR,"Unknown Kokkos surface collide method");
}
}
if (nspec > KOKKOS_MAX_SURF_COLL_PER_TYPE || ndiff > KOKKOS_MAX_SURF_COLL_PER_TYPE ||
nvan > KOKKOS_MAX_SURF_COLL_PER_TYPE || npist > KOKKOS_MAX_SURF_COLL_PER_TYPE ||
- ntrans > KOKKOS_MAX_SURF_COLL_PER_TYPE)
+ ntrans > KOKKOS_MAX_SURF_COLL_PER_TYPE || nadia > KOKKOS_MAX_SURF_COLL_PER_TYPE ||
+ nimpul > KOKKOS_MAX_SURF_COLL_PER_TYPE || ntd > KOKKOS_MAX_SURF_COLL_PER_TYPE ||
+ ncll > KOKKOS_MAX_SURF_COLL_PER_TYPE)
error->all(FLERR,"Kokkos currently supports two instances of each surface collide method");
}
@@ -713,8 +789,8 @@ template < int DIM, int SURF, int REACT, int OPT > void UpdateKokkos::move()
}
if (surf->nsc > 0) {
- int nspec,ndiff,nvan,npist,ntrans;
- nspec = ndiff = nvan = npist = ntrans = 0;
+ int nspec,ndiff,nvan,npist,ntrans,nadia,nimpul,ntd,ncll;
+ nspec = ndiff = nvan = npist = ntrans = nadia = nimpul = ntd = ncll = 0;
for (int n = 0; n < surf->nsc; n++) {
if (strcmp(surf->sc[n]->style,"specular") == 0) {
sc_kk_specular_copy[nspec].obj.post_collide();
@@ -731,6 +807,18 @@ template < int DIM, int SURF, int REACT, int OPT > void UpdateKokkos::move()
} else if (strcmp(surf->sc[n]->style,"transparent") == 0) {
sc_kk_transparent_copy[ntrans].obj.post_collide();
ntrans++;
+ } else if (strcmp(surf->sc[n]->style,"adiabatic") == 0) {
+ sc_kk_adiabatic_copy[nadia].obj.post_collide();
+ nadia++;
+ } else if (strcmp(surf->sc[n]->style,"impulsive") == 0) {
+ sc_kk_impulsive_copy[nimpul].obj.post_collide();
+ nimpul++;
+ } else if (strcmp(surf->sc[n]->style,"td") == 0) {
+ sc_kk_td_copy[ntd].obj.post_collide();
+ ntd++;
+ } else if (strcmp(surf->sc[n]->style,"cll") == 0) {
+ sc_kk_cll_copy[ncll].obj.post_collide();
+ ncll++;
}
}
}
@@ -797,8 +885,22 @@ template < int DIM, int SURF, int REACT, int OPT > void UpdateKokkos::move()
if (nsurf_tally) {
for (int m = 0; m < nsurf_tally; m++) {
- ComputeSurfKokkos* compute_surf_kk = (ComputeSurfKokkos*)(slist_active[m]);
- compute_surf_kk->post_surf_tally();
+ if (strcmp(slist_active[m]->style,"isurf/grid") == 0) {
+ ComputeISurfGridKokkos* compute_isurf_kk =
+ (ComputeISurfGridKokkos*)(slist_active[m]);
+ compute_isurf_kk->post_surf_tally();
+ } else if (strcmp(slist_active[m]->style,"react/isurf/grid") == 0) {
+ ComputeReactISurfGridKokkos* compute_react_isurf_kk =
+ (ComputeReactISurfGridKokkos*)(slist_active[m]);
+ compute_react_isurf_kk->post_surf_tally();
+ } else if (strcmp(slist_active[m]->style,"react/surf") == 0) {
+ ComputeReactSurfKokkos* compute_react_surf_kk =
+ (ComputeReactSurfKokkos*)(slist_active[m]);
+ compute_react_surf_kk->post_surf_tally();
+ } else {
+ ComputeSurfKokkos* compute_surf_kk = (ComputeSurfKokkos*)(slist_active[m]);
+ compute_surf_kk->post_surf_tally();
+ }
}
}
@@ -1382,6 +1484,18 @@ void UpdateKokkos::operator()(TagUpdateMove
} else if (sc_type == 4) {
jpart = sc_kk_transparent_copy[m].obj.
collide_kokkos(ipart,dtremain,minsurf,tri->norm,tri->isr,reaction,d_retry,d_nlocal);
+ } else if (sc_type == 5) {
+ jpart = sc_kk_adiabatic_copy[m].obj.
+ collide_kokkos(ipart,dtremain,minsurf,tri->norm,tri->isr,reaction,d_retry,d_nlocal);
+ } else if (sc_type == 6) {
+ jpart = sc_kk_impulsive_copy[m].obj.
+ collide_kokkos(ipart,dtremain,minsurf,tri->norm,tri->isr,reaction,d_retry,d_nlocal);
+ } else if (sc_type == 7) {
+ jpart = sc_kk_td_copy[m].obj.
+ collide_kokkos(ipart,dtremain,minsurf,tri->norm,tri->isr,reaction,d_retry,d_nlocal);
+ } else if (sc_type == 8) {
+ jpart = sc_kk_cll_copy[m].obj.
+ collide_kokkos(ipart,dtremain,minsurf,tri->norm,tri->isr,reaction,d_retry,d_nlocal);
}
}
@@ -1401,6 +1515,18 @@ void UpdateKokkos::operator()(TagUpdateMove
} else if (sc_type == 4) {
jpart = sc_kk_transparent_copy[m].obj.
collide_kokkos(ipart,dtremain,minsurf,line->norm,line->isr,reaction,d_retry,d_nlocal);
+ } else if (sc_type == 5) {
+ jpart = sc_kk_adiabatic_copy[m].obj.
+ collide_kokkos(ipart,dtremain,minsurf,line->norm,line->isr,reaction,d_retry,d_nlocal);
+ } else if (sc_type == 6) {
+ jpart = sc_kk_impulsive_copy[m].obj.
+ collide_kokkos(ipart,dtremain,minsurf,line->norm,line->isr,reaction,d_retry,d_nlocal);
+ } else if (sc_type == 7) {
+ jpart = sc_kk_td_copy[m].obj.
+ collide_kokkos(ipart,dtremain,minsurf,line->norm,line->isr,reaction,d_retry,d_nlocal);
+ } else if (sc_type == 8) {
+ jpart = sc_kk_cll_copy[m].obj.
+ collide_kokkos(ipart,dtremain,minsurf,line->norm,line->isr,reaction,d_retry,d_nlocal);
}
}
@@ -1412,10 +1538,20 @@ void UpdateKokkos::operator()(TagUpdateMove
jpart->weight = particle_i.weight;
}
- if (nsurf_tally)
- for (m = 0; m < nsurf_tally; m++)
+ if (nsurf_tally) {
+ for (m = 0; m < nslist_surf; m++)
slist_active_copy[m].obj.
surf_tally_kk(dtremain,minsurf,icell,reaction,&iorig,ipart,jpart);
+ for (m = 0; m < nslist_isurf; m++)
+ slist_active_isurf_copy[m].obj.
+ surf_tally_kk(dtremain,minsurf,icell,reaction,&iorig,ipart,jpart);
+ for (m = 0; m < nslist_react_isurf; m++)
+ slist_active_react_isurf_copy[m].obj.
+ surf_tally_kk(dtremain,minsurf,icell,reaction,&iorig,ipart,jpart);
+ for (m = 0; m < nslist_react_surf; m++)
+ slist_active_react_surf_copy[m].obj.
+ surf_tally_kk(dtremain,minsurf,icell,reaction,&iorig,ipart,jpart);
+ }
// stuck_iterate = consecutive iterations particle is immobile
@@ -1648,6 +1784,18 @@ void UpdateKokkos::operator()(TagUpdateMove
else if (sc_type == 4)
jpart = sc_kk_transparent_copy[m].obj.
collide_kokkos(ipart,dtremain,-(outface+1),domain_kk_copy.obj.norm[outface],domain_kk_copy.obj.surf_react[outface],reaction,d_retry,d_nlocal);
+ else if (sc_type == 5)
+ jpart = sc_kk_adiabatic_copy[m].obj.
+ collide_kokkos(ipart,dtremain,-(outface+1),domain_kk_copy.obj.norm[outface],domain_kk_copy.obj.surf_react[outface],reaction,d_retry,d_nlocal);
+ else if (sc_type == 6)
+ jpart = sc_kk_impulsive_copy[m].obj.
+ collide_kokkos(ipart,dtremain,-(outface+1),domain_kk_copy.obj.norm[outface],domain_kk_copy.obj.surf_react[outface],reaction,d_retry,d_nlocal);
+ else if (sc_type == 7)
+ jpart = sc_kk_td_copy[m].obj.
+ collide_kokkos(ipart,dtremain,-(outface+1),domain_kk_copy.obj.norm[outface],domain_kk_copy.obj.surf_react[outface],reaction,d_retry,d_nlocal);
+ else if (sc_type == 8)
+ jpart = sc_kk_cll_copy[m].obj.
+ collide_kokkos(ipart,dtremain,-(outface+1),domain_kk_copy.obj.norm[outface],domain_kk_copy.obj.surf_react[outface],reaction,d_retry,d_nlocal);
if (ipart) {
double *x = ipart->x;
@@ -1967,31 +2115,72 @@ void UpdateKokkos::tally_set(bigint ntimestep)
}
}
- if (nsurf_tally > KOKKOS_MAX_SLIST)
- error->all(FLERR,"Kokkos currently only supports two instances of compute surface");
+ // partition surf tally computes into "compute surf" (slist_active_copy) and
+ // "compute isurf/grid" (slist_active_isurf_copy); both tally on-device via
+ // surf_tally_kk(), invoked from the move kernel's surface collision loop
+
+ nslist_surf = nslist_isurf = nslist_react_isurf = nslist_react_surf = 0;
if (nsurf_tally) {
for (i = 0; i < nsurf_tally; i++) {
- if (strcmp(slist_active[i]->style,"isurf/grid") == 0)
- error->all(FLERR,"Kokkos doesn't yet support compute isurf/grid");
- ComputeSurfKokkos* compute_surf_kk = dynamic_cast(slist_active[i]);
- if (!compute_surf_kk)
- error->all(FLERR,"Kokkos does not (yet) support compute surf/collision/tally or compute surf/reaction/tally");
- compute_surf_kk->pre_surf_tally();
- slist_active_copy[i].copy(compute_surf_kk);
+ if (strcmp(slist_active[i]->style,"isurf/grid") == 0) {
+ ComputeISurfGridKokkos* compute_isurf_kk =
+ dynamic_cast(slist_active[i]);
+ if (!compute_isurf_kk)
+ error->all(FLERR,"Must use Kokkos-enabled compute isurf/grid with Kokkos");
+ if (nslist_isurf >= KOKKOS_MAX_SLIST)
+ error->all(FLERR,"Kokkos currently only supports two instances of compute isurf/grid");
+ compute_isurf_kk->pre_surf_tally();
+ slist_active_isurf_copy[nslist_isurf].copy(compute_isurf_kk);
+ nslist_isurf++;
+ } else if (strcmp(slist_active[i]->style,"react/isurf/grid") == 0) {
+ ComputeReactISurfGridKokkos* compute_react_isurf_kk =
+ dynamic_cast(slist_active[i]);
+ if (!compute_react_isurf_kk)
+ error->all(FLERR,"Must use Kokkos-enabled compute react/isurf/grid with Kokkos");
+ if (nslist_react_isurf >= KOKKOS_MAX_SLIST)
+ error->all(FLERR,"Kokkos currently only supports two instances of compute react/isurf/grid");
+ compute_react_isurf_kk->pre_surf_tally();
+ slist_active_react_isurf_copy[nslist_react_isurf].copy(compute_react_isurf_kk);
+ nslist_react_isurf++;
+ } else if (strcmp(slist_active[i]->style,"react/surf") == 0) {
+ ComputeReactSurfKokkos* compute_react_surf_kk =
+ dynamic_cast(slist_active[i]);
+ if (!compute_react_surf_kk)
+ error->all(FLERR,"Must use Kokkos-enabled compute react/surf with Kokkos");
+ if (nslist_react_surf >= KOKKOS_MAX_SLIST)
+ error->all(FLERR,"Kokkos currently only supports two instances of compute react/surf");
+ compute_react_surf_kk->pre_surf_tally();
+ slist_active_react_surf_copy[nslist_react_surf].copy(compute_react_surf_kk);
+ nslist_react_surf++;
+ } else {
+ ComputeSurfKokkos* compute_surf_kk =
+ dynamic_cast(slist_active[i]);
+ if (!compute_surf_kk)
+ error->all(FLERR,"Kokkos does not (yet) support compute surf/collision/tally or compute surf/reaction/tally");
+ if (nslist_surf >= KOKKOS_MAX_SLIST)
+ error->all(FLERR,"Kokkos currently only supports two instances of compute surface");
+ compute_surf_kk->pre_surf_tally();
+ slist_active_copy[nslist_surf].copy(compute_surf_kk);
+ nslist_surf++;
+ }
}
- } else {
- for (int i = 0; i < KOKKOS_MAX_SLIST; i++) {
+ }
- // use temporary to avoid the copy getting stale leading to an issue
- // with view reference counting
+ // fill unused slots of each typed copy list with the temporary
+ // to avoid the copy getting stale leading to an issue with view ref counting
- slist_active_copy[i].copy(&tmp_compute_surf_kk);
- }
- }
+ for (i = nslist_surf; i < KOKKOS_MAX_SLIST; i++)
+ slist_active_copy[i].copy(&tmp_compute_surf_kk);
+ for (i = nslist_isurf; i < KOKKOS_MAX_SLIST; i++)
+ slist_active_isurf_copy[i].copy(&tmp_compute_isurf_grid_kk);
+ for (i = nslist_react_isurf; i < KOKKOS_MAX_SLIST; i++)
+ slist_active_react_isurf_copy[i].copy(&tmp_compute_react_isurf_grid_kk);
+ for (i = nslist_react_surf; i < KOKKOS_MAX_SLIST; i++)
+ slist_active_react_surf_copy[i].copy(&tmp_compute_react_surf_kk);
- if (ngas_tally)
- error->all(FLERR,"Kokkos does not (yet) support tallying gas/gas collisions or reactions");
+ // gas/gas tally computes are validated and set up by CollideVSSKokkos,
+ // which invokes their on-device gas_tally_kk() from the collision kernel
}
/* ---------------------------------------------------------------------- */
@@ -2005,8 +2194,8 @@ void UpdateKokkos::backup()
Kokkos::deep_copy(d_particles_backup,d_particles);
if (surf->nsc > 0) {
- int nspec,ndiff,npist;
- nspec = ndiff = npist = 0;
+ int nspec,ndiff,npist,nadia,nimpul,ntd,ncll;
+ nspec = ndiff = npist = nadia = nimpul = ntd = ncll = 0;
for (int n = 0; n < surf->nsc; n++) {
if (strcmp(surf->sc[n]->style,"specular") == 0) {
sc_kk_specular_copy[nspec].obj.backup();
@@ -2017,6 +2206,18 @@ void UpdateKokkos::backup()
} else if (strcmp(surf->sc[n]->style,"piston") == 0) {
sc_kk_piston_copy[npist].obj.backup();
npist++;
+ } else if (strcmp(surf->sc[n]->style,"adiabatic") == 0) {
+ sc_kk_adiabatic_copy[nadia].obj.backup();
+ nadia++;
+ } else if (strcmp(surf->sc[n]->style,"impulsive") == 0) {
+ sc_kk_impulsive_copy[nimpul].obj.backup();
+ nimpul++;
+ } else if (strcmp(surf->sc[n]->style,"td") == 0) {
+ sc_kk_td_copy[ntd].obj.backup();
+ ntd++;
+ } else if (strcmp(surf->sc[n]->style,"cll") == 0) {
+ sc_kk_cll_copy[ncll].obj.backup();
+ ncll++;
}
}
}
@@ -2031,8 +2232,8 @@ void UpdateKokkos::restore()
d_particles = particle_kk->k_particles.view_device();
if (surf->nsc > 0) {
- int nspec,ndiff,npist;
- nspec = ndiff = npist = 0;
+ int nspec,ndiff,npist,nadia,nimpul,ntd,ncll;
+ nspec = ndiff = npist = nadia = nimpul = ntd = ncll = 0;
for (int n = 0; n < surf->nsc; n++) {
if (strcmp(surf->sc[n]->style,"specular") == 0) {
sc_kk_specular_copy[nspec].obj.restore();
@@ -2043,6 +2244,18 @@ void UpdateKokkos::restore()
} else if (strcmp(surf->sc[n]->style,"piston") == 0) {
sc_kk_piston_copy[npist].obj.restore();
npist++;
+ } else if (strcmp(surf->sc[n]->style,"adiabatic") == 0) {
+ sc_kk_adiabatic_copy[nadia].obj.restore();
+ nadia++;
+ } else if (strcmp(surf->sc[n]->style,"impulsive") == 0) {
+ sc_kk_impulsive_copy[nimpul].obj.restore();
+ nimpul++;
+ } else if (strcmp(surf->sc[n]->style,"td") == 0) {
+ sc_kk_td_copy[ntd].obj.restore();
+ ntd++;
+ } else if (strcmp(surf->sc[n]->style,"cll") == 0) {
+ sc_kk_cll_copy[ncll].obj.restore();
+ ncll++;
}
}
}
diff --git a/src/KOKKOS/update_kokkos.h b/src/KOKKOS/update_kokkos.h
index 7ac60fa88..ad054a071 100644
--- a/src/KOKKOS/update_kokkos.h
+++ b/src/KOKKOS/update_kokkos.h
@@ -26,8 +26,15 @@
#include "surf_collide_vanish_kokkos.h"
#include "surf_collide_piston_kokkos.h"
#include "surf_collide_transparent_kokkos.h"
+#include "surf_collide_adiabatic_kokkos.h"
+#include "surf_collide_impulsive_kokkos.h"
+#include "surf_collide_td_kokkos.h"
+#include "surf_collide_cll_kokkos.h"
#include "compute_boundary_kokkos.h"
#include "compute_surf_kokkos.h"
+#include "compute_isurf_grid_kokkos.h"
+#include "compute_react_isurf_grid_kokkos.h"
+#include "compute_react_surf_kokkos.h"
namespace SPARTA_NS {
@@ -136,13 +143,31 @@ class UpdateKokkos : public Update {
KKCopy sc_kk_vanish_copy[KOKKOS_MAX_SURF_COLL_PER_TYPE];
KKCopy sc_kk_piston_copy[KOKKOS_MAX_SURF_COLL_PER_TYPE];
KKCopy sc_kk_transparent_copy[KOKKOS_MAX_SURF_COLL_PER_TYPE];
+ KKCopy sc_kk_adiabatic_copy[KOKKOS_MAX_SURF_COLL_PER_TYPE];
+ KKCopy sc_kk_impulsive_copy[KOKKOS_MAX_SURF_COLL_PER_TYPE];
+ KKCopy sc_kk_td_copy[KOKKOS_MAX_SURF_COLL_PER_TYPE];
+ KKCopy sc_kk_cll_copy[KOKKOS_MAX_SURF_COLL_PER_TYPE];
//KKCopy blist_active_copy[KOKKOS_MAX_GLIST];
KKCopy slist_active_copy[KOKKOS_MAX_SLIST];
+ KKCopy slist_active_isurf_copy[KOKKOS_MAX_SLIST];
+ KKCopy slist_active_react_isurf_copy[KOKKOS_MAX_SLIST];
+ KKCopy slist_active_react_surf_copy[KOKKOS_MAX_SLIST];
KKCopy blist_active_copy[KOKKOS_MAX_BLIST];
+ // partition of slist_active (set in tally_set):
+ // nslist_surf = # of compute surf style tallies (slist_active_copy)
+ // nslist_isurf = # of compute isurf/grid tallies (slist_active_isurf_copy)
+ // nslist_react_isurf = # of compute react/isurf/grid tallies
+ // nslist_surf + nslist_isurf + nslist_react_isurf == nsurf_tally
+
+ int nslist_surf,nslist_isurf,nslist_react_isurf,nslist_react_surf;
+
ComputeBoundaryKokkos tmp_compute_boundary_kk;
ComputeSurfKokkos tmp_compute_surf_kk;
+ ComputeISurfGridKokkos tmp_compute_isurf_grid_kk;
+ ComputeReactISurfGridKokkos tmp_compute_react_isurf_grid_kk;
+ ComputeReactSurfKokkos tmp_compute_react_surf_kk;
typedef Kokkos::DualView tdual_int_14;
typedef tdual_int_14::t_dev t_int_14;
diff --git a/src/compute_gas_collision_grid.h b/src/compute_gas_collision_grid.h
index 4272f93e9..34e2a5d18 100644
--- a/src/compute_gas_collision_grid.h
+++ b/src/compute_gas_collision_grid.h
@@ -29,6 +29,7 @@ namespace SPARTA_NS {
class ComputeGasCollisionGrid : public Compute {
public:
ComputeGasCollisionGrid(class SPARTA *, int, char **);
+ ComputeGasCollisionGrid(class SPARTA* sparta) : Compute(sparta) {} // needed for Kokkos
~ComputeGasCollisionGrid();
void init();
void compute_per_grid();
diff --git a/src/compute_gas_reaction_grid.h b/src/compute_gas_reaction_grid.h
index 5cb5a8f59..77c8b2bb4 100644
--- a/src/compute_gas_reaction_grid.h
+++ b/src/compute_gas_reaction_grid.h
@@ -29,6 +29,7 @@ namespace SPARTA_NS {
class ComputeGasReactionGrid : public Compute {
public:
ComputeGasReactionGrid(class SPARTA *, int, char **);
+ ComputeGasReactionGrid(class SPARTA* sparta) : Compute(sparta) {} // needed for Kokkos
~ComputeGasReactionGrid();
void init();
void compute_per_grid();
diff --git a/src/compute_isurf_grid.cpp b/src/compute_isurf_grid.cpp
index fec71e56e..11c4e758d 100644
--- a/src/compute_isurf_grid.cpp
+++ b/src/compute_isurf_grid.cpp
@@ -101,6 +101,8 @@ ComputeISurfGrid::ComputeISurfGrid(SPARTA *sparta, int narg, char **arg) :
ComputeISurfGrid::~ComputeISurfGrid()
{
+ if (copy || copymode) return;
+
delete [] which;
memory->destroy(array_surf_tally);
memory->destroy(tally2surf);
diff --git a/src/compute_isurf_grid.h b/src/compute_isurf_grid.h
index 817de99fe..844123d32 100644
--- a/src/compute_isurf_grid.h
+++ b/src/compute_isurf_grid.h
@@ -31,6 +31,7 @@ namespace SPARTA_NS {
class ComputeISurfGrid : public Compute {
public:
ComputeISurfGrid(class SPARTA *, int, char **);
+ ComputeISurfGrid(class SPARTA* sparta) : Compute(sparta) {} // needed for Kokkos
~ComputeISurfGrid();
virtual void init();
void compute_per_grid();
@@ -73,8 +74,8 @@ class ComputeISurfGrid : public Compute {
double weight; // particle weight, based on initial cell
double *normflux; // normalization factor for each surf element
- void init_normflux();
- void grow_tally();
+ virtual void init_normflux();
+ virtual void grow_tally();
};
}
diff --git a/src/compute_react_isurf_grid.cpp b/src/compute_react_isurf_grid.cpp
index 0bb4fd5c8..5b1afb3e7 100644
--- a/src/compute_react_isurf_grid.cpp
+++ b/src/compute_react_isurf_grid.cpp
@@ -117,6 +117,8 @@ ComputeReactISurfGrid(SPARTA *sparta, int narg, char **arg) :
ComputeReactISurfGrid::~ComputeReactISurfGrid()
{
+ if (copy || copymode) return;
+
memory->destroy(reaction2col);
memory->destroy(array_surf_tally);
memory->destroy(tally2surf);
diff --git a/src/compute_react_isurf_grid.h b/src/compute_react_isurf_grid.h
index 370f649fb..528a739b9 100644
--- a/src/compute_react_isurf_grid.h
+++ b/src/compute_react_isurf_grid.h
@@ -31,6 +31,7 @@ namespace SPARTA_NS {
class ComputeReactISurfGrid : public Compute {
public:
ComputeReactISurfGrid(class SPARTA *, int, char **);
+ ComputeReactISurfGrid(class SPARTA* sparta) : Compute(sparta) {} // needed for Kokkos
~ComputeReactISurfGrid();
virtual void init();
void compute_per_grid();
@@ -38,7 +39,7 @@ class ComputeReactISurfGrid : public Compute {
virtual void surf_tally(double, int, int, int, Particle::OnePart *,
Particle::OnePart *, Particle::OnePart *);
virtual int tallyinfo(surfint *&);
- void post_process_isurf_grid();
+ virtual void post_process_isurf_grid();
bigint memory_usage();
protected:
@@ -69,7 +70,7 @@ class ComputeReactISurfGrid : public Compute {
Surf::Line *lines;
Surf::Tri *tris;
- void grow_tally();
+ virtual void grow_tally();
};
}
diff --git a/src/compute_react_surf.cpp b/src/compute_react_surf.cpp
index 4b49c3e45..a56ed9ef1 100644
--- a/src/compute_react_surf.cpp
+++ b/src/compute_react_surf.cpp
@@ -108,6 +108,8 @@ ComputeReactSurf::ComputeReactSurf(SPARTA *sparta, int narg, char **arg) :
ComputeReactSurf::~ComputeReactSurf()
{
+ if (copy || copymode) return;
+
memory->destroy(reaction2col);
memory->destroy(array_surf_tally);
memory->destroy(tally2surf);
diff --git a/src/compute_react_surf.h b/src/compute_react_surf.h
index c91184331..c041e738a 100644
--- a/src/compute_react_surf.h
+++ b/src/compute_react_surf.h
@@ -30,6 +30,7 @@ namespace SPARTA_NS {
class ComputeReactSurf : public Compute {
public:
ComputeReactSurf(class SPARTA *, int, char **);
+ ComputeReactSurf(class SPARTA* sparta) : Compute(sparta) {} // needed for Kokkos
~ComputeReactSurf();
virtual void init();
void compute_per_surf();
@@ -68,7 +69,7 @@ class ComputeReactSurf : public Compute {
Surf::Line *lines;
Surf::Tri *tris;
- void grow_tally();
+ virtual void grow_tally();
};
}
diff --git a/src/grid.cpp b/src/grid.cpp
index 849384c84..e1a1d1a0c 100644
--- a/src/grid.cpp
+++ b/src/grid.cpp
@@ -76,6 +76,7 @@ int corners[6][4] = {{0,2,4,6}, {1,3,5,7}, {0,1,4,5}, {2,3,6,7},
Grid::Grid(SPARTA *sparta) : Pointers(sparta)
{
exist = exist_ghost = clumped = 0;
+ changed = 0;
MPI_Comm_rank(world,&me);
gnames = (char **) memory->smalloc(MAXGROUP*sizeof(char *),"grid:gnames");
@@ -367,6 +368,8 @@ void Grid::add_sub_cell(int icell, int ownflag)
void Grid::notify_changed()
{
+ changed = 1;
+
if (modify->n_pergrid) modify->grid_changed();
Compute **compute = modify->compute;
diff --git a/src/grid.h b/src/grid.h
index 2f40084fb..914106e62 100644
--- a/src/grid.h
+++ b/src/grid.h
@@ -27,6 +27,9 @@ class Grid : protected Pointers {
public:
int exist; // 1 if grid is defined
int exist_ghost; // 1 if ghost cells exist
+ int changed; // set by notify_changed() when grid/surf topology changes
+ // (e.g. ablation, adaptation); consumed by KOKKOS to
+ // resync per-cell surf graphs to device mid-run
int clumped; // 1 if grid ownership is clumped, due to RCB
// if not, some operations are not allowed
diff --git a/src/surf_collide.h b/src/surf_collide.h
index e6f161a58..7b385935b 100644
--- a/src/surf_collide.h
+++ b/src/surf_collide.h
@@ -40,6 +40,7 @@ class SurfCollide : protected Pointers {
virtual Particle::OnePart *collide(Particle::OnePart *&, double &,
int, double *, int, int &) = 0;
virtual void wrapper(Particle::OnePart *, double *, int *, double *) {}
+ virtual class RanKnuth *kokkos_random() { return NULL; }
virtual void flags_and_coeffs(int *, double *) {}
virtual void dynamic();
diff --git a/src/surf_collide_adiabatic.h b/src/surf_collide_adiabatic.h
index 875e4842e..d90d052b7 100644
--- a/src/surf_collide_adiabatic.h
+++ b/src/surf_collide_adiabatic.h
@@ -35,6 +35,8 @@ class SurfCollideAdiabatic : public SurfCollide {
void wrapper(Particle::OnePart *, double *, int *, double*);
void flags_and_coeffs(int *, double *) {}
+ class RanKnuth *kokkos_random() { return random; }
+
protected:
class RanKnuth *random; // RNG for particle reflection
diff --git a/src/surf_collide_cll.cpp b/src/surf_collide_cll.cpp
index 2751c3fd3..836789bb3 100644
--- a/src/surf_collide_cll.cpp
+++ b/src/surf_collide_cll.cpp
@@ -136,6 +136,8 @@ SurfCollideCLL::SurfCollideCLL(SPARTA *sparta, int narg, char **arg) :
SurfCollideCLL::~SurfCollideCLL()
{
+ if (copy) return;
+
delete random;
}
diff --git a/src/surf_collide_cll.h b/src/surf_collide_cll.h
index 9bdfb200b..ffb401b0f 100644
--- a/src/surf_collide_cll.h
+++ b/src/surf_collide_cll.h
@@ -29,6 +29,7 @@ namespace SPARTA_NS {
class SurfCollideCLL : public SurfCollide {
public:
SurfCollideCLL(class SPARTA *, int, char **);
+ SurfCollideCLL(class SPARTA *sparta) : SurfCollide(sparta) {} // needed for Kokkos
~SurfCollideCLL();
void init();
Particle::OnePart *collide(Particle::OnePart *&, double &,
@@ -36,7 +37,9 @@ class SurfCollideCLL : public SurfCollide {
void wrapper(Particle::OnePart *, double *, int *, double*);
void flags_and_coeffs(int *, double *);
- private:
+ class RanKnuth *kokkos_random() { return random; }
+
+ protected:
double acc_n,acc_t,acc_rot,acc_vib; // surface accomodation coeffs
double vx,vy,vz; // translational velocity of surface
double wx,wy,wz; // angular velocity of surface
diff --git a/src/surf_collide_diffuse.h b/src/surf_collide_diffuse.h
index 76ae89742..584a4d9b9 100644
--- a/src/surf_collide_diffuse.h
+++ b/src/surf_collide_diffuse.h
@@ -37,6 +37,8 @@ class SurfCollideDiffuse : public SurfCollide {
void wrapper(Particle::OnePart *, double *, int *, double*);
void flags_and_coeffs(int *, double *);
+ class RanKnuth *kokkos_random() { return random; }
+
protected:
double acc; // surface accomodation coeff
double vx,vy,vz; // translational velocity of surface
diff --git a/src/surf_collide_impulsive.cpp b/src/surf_collide_impulsive.cpp
index 37e94a732..e55036426 100644
--- a/src/surf_collide_impulsive.cpp
+++ b/src/surf_collide_impulsive.cpp
@@ -143,6 +143,8 @@ SurfCollideImpulsive::SurfCollideImpulsive(SPARTA *sparta, int narg, char **arg)
SurfCollideImpulsive::~SurfCollideImpulsive()
{
+ if (copy) return;
+
delete random;
}
diff --git a/src/surf_collide_impulsive.h b/src/surf_collide_impulsive.h
index aa5c1eda2..8c3a3cba9 100644
--- a/src/surf_collide_impulsive.h
+++ b/src/surf_collide_impulsive.h
@@ -28,6 +28,7 @@ namespace SPARTA_NS {
class SurfCollideImpulsive : public SurfCollide {
public:
SurfCollideImpulsive(class SPARTA *, int, char **);
+ SurfCollideImpulsive(class SPARTA *sparta) : SurfCollide(sparta) {} // needed for Kokkos
~SurfCollideImpulsive();
void init();
Particle::OnePart *collide(Particle::OnePart *&, double &,
@@ -35,7 +36,9 @@ class SurfCollideImpulsive : public SurfCollide {
void wrapper(Particle::OnePart *, double *, int *, double*);
void flags_and_coeffs(int *, double *);
- private:
+ class RanKnuth *kokkos_random() { return random; }
+
+ protected:
double eng_ratio,eff_mass; // energy ratio and effective mass
// of the surface for soft-sphere model
double u0_a, u0_b; // u0 values for the direct case
diff --git a/src/surf_collide_td.cpp b/src/surf_collide_td.cpp
index 1da0b2a48..a291b5d21 100644
--- a/src/surf_collide_td.cpp
+++ b/src/surf_collide_td.cpp
@@ -102,6 +102,8 @@ SurfCollideTD::SurfCollideTD(SPARTA *sparta, int narg, char **arg) :
SurfCollideTD::~SurfCollideTD()
{
+ if (copy) return;
+
delete random;
}
diff --git a/src/surf_collide_td.h b/src/surf_collide_td.h
index 3b6b516f5..e98fbbc89 100644
--- a/src/surf_collide_td.h
+++ b/src/surf_collide_td.h
@@ -28,6 +28,7 @@ namespace SPARTA_NS {
class SurfCollideTD : public SurfCollide {
public:
SurfCollideTD(class SPARTA *, int, char **);
+ SurfCollideTD(class SPARTA *sparta) : SurfCollide(sparta) {} // needed for Kokkos
~SurfCollideTD();
void init();
Particle::OnePart *collide(Particle::OnePart *&, double &,
@@ -35,7 +36,9 @@ class SurfCollideTD : public SurfCollide {
void wrapper(Particle::OnePart *, double *, int *, double*);
void flags_and_coeffs(int *, double *);
- private:
+ class RanKnuth *kokkos_random() { return random; }
+
+ protected:
double barrier_val;
double initen_trans, initen_rot, initen_vib;
double bond_trans, bond_rot, bond_vib;
diff --git a/src/surf_custom.cpp b/src/surf_custom.cpp
index 0630b86d4..075eef491 100644
--- a/src/surf_custom.cpp
+++ b/src/surf_custom.cpp
@@ -229,6 +229,13 @@ void Surf::remove_custom(int index)
{
if (!ename || !ename[index]) return;
+ // ewhich may already be freed during Kokkos teardown: SurfKokkos's destructor
+ // runs (and frees the Kokkos-managed custom data + nulls ewhich) before the
+ // base Surf destructor deletes surf-react instances, whose destructors call
+ // remove_custom. The data is already freed there, so skip safely.
+
+ if (!ewhich) return;
+
delete [] ename[index];
ename[index] = NULL;
diff --git a/src/surf_react_adsorb.cpp b/src/surf_react_adsorb.cpp
index 13c33ef78..66864fb79 100644
--- a/src/surf_react_adsorb.cpp
+++ b/src/surf_react_adsorb.cpp
@@ -216,6 +216,8 @@ SurfReactAdsorb::SurfReactAdsorb(SPARTA *sparta, int narg, char **arg) :
SurfReactAdsorb::~SurfReactAdsorb()
{
+ if (copy) return;
+
delete random;
// surface species
diff --git a/src/surf_react_adsorb.h b/src/surf_react_adsorb.h
index 3daa09553..be68a7a69 100644
--- a/src/surf_react_adsorb.h
+++ b/src/surf_react_adsorb.h
@@ -28,8 +28,9 @@ namespace SPARTA_NS {
class SurfReactAdsorb : public SurfReact {
public:
SurfReactAdsorb(class SPARTA *, int, char **);
+ SurfReactAdsorb(class SPARTA *sparta) : SurfReact(sparta) {} // needed for Kokkos
~SurfReactAdsorb();
- void init();
+ virtual void init();
int react(Particle::OnePart *&, int, double *, Particle::OnePart *&, int &);
char *reactionID(int);
@@ -37,10 +38,10 @@ class SurfReactAdsorb : public SurfReact {
int match_reactant(char *, int);
int match_product(char *, int);
- void tally_update();
+ virtual void tally_update();
void grid_changed();
- private:
+ protected:
int me,nprocs;
int distributed;