XGCa
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
field_following_coordinates.hpp
Go to the documentation of this file.
1 #ifndef FIELD_FOLLOWING_COORDINATES_HPP
2 #define FIELD_FOLLOWING_COORDINATES_HPP
3 
4 #include "my_subview.hpp"
5 #include "grid.hpp"
6 #include "grid_field.hpp"
7 
8 
10  View<int**,CLayout,DeviceType> tr;
11  View<double***,CLayout,DeviceType> p;
12 
13  // View for temporarily storing result data for in-place conversions
14  View<double**,CLayout,DeviceType> result;
15 
16  public:
17 
19 
21  : tr(NoInit("tr"),projection.tr.layout()),
22  p(NoInit("p"),projection.p.layout()),
23  result(NoInit("result"),tr.layout())
24  {
25  // Copy grid conversion info to device
26  Kokkos::deep_copy(tr, projection.tr);
27  Kokkos::deep_copy(p, projection.p);
28  }
29 
30  /* Conversion from field-following coordinates to toroidal coordinates
31  * @param[in] grid is the Grid object needed here to map elements to nodes
32  * @param[in] input is the array one wants to convert. It is at the midplane so must be size midplane.nnode
33  * @param[out] output is the resulting converted array. It is at the edge planes so must be size max(rplane.nnode, lplane.nnode)
34  * */
35  void cnvt_grid_ff2real(const Grid<DeviceType>& grid, const View<double**,CLayout,DeviceType>& input, const View<double**,Kokkos::LayoutRight,DeviceType>& output) const{
36  // Check the input and output views are the correct size
37  if(input.extent(0) != 2 || input.extent(1) != grid.midplane.nnode ||
38  output.extent(0)!= 2 || output.extent(1)!= std::max(grid.rplane.nnode, grid.lplane.nnode) ) exit_XGC("\nError in cnvt_grid_ff2real: Unexpected View sizes. \n");
39 
40  // Initialize output to zero
41  Kokkos::deep_copy(output, 0.0);
42 
43  // Initialize vol to zero
44  View<double**,CLayout,DeviceType> vol("vol",input.layout());
45 
46  // Copy grid%node_vol_ff from fortran
47  View<double**,CLayout,DeviceType> node_vol_ff(NoInit("node_vol_ff"),input.layout());
48  // For stellarators, Dir 0 uses left plane node volume, Dir 1 uses right plane node volume
49  Kokkos::deep_copy(my_subview(node_vol_ff,0), my_subview(grid.volumes_and_areas.node_vol_ff_l_h,0));
50  Kokkos::deep_copy(my_subview(node_vol_ff,1), my_subview(grid.volumes_and_areas.node_vol_ff_r_h,1));
51 
52  // Make a (SHALLOW) copy so the lambda doesn't have to implicitly capture *this
53  FieldFollowingCoordinates ff = *this;
54 
55  // for all node point of ff
56  constexpr int LEFTWARD=0;
57  Kokkos::parallel_for("cnvt_grid_ff2real", Kokkos::RangePolicy<ExSpace>(0,grid.midplane.nnode), KOKKOS_LAMBDA( const int i ){
58  for (int j=0;j<3;j++){
59  // node and weight
60  int nd=grid.lplane.get_node_index(ff.tr(LEFTWARD,i), j);
61  double wt=ff.p(LEFTWARD,i,j)*node_vol_ff(LEFTWARD,nd);
62 
63  // accumulate values
64  Kokkos::atomic_add(&(output(LEFTWARD,nd)), wt*input(LEFTWARD,i));
65  Kokkos::atomic_add(&(vol(LEFTWARD,nd)), wt);
66  }
67  });
68  constexpr int RIGHTWARD=1;
69  Kokkos::parallel_for("cnvt_grid_ff2real", Kokkos::RangePolicy<ExSpace>(0,grid.midplane.nnode), KOKKOS_LAMBDA( const int i ){
70  for (int j=0;j<3;j++){
71  // node and weight
72  int nd=grid.rplane.get_node_index(ff.tr(RIGHTWARD,i), j);
73  double wt=ff.p(RIGHTWARD,i,j)*node_vol_ff(RIGHTWARD,nd);
74 
75  // accumulate values
76  Kokkos::atomic_add(&(output(RIGHTWARD,nd)), wt*input(RIGHTWARD,i));
77  Kokkos::atomic_add(&(vol(RIGHTWARD,nd)), wt);
78  }
79  });
80 
81  // Normalize to vol
82  Kokkos::parallel_for("cnvt_grid_ff2real_norm", Kokkos::RangePolicy<ExSpace>(0,output.extent(1)), KOKKOS_LAMBDA( const int i ){
83  for (int dir=0;dir<2;dir++){
84  if(vol(dir, i) == 0.0){
85  output(dir, i) = 0.0;
86  }else{
87  output(dir, i) /= vol(dir, i);
88  }
89  }
90  });
91 #ifndef STELLARATOR
92  // I believe this logic assumes axisymmetric grid:
93  // - ff.tr is size midplane.nnode, but this loop is over edge plane vertices.
94  // - ff.tr(RIGHTWARD) cannot be interpreted on lplane.
95  // Don't use for stellarators yet - ALS
96 
97  // fill empty area --- replace nan values
98  Kokkos::parallel_for("cnvt_grid_ff2real_fillempty", Kokkos::RangePolicy<ExSpace>(0,output.extent(1)), KOKKOS_LAMBDA( const int i ){
99  if(vol(LEFTWARD,i)==0.0){
100  output(LEFTWARD,i)=0.0;
101  for (int j=0;j<3;j++){
102  int nd=grid.lplane.get_node_index(ff.tr(RIGHTWARD,i), j);
103  double wt=ff.p(RIGHTWARD,i,j);
104 
105  // obtain interpolated values
106  output(LEFTWARD,i) += wt*input(LEFTWARD,nd);
107  }
108  }
109  });
110  Kokkos::parallel_for("cnvt_grid_ff2real_fillempty", Kokkos::RangePolicy<ExSpace>(0,output.extent(1)), KOKKOS_LAMBDA( const int i ){
111  if(vol(RIGHTWARD,i)==0.0){
112  output(RIGHTWARD,i)=0.0;
113  for (int j=0;j<3;j++){
114  int nd=grid.rplane.get_node_index(ff.tr(LEFTWARD,i), j);
115  double wt=ff.p(LEFTWARD,i,j);
116 
117  // obtain interpolated values
118  output(RIGHTWARD,i) += wt*input(RIGHTWARD,nd);
119  }
120  }
121  });
122 #endif
123  Kokkos::fence();
124  }
125 
126  /* Conversion from field-following coordinates to real coordinates when views are on host
127  * User also provides a temporary device allocation
128  * @param[in] grid is the Grid object needed here to map elements to nodes
129  * @param[in] input is the array one wants to convert
130  * @param[out] output is the resulting converted array
131  * @param[in] tmp is an array with the same size as input/output used for temporary storage
132  * */
133  void cnvt_grid_ff2real(const Grid<DeviceType>& grid, const View<double**,CLayout,HostType>& input, const View<double**,CLayout,HostType>& output, const View<double**,CLayout,DeviceType>& tmp) const{
134  // Copy input to tmp
135  Kokkos::deep_copy(tmp, input);
136 
137  // Convert
138  cnvt_grid_ff2real(grid, tmp, result);
139 
140  // Copy to output
141  Kokkos::deep_copy(output, result);
142  }
143 
144 #ifdef USE_GPU
145  /* Conversion from field-following coordinates to real coordinates when input view is on host but output view is on device
146  * User also provides a temporary device allocation
147  * @param[in] grid is the Grid object needed here to map elements to nodes
148  * @param[in] input is the array one wants to convert
149  * @param[out] output is the resulting converted array
150  * */
151  void cnvt_grid_ff2real(const Grid<DeviceType>& grid, const View<double**,CLayout,HostType>& input, const View<double**,CLayout,DeviceType>& output) const{
152  // Copy input to output
153  Kokkos::deep_copy(output, input);
154 
155  // Convert
156  cnvt_grid_ff2real(grid, output, result);
157 
158  // Copy to output
159  Kokkos::deep_copy(output, result);
160  }
161 #endif
162 
163  /* Conversion from real coordinates to field-following coordinates
164  * @param[in] grid is the Grid object needed here to map elements to nodes
165  * @param[in] input is the array one wants to convert. The input values are at the two edge planes. If the two planes have a different
166  * number of vertices, it must be of size max(lplane.nnode, rplane.nnode)
167  * @param[out] output is the resulting converted array. It corresponds to the midplane so it must be size midplane.nnode
168  * */
169  void cnvt_grid_real2ff(const Grid<DeviceType>& grid, const View<double**,CLayout,DeviceType>& input, const View<double**,Kokkos::LayoutRight,DeviceType>& output) const{
170  // Check the input and output views are the correct size
171  if(input.extent(0) != 2 || input.extent(1) != std::max(grid.rplane.nnode, grid.lplane.nnode) ||
172  output.extent(0)!= 2 || output.extent(1)!= grid.midplane.nnode ) exit_XGC("\nError in cnvt_grid_real2ff: Unexpected View sizes. \n");
173 
174  // Initialize output to zero
175  Kokkos::deep_copy(output, 0.0);
176 
177 
178  // Make a (SHALLOW) copy so the lambda doesn't have to implicitly capture *this
179  FieldFollowingCoordinates ff = *this;
180 
181  constexpr int LEFTWARD=0;
182  Kokkos::parallel_for("cnvt_grid_real2ff", Kokkos::RangePolicy<ExSpace>(0,grid.midplane.nnode), KOKKOS_LAMBDA( const int i ){
183  for (int j=0;j<3;j++){
184  int nd=grid.lplane.get_node_index(ff.tr(LEFTWARD,i), j);
185 
186  double wt=ff.p(LEFTWARD,i,j);
187 
188  // obtain interpolated values
189  output(LEFTWARD,i) += wt*input(LEFTWARD,nd);
190  }
191  });
192  constexpr int RIGHTWARD=1;
193  Kokkos::parallel_for("cnvt_grid_real2ff", Kokkos::RangePolicy<ExSpace>(0,grid.midplane.nnode), KOKKOS_LAMBDA( const int i ){
194  for (int j=0;j<3;j++){
195  int nd=grid.rplane.get_node_index(ff.tr(RIGHTWARD,i), j);
196 
197  double wt=ff.p(RIGHTWARD,i,j);
198 
199  // obtain interpolated values
200  output(RIGHTWARD,i) += wt*input(RIGHTWARD,nd);
201  }
202  });
203  Kokkos::fence();
204  }
205 
206  /* In-place conversion from real coordinates to field-following coordinates, using a buffer array to store the result temporarily
207  * before copying back to the input array
208  * @param[in] grid is the Grid object needed here to map elements to nodes
209  * @param[inout] input is the array one wants to convert, and the resulting converted array
210  * */
211  void cnvt_grid_real2ff(const Grid<DeviceType>& grid, const View<double**,CLayout,DeviceType>& input) const{
212  cnvt_grid_real2ff(grid, input, result);
213 
214  // Copy back to original array
215  Kokkos::deep_copy(input, result);
216  }
217 
218  /* Conversion from real coordinates to field-following coordinates for vectors
219  * @param[in] grid is the Grid object needed here to map elements to nodes
220  * @param[in] input is the array one wants to convert
221  * @param[out] output is the resulting converted array
222  * */
223  void cnvt_grid_real2ff(const Grid<DeviceType>& grid, const View<double***,CLayout,DeviceType>& input, const View<double***,Kokkos::LayoutRight,DeviceType>& output) const{
224  for(int i = 0; i<input.extent(0); i++){
225  auto input_i = my_subview(input, i);
226  auto output_i = my_subview(output, i);
227 
228  cnvt_grid_real2ff(grid, input_i, output_i);
229  }
230  }
231 
232  /* In-place conversion from real coordinates to field-following coordinates for vectors, using a buffer array to store the result temporarily
233  * before copying back to the input array
234  * @param[in] grid is the Grid object needed here to map elements to nodes
235  * @param[inout] input is the array one wants to convert, and the resulting converted array
236  * */
237  void cnvt_grid_real2ff(const Grid<DeviceType>& grid, const View<double***,CLayout,DeviceType>& input)const{
238  for(int i = 0; i<input.extent(0); i++){
239  auto input_i = my_subview(input, i);
240  cnvt_grid_real2ff(grid, input_i);
241  }
242  }
243 
244 
245  /* Conversion from real coordinates to field-following coordinates from a GridField with PhiInterpType::None to a GridField with PhiInterpType::Planes.
246  * This implementation could be improved. It uses the existing algorithm which requires the input to have nphi=2, so there is a lot of superfluous copying etc. */
247  void cnvt_grid_real2ff(const Grid<DeviceType>& grid, const ScalarGridField& field_in,
249 
250  // Field-following algorithm requires argument to be nphi=2, and inode as the contiguous index
251  int nphi = 2;
252  Kokkos::View<double**,Kokkos::LayoutRight,DeviceType> dest_view(NoInit("dest_view"), nphi, field_in.nnode());
253 
254  // Since the input is a ScalarGridField (nphi=1), copy the field into both the iphi=0 and iphi=1 indices
255  field_in.copy_to_double_view(my_subview(dest_view, 0)); // iphi=0;
256  field_in.copy_to_double_view(my_subview(dest_view, 1)); // iphi=1;
257 
258  // Convert to field-following
259  cnvt_grid_real2ff(grid, dest_view, result);
260 
261  // Now transpose and copy into the output argument:
262  field_out.transpose_copy_from_double_view(result);
263  }
264 
265 };
266 
267 #endif
VolumesAndAreas volumes_and_areas
Definition: grid.hpp:182
Definition: field_following_coordinates.hpp:9
void cnvt_grid_ff2real(const Grid< DeviceType > &grid, const View< double **, CLayout, HostType > &input, const View< double **, CLayout, DeviceType > &output) const
Definition: field_following_coordinates.hpp:151
void cnvt_grid_ff2real(const Grid< DeviceType > &grid, const View< double **, CLayout, DeviceType > &input, const View< double **, Kokkos::LayoutRight, DeviceType > &output) const
Definition: field_following_coordinates.hpp:35
void cnvt_grid_real2ff(const Grid< DeviceType > &grid, const View< double **, CLayout, DeviceType > &input, const View< double **, Kokkos::LayoutRight, DeviceType > &output) const
Definition: field_following_coordinates.hpp:169
KOKKOS_INLINE_FUNCTION int get_node_index(int triangle_index, int tri_vertex_index) const
Definition: plane.tpp:680
FieldFollowingCoordinates(const Projection< HostType > &projection)
Definition: field_following_coordinates.hpp:20
FieldFollowingCoordinates()
Definition: field_following_coordinates.hpp:18
View< int **, CLayout, DeviceType > tr
Definition: field_following_coordinates.hpp:10
Definition: grid_field.hpp:22
View< double ***, CLayout, Device > p
Definition: ff_projection.hpp:24
void cnvt_grid_real2ff(const Grid< DeviceType > &grid, const View< double ***, CLayout, DeviceType > &input, const View< double ***, Kokkos::LayoutRight, DeviceType > &output) const
Definition: field_following_coordinates.hpp:223
int nnode
Number of grid nodes.
Definition: plane.hpp:247
View< double **, CLayout, HostType > node_vol_ff_l_h
Definition: volumes_and_areas.hpp:12
void cnvt_grid_real2ff(const Grid< DeviceType > &grid, const View< double ***, CLayout, DeviceType > &input) const
Definition: field_following_coordinates.hpp:237
void cnvt_grid_ff2real(const Grid< DeviceType > &grid, const View< double **, CLayout, HostType > &input, const View< double **, CLayout, HostType > &output, const View< double **, CLayout, DeviceType > &tmp) const
Definition: field_following_coordinates.hpp:133
void cnvt_grid_real2ff(const Grid< DeviceType > &grid, const View< double **, CLayout, DeviceType > &input) const
Definition: field_following_coordinates.hpp:211
void exit_XGC(std::string msg)
Definition: globals.hpp:37
View< double **, CLayout, HostType > node_vol_ff_r_h
Definition: volumes_and_areas.hpp:13
View< double ***, CLayout, DeviceType > p
Definition: field_following_coordinates.hpp:11
Kokkos::View< T *, Kokkos::LayoutRight, Device > my_subview(const Kokkos::View< T ****, Kokkos::LayoutRight, Device > &view, int i, int j, int k)
Definition: my_subview.hpp:8
View< int **, CLayout, Device > tr
The triangles that are mapped to by the projection. One-indexed.
Definition: ff_projection.hpp:22
void parallel_for(const std::string name, int n_ptl, Function func, Option option, HostAoSoA aosoa_h, DeviceAoSoA aosoa_d)
Definition: streamed_parallel_for.hpp:252
View< double **, CLayout, DeviceType > result
Definition: field_following_coordinates.hpp:14
Plane< Device > midplane
Definition: grid.hpp:160
Plane< Device > lplane
Definition: grid.hpp:161
void cnvt_grid_real2ff(const Grid< DeviceType > &grid, const ScalarGridField &field_in, const GridField< DeviceType, VarType::Scalar, PhiInterpType::Planes, TorType::OnePlane, KinType::DriftKin > &field_out) const
Definition: field_following_coordinates.hpp:247
Kokkos::ViewAllocateWithoutInitializing NoInit
Definition: space_settings.hpp:69
Plane< Device > rplane
Definition: grid.hpp:162