<commit_before>#include "Phantom_vrepHW.h"

#include "../v_repLib.h"

#include <string>
#include <iostream>

namespace MR
{

std::string Phantom_vrepHW::sm_jointsName[MR_JOINTS_NUM] = {
  "PhantomXPincher_joint1",
  "PhantomXPincher_joint2",
  "PhantomXPincher_joint3",
  "PhantomXPincher_joint4"
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Phantom_vrepHW::Phantom_vrepHW() :
    hardware_interface::RobotHW()
{
    // Init arrays m_cmd[], m_pos[], m_vel[], m_eff[].
    for (int i = 0; i < MR_JOINTS_NUM; ++i)
    {
        m_pos_cmd[i] = 0.0;
        m_vel_cmd[i] = 0.0;
        m_pos[i] = 0.0;
        m_vel[i] = 0.0;
        m_eff[i] = 0.0;
    }

    // Init and get handles of the joints to control.
    for (int i = 0; i < MR_JOINTS_NUM; ++i)
        m_vrepJointsHandle[i] = -1;

    // Register joint interfaces.
    registerHardwareInterfaces();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Phantom_vrepHW::init()
{
    // Get joint handles.
    for (int i = 0; i < MR_JOINTS_NUM; ++i)
    {
        int vrepJointsHandle = simGetObjectHandle(sm_jointsName[i].c_str());

        if (vrepJointsHandle == -1)
        {
            ROS_ERROR_STREAM("MR robot interface not able to get handle for '" << sm_jointsName[i] << "'." << std::endl);

            return false;
        }

        m_vrepJointsHandle[i] = vrepJointsHandle;
    }

    return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Phantom_vrepHW::registerHardwareInterfaces()
{
    for (int i = 0; i < MR_JOINTS_NUM; ++i)
    {
        // Joint state interface.
        hardware_interface::JointStateHandle jointStateHandle(sm_jointsName[i], &m_pos[i], &m_vel[i], &m_eff[i]);
        m_jointState_interface.registerHandle(jointStateHandle);

        // Joint velocity command interface
        hardware_interface::JointHandle jointVelocityHandle(jointStateHandle, &m_vel_cmd[i]);
        m_jointVelocity_interface.registerHandle(jointVelocityHandle);

	//Joint position command interface
	hardware_interface::JointHandle jointPositionHandle(jointStateHandle, &m_pos_cmd[i]);
	m_jointPosition_interface.registerHandle(jointPositionHandle);

    }

    registerInterface(&m_jointState_interface);
    registerInterface(&m_jointVelocity_interface);
    registerInterface(&m_jointPosition_interface);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Phantom_vrepHW::read()
{
    for (int i = 0; i < MR_JOINTS_NUM; ++i)
    {
        float pos,
              vel,
              eff;

        if (simGetJointPosition(m_vrepJointsHandle[i], &pos) == -1 ||
            simGetObjectFloatParameter(m_vrepJointsHandle[i], 2012, &vel) == -1 || // Velocity.
            simGetJointForce(m_vrepJointsHandle[i], &eff) == -1)
        {
            ROS_ERROR_STREAM("MR robot interface not able to get state for '" << sm_jointsName[i] << "'." << std::endl);

            return false;
        }

        m_pos[i] = pos;
        m_vel[i] = vel;
        m_eff[i] = eff;
    }

    return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Phantom_vrepHW::write()
{
    for (int i = 0; i < MR_JOINTS_NUM; ++i){
      //====== Velocity Control =========

      /*if (simSetJointTargetVelocity(m_vrepJointsHandle[i], m_vel_cmd[i]) == -1) {
	ROS_ERROR_STREAM("MR robot interface not able to get state for '" << sm_jointsName[i] << "'." << std::endl);
	return false;
      }
      */

      //===== Position Control =======
      if (simSetJointTargetPosition(m_vrepJointsHandle[i], m_pos_cmd[i]) == -1) {
	ROS_ERROR_STREAM("MR robot interface not able to get state for '" << sm_jointsName[i] << "'." << std::endl);
	return false;
      }
    }
    return true;
}

} // namespace MR.
<commit_msg>Added gripper controller - forgot a file<commit_after>#include "Phantom_vrepHW.h"

#include "../v_repLib.h"

#include <string>
#include <iostream>

namespace MR
{

std::string Phantom_vrepHW::sm_jointsName[MR_JOINTS_NUM] = {
  "PhantomXPincher_joint1",
  "PhantomXPincher_joint2",
  "PhantomXPincher_joint3",
  "PhantomXPincher_joint4",
  "PhantomXPincher_gripperClose_joint"
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Phantom_vrepHW::Phantom_vrepHW() :
    hardware_interface::RobotHW()
{
    // Init arrays m_cmd[], m_pos[], m_vel[], m_eff[].
    for (int i = 0; i < MR_JOINTS_NUM; ++i)
    {
        m_pos_cmd[i] = 0.0;
        m_vel_cmd[i] = 0.0;
        m_pos[i] = 0.0;
        m_vel[i] = 0.0;
        m_eff[i] = 0.0;
    }

    // Init and get handles of the joints to control.
    for (int i = 0; i < MR_JOINTS_NUM; ++i)
        m_vrepJointsHandle[i] = -1;

    // Register joint interfaces.
    registerHardwareInterfaces();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Phantom_vrepHW::init()
{
    // Get joint handles.
    for (int i = 0; i < MR_JOINTS_NUM; ++i)
    {
        int vrepJointsHandle = simGetObjectHandle(sm_jointsName[i].c_str());

        if (vrepJointsHandle == -1)
        {
            ROS_ERROR_STREAM("MR robot interface not able to get handle for '" << sm_jointsName[i] << "'." << std::endl);

            return false;
        }

        m_vrepJointsHandle[i] = vrepJointsHandle;
    }

    return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Phantom_vrepHW::registerHardwareInterfaces()
{
    for (int i = 0; i < MR_JOINTS_NUM; ++i)
    {
        // Joint state interface.
        hardware_interface::JointStateHandle jointStateHandle(sm_jointsName[i], &m_pos[i], &m_vel[i], &m_eff[i]);
        m_jointState_interface.registerHandle(jointStateHandle);

        // Joint velocity command interface
        hardware_interface::JointHandle jointVelocityHandle(jointStateHandle, &m_vel_cmd[i]);
        m_jointVelocity_interface.registerHandle(jointVelocityHandle);

	//Joint position command interface
	hardware_interface::JointHandle jointPositionHandle(jointStateHandle, &m_pos_cmd[i]);
	m_jointPosition_interface.registerHandle(jointPositionHandle);

    }

    registerInterface(&m_jointState_interface);
    registerInterface(&m_jointVelocity_interface);
    registerInterface(&m_jointPosition_interface);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Phantom_vrepHW::read()
{
    for (int i = 0; i < MR_JOINTS_NUM; ++i)
    {
        float pos,
              vel,
              eff;

        if (simGetJointPosition(m_vrepJointsHandle[i], &pos) == -1 ||
            simGetObjectFloatParameter(m_vrepJointsHandle[i], 2012, &vel) == -1 || // Velocity.
            simGetJointForce(m_vrepJointsHandle[i], &eff) == -1)
        {
            ROS_ERROR_STREAM("MR robot interface not able to get state for '" << sm_jointsName[i] << "'." << std::endl);

            return false;
        }

        m_pos[i] = pos;
        m_vel[i] = vel;
        m_eff[i] = eff;
    }

    return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool Phantom_vrepHW::write()
{
    for (int i = 0; i < MR_JOINTS_NUM; ++i){
      //====== Velocity Control =========

      /*if (simSetJointTargetVelocity(m_vrepJointsHandle[i], m_vel_cmd[i]) == -1) {
	ROS_ERROR_STREAM("MR robot interface not able to get state for '" << sm_jointsName[i] << "'." << std::endl);
	return false;
      }
      */

      //===== Position Control =======
      if (simSetJointTargetPosition(m_vrepJointsHandle[i], m_pos_cmd[i]) == -1) {
	ROS_ERROR_STREAM("MR robot interface not able to get state for '" << sm_jointsName[i] << "'." << std::endl);
	return false;
      }
    }
    return true;
}

} // namespace MR.
<|endoftext|>


<commit_before>/*
Highly Optimized Object-Oriented Molecular Dynamics (HOOMD) Open
Source Software License
Copyright (c) 2008 Ames Laboratory Iowa State University
All rights reserved.

Redistribution and use of HOOMD, in source and binary forms, with or
without modification, are permitted, provided that the following
conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names HOOMD's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND
CONTRIBUTORS ``AS IS''  AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS  BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/

// $Id$
// $URL$

/*! \file LJForceComputeGPU.cc
	\brief Defines the LJForceComputeGPU class
*/

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4103 4244 )
#endif

#include "LJForceComputeGPU.h"
#include "cuda_runtime.h"

#include <stdexcept>

#include <boost/python.hpp>
using namespace boost::python;

#include <boost/bind.hpp>

using namespace boost;
using namespace std;

/*! \param sysdef System to compute forces on
 	\param nlist Neighborlist to use for computing the forces
	\param r_cut Cuttoff radius beyond which the force is 0
	\param slj	 Determines if Diameter Shifted LJ potential is being used
	\post memory is allocated and all parameters lj1 and lj2 are set to 0.0
	\note The LJForceComputeGPU does not own the Neighborlist, the caller should
		delete the neighborlist when done.
*/
LJForceComputeGPU::LJForceComputeGPU(boost::shared_ptr<SystemDefinition> sysdef, boost::shared_ptr<NeighborList> nlist, Scalar r_cut)
	: LJForceCompute(sysdef, nlist, r_cut)
	{
	// can't run on the GPU if there aren't any GPUs in the execution configuration
	if (exec_conf.gpu.size() == 0)
		{
		cerr << endl << "***Error! Creating a LJForceComputeGPU with no GPU in the execution configuration" << endl << endl;
		throw std::runtime_error("Error initializing LJForceComputeGPU");
		}

	if (m_ntypes > 44)
		{
		cerr << endl << "***Error! LJForceComputeGPU cannot handle " << m_ntypes << " types" << endl << endl;
		throw runtime_error("Error initializing LJForceComputeGPU");
		}

	// default block size is the highest performance in testing on different hardware
	// choose based on compute capability of the device
	cudaDeviceProp deviceProp;
	int dev;
	exec_conf.gpu[0]->call(bind(cudaGetDevice, &dev));
	exec_conf.gpu[0]->call(bind(cudaGetDeviceProperties, &deviceProp, dev));
	if (deviceProp.major == 1 && deviceProp.minor == 0)
		m_block_size = 320;
	else if (deviceProp.major == 1 && deviceProp.minor == 1)
		m_block_size = 256;
	else if (deviceProp.major == 1 && deviceProp.minor < 4)
		m_block_size = 384;
	else
		{
		cout << "***Warning! Unknown compute " << deviceProp.major << "." << deviceProp.minor << " when tuning block size for LJForceComputeGPU" << endl;
		m_block_size = 64;
		}

	// ulf workaround setup
	#ifndef DISABLE_ULF_WORKAROUND
	// the ULF workaround is needed on GTX280 and older GPUS
	// it is not needed on C1060, S1070, GTX285, GTX295, and (hopefully) newer ones
	m_ulf_workaround = true;

	if (deviceProp.major == 1 && deviceProp.minor >= 3)
		m_ulf_workaround = false;
	if (string(deviceProp.name) == "GTX 280")
		m_ulf_workaround = true;

	if (m_ulf_workaround)
		cout << "Notice: ULF bug workaround enabled for LJForceComputeGPU" << endl;
	#else
	m_ulf_workaround = false;
	#endif
/*
	if (m_slj) cout << "Notice: Using Diameter-Shifted LJ Pair Potential for LJForceComputeGPU" << endl;
	else cout << "Diameter-Shifted LJ Pair Potential is NOT set for LJForceComputeGPU" << endl;
*/
	// allocate the coeff data on the GPU
	int nbytes = sizeof(float2)*m_pdata->getNTypes()*m_pdata->getNTypes();

	d_coeffs.resize(exec_conf.gpu.size());
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		{
		exec_conf.gpu[cur_gpu]->setTag(__FILE__, __LINE__);
		exec_conf.gpu[cur_gpu]->call(bind(cudaMalloc, (void **)((void *)&d_coeffs[cur_gpu]), nbytes));
		assert(d_coeffs[cur_gpu]);
		exec_conf.gpu[cur_gpu]->call(bind(cudaMemset, (void *)d_coeffs[cur_gpu], 0, nbytes));
		}
	// allocate the coeff data on the CPU
	h_coeffs = new float2[m_pdata->getNTypes()*m_pdata->getNTypes()];
	}

LJForceComputeGPU::~LJForceComputeGPU()
	{
	// free the coefficients on the GPU
	exec_conf.tagAll(__FILE__, __LINE__);
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		{
		assert(d_coeffs[cur_gpu]);
		exec_conf.gpu[cur_gpu]->call(bind(cudaFree, (void *)d_coeffs[cur_gpu]));
		}
	delete[] h_coeffs;
	}

/*! \param block_size Size of the block to run on the device
	Performance of the code may be dependant on the block size run
	on the GPU. \a block_size should be set to be a multiple of 32.
*/
void LJForceComputeGPU::setBlockSize(int block_size)
	{
	m_block_size = block_size;
	}

/*! \post The parameters \a lj1 and \a lj2 are set for the pairs \a typ1, \a typ2 and \a typ2, \a typ1.
	\note \a lj? are low level parameters used in the calculation. In order to specify
	these for a normal lennard jones formula (with alpha), they should be set to the following.
	- \a lj1 = 4.0 * epsilon * pow(sigma,12.0)
	- \a lj2 = alpha * 4.0 * epsilon * pow(sigma,6.0);

	Setting the parameters for typ1,typ2 automatically sets the same parameters for typ2,typ1: there
	is no need to call this funciton for symmetric pairs. Any pairs that this function is not called
	for will have lj1 and lj2 set to 0.0.

	\param typ1 Specifies one type of the pair
	\param typ2 Specifies the second type of the pair
	\param lj1 First parameter used to calcluate forces
	\param lj2 Second parameter used to calculate forces
*/
void LJForceComputeGPU::setParams(unsigned int typ1, unsigned int typ2, Scalar lj1, Scalar lj2)
	{
	assert(h_coeffs);
	if (typ1 >= m_ntypes || typ2 >= m_ntypes)
		{
		cerr << endl << "***Error! Trying to set LJ params for a non existant type! " << typ1 << "," << typ2 << endl << endl;
		throw runtime_error("LJForceComputeGpu::setParams argument error");
		}

	// set coeffs in both symmetric positions in the matrix
	h_coeffs[typ1*m_pdata->getNTypes() + typ2] = make_float2(lj1, lj2);
	h_coeffs[typ2*m_pdata->getNTypes() + typ1] = make_float2(lj1, lj2);

	int nbytes = sizeof(float2)*m_pdata->getNTypes()*m_pdata->getNTypes();
	exec_conf.tagAll(__FILE__, __LINE__);
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		exec_conf.gpu[cur_gpu]->call(bind(cudaMemcpy, d_coeffs[cur_gpu], h_coeffs, nbytes, cudaMemcpyHostToDevice));
	}

/*! \post The lennard jones forces are computed for the given timestep on the GPU.
	The neighborlist's compute method is called to ensure that it is up to date
	before forces are computed.
 	\param timestep Current time step of the simulation

 	Calls gpu_compute_lj_forces to do the dirty work.
*/
void LJForceComputeGPU::computeForces(unsigned int timestep)
	{
	// start by updating the neighborlist
	m_nlist->compute(timestep);

	// start the profile
	if (m_prof) m_prof->push(exec_conf, "LJ pair");

	// The GPU implementation CANNOT handle a half neighborlist, error out now
	bool third_law = m_nlist->getStorageMode() == NeighborList::half;
	if (third_law)
		{
		cerr << endl << "***Error! LJForceComputeGPU cannot handle a half neighborlist" << endl << endl;
		throw runtime_error("Error computing forces in LJForceComputeGPU");
		}

	// access the neighbor list, which just selects the neighborlist into the device's memory, copying
	// it there if needed
	vector<gpu_nlist_array>& nlist = m_nlist->getListGPU();

	// access the particle data
	vector<gpu_pdata_arrays>& pdata = m_pdata->acquireReadOnlyGPU();
	gpu_boxsize box = m_pdata->getBoxGPU();

	// run the kernel on all GPUs in parallel
	exec_conf.tagAll(__FILE__, __LINE__);
	lj_options opt;
	opt.r_cutsq = m_r_cut * m_r_cut;
	opt.block_size = m_block_size;
	opt.xplor_fraction = m_xplor_fraction;
	opt.ulf_workaround = m_ulf_workaround;
	opt.shift_mode = m_shift_mode;
	opt.slj = m_slj;

	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		exec_conf.gpu[cur_gpu]->callAsync(bind(gpu_compute_lj_forces, m_gpu_forces[cur_gpu].d_data, pdata[cur_gpu], box, nlist[cur_gpu], d_coeffs[cur_gpu], m_pdata->getNTypes(), opt));
	exec_conf.syncAll();

	m_pdata->release();

	// the force data is now only up to date on the gpu
	m_data_location = gpu;

	Scalar avg_neigh = m_nlist->estimateNNeigh();
	int64_t n_calc = int64_t(avg_neigh * m_pdata->getN());
	int64_t mem_transfer = m_pdata->getN() * (4 + 16 + 20) + n_calc * (4 + 16);
	int64_t flops = n_calc * (3+12+5+2+2+6+3+2+7);
	if (m_slj) {
		flops += 11;
		mem_transfer += m_pdata->getN() * (4) + n_calc * (4)
		}
	if (m_prof) m_prof->pop(exec_conf, flops, mem_transfer);
	}

void export_LJForceComputeGPU()
	{
	class_<LJForceComputeGPU, boost::shared_ptr<LJForceComputeGPU>, bases<LJForceCompute>, boost::noncopyable >
		("LJForceComputeGPU", init< boost::shared_ptr<SystemDefinition>, boost::shared_ptr<NeighborList>, Scalar >())
		.def("setBlockSize", &LJForceComputeGPU::setBlockSize)
		;
	}

#ifdef WIN32
#pragma warning( pop )
#endif

<commit_msg>left off a semi-colon<commit_after>/*
Highly Optimized Object-Oriented Molecular Dynamics (HOOMD) Open
Source Software License
Copyright (c) 2008 Ames Laboratory Iowa State University
All rights reserved.

Redistribution and use of HOOMD, in source and binary forms, with or
without modification, are permitted, provided that the following
conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names HOOMD's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND
CONTRIBUTORS ``AS IS''  AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS  BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/

// $Id$
// $URL$

/*! \file LJForceComputeGPU.cc
	\brief Defines the LJForceComputeGPU class
*/

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4103 4244 )
#endif

#include "LJForceComputeGPU.h"
#include "cuda_runtime.h"

#include <stdexcept>

#include <boost/python.hpp>
using namespace boost::python;

#include <boost/bind.hpp>

using namespace boost;
using namespace std;

/*! \param sysdef System to compute forces on
 	\param nlist Neighborlist to use for computing the forces
	\param r_cut Cuttoff radius beyond which the force is 0
	\param slj	 Determines if Diameter Shifted LJ potential is being used
	\post memory is allocated and all parameters lj1 and lj2 are set to 0.0
	\note The LJForceComputeGPU does not own the Neighborlist, the caller should
		delete the neighborlist when done.
*/
LJForceComputeGPU::LJForceComputeGPU(boost::shared_ptr<SystemDefinition> sysdef, boost::shared_ptr<NeighborList> nlist, Scalar r_cut)
	: LJForceCompute(sysdef, nlist, r_cut)
	{
	// can't run on the GPU if there aren't any GPUs in the execution configuration
	if (exec_conf.gpu.size() == 0)
		{
		cerr << endl << "***Error! Creating a LJForceComputeGPU with no GPU in the execution configuration" << endl << endl;
		throw std::runtime_error("Error initializing LJForceComputeGPU");
		}

	if (m_ntypes > 44)
		{
		cerr << endl << "***Error! LJForceComputeGPU cannot handle " << m_ntypes << " types" << endl << endl;
		throw runtime_error("Error initializing LJForceComputeGPU");
		}

	// default block size is the highest performance in testing on different hardware
	// choose based on compute capability of the device
	cudaDeviceProp deviceProp;
	int dev;
	exec_conf.gpu[0]->call(bind(cudaGetDevice, &dev));
	exec_conf.gpu[0]->call(bind(cudaGetDeviceProperties, &deviceProp, dev));
	if (deviceProp.major == 1 && deviceProp.minor == 0)
		m_block_size = 320;
	else if (deviceProp.major == 1 && deviceProp.minor == 1)
		m_block_size = 256;
	else if (deviceProp.major == 1 && deviceProp.minor < 4)
		m_block_size = 384;
	else
		{
		cout << "***Warning! Unknown compute " << deviceProp.major << "." << deviceProp.minor << " when tuning block size for LJForceComputeGPU" << endl;
		m_block_size = 64;
		}

	// ulf workaround setup
	#ifndef DISABLE_ULF_WORKAROUND
	// the ULF workaround is needed on GTX280 and older GPUS
	// it is not needed on C1060, S1070, GTX285, GTX295, and (hopefully) newer ones
	m_ulf_workaround = true;

	if (deviceProp.major == 1 && deviceProp.minor >= 3)
		m_ulf_workaround = false;
	if (string(deviceProp.name) == "GTX 280")
		m_ulf_workaround = true;

	if (m_ulf_workaround)
		cout << "Notice: ULF bug workaround enabled for LJForceComputeGPU" << endl;
	#else
	m_ulf_workaround = false;
	#endif
/*
	if (m_slj) cout << "Notice: Using Diameter-Shifted LJ Pair Potential for LJForceComputeGPU" << endl;
	else cout << "Diameter-Shifted LJ Pair Potential is NOT set for LJForceComputeGPU" << endl;
*/
	// allocate the coeff data on the GPU
	int nbytes = sizeof(float2)*m_pdata->getNTypes()*m_pdata->getNTypes();

	d_coeffs.resize(exec_conf.gpu.size());
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		{
		exec_conf.gpu[cur_gpu]->setTag(__FILE__, __LINE__);
		exec_conf.gpu[cur_gpu]->call(bind(cudaMalloc, (void **)((void *)&d_coeffs[cur_gpu]), nbytes));
		assert(d_coeffs[cur_gpu]);
		exec_conf.gpu[cur_gpu]->call(bind(cudaMemset, (void *)d_coeffs[cur_gpu], 0, nbytes));
		}
	// allocate the coeff data on the CPU
	h_coeffs = new float2[m_pdata->getNTypes()*m_pdata->getNTypes()];
	}

LJForceComputeGPU::~LJForceComputeGPU()
	{
	// free the coefficients on the GPU
	exec_conf.tagAll(__FILE__, __LINE__);
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		{
		assert(d_coeffs[cur_gpu]);
		exec_conf.gpu[cur_gpu]->call(bind(cudaFree, (void *)d_coeffs[cur_gpu]));
		}
	delete[] h_coeffs;
	}

/*! \param block_size Size of the block to run on the device
	Performance of the code may be dependant on the block size run
	on the GPU. \a block_size should be set to be a multiple of 32.
*/
void LJForceComputeGPU::setBlockSize(int block_size)
	{
	m_block_size = block_size;
	}

/*! \post The parameters \a lj1 and \a lj2 are set for the pairs \a typ1, \a typ2 and \a typ2, \a typ1.
	\note \a lj? are low level parameters used in the calculation. In order to specify
	these for a normal lennard jones formula (with alpha), they should be set to the following.
	- \a lj1 = 4.0 * epsilon * pow(sigma,12.0)
	- \a lj2 = alpha * 4.0 * epsilon * pow(sigma,6.0);

	Setting the parameters for typ1,typ2 automatically sets the same parameters for typ2,typ1: there
	is no need to call this funciton for symmetric pairs. Any pairs that this function is not called
	for will have lj1 and lj2 set to 0.0.

	\param typ1 Specifies one type of the pair
	\param typ2 Specifies the second type of the pair
	\param lj1 First parameter used to calcluate forces
	\param lj2 Second parameter used to calculate forces
*/
void LJForceComputeGPU::setParams(unsigned int typ1, unsigned int typ2, Scalar lj1, Scalar lj2)
	{
	assert(h_coeffs);
	if (typ1 >= m_ntypes || typ2 >= m_ntypes)
		{
		cerr << endl << "***Error! Trying to set LJ params for a non existant type! " << typ1 << "," << typ2 << endl << endl;
		throw runtime_error("LJForceComputeGpu::setParams argument error");
		}

	// set coeffs in both symmetric positions in the matrix
	h_coeffs[typ1*m_pdata->getNTypes() + typ2] = make_float2(lj1, lj2);
	h_coeffs[typ2*m_pdata->getNTypes() + typ1] = make_float2(lj1, lj2);

	int nbytes = sizeof(float2)*m_pdata->getNTypes()*m_pdata->getNTypes();
	exec_conf.tagAll(__FILE__, __LINE__);
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		exec_conf.gpu[cur_gpu]->call(bind(cudaMemcpy, d_coeffs[cur_gpu], h_coeffs, nbytes, cudaMemcpyHostToDevice));
	}

/*! \post The lennard jones forces are computed for the given timestep on the GPU.
	The neighborlist's compute method is called to ensure that it is up to date
	before forces are computed.
 	\param timestep Current time step of the simulation

 	Calls gpu_compute_lj_forces to do the dirty work.
*/
void LJForceComputeGPU::computeForces(unsigned int timestep)
	{
	// start by updating the neighborlist
	m_nlist->compute(timestep);

	// start the profile
	if (m_prof) m_prof->push(exec_conf, "LJ pair");

	// The GPU implementation CANNOT handle a half neighborlist, error out now
	bool third_law = m_nlist->getStorageMode() == NeighborList::half;
	if (third_law)
		{
		cerr << endl << "***Error! LJForceComputeGPU cannot handle a half neighborlist" << endl << endl;
		throw runtime_error("Error computing forces in LJForceComputeGPU");
		}

	// access the neighbor list, which just selects the neighborlist into the device's memory, copying
	// it there if needed
	vector<gpu_nlist_array>& nlist = m_nlist->getListGPU();

	// access the particle data
	vector<gpu_pdata_arrays>& pdata = m_pdata->acquireReadOnlyGPU();
	gpu_boxsize box = m_pdata->getBoxGPU();

	// run the kernel on all GPUs in parallel
	exec_conf.tagAll(__FILE__, __LINE__);
	lj_options opt;
	opt.r_cutsq = m_r_cut * m_r_cut;
	opt.block_size = m_block_size;
	opt.xplor_fraction = m_xplor_fraction;
	opt.ulf_workaround = m_ulf_workaround;
	opt.shift_mode = m_shift_mode;
	opt.slj = m_slj;

	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		exec_conf.gpu[cur_gpu]->callAsync(bind(gpu_compute_lj_forces, m_gpu_forces[cur_gpu].d_data, pdata[cur_gpu], box, nlist[cur_gpu], d_coeffs[cur_gpu], m_pdata->getNTypes(), opt));
	exec_conf.syncAll();

	m_pdata->release();

	// the force data is now only up to date on the gpu
	m_data_location = gpu;

	Scalar avg_neigh = m_nlist->estimateNNeigh();
	int64_t n_calc = int64_t(avg_neigh * m_pdata->getN());
	int64_t mem_transfer = m_pdata->getN() * (4 + 16 + 20) + n_calc * (4 + 16);
	int64_t flops = n_calc * (3+12+5+2+2+6+3+2+7);
	if (m_slj) {
		flops += 11;
		mem_transfer += m_pdata->getN() * (4) + n_calc * (4);
		}
	if (m_prof) m_prof->pop(exec_conf, flops, mem_transfer);
	}

void export_LJForceComputeGPU()
	{
	class_<LJForceComputeGPU, boost::shared_ptr<LJForceComputeGPU>, bases<LJForceCompute>, boost::noncopyable >
		("LJForceComputeGPU", init< boost::shared_ptr<SystemDefinition>, boost::shared_ptr<NeighborList>, Scalar >())
		.def("setBlockSize", &LJForceComputeGPU::setBlockSize)
		;
	}

#ifdef WIN32
#pragma warning( pop )
#endif

<|endoftext|>


<commit_before><commit_msg>sensord cleanup (#2111)<commit_after><|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Tests for writing out the basic record types.
//
// TODO(schwehr): Does this have to free what was read?

#include <sys/stat.h>

#include <memory>
#include <string>

#include "gsf.h"
#include "gsf_ft.h"
#include "gsf_test_util.h"
#include "gtest/gtest.h"

// #include <iostream>  // TODO(schwehr): Remove.
// using namespace std;  // TODO(schwehr): Remove.

using std::unique_ptr;

namespace generic_sensor_format {
namespace test {
namespace {

// TODO(schwehr): Switch to make_unique when C++14 is available on Travis-CI.
template <typename T, typename... Args>
std::unique_ptr<T> MakeUnique(Args &&... args) {
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

TEST(GsfWriteSimple, HeaderOnly) {
  int handle;
  char filename[] = "header-only.gsf";
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));
  ASSERT_EQ(0, gsfClose(handle));
  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(20, buf.st_size);
}

// TODO(schwehr): GSF_RECORD_SWATH_BATHYMETRY_PING.
// TODO(schwehr): GSF_RECORD_SOUND_VELOCITY_PROFILE
// TODO(schwehr): GSF_RECORD_PROCESSING_PARAMETERS
// TODO(schwehr): GSF_RECORD_SENSOR_PARAMETERS

void ValidateWriteComment(const char *filename, bool checksum,
                          int expected_write_size, const char *comment,
                          int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 20);
  ASSERT_NE(nullptr, comment);
  ASSERT_GE(expected_file_size, 40);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_COMMENT, 0};
  gsfRecords record;
  record.comment = GsfComment({1, 2}, comment);
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));

  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_COMMENT, data_id.recordID);
  VerifyComment(record.comment, read_record.comment);
}

TEST(GsfWriteSimple, CommentEmpty) {
  char filename[] = "comment-empty.gsf";
  char comment[] = "";
  ValidateWriteComment(filename, false, 20, comment, 40);
}

TEST(GsfWriteSimple, CommentEmptyChecksum) {
  char filename[] = "comment-empty-checksum.gsf";
  char comment[] = "";
  ValidateWriteComment(filename, true, 24, comment, 44);
}

TEST(GsfWriteSimple, CommentUpTo5) {
  char filename1[] = "comment-1.gsf";
  char comment1[] = "a";
  ValidateWriteComment(filename1, false, 24, comment1, 44);
  char filename2[] = "comment-2.gsf";
  char comment2[] = "ab";
  ValidateWriteComment(filename2, false, 24, comment2, 44);
  char filename3[] = "comment-3.gsf";
  char comment3[] = "abc";
  ValidateWriteComment(filename3, false, 24, comment3, 44);
  char filename4[] = "comment-4.gsf";
  char comment4[] = "abcd";
  ValidateWriteComment(filename4, false, 24, comment4, 44);
  char filename5[] = "comment-5.gsf";
  char comment5[] = "abcde";
  ValidateWriteComment(filename5, false, 28, comment5, 48);
}

TEST(GsfWriteSimple, CommentLarge) {
  char filename[] = "comment-large.gsf";
  char comment[] =
      "ab"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "de";
  ValidateWriteComment(filename, false, 656, comment, 676);
}

void ValidateWriteHistory(const char *filename, bool checksum,
                          int expected_write_size, const char *host_name,
                          const char *operator_name, const char *command_line,
                          const char *comment, int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 28);
  ASSERT_NE(nullptr, host_name);
  ASSERT_LE(strlen(host_name), GSF_HOST_NAME_LENGTH);
  ASSERT_NE(nullptr, operator_name);
  ASSERT_LE(strlen(operator_name), GSF_OPERATOR_LENGTH);
  ASSERT_NE(nullptr, command_line);
  ASSERT_NE(nullptr, comment);
  ASSERT_GE(expected_file_size, 48);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_HISTORY, 0};
  gsfRecords record;
  record.history =
      GsfHistory({1, 2}, host_name, operator_name, command_line, comment);
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));
  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_HISTORY, data_id.recordID);
  VerifyHistory(record.history, read_record.history);
}

TEST(GsfWriteSimple, History) {
  ValidateWriteHistory("history-empty.gsf", false, 28, "", "", "", "", 48);
  ValidateWriteHistory("history-empty-checksum.gsf", true, 32, "", "", "", "",
                       52);
  ValidateWriteHistory("history-1.gsf", false, 32, "a", "b", "c", "d", 52);
  ValidateWriteHistory("history-longer.gsf", false, 40, "ab", "cde", "fghi",
                       "jklm", 60);
}

// TODO(schwehr): GSF_RECORD_NAVIGATION_ERROR
// TODO(schwehr): GSF_RECORD_SWATH_BATHY_SUMMARY
// TODO(schwehr): GSF_RECORD_SINGLE_BEAM_PING
// TODO(schwehr): GSF_RECORD_HV_NAVIGATION_ERROR

void ValidateWriteAttitude(const char *filename, bool checksum,
                           int expected_write_size, const gsfAttitude &attitude,
                           int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 20);
  ASSERT_GE(expected_file_size, 40);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_ATTITUDE, 0};
  gsfRecords record;
  record.attitude = attitude;
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));
  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_ATTITUDE, data_id.recordID);
  VerifyAttitude(record.attitude, read_record.attitude);
}

unique_ptr<gsfAttitude> WriteAttitudeAndReturnRead(const char *filename,
                                                   const gsfAttitude &attitude,
                                                   int *file_size,
                                                   int *num_bytes) {
  // TODO(schwehr): Add error handling.
  int handle;
  if (0 != gsfOpen(filename, GSF_CREATE, &handle)) {
    return nullptr;
  }

  gsfDataID data_id = {false, 0, GSF_RECORD_ATTITUDE, 0};
  gsfRecords record;
  record.attitude = attitude;
  if (gsfWrite(handle, &data_id, &record) < 20) {
    // Do not check return since we are already in trouble.
    gsfClose(handle);
    return nullptr;
  }
  if (0 != gsfClose(handle)) {
    return nullptr;
  }

  struct stat buf;
  if (0 != stat(filename, &buf)) {
    return nullptr;
  }
  *file_size = buf.st_size;

  if (0 != gsfOpen(filename, GSF_READONLY, &handle)) {
    return nullptr;
  }

  gsfRecords read_record;
  *num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  if (*num_bytes < 24) {
    return nullptr;
  }

  return MakeUnique<gsfAttitude>(read_record.attitude);
}

TEST(GsfWriteSimple, AttitudeEmpty) {
  const struct timespec times[] = {{3, 4}};
  const gsfAttitude attitude =
      GsfAttitude(0, times, nullptr, nullptr, nullptr, nullptr);
  ValidateWriteAttitude("attitude-empty.gsf", false, 20, attitude, 40);
}

TEST(GsfWriteSimple, AttitudeLength1Zeros) {
  const struct timespec times0[] = {{5, 6}};
  const double data0[] = {0.0};
  const gsfAttitude attitude0 =
      GsfAttitude(1, times0, data0, data0, data0, data0);
  ValidateWriteAttitude("attitude-length1-0.gsf", false, 28, attitude0, 48);
}

TEST(GsfWriteSimple, AttitudeLength1) {
  const struct timespec times[] = {{7, 8}};
  const double pitch[] = {1.2};
  const double roll[] = {2.2};
  const double heave[] = {3.3};
  const double heading[] = {4.4};
  const gsfAttitude attitude =
      GsfAttitude(1, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length1-1-checksum.gsf", true, 32, attitude,
                        52);
}

TEST(GsfWriteSimple, AttitudeLength1Negative) {
  const struct timespec times[] = {{9, 10}};
  const double pitch[] = {-1.2};
  const double roll[] = {-3.4};
  const double heave[] = {-5.6};
  // Stored as a int16 with value * 100, so cannot test negative.
  const double heading[] = {7.8};
  const gsfAttitude attitude =
      GsfAttitude(1, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length1-neg.gsf", false, 28, attitude, 48);
}

TEST(GsfWriteSimple, AttitudeRounding) {
  const struct timespec times[] = {{11, 12}};
  const double pitch[] = {-1.002};
  const double roll[] = {-2.0052};
  const double heave[] = {3.0045};
  const double heading[] = {4.0052};
  const gsfAttitude src = GsfAttitude(1, times, pitch, roll, heave, heading);
  int file_size;
  int num_bytes;
  const unique_ptr<gsfAttitude> dst = WriteAttitudeAndReturnRead(
      "attitude-round.gsf", src, &file_size, &num_bytes);
  ASSERT_NE(nullptr, dst);
  ASSERT_EQ(48, file_size);
  ASSERT_EQ(28, num_bytes);

  const double pitch2[] = {-1.00};
  const double roll2[] = {-2.01};
  const double heave2[] = {3.00};
  const double heading2[] = {4.01};
  const gsfAttitude expected =
      GsfAttitude(1, times, pitch2, roll2, heave2, heading2);
  VerifyAttitude(expected, *dst);
}

TEST(GsfWriteSimple, AttitudeLength2) {
  // TODO(schwehr): Why do the nanoseconds not match?
  const struct timespec times[] = {{1438016822, 0},
                                   {1438016823, 0}};
  const double pitch[] = {1.2, -3.4};
  const double roll[] = {-100.1, 100.2};
  const double heave[] = {-200.2, -200.3};
  // Stored as a int16 with value * 100, so cannot test negative.
  const double heading[] = {181, 359};
  const gsfAttitude attitude =
      GsfAttitude(2, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length2.gsf", false, 40, attitude, 60);
}

}  // namespace
}  // namespace test
}  // namespace generic_sensor_format
<commit_msg>Make a tv_nsec time big enough to not be lost in precision conversion<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Tests for writing out the basic record types.
//
// TODO(schwehr): Does this have to free what was read?

#include <sys/stat.h>

#include <memory>
#include <string>

#include "gsf.h"
#include "gsf_ft.h"
#include "gsf_test_util.h"
#include "gtest/gtest.h"

// #include <iostream>  // TODO(schwehr): Remove.
// using namespace std;  // TODO(schwehr): Remove.

using std::unique_ptr;

namespace generic_sensor_format {
namespace test {
namespace {

// TODO(schwehr): Switch to make_unique when C++14 is available on Travis-CI.
template <typename T, typename... Args>
std::unique_ptr<T> MakeUnique(Args &&... args) {
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

TEST(GsfWriteSimple, HeaderOnly) {
  int handle;
  char filename[] = "header-only.gsf";
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));
  ASSERT_EQ(0, gsfClose(handle));
  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(20, buf.st_size);
}

// TODO(schwehr): GSF_RECORD_SWATH_BATHYMETRY_PING.
// TODO(schwehr): GSF_RECORD_SOUND_VELOCITY_PROFILE
// TODO(schwehr): GSF_RECORD_PROCESSING_PARAMETERS
// TODO(schwehr): GSF_RECORD_SENSOR_PARAMETERS

void ValidateWriteComment(const char *filename, bool checksum,
                          int expected_write_size, const char *comment,
                          int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 20);
  ASSERT_NE(nullptr, comment);
  ASSERT_GE(expected_file_size, 40);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_COMMENT, 0};
  gsfRecords record;
  record.comment = GsfComment({1, 2}, comment);
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));

  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_COMMENT, data_id.recordID);
  VerifyComment(record.comment, read_record.comment);
}

TEST(GsfWriteSimple, CommentEmpty) {
  char filename[] = "comment-empty.gsf";
  char comment[] = "";
  ValidateWriteComment(filename, false, 20, comment, 40);
}

TEST(GsfWriteSimple, CommentEmptyChecksum) {
  char filename[] = "comment-empty-checksum.gsf";
  char comment[] = "";
  ValidateWriteComment(filename, true, 24, comment, 44);
}

TEST(GsfWriteSimple, CommentUpTo5) {
  char filename1[] = "comment-1.gsf";
  char comment1[] = "a";
  ValidateWriteComment(filename1, false, 24, comment1, 44);
  char filename2[] = "comment-2.gsf";
  char comment2[] = "ab";
  ValidateWriteComment(filename2, false, 24, comment2, 44);
  char filename3[] = "comment-3.gsf";
  char comment3[] = "abc";
  ValidateWriteComment(filename3, false, 24, comment3, 44);
  char filename4[] = "comment-4.gsf";
  char comment4[] = "abcd";
  ValidateWriteComment(filename4, false, 24, comment4, 44);
  char filename5[] = "comment-5.gsf";
  char comment5[] = "abcde";
  ValidateWriteComment(filename5, false, 28, comment5, 48);
}

TEST(GsfWriteSimple, CommentLarge) {
  char filename[] = "comment-large.gsf";
  char comment[] =
      "ab"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "de";
  ValidateWriteComment(filename, false, 656, comment, 676);
}

void ValidateWriteHistory(const char *filename, bool checksum,
                          int expected_write_size, const char *host_name,
                          const char *operator_name, const char *command_line,
                          const char *comment, int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 28);
  ASSERT_NE(nullptr, host_name);
  ASSERT_LE(strlen(host_name), GSF_HOST_NAME_LENGTH);
  ASSERT_NE(nullptr, operator_name);
  ASSERT_LE(strlen(operator_name), GSF_OPERATOR_LENGTH);
  ASSERT_NE(nullptr, command_line);
  ASSERT_NE(nullptr, comment);
  ASSERT_GE(expected_file_size, 48);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_HISTORY, 0};
  gsfRecords record;
  record.history =
      GsfHistory({1, 2}, host_name, operator_name, command_line, comment);
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));
  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_HISTORY, data_id.recordID);
  VerifyHistory(record.history, read_record.history);
}

TEST(GsfWriteSimple, History) {
  ValidateWriteHistory("history-empty.gsf", false, 28, "", "", "", "", 48);
  ValidateWriteHistory("history-empty-checksum.gsf", true, 32, "", "", "", "",
                       52);
  ValidateWriteHistory("history-1.gsf", false, 32, "a", "b", "c", "d", 52);
  ValidateWriteHistory("history-longer.gsf", false, 40, "ab", "cde", "fghi",
                       "jklm", 60);
}

// TODO(schwehr): GSF_RECORD_NAVIGATION_ERROR
// TODO(schwehr): GSF_RECORD_SWATH_BATHY_SUMMARY
// TODO(schwehr): GSF_RECORD_SINGLE_BEAM_PING
// TODO(schwehr): GSF_RECORD_HV_NAVIGATION_ERROR

void ValidateWriteAttitude(const char *filename, bool checksum,
                           int expected_write_size, const gsfAttitude &attitude,
                           int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 20);
  ASSERT_GE(expected_file_size, 40);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_ATTITUDE, 0};
  gsfRecords record;
  record.attitude = attitude;
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));
  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_ATTITUDE, data_id.recordID);
  VerifyAttitude(record.attitude, read_record.attitude);
}

unique_ptr<gsfAttitude> WriteAttitudeAndReturnRead(const char *filename,
                                                   const gsfAttitude &attitude,
                                                   int *file_size,
                                                   int *num_bytes) {
  // TODO(schwehr): Add error handling.
  int handle;
  if (0 != gsfOpen(filename, GSF_CREATE, &handle)) {
    return nullptr;
  }

  gsfDataID data_id = {false, 0, GSF_RECORD_ATTITUDE, 0};
  gsfRecords record;
  record.attitude = attitude;
  if (gsfWrite(handle, &data_id, &record) < 20) {
    // Do not check return since we are already in trouble.
    gsfClose(handle);
    return nullptr;
  }
  if (0 != gsfClose(handle)) {
    return nullptr;
  }

  struct stat buf;
  if (0 != stat(filename, &buf)) {
    return nullptr;
  }
  *file_size = buf.st_size;

  if (0 != gsfOpen(filename, GSF_READONLY, &handle)) {
    return nullptr;
  }

  gsfRecords read_record;
  *num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  if (*num_bytes < 24) {
    return nullptr;
  }

  return MakeUnique<gsfAttitude>(read_record.attitude);
}

TEST(GsfWriteSimple, AttitudeEmpty) {
  const struct timespec times[] = {{3, 4}};
  const gsfAttitude attitude =
      GsfAttitude(0, times, nullptr, nullptr, nullptr, nullptr);
  ValidateWriteAttitude("attitude-empty.gsf", false, 20, attitude, 40);
}

TEST(GsfWriteSimple, AttitudeLength1Zeros) {
  const struct timespec times0[] = {{5, 6}};
  const double data0[] = {0.0};
  const gsfAttitude attitude0 =
      GsfAttitude(1, times0, data0, data0, data0, data0);
  ValidateWriteAttitude("attitude-length1-0.gsf", false, 28, attitude0, 48);
}

TEST(GsfWriteSimple, AttitudeLength1) {
  const struct timespec times[] = {{7, 8}};
  const double pitch[] = {1.2};
  const double roll[] = {2.2};
  const double heave[] = {3.3};
  const double heading[] = {4.4};
  const gsfAttitude attitude =
      GsfAttitude(1, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length1-1-checksum.gsf", true, 32, attitude,
                        52);
}

TEST(GsfWriteSimple, AttitudeLength1Negative) {
  const struct timespec times[] = {{9, 10}};
  const double pitch[] = {-1.2};
  const double roll[] = {-3.4};
  const double heave[] = {-5.6};
  // Stored as a int16 with value * 100, so cannot test negative.
  const double heading[] = {7.8};
  const gsfAttitude attitude =
      GsfAttitude(1, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length1-neg.gsf", false, 28, attitude, 48);
}

TEST(GsfWriteSimple, AttitudeRounding) {
  const struct timespec times[] = {{11, 12}};
  const double pitch[] = {-1.002};
  const double roll[] = {-2.0052};
  const double heave[] = {3.0045};
  const double heading[] = {4.0052};
  const gsfAttitude src = GsfAttitude(1, times, pitch, roll, heave, heading);
  int file_size;
  int num_bytes;
  const unique_ptr<gsfAttitude> dst = WriteAttitudeAndReturnRead(
      "attitude-round.gsf", src, &file_size, &num_bytes);
  ASSERT_NE(nullptr, dst);
  ASSERT_EQ(48, file_size);
  ASSERT_EQ(28, num_bytes);

  const double pitch2[] = {-1.00};
  const double roll2[] = {-2.01};
  const double heave2[] = {3.00};
  const double heading2[] = {4.01};
  const gsfAttitude expected =
      GsfAttitude(1, times, pitch2, roll2, heave2, heading2);
  VerifyAttitude(expected, *dst);
}

TEST(GsfWriteSimple, AttitudeLength2) {
  // TODO(schwehr): Why do the nanoseconds not match?
  const struct timespec times[] = {{1438016822, 2},
                                   {1438016823, 90000000}};
  const double pitch[] = {1.2, -3.4};
  const double roll[] = {-100.1, 100.2};
  const double heave[] = {-200.2, -200.3};
  // Stored as a int16 with value * 100, so cannot test negative.
  const double heading[] = {181, 359};
  const gsfAttitude attitude =
      GsfAttitude(2, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length2.gsf", false, 40, attitude, 60);
}

}  // namespace
}  // namespace test
}  // namespace generic_sensor_format
<|endoftext|>


<commit_before>/*
Highly Optimized Object-Oriented Molecular Dynamics (HOOMD) Open
Source Software License
Copyright (c) 2008 Ames Laboratory Iowa State University
All rights reserved.

Redistribution and use of HOOMD, in source and binary forms, with or
without modification, are permitted, provided that the following
conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names HOOMD's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND
CONTRIBUTORS ``AS IS''  AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS  BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/

// $Id$
// $URL$

/*! \file LJForceComputeGPU.cc
	\brief Defines the LJForceComputeGPU class
*/

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4103 4244 )
#endif

#include "LJForceComputeGPU.h"
#include "cuda_runtime.h"

#include <stdexcept>

#include <boost/python.hpp>
using namespace boost::python;

#include <boost/bind.hpp>

using namespace boost;
using namespace std;

/*! \param pdata Particle Data to compute forces on
 	\param nlist Neighborlist to use for computing the forces
	\param r_cut Cuttoff radius beyond which the force is 0
	\post memory is allocated and all parameters lj1 and lj2 are set to 0.0
	\note The LJForceComputeGPU does not own the Neighborlist, the caller should
		delete the neighborlist when done.
*/
LJForceComputeGPU::LJForceComputeGPU(boost::shared_ptr<ParticleData> pdata, boost::shared_ptr<NeighborList> nlist, Scalar r_cut)
	: LJForceCompute(pdata, nlist, r_cut)
	{
	// can't run on the GPU if there aren't any GPUs in the execution configuration
	if (exec_conf.gpu.size() == 0)
		{
		cerr << endl << "***Error! Creating a LJForceComputeGPU with no GPU in the execution configuration" << endl << endl;
		throw std::runtime_error("Error initializing LJForceComputeGPU");
		}

	if (m_ntypes > 44)
		{
		cerr << endl << "***Error! LJForceComputeGPU cannot handle " << m_ntypes << " types" << endl << endl;
		throw runtime_error("Error initializing LJForceComputeGPU");
		}

	// default block size is the highest performance in testing on different hardware
	// choose based on compute capability of the device
	cudaDeviceProp deviceProp;
	int dev;
	exec_conf.gpu[0]->call(bind(cudaGetDevice, &dev));
	exec_conf.gpu[0]->call(bind(cudaGetDeviceProperties, &deviceProp, dev));
	if (deviceProp.major == 1 && deviceProp.minor == 0)
		m_block_size = 320;
	else if (deviceProp.major == 1 && deviceProp.minor == 1)
		m_block_size = 256;
	else if (deviceProp.major == 1 && deviceProp.minor < 4)
		m_block_size = 352;
	else
		{
		cout << "***Warning! Unknown compute " << deviceProp.major << "." << deviceProp.minor << " when tuning block size for LJForceComputeGPU" << endl;
		m_block_size = 64;
		}

	// ulf workaround setup
	#ifndef DISABLE_ULF_WORKAROUND
	// the ULF workaround is needed on GTX280 and older GPUS
	// it is not needed on C1060, S1070, GTX285, GTX295, and (hopefully) newer ones
	m_ulf_workaround = true;

	if (deviceProp.major == 1 && deviceProp.minor >= 3)
		m_ulf_workaround = false;
	if (string(deviceProp.name) == "GTX 280")
		m_ulf_workaround = true;

	if (m_ulf_workaround)
		cout << "Notice: ULF bug workaround enabled for LJForceComputeGPU" << endl;
	#else
	m_ulf_workaround = false;
	#endif

	// allocate the coeff data on the GPU
	int nbytes = sizeof(float2)*m_pdata->getNTypes()*m_pdata->getNTypes();

	d_coeffs.resize(exec_conf.gpu.size());
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		{
		exec_conf.gpu[cur_gpu]->setTag(__FILE__, __LINE__);
		exec_conf.gpu[cur_gpu]->call(bind(cudaMalloc, (void **)((void *)&d_coeffs[cur_gpu]), nbytes));
		assert(d_coeffs[cur_gpu]);
		exec_conf.gpu[cur_gpu]->call(bind(cudaMemset, (void *)d_coeffs[cur_gpu], 0, nbytes));
		}
	// allocate the coeff data on the CPU
	h_coeffs = new float2[m_pdata->getNTypes()*m_pdata->getNTypes()];
	}

LJForceComputeGPU::~LJForceComputeGPU()
	{
	// free the coefficients on the GPU
	exec_conf.tagAll(__FILE__, __LINE__);
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		{
		assert(d_coeffs[cur_gpu]);
		exec_conf.gpu[cur_gpu]->call(bind(cudaFree, (void *)d_coeffs[cur_gpu]));
		}
	delete[] h_coeffs;
	}

/*! \param block_size Size of the block to run on the device
	Performance of the code may be dependant on the block size run
	on the GPU. \a block_size should be set to be a multiple of 32.
*/
void LJForceComputeGPU::setBlockSize(int block_size)
	{
	m_block_size = block_size;
	}

/*! \post The parameters \a lj1 and \a lj2 are set for the pairs \a typ1, \a typ2 and \a typ2, \a typ1.
	\note \a lj? are low level parameters used in the calculation. In order to specify
	these for a normal lennard jones formula (with alpha), they should be set to the following.
	- \a lj1 = 4.0 * epsilon * pow(sigma,12.0)
	- \a lj2 = alpha * 4.0 * epsilon * pow(sigma,6.0);

	Setting the parameters for typ1,typ2 automatically sets the same parameters for typ2,typ1: there
	is no need to call this funciton for symmetric pairs. Any pairs that this function is not called
	for will have lj1 and lj2 set to 0.0.

	\param typ1 Specifies one type of the pair
	\param typ2 Specifies the second type of the pair
	\param lj1 First parameter used to calcluate forces
	\param lj2 Second parameter used to calculate forces
*/
void LJForceComputeGPU::setParams(unsigned int typ1, unsigned int typ2, Scalar lj1, Scalar lj2)
	{
	assert(h_coeffs);
	if (typ1 >= m_ntypes || typ2 >= m_ntypes)
		{
		cerr << endl << "***Error! Trying to set LJ params for a non existant type! " << typ1 << "," << typ2 << endl << endl;
		throw runtime_error("LJForceComputeGpu::setParams argument error");
		}

	// set coeffs in both symmetric positions in the matrix
	h_coeffs[typ1*m_pdata->getNTypes() + typ2] = make_float2(lj1, lj2);
	h_coeffs[typ2*m_pdata->getNTypes() + typ1] = make_float2(lj1, lj2);

	int nbytes = sizeof(float2)*m_pdata->getNTypes()*m_pdata->getNTypes();
	exec_conf.tagAll(__FILE__, __LINE__);
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		exec_conf.gpu[cur_gpu]->call(bind(cudaMemcpy, d_coeffs[cur_gpu], h_coeffs, nbytes, cudaMemcpyHostToDevice));
	}

/*! \post The lennard jones forces are computed for the given timestep on the GPU.
	The neighborlist's compute method is called to ensure that it is up to date
	before forces are computed.
 	\param timestep Current time step of the simulation

 	Calls gpu_compute_lj_forces to do the dirty work.
*/
void LJForceComputeGPU::computeForces(unsigned int timestep)
	{
	// start by updating the neighborlist
	m_nlist->compute(timestep);

	// start the profile
	if (m_prof) m_prof->push(exec_conf, "LJ pair");

	// The GPU implementation CANNOT handle a half neighborlist, error out now
	bool third_law = m_nlist->getStorageMode() == NeighborList::half;
	if (third_law)
		{
		cerr << endl << "***Error! LJForceComputeGPU cannot handle a half neighborlist" << endl << endl;
		throw runtime_error("Error computing forces in LJForceComputeGPU");
		}

	// access the neighbor list, which just selects the neighborlist into the device's memory, copying
	// it there if needed
	vector<gpu_nlist_array>& nlist = m_nlist->getListGPU();

	// access the particle data
	vector<gpu_pdata_arrays>& pdata = m_pdata->acquireReadOnlyGPU();
	gpu_boxsize box = m_pdata->getBoxGPU();

	// run the kernel on all GPUs in parallel
	exec_conf.tagAll(__FILE__, __LINE__);
	lj_options opt;
	opt.r_cutsq = m_r_cut * m_r_cut;
	opt.block_size = m_block_size;
	opt.xplor_fraction = m_xplor_fraction;
	opt.ulf_workaround = m_ulf_workaround;
	opt.shift_mode = m_shift_mode;

	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		exec_conf.gpu[cur_gpu]->callAsync(bind(gpu_compute_lj_forces, m_gpu_forces[cur_gpu].d_data, pdata[cur_gpu], box, nlist[cur_gpu], d_coeffs[cur_gpu], m_pdata->getNTypes(), opt));
	exec_conf.syncAll();

	m_pdata->release();

	// the force data is now only up to date on the gpu
	m_data_location = gpu;

	Scalar avg_neigh = m_nlist->estimateNNeigh();
	int64_t n_calc = int64_t(avg_neigh * m_pdata->getN());
	int64_t mem_transfer = m_pdata->getN() * (4 + 16 + 20) + n_calc * (4 + 16);
	int64_t flops = n_calc * (3+12+5+2+2+6+3+2+7);
	if (m_prof) m_prof->pop(exec_conf, flops, mem_transfer);
	}

void export_LJForceComputeGPU()
	{
	class_<LJForceComputeGPU, boost::shared_ptr<LJForceComputeGPU>, bases<LJForceCompute>, boost::noncopyable >
		("LJForceComputeGPU", init< boost::shared_ptr<ParticleData>, boost::shared_ptr<NeighborList>, Scalar >())
		.def("setBlockSize", &LJForceComputeGPU::setBlockSize)
		;
	}

#ifdef WIN32
#pragma warning( pop )
#endif

<commit_msg>Fixed a bug where the ULF workaround was not being enabled in GTX 280 when using CUDA 2.2.<commit_after>/*
Highly Optimized Object-Oriented Molecular Dynamics (HOOMD) Open
Source Software License
Copyright (c) 2008 Ames Laboratory Iowa State University
All rights reserved.

Redistribution and use of HOOMD, in source and binary forms, with or
without modification, are permitted, provided that the following
conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names HOOMD's
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND
CONTRIBUTORS ``AS IS''  AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS  BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/

// $Id$
// $URL$

/*! \file LJForceComputeGPU.cc
	\brief Defines the LJForceComputeGPU class
*/

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4103 4244 )
#endif

#include "LJForceComputeGPU.h"
#include "cuda_runtime.h"

#include <stdexcept>

#include <boost/python.hpp>
using namespace boost::python;

#include <boost/bind.hpp>

using namespace boost;
using namespace std;

/*! \param pdata Particle Data to compute forces on
 	\param nlist Neighborlist to use for computing the forces
	\param r_cut Cuttoff radius beyond which the force is 0
	\post memory is allocated and all parameters lj1 and lj2 are set to 0.0
	\note The LJForceComputeGPU does not own the Neighborlist, the caller should
		delete the neighborlist when done.
*/
LJForceComputeGPU::LJForceComputeGPU(boost::shared_ptr<ParticleData> pdata, boost::shared_ptr<NeighborList> nlist, Scalar r_cut)
	: LJForceCompute(pdata, nlist, r_cut)
	{
	// can't run on the GPU if there aren't any GPUs in the execution configuration
	if (exec_conf.gpu.size() == 0)
		{
		cerr << endl << "***Error! Creating a LJForceComputeGPU with no GPU in the execution configuration" << endl << endl;
		throw std::runtime_error("Error initializing LJForceComputeGPU");
		}

	if (m_ntypes > 44)
		{
		cerr << endl << "***Error! LJForceComputeGPU cannot handle " << m_ntypes << " types" << endl << endl;
		throw runtime_error("Error initializing LJForceComputeGPU");
		}

	// default block size is the highest performance in testing on different hardware
	// choose based on compute capability of the device
	cudaDeviceProp deviceProp;
	int dev;
	exec_conf.gpu[0]->call(bind(cudaGetDevice, &dev));
	exec_conf.gpu[0]->call(bind(cudaGetDeviceProperties, &deviceProp, dev));
	if (deviceProp.major == 1 && deviceProp.minor == 0)
		m_block_size = 320;
	else if (deviceProp.major == 1 && deviceProp.minor == 1)
		m_block_size = 256;
	else if (deviceProp.major == 1 && deviceProp.minor < 4)
		m_block_size = 352;
	else
		{
		cout << "***Warning! Unknown compute " << deviceProp.major << "." << deviceProp.minor << " when tuning block size for LJForceComputeGPU" << endl;
		m_block_size = 64;
		}

	// ulf workaround setup
	#ifndef DISABLE_ULF_WORKAROUND
	// the ULF workaround is needed on GTX280 and older GPUS
	// it is not needed on C1060, S1070, GTX285, GTX295, and (hopefully) newer ones
	m_ulf_workaround = true;

	if (deviceProp.major == 1 && deviceProp.minor >= 3)
		m_ulf_workaround = false;
	if (string(deviceProp.name) == "GTX 280")
		m_ulf_workaround = true;
	if (string(deviceProp.name) == "GeForce GTX 280")
		m_ulf_workaround = true;

	if (m_ulf_workaround)
		cout << "Notice: ULF bug workaround enabled for LJForceComputeGPU" << endl;
	#else
	m_ulf_workaround = false;
	#endif

	// allocate the coeff data on the GPU
	int nbytes = sizeof(float2)*m_pdata->getNTypes()*m_pdata->getNTypes();

	d_coeffs.resize(exec_conf.gpu.size());
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		{
		exec_conf.gpu[cur_gpu]->setTag(__FILE__, __LINE__);
		exec_conf.gpu[cur_gpu]->call(bind(cudaMalloc, (void **)((void *)&d_coeffs[cur_gpu]), nbytes));
		assert(d_coeffs[cur_gpu]);
		exec_conf.gpu[cur_gpu]->call(bind(cudaMemset, (void *)d_coeffs[cur_gpu], 0, nbytes));
		}
	// allocate the coeff data on the CPU
	h_coeffs = new float2[m_pdata->getNTypes()*m_pdata->getNTypes()];
	}

LJForceComputeGPU::~LJForceComputeGPU()
	{
	// free the coefficients on the GPU
	exec_conf.tagAll(__FILE__, __LINE__);
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		{
		assert(d_coeffs[cur_gpu]);
		exec_conf.gpu[cur_gpu]->call(bind(cudaFree, (void *)d_coeffs[cur_gpu]));
		}
	delete[] h_coeffs;
	}

/*! \param block_size Size of the block to run on the device
	Performance of the code may be dependant on the block size run
	on the GPU. \a block_size should be set to be a multiple of 32.
*/
void LJForceComputeGPU::setBlockSize(int block_size)
	{
	m_block_size = block_size;
	}

/*! \post The parameters \a lj1 and \a lj2 are set for the pairs \a typ1, \a typ2 and \a typ2, \a typ1.
	\note \a lj? are low level parameters used in the calculation. In order to specify
	these for a normal lennard jones formula (with alpha), they should be set to the following.
	- \a lj1 = 4.0 * epsilon * pow(sigma,12.0)
	- \a lj2 = alpha * 4.0 * epsilon * pow(sigma,6.0);

	Setting the parameters for typ1,typ2 automatically sets the same parameters for typ2,typ1: there
	is no need to call this funciton for symmetric pairs. Any pairs that this function is not called
	for will have lj1 and lj2 set to 0.0.

	\param typ1 Specifies one type of the pair
	\param typ2 Specifies the second type of the pair
	\param lj1 First parameter used to calcluate forces
	\param lj2 Second parameter used to calculate forces
*/
void LJForceComputeGPU::setParams(unsigned int typ1, unsigned int typ2, Scalar lj1, Scalar lj2)
	{
	assert(h_coeffs);
	if (typ1 >= m_ntypes || typ2 >= m_ntypes)
		{
		cerr << endl << "***Error! Trying to set LJ params for a non existant type! " << typ1 << "," << typ2 << endl << endl;
		throw runtime_error("LJForceComputeGpu::setParams argument error");
		}

	// set coeffs in both symmetric positions in the matrix
	h_coeffs[typ1*m_pdata->getNTypes() + typ2] = make_float2(lj1, lj2);
	h_coeffs[typ2*m_pdata->getNTypes() + typ1] = make_float2(lj1, lj2);

	int nbytes = sizeof(float2)*m_pdata->getNTypes()*m_pdata->getNTypes();
	exec_conf.tagAll(__FILE__, __LINE__);
	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		exec_conf.gpu[cur_gpu]->call(bind(cudaMemcpy, d_coeffs[cur_gpu], h_coeffs, nbytes, cudaMemcpyHostToDevice));
	}

/*! \post The lennard jones forces are computed for the given timestep on the GPU.
	The neighborlist's compute method is called to ensure that it is up to date
	before forces are computed.
 	\param timestep Current time step of the simulation

 	Calls gpu_compute_lj_forces to do the dirty work.
*/
void LJForceComputeGPU::computeForces(unsigned int timestep)
	{
	// start by updating the neighborlist
	m_nlist->compute(timestep);

	// start the profile
	if (m_prof) m_prof->push(exec_conf, "LJ pair");

	// The GPU implementation CANNOT handle a half neighborlist, error out now
	bool third_law = m_nlist->getStorageMode() == NeighborList::half;
	if (third_law)
		{
		cerr << endl << "***Error! LJForceComputeGPU cannot handle a half neighborlist" << endl << endl;
		throw runtime_error("Error computing forces in LJForceComputeGPU");
		}

	// access the neighbor list, which just selects the neighborlist into the device's memory, copying
	// it there if needed
	vector<gpu_nlist_array>& nlist = m_nlist->getListGPU();

	// access the particle data
	vector<gpu_pdata_arrays>& pdata = m_pdata->acquireReadOnlyGPU();
	gpu_boxsize box = m_pdata->getBoxGPU();

	// run the kernel on all GPUs in parallel
	exec_conf.tagAll(__FILE__, __LINE__);
	lj_options opt;
	opt.r_cutsq = m_r_cut * m_r_cut;
	opt.block_size = m_block_size;
	opt.xplor_fraction = m_xplor_fraction;
	opt.ulf_workaround = m_ulf_workaround;
	opt.shift_mode = m_shift_mode;

	for (unsigned int cur_gpu = 0; cur_gpu < exec_conf.gpu.size(); cur_gpu++)
		exec_conf.gpu[cur_gpu]->callAsync(bind(gpu_compute_lj_forces, m_gpu_forces[cur_gpu].d_data, pdata[cur_gpu], box, nlist[cur_gpu], d_coeffs[cur_gpu], m_pdata->getNTypes(), opt));
	exec_conf.syncAll();

	m_pdata->release();

	// the force data is now only up to date on the gpu
	m_data_location = gpu;

	Scalar avg_neigh = m_nlist->estimateNNeigh();
	int64_t n_calc = int64_t(avg_neigh * m_pdata->getN());
	int64_t mem_transfer = m_pdata->getN() * (4 + 16 + 20) + n_calc * (4 + 16);
	int64_t flops = n_calc * (3+12+5+2+2+6+3+2+7);
	if (m_prof) m_prof->pop(exec_conf, flops, mem_transfer);
	}

void export_LJForceComputeGPU()
	{
	class_<LJForceComputeGPU, boost::shared_ptr<LJForceComputeGPU>, bases<LJForceCompute>, boost::noncopyable >
		("LJForceComputeGPU", init< boost::shared_ptr<ParticleData>, boost::shared_ptr<NeighborList>, Scalar >())
		.def("setBlockSize", &LJForceComputeGPU::setBlockSize)
		;
	}

#ifdef WIN32
#pragma warning( pop )
#endif

<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Tests for writing out the basic record types.
//
// TODO(schwehr): Does this have to free what was read?

#include <sys/stat.h>

#include <memory>
#include <string>

#include "gsf.h"
#include "gsf_ft.h"
#include "gsf_test_util.h"
#include "gtest/gtest.h"

// #include <iostream>  // TODO(schwehr): Remove.
// using namespace std;  // TODO(schwehr): Remove.

using std::unique_ptr;

namespace generic_sensor_format {
namespace test {
namespace {

// TODO(schwehr): Switch to make_unique when C++14 is available on Travis-CI.
template <typename T, typename... Args>
std::unique_ptr<T> MakeUnique(Args &&... args) {
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

TEST(GsfWriteSimple, HeaderOnly) {
  int handle;
  char filename[] = "header-only.gsf";
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));
  ASSERT_EQ(0, gsfClose(handle));
  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(20, buf.st_size);
}

// TODO(schwehr): GSF_RECORD_SWATH_BATHYMETRY_PING.
// TODO(schwehr): GSF_RECORD_SOUND_VELOCITY_PROFILE
// TODO(schwehr): GSF_RECORD_PROCESSING_PARAMETERS
// TODO(schwehr): GSF_RECORD_SENSOR_PARAMETERS

void ValidateWriteComment(const char *filename, bool checksum,
                          int expected_write_size, const char *comment,
                          int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 20);
  ASSERT_NE(nullptr, comment);
  ASSERT_GE(expected_file_size, 40);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_COMMENT, 0};
  gsfRecords record;
  record.comment = GsfComment({1, 2}, comment);
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));

  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_COMMENT, data_id.recordID);
  VerifyComment(record.comment, read_record.comment);
}

TEST(GsfWriteSimple, CommentEmpty) {
  char filename[] = "comment-empty.gsf";
  char comment[] = "";
  ValidateWriteComment(filename, false, 20, comment, 40);
}

TEST(GsfWriteSimple, CommentEmptyChecksum) {
  char filename[] = "comment-empty-checksum.gsf";
  char comment[] = "";
  ValidateWriteComment(filename, true, 24, comment, 44);
}

TEST(GsfWriteSimple, CommentUpTo5) {
  char filename1[] = "comment-1.gsf";
  char comment1[] = "a";
  ValidateWriteComment(filename1, false, 24, comment1, 44);
  char filename2[] = "comment-2.gsf";
  char comment2[] = "ab";
  ValidateWriteComment(filename2, false, 24, comment2, 44);
  char filename3[] = "comment-3.gsf";
  char comment3[] = "abc";
  ValidateWriteComment(filename3, false, 24, comment3, 44);
  char filename4[] = "comment-4.gsf";
  char comment4[] = "abcd";
  ValidateWriteComment(filename4, false, 24, comment4, 44);
  char filename5[] = "comment-5.gsf";
  char comment5[] = "abcde";
  ValidateWriteComment(filename5, false, 28, comment5, 48);
}

TEST(GsfWriteSimple, CommentLarge) {
  char filename[] = "comment-large.gsf";
  char comment[] =
      "ab"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "de";
  ValidateWriteComment(filename, false, 656, comment, 676);
}

void ValidateWriteHistory(const char *filename, bool checksum,
                          int expected_write_size, const char *host_name,
                          const char *operator_name, const char *command_line,
                          const char *comment, int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 28);
  ASSERT_NE(nullptr, host_name);
  ASSERT_LE(strlen(host_name), GSF_HOST_NAME_LENGTH);
  ASSERT_NE(nullptr, operator_name);
  ASSERT_LE(strlen(operator_name), GSF_OPERATOR_LENGTH);
  ASSERT_NE(nullptr, command_line);
  ASSERT_NE(nullptr, comment);
  ASSERT_GE(expected_file_size, 48);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_HISTORY, 0};
  gsfRecords record;
  record.history =
      GsfHistory({1, 2}, host_name, operator_name, command_line, comment);
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));
  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_HISTORY, data_id.recordID);
  VerifyHistory(record.history, read_record.history);
}

TEST(GsfWriteSimple, History) {
  ValidateWriteHistory("history-empty.gsf", false, 28, "", "", "", "", 48);
  ValidateWriteHistory("history-empty-checksum.gsf", true, 32, "", "", "", "",
                       52);
  ValidateWriteHistory("history-1.gsf", false, 32, "a", "b", "c", "d", 52);
  ValidateWriteHistory("history-longer.gsf", false, 40, "ab", "cde", "fghi",
                       "jklm", 60);
}

// TODO(schwehr): GSF_RECORD_NAVIGATION_ERROR
// TODO(schwehr): GSF_RECORD_SWATH_BATHY_SUMMARY
// TODO(schwehr): GSF_RECORD_SINGLE_BEAM_PING
// TODO(schwehr): GSF_RECORD_HV_NAVIGATION_ERROR

void ValidateWriteAttitude(const char *filename, bool checksum,
                           int expected_write_size, const gsfAttitude &attitude,
                           int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 20);
  ASSERT_GE(expected_file_size, 40);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_ATTITUDE, 0};
  gsfRecords record;
  record.attitude = attitude;
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));
  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_ATTITUDE, data_id.recordID);
  VerifyAttitude(record.attitude, read_record.attitude);
}

unique_ptr<gsfAttitude> WriteAttitudeAndReturnRead(const char *filename,
                                                   const gsfAttitude &attitude,
                                                   int *file_size,
                                                   int *num_bytes) {
  // TODO(schwehr): Add error handling.
  int handle;
  if (0 != gsfOpen(filename, GSF_CREATE, &handle)) {
    return nullptr;
  }

  gsfDataID data_id = {false, 0, GSF_RECORD_ATTITUDE, 0};
  gsfRecords record;
  record.attitude = attitude;
  if (gsfWrite(handle, &data_id, &record) < 20) {
    // Do not check return since we are already in trouble.
    gsfClose(handle);
    return nullptr;
  }
  if (0 != gsfClose(handle)) {
    return nullptr;
  }

  struct stat buf;
  if (0 != stat(filename, &buf)) {
    return nullptr;
  }
  *file_size = buf.st_size;

  if (0 != gsfOpen(filename, GSF_READONLY, &handle)) {
    return nullptr;
  }

  gsfRecords read_record;
  *num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  if (*num_bytes < 24) {
    return nullptr;
  }

  return MakeUnique<gsfAttitude>(read_record.attitude);
}

TEST(GsfWriteSimple, AttitudeEmpty) {
  const struct timespec times[] = {{3, 4}};
  const gsfAttitude attitude =
      GsfAttitude(0, times, nullptr, nullptr, nullptr, nullptr);
  ValidateWriteAttitude("attitude-empty.gsf", false, 20, attitude, 40);
}

TEST(GsfWriteSimple, AttitudeLength1Zeros) {
  const struct timespec times0[] = {{5, 6}};
  const double data0[] = {0.0};
  const gsfAttitude attitude0 =
      GsfAttitude(1, times0, data0, data0, data0, data0);
  ValidateWriteAttitude("attitude-length1-0.gsf", false, 28, attitude0, 48);
}

TEST(GsfWriteSimple, AttitudeLength1) {
  const struct timespec times[] = {{7, 8}};
  const double pitch[] = {1.2};
  const double roll[] = {2.2};
  const double heave[] = {3.3};
  const double heading[] = {4.4};
  const gsfAttitude attitude =
      GsfAttitude(1, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length1-1-checksum.gsf", true, 32, attitude,
                        52);
}

TEST(GsfWriteSimple, AttitudeLength1Negative) {
  const struct timespec times[] = {{9, 10}};
  const double pitch[] = {-1.2};
  const double roll[] = {-3.4};
  const double heave[] = {-5.6};
  // Stored as a int16 with value * 100, so cannot test negative.
  const double heading[] = {7.8};
  const gsfAttitude attitude =
      GsfAttitude(1, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length1-neg.gsf", false, 28, attitude, 48);
}

TEST(GsfWriteSimple, AttitudeRounding) {
  const struct timespec times[] = {{11, 12}};
  const double pitch[] = {-1.002};
  const double roll[] = {-2.0052};
  const double heave[] = {3.0045};
  const double heading[] = {4.0052};
  const gsfAttitude src = GsfAttitude(1, times, pitch, roll, heave, heading);
  int file_size;
  int num_bytes;
  const unique_ptr<gsfAttitude> dst = WriteAttitudeAndReturnRead(
      "attitude-round.gsf", src, &file_size, &num_bytes);
  ASSERT_NE(nullptr, dst);
  ASSERT_EQ(48, file_size);
  ASSERT_EQ(28, num_bytes);

  const double pitch2[] = {-1.00};
  const double roll2[] = {-2.01};
  const double heave2[] = {3.00};
  const double heading2[] = {4.01};
  const gsfAttitude expected =
      GsfAttitude(1, times, pitch2, roll2, heave2, heading2);
  VerifyAttitude(expected, *dst);
}

TEST(GsfWriteSimple, AttitudeLength2) {
  // TODO(schwehr): Why do the nanoseconds not match?
  const struct timespec times[] = {{1438016822, 2},
                                   {1438016823, 90000000}};
  const double pitch[] = {1.2, -3.4};
  const double roll[] = {-100.1, 100.2};
  const double heave[] = {-200.2, -200.3};
  // Stored as a int16 with value * 100, so cannot test negative.
  const double heading[] = {181, 359};
  const gsfAttitude attitude =
      GsfAttitude(2, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length2.gsf", false, 40, attitude, 60);
}

}  // namespace
}  // namespace test
}  // namespace generic_sensor_format
<commit_msg>Change tv_nsec to be large enough to avoid being lost in rounding.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Tests for writing out the basic record types.
//
// TODO(schwehr): Does this have to free what was read?

#include <sys/stat.h>

#include <memory>
#include <string>

#include "gsf.h"
#include "gsf_ft.h"
#include "gsf_test_util.h"
#include "gtest/gtest.h"

// #include <iostream>  // TODO(schwehr): Remove.
// using namespace std;  // TODO(schwehr): Remove.

using std::unique_ptr;

namespace generic_sensor_format {
namespace test {
namespace {

// TODO(schwehr): Switch to make_unique when C++14 is available on Travis-CI.
template <typename T, typename... Args>
std::unique_ptr<T> MakeUnique(Args &&... args) {
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

TEST(GsfWriteSimple, HeaderOnly) {
  int handle;
  char filename[] = "header-only.gsf";
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));
  ASSERT_EQ(0, gsfClose(handle));
  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(20, buf.st_size);
}

// TODO(schwehr): GSF_RECORD_SWATH_BATHYMETRY_PING.
// TODO(schwehr): GSF_RECORD_SOUND_VELOCITY_PROFILE
// TODO(schwehr): GSF_RECORD_PROCESSING_PARAMETERS
// TODO(schwehr): GSF_RECORD_SENSOR_PARAMETERS

void ValidateWriteComment(const char *filename, bool checksum,
                          int expected_write_size, const char *comment,
                          int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 20);
  ASSERT_NE(nullptr, comment);
  ASSERT_GE(expected_file_size, 40);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_COMMENT, 0};
  gsfRecords record;
  record.comment = GsfComment({1, 2}, comment);
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));

  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_COMMENT, data_id.recordID);
  VerifyComment(record.comment, read_record.comment);
}

TEST(GsfWriteSimple, CommentEmpty) {
  char filename[] = "comment-empty.gsf";
  char comment[] = "";
  ValidateWriteComment(filename, false, 20, comment, 40);
}

TEST(GsfWriteSimple, CommentEmptyChecksum) {
  char filename[] = "comment-empty-checksum.gsf";
  char comment[] = "";
  ValidateWriteComment(filename, true, 24, comment, 44);
}

TEST(GsfWriteSimple, CommentUpTo5) {
  char filename1[] = "comment-1.gsf";
  char comment1[] = "a";
  ValidateWriteComment(filename1, false, 24, comment1, 44);
  char filename2[] = "comment-2.gsf";
  char comment2[] = "ab";
  ValidateWriteComment(filename2, false, 24, comment2, 44);
  char filename3[] = "comment-3.gsf";
  char comment3[] = "abc";
  ValidateWriteComment(filename3, false, 24, comment3, 44);
  char filename4[] = "comment-4.gsf";
  char comment4[] = "abcd";
  ValidateWriteComment(filename4, false, 24, comment4, 44);
  char filename5[] = "comment-5.gsf";
  char comment5[] = "abcde";
  ValidateWriteComment(filename5, false, 28, comment5, 48);
}

TEST(GsfWriteSimple, CommentLarge) {
  char filename[] = "comment-large.gsf";
  char comment[] =
      "ab"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      "de";
  ValidateWriteComment(filename, false, 656, comment, 676);
}

void ValidateWriteHistory(const char *filename, bool checksum,
                          int expected_write_size, const char *host_name,
                          const char *operator_name, const char *command_line,
                          const char *comment, int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 28);
  ASSERT_NE(nullptr, host_name);
  ASSERT_LE(strlen(host_name), GSF_HOST_NAME_LENGTH);
  ASSERT_NE(nullptr, operator_name);
  ASSERT_LE(strlen(operator_name), GSF_OPERATOR_LENGTH);
  ASSERT_NE(nullptr, command_line);
  ASSERT_NE(nullptr, comment);
  ASSERT_GE(expected_file_size, 48);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_HISTORY, 0};
  gsfRecords record;
  record.history =
      GsfHistory({1, 2}, host_name, operator_name, command_line, comment);
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));
  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_HISTORY, data_id.recordID);
  VerifyHistory(record.history, read_record.history);
}

TEST(GsfWriteSimple, History) {
  ValidateWriteHistory("history-empty.gsf", false, 28, "", "", "", "", 48);
  ValidateWriteHistory("history-empty-checksum.gsf", true, 32, "", "", "", "",
                       52);
  ValidateWriteHistory("history-1.gsf", false, 32, "a", "b", "c", "d", 52);
  ValidateWriteHistory("history-longer.gsf", false, 40, "ab", "cde", "fghi",
                       "jklm", 60);
}

// TODO(schwehr): GSF_RECORD_NAVIGATION_ERROR
// TODO(schwehr): GSF_RECORD_SWATH_BATHY_SUMMARY
// TODO(schwehr): GSF_RECORD_SINGLE_BEAM_PING
// TODO(schwehr): GSF_RECORD_HV_NAVIGATION_ERROR

void ValidateWriteAttitude(const char *filename, bool checksum,
                           int expected_write_size, const gsfAttitude &attitude,
                           int expected_file_size) {
  ASSERT_NE(nullptr, filename);
  ASSERT_GE(expected_write_size, 20);
  ASSERT_GE(expected_file_size, 40);

  int handle;
  ASSERT_EQ(0, gsfOpen(filename, GSF_CREATE, &handle));

  gsfDataID data_id = {checksum, 0, GSF_RECORD_ATTITUDE, 0};
  gsfRecords record;
  record.attitude = attitude;
  ASSERT_EQ(expected_write_size, gsfWrite(handle, &data_id, &record));
  ASSERT_EQ(0, gsfClose(handle));

  struct stat buf;
  ASSERT_EQ(0, stat(filename, &buf));
  ASSERT_EQ(expected_file_size, buf.st_size);

  ASSERT_EQ(0, gsfOpen(filename, GSF_READONLY, &handle));
  ASSERT_GE(handle, 0);
  gsfRecords read_record;
  const int num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  ASSERT_EQ(expected_write_size, num_bytes);
  ASSERT_EQ(GSF_RECORD_ATTITUDE, data_id.recordID);
  VerifyAttitude(record.attitude, read_record.attitude);
}

unique_ptr<gsfAttitude> WriteAttitudeAndReturnRead(const char *filename,
                                                   const gsfAttitude &attitude,
                                                   int *file_size,
                                                   int *num_bytes) {
  // TODO(schwehr): Add error handling.
  int handle;
  if (0 != gsfOpen(filename, GSF_CREATE, &handle)) {
    return nullptr;
  }

  gsfDataID data_id = {false, 0, GSF_RECORD_ATTITUDE, 0};
  gsfRecords record;
  record.attitude = attitude;
  if (gsfWrite(handle, &data_id, &record) < 20) {
    // Do not check return since we are already in trouble.
    gsfClose(handle);
    return nullptr;
  }
  if (0 != gsfClose(handle)) {
    return nullptr;
  }

  struct stat buf;
  if (0 != stat(filename, &buf)) {
    return nullptr;
  }
  *file_size = buf.st_size;

  if (0 != gsfOpen(filename, GSF_READONLY, &handle)) {
    return nullptr;
  }

  gsfRecords read_record;
  *num_bytes =
      gsfRead(handle, GSF_NEXT_RECORD, &data_id, &read_record, nullptr, 0);
  if (*num_bytes < 24) {
    return nullptr;
  }

  return MakeUnique<gsfAttitude>(read_record.attitude);
}

TEST(GsfWriteSimple, AttitudeEmpty) {
  const struct timespec times[] = {{3, 4}};
  const gsfAttitude attitude =
      GsfAttitude(0, times, nullptr, nullptr, nullptr, nullptr);
  ValidateWriteAttitude("attitude-empty.gsf", false, 20, attitude, 40);
}

TEST(GsfWriteSimple, AttitudeLength1Zeros) {
  const struct timespec times0[] = {{5, 6}};
  const double data0[] = {0.0};
  const gsfAttitude attitude0 =
      GsfAttitude(1, times0, data0, data0, data0, data0);
  ValidateWriteAttitude("attitude-length1-0.gsf", false, 28, attitude0, 48);
}

TEST(GsfWriteSimple, AttitudeLength1) {
  const struct timespec times[] = {{7, 8}};
  const double pitch[] = {1.2};
  const double roll[] = {2.2};
  const double heave[] = {3.3};
  const double heading[] = {4.4};
  const gsfAttitude attitude =
      GsfAttitude(1, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length1-1-checksum.gsf", true, 32, attitude,
                        52);
}

TEST(GsfWriteSimple, AttitudeLength1Negative) {
  const struct timespec times[] = {{9, 10}};
  const double pitch[] = {-1.2};
  const double roll[] = {-3.4};
  const double heave[] = {-5.6};
  // Stored as a int16 with value * 100, so cannot test negative.
  const double heading[] = {7.8};
  const gsfAttitude attitude =
      GsfAttitude(1, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length1-neg.gsf", false, 28, attitude, 48);
}

TEST(GsfWriteSimple, AttitudeRounding) {
  const struct timespec times[] = {{11, 12}};
  const double pitch[] = {-1.002};
  const double roll[] = {-2.0052};
  const double heave[] = {3.0045};
  const double heading[] = {4.0052};
  const gsfAttitude src = GsfAttitude(1, times, pitch, roll, heave, heading);
  int file_size;
  int num_bytes;
  const unique_ptr<gsfAttitude> dst = WriteAttitudeAndReturnRead(
      "attitude-round.gsf", src, &file_size, &num_bytes);
  ASSERT_NE(nullptr, dst);
  ASSERT_EQ(48, file_size);
  ASSERT_EQ(28, num_bytes);

  const double pitch2[] = {-1.00};
  const double roll2[] = {-2.01};
  const double heave2[] = {3.00};
  const double heading2[] = {4.01};
  const gsfAttitude expected =
      GsfAttitude(1, times, pitch2, roll2, heave2, heading2);
  VerifyAttitude(expected, *dst);
}

TEST(GsfWriteSimple, AttitudeLength2) {
  // TODO(schwehr): Why do the nanoseconds not match?
  const struct timespec times[] = {{1438016822, 80000000},
                                   {1438016823, 90000000}};
  const double pitch[] = {1.2, -3.4};
  const double roll[] = {-100.1, 100.2};
  const double heave[] = {-200.2, -200.3};
  // Stored as a int16 with value * 100, so cannot test negative.
  const double heading[] = {181, 359};
  const gsfAttitude attitude =
      GsfAttitude(2, times, pitch, roll, heave, heading);
  ValidateWriteAttitude("attitude-length2.gsf", false, 40, attitude, 60);
}

}  // namespace
}  // namespace test
}  // namespace generic_sensor_format
<|endoftext|>