#ifndef _GPUMAGNETICFIELD_CU_ #define _GPUMAGNETICFIELD_CU_ #include #include #include #include #include #include #include #include #include using namespace std; #include "SimpleException.h" #include "GPUMagneticField.h" /// Declare our texture reference. This MUST be a global variable! /// float4 = texel element is 4-D value using CUDA array type float4 (only float, float2, and float4 are allowed) /// cudaTextureType2D = indexing will use 2-dimensions /// cudaReadModeElementType = Values returned will be same type as stored (no /// transformation to normalized values) texture bfield_GPU_texref; /// This global variable is declared in device constant memory /// to hold the map deminsions info so that it can be accessed quickly /// and without having to pass a reference to the structure with every call. __device__ __constant__ Bfield_parms_t BFIELD_PARMS; // Local routines not exposed as part of the library static float4* ReadBfieldFromFile(string fname, Bfield_parms_t &bparms); //---------------- // InitCUDA //---------------- void InitCUDA(bool quiet) { /// Verify that a CUDA device exists and optionally /// print some info about it. This really does nothing /// to set up the device, just verifies one exists /// and informs the user what type of GPUs exist and /// which one we are using. Calling InitCUDA is not /// required. int deviceCount=0; cudaGetDeviceCount(&deviceCount); if(deviceCount<1){ cout<<"No CUDA devices available!"<(); // Bind texture object to allocated memory on device cout << "Binding B-field to texture object ...." << endl; err = cudaBindTexture2D(NULL, &bfield_GPU_texref, d_bfield_table, &desc, Nzbins, Nrbins, pitch); if(err != cudaSuccess) throw SimpleException(string("CUDA ERROR: ") + cudaGetErrorString(err)); cout << "B-field ready on GPU." << endl; } //---------------- // ReadBfield //---------------- float4* ReadBfieldFromFile(string fname, Bfield_parms_t &bparms) { /// This opens an ASCII file containing the b-field map formatted /// to have the values "x y z Bx By Bz" on each line. The "x" /// and "Bx" values are taken to be "r" and "Br" while both /// of the y values are ignored. The number of bins and range /// are determined by the entries found in the file. Values /// are assumed to be in inches and Tesla. // Open the input file and read in all values to figure out how // many bins we have in z and r. //string fname = "solenoid_1500_poisson_20090814_01"; ifstream ifs(fname.c_str()); if(!ifs.is_open()) throw SimpleException(string("Unable to open B-field map file: " + fname)); // Read all elements into a vector, storing x(=r) and z coordinates in // separate set containers so we can know how many elements and what their // range is for each dimension. set z_vals; set r_vals; vector map_vals; // .x=r .y=z .z=Br .w=Bz while(ifs.good()){ char line[1024]; ifs.getline(line, 1024); if(!ifs.good()) break; if(line[0] == '#') continue; stringstream ss(line); float x,y,z, Bx, By, Bz; ss>>x>>y>>z>>Bx>>By>>Bz; x *= 2.54; // convert to cm from inches z *= 2.54; // convert to cm from inches z_vals.insert(z); r_vals.insert(x); float4 tmp = {x, z, Bx, Bz}; map_vals.push_back(tmp); } // Copy ranges to caller's variables and report map stats to user bparms.Nzbins = z_vals.size(); bparms.Nrbins = r_vals.size(); bparms.rmin = *(r_vals.begin()); // assume set orders by ascending value! bparms.rmax = *(r_vals.rbegin()); // assume set orders by ascending value! bparms.zmin = *(z_vals.begin()); // assume set orders by ascending value! bparms.zmax = *(z_vals.rbegin()); // assume set orders by ascending value! cout << " Found "< indexes; for(unsigned int i=0; i // "z" = normalized Bz // "w" = field magnitude bfield_table[index].x = (B==0.0 ? 0.0:Br/B); bfield_table[index].y = 0.0; bfield_table[index].z = (B==0.0 ? 0.0:Bz/B); bfield_table[index].w = B; } cout <<" " << indexes.size() << " elements entered ranging from " << *(indexes.begin()) << " to " << *(indexes.rbegin()) << endl; return bfield_table; } //---------------- // GPU_GetBfieldPoints //---------------- __global__ void GPU_GetBfieldPoints(int N, float3 *d_coordinates, float3 *d_B) { /// This is called from the host to evaluate the /// field at a set of points. It is primarily /// intended for testing. int index = threadIdx.x + blockDim.x*blockIdx.x; if(indexx, &B->y, &B->z); } //---------------- // GPU_GetBfieldXYZ //---------------- static __device__ void GPU_GetBfieldXYZ(float x, float y, float z, float *Bx, float *By, float *Bz) { /// Access the field map in order to return the value /// of the B-field at the specified location. The /// field map is kept in r and z only so rotation /// into and out of that must be done for every call. /// If the r and z values in lab coordinates are already /// known, the GPU_GetBfieldRZ routine will be faster. /// The map is stored as magnitude and direction components /// so it will be faster to use the other GPU_GetBfieldXYZ /// routine, especially if you need the magnitude since /// it will save calculating it from the components. // convert into cylindrical coordinates float phi = atan2(y, x); float r = sqrt(x*x + y*y); // calculate normalized coordinates float znorm = (z - BFIELD_PARMS.zmin)*BFIELD_PARMS.z_scale; float rnorm = (r - BFIELD_PARMS.rmin)*BFIELD_PARMS.r_scale; // evaluate map at specified coordinates float4 tmp = tex2D(bfield_GPU_texref, znorm, rnorm); // copy results into caller-supplied vector, // converting back into 3D coordinates float B = tmp.w; float Br = B*tmp.x; *Bx = Br*cos(phi); *By = Br*sin(phi); *Bz = B*tmp.z; } //---------------- // GPU_GetBfieldRZ //---------------- static __device__ void GPU_GetBfieldRZ(float r, float z, float *Br, float *Bz) { /// Access the field map in order to return the value /// of the B-field at the specified location in cylindrical /// coordinates. /// The map is stored as magnitude and direction components /// so it will be faster to use the other GPU_GetBfieldRZ /// routine, especially if you need the magnitude since /// it will save calculating it from the components. // calculate normalized coordinates float znorm = (z - BFIELD_PARMS.zmin)*BFIELD_PARMS.z_scale; float rnorm = (r - BFIELD_PARMS.rmin)*BFIELD_PARMS.r_scale; // evaluate map at specified coordinates float4 tmp = tex2D(bfield_GPU_texref, znorm, rnorm); // copy results into caller-supplied vector, // converting back into 3D coordinates float B = tmp.w; *Br = B*tmp.x; *Bz = B*tmp.z; } //---------------- // GPU_GetBfield //---------------- static __device__ void GPU_GetBfield(float3 pos, float4 *B) { /// This is a convenience method that just wraps the /// GPU_GetBfieldXYZ routine so the field can be /// accessed using float3 and float 4 instead of /// individual float values. GPU_GetBfieldXYZ(pos.x, pos.y, pos.z, &B->w, &B->x, &B->y, &B->z); } //---------------- // GPU_GetBfieldXYZ //---------------- static __device__ void GPU_GetBfieldXYZ(float x, float y, float z, float *B, float *bx, float *by, float *bz) { /// Access the field map in order to return the value /// of the B-field at the specified location. The /// field map is kept in r and z only so rotation /// into and out of that must be done for every call. /// If the r and z values in lab coordinates are already /// known, the GPU_GetBfieldRZ routine will be faster. /// The map is stored as magnitude and direction components /// so it will be faster to use the other GPU_GetBfieldXYZ /// routine, especially if you need the magnitude since /// it will save calculating it from the components. // convert into cylindrical coordinates float phi = atan2(y, x); float r = sqrt(x*x + y*y); // calculate normalized coordinates float znorm = (z - BFIELD_PARMS.zmin)*BFIELD_PARMS.z_scale; float rnorm = (r - BFIELD_PARMS.rmin)*BFIELD_PARMS.r_scale; // evaluate map at specified coordinates float4 tmp = tex2D(bfield_GPU_texref, znorm, rnorm); // copy results into caller-supplied vector, // converting back into 3D coordinates *B = tmp.w; float &br = tmp.x; *bx = br*cos(phi); *by = br*sin(phi); *bz = tmp.z; } //---------------- // GPU_GetBfieldRZ //---------------- static __device__ void GPU_GetBfieldRZ(float r, float z, float *B, float *br, float *bz) { /// Access the field map in order to return the value /// of the B-field at the specified location in cylindrical /// coordinates. /// The map is stored as magnitude and direction components /// so it will be faster to use the other GPU_GetBfieldRZ /// routine, especially if you need the magnitude since /// it will save calculating it from the components. // calculate normalized coordinates float znorm = (z - BFIELD_PARMS.zmin)*BFIELD_PARMS.z_scale; float rnorm = (r - BFIELD_PARMS.rmin)*BFIELD_PARMS.r_scale; // evaluate map at specified coordinates float4 tmp = tex2D(bfield_GPU_texref, znorm, rnorm); // copy results into caller-supplied vector, // converting back into 3D coordinates *B = tmp.w; *br = tmp.x; *bz = tmp.z; } #endif // _GPUMAGNETICFIELD_CU_