I am using the Boost Units library to support molecule scale dynamic simulations, but I am pretty sure I am using Boost Units wrong. I am looking for some guidance and explanations for some confusing compile errors I get (see below). I am creating a units system using a mixture of custom base_units (mass, amount, charge), scaled base units based on si::base_units (time, length), and unmodified si::base_units (plane_angle, solid_angle, temperature). The full set of base units I intend to use are as follows: * time in picoseconds * mass in daltons * length in nanometers * amount in molecules * angle in radians * solid angle in steradians * charge in elementary charges * temperature in kelvin * [I'm ignoring luminous intensity for now] Perhaps I am being too picky, because I can build up a molecular dynamics scale units system if I explicitly create every base_unit myself. But I would like to take maximum advantage of the SI units infrastructure that is already available. I suspect that directly using some of the SI base_units would be more efficient than the method I am currently using, which establishes conversion factors to the SI units. Some of these conversion factors are 1.0 (unity). Listing 1 shows a stripped down example using three base_units: mass, length, and time. This program compiles and runs, but notice the conversion factors of 1.0 in the BOOST_UNITS_DEFINE_CONVERSION_FACTOR macros toward the end. Listing 1: Compiles and runs, but I had to explicitly create every base_unit. THIS PROGRAM WORKS -- LOOK FARTHER DOWN FOR THE ONE THAT FAILS *** BEGIN LISTING 1 *** #include <boost/units/conversion.hpp> #include <boost/units/systems/si.hpp> #include <iostream> namespace si = boost::units::si; namespace md { using boost::units::length_dimension; using boost::units::mass_dimension; using boost::units::time_dimension; using boost::units::quantity; using boost::units::unit; using boost::units::scaled_base_unit; using boost::units::scale; using boost::units::static_rational; // length struct meter_base_unit : boost::units::base_unit<meter_base_unit, length_dimension, 1> { static std::string name() { return "meter"; } static std::string symbol() { return "m"; } }; typedef scaled_base_unit< meter_base_unit, scale<10, static_rational<-9> >
nanometer_base_unit;
// mass struct dalton_base_unit : boost::units::base_unit<dalton_base_unit, mass_dimension, 2> { static std::string name() { return "Dalton"; } static std::string symbol() { return "Da"; } }; // time struct second_base_unit : boost::units::base_unit<second_base_unit, time_dimension, 3> { static std::string name() { return "second"; } static std::string symbol() { return "s"; } }; typedef scaled_base_unit< second_base_unit, scale<10, static_rational<-12> >
picosecond_base_unit;
// create the units system typedef boost::units::make_system< nanometer_base_unit, dalton_base_unit, picosecond_base_unit
::type md_units_system;
// define quantity types typedef unit<length_dimension, md_units_system> nanometer_unit; BOOST_UNITS_STATIC_CONSTANT(nanometer, nanometer_unit); BOOST_UNITS_STATIC_CONSTANT(nanometers, nanometer_unit); typedef quantity<nanometer_unit> length_t; typedef unit<mass_dimension, md_units_system> dalton_unit; BOOST_UNITS_STATIC_CONSTANT(dalton, dalton_unit); BOOST_UNITS_STATIC_CONSTANT(daltons, dalton_unit); BOOST_UNITS_STATIC_CONSTANT(atomic_mass_unit, dalton_unit); BOOST_UNITS_STATIC_CONSTANT(atomic_mass_units, dalton_unit); typedef quantity<dalton_unit> mass_t; typedef unit<time_dimension, md_units_system> picosecond_unit; BOOST_UNITS_STATIC_CONSTANT(picosecond, picosecond_unit); BOOST_UNITS_STATIC_CONSTANT(picoseconds, picosecond_unit); typedef quantity<picosecond_unit> time_t; } // namespace md // length conversions BOOST_UNITS_DEFINE_CONVERSION_FACTOR( md::meter_base_unit, si::meter_base_unit, double, 1.0); // mass conversions BOOST_UNITS_DEFINE_CONVERSION_FACTOR( md::dalton_base_unit, si::kilogram_base_unit, double, 1.6605388628e-27); // time conversions BOOST_UNITS_DEFINE_CONVERSION_FACTOR( md::second_base_unit, si::second_base_unit, double, 1.0); using namespace md; using namespace std; int main () { md::time_t timeStep = 0.002 * picoseconds; cout << "time step = " << timeStep << endl; mass_t carbonMass = 12.0 * daltons; cout << "carbon mass = " << carbonMass << endl; length_t bondLength = 0.15 * nanometers; cout << "bond length = " << bondLength << endl; // nonsense, but exercises composite dimensions cout << "composite value = " << timeStep * bondLength << endl; return 0; } *** END LISTING 1 *** Actually, I can directly base the LENGTH dimension on the SI units by changing line 27 of Listing 1 from 26: typedef scaled_base_unit< 27: meter_base_unit, 28: scale<10, static_rational<-9> > 29: > nanometer_base_unit; to 26: typedef scaled_base_unit< 27: si::meter_base_unit, // <== note si prefix 28: scale<10, static_rational<-9> > 29: > nanometer_base_unit; So shouldn't I be able to do the same thing for all of the other SI units? I would have thought so. But doing the same thing for the the TIME dimension always results in trouble, in my experience. Witness listing 2, below. Listing 2: Fails to compile; lots of indecipherable template errors. Time dimension is based on si::second_base_unit. THIS PROGRAM FAILS TO COMPILE *** BEGIN LISTING 2 *** #include <boost/units/conversion.hpp> #include <boost/units/systems/si.hpp> #include <iostream> namespace si = boost::units::si; namespace md { using boost::units::mass_dimension; using boost::units::time_dimension; using boost::units::quantity; using boost::units::unit; using boost::units::scaled_base_unit; using boost::units::scale; using boost::units::static_rational; // mass struct dalton_base_unit : boost::units::base_unit<dalton_base_unit, mass_dimension, 1> { static std::string name() { return "Dalton"; } static std::string symbol() { return "Da"; } }; // time typedef scaled_base_unit< si::second_base_unit, scale<10, static_rational<-12> >
picosecond_base_unit;
// create the units system typedef boost::units::make_system< dalton_base_unit, picosecond_base_unit
::type md_units_system;
// define quantity types typedef unit<mass_dimension, md_units_system> dalton_unit; BOOST_UNITS_STATIC_CONSTANT(dalton, dalton_unit); BOOST_UNITS_STATIC_CONSTANT(daltons, dalton_unit); BOOST_UNITS_STATIC_CONSTANT(atomic_mass_unit, dalton_unit); BOOST_UNITS_STATIC_CONSTANT(atomic_mass_units, dalton_unit); typedef quantity<dalton_unit> mass_t; typedef unit<time_dimension, md_units_system> picosecond_unit; BOOST_UNITS_STATIC_CONSTANT(picosecond, picosecond_unit); BOOST_UNITS_STATIC_CONSTANT(picoseconds, picosecond_unit); typedef quantity<picosecond_unit> time_t; } // namespace md // mass conversions BOOST_UNITS_DEFINE_CONVERSION_FACTOR( md::dalton_base_unit, si::kilogram_base_unit, double, 1.6605388628e-27); using namespace md; using namespace std; int main () { md::time_t timeStep = 0.002 * picoseconds; cout << "time step = " << timeStep << endl; mass_t carbonMass = 12.0 * daltons; cout << "carbon mass = " << carbonMass << endl; // nonsense, but exercises composite dimensions cout << "composite value = " << timeStep * carbonMass << endl; return 0; } *** END LISTING 2 *** One way to get listing 2 to compile and run is to change line 20 from 19: struct dalton_base_unit : 20: boost::units::base_unit<dalton_base_unit, mass_dimension, 1> to 19: struct dalton_base_unit : 20: boost::units::base_unit<dalton_base_unit, mass_dimension, -1000> // <== The program compiles when I use a large negative value for the base_unit id. But the documentation states that negative values are reserved. I am a bad boy for even trying. In any case, that "-1000" trick only works when the TIME dimension is the only dimension that is directly based on SI units. ERROR MESSAGES FROM LISTING 2: **** small sample of the top of the compile errors I get with unmodified Listing 2: **** 1>C:\cygwin\home\Christopher\svn\dnamodel\include\boost/units/detail/linear_algebra.hpp(207) : error C2976: 'boost::units::detail::determine_extra_equations_skip_zeros_impl<true,true>::apply' : too few template arguments 1> C:\cygwin\home\Christopher\svn\dnamodel\include\boost/units/detail/linear_algebra.hpp(217) : see declaration of 'boost::units::detail::determine_extra_equations_skip_zeros_impl<true,true>::apply' 1> C:\cygwin\home\Christopher\svn\dnamodel\include\boost/units/detail/linear_algebra.hpp(259) : see reference to class template instantiation 'boost::units::detail::determine_extra_equations_skip_zeros_impl<true,false>::apply<RowsBegin,RemainingRows,CurrentColumn,TotalColumns,Result>' being compiled 1> with 1> [ 1> RowsBegin=boost::units::list<boost::units::list<boost::units::detail::calculate_base_dimension_coefficients_func<false>::apply<boost::units::list<boost::units::dim<boost::units::time_base_dimension,boost::units::static_rational<1>>,boost::units::dimensionless_type>>::type,boost::units::list<boost::units::static_rational<1>,boost::units::detail::expand_dimensions<0>::apply<boost::units::dimensionless_type,boost::units::dimensionless_type>::type>>,boost::units::list<boost::units::list<boost::units::static_rational<1>,boost::units::list<boost::units::detail::calculate_base_dimension_coefficients_func<false>::apply<boost::units::dimensionless_type>::type,boost::units::detail::expand_dimensions<0>::apply<boost::units::dimensionless_type,boost::units::dimensionless_type>::type>>,boost::units::dimensionless_type>>, 1> RemainingRows=2, 1> CurrentColumn=0, 1> TotalColumns=2, 1> Result=boost::units::list<boost::units::list<boost::units::detail::calculate_base_dimension_coefficients_func<false>::apply<boost::units::list<boost::units::dim<boost::units::time_base_dimension,boost::units::static_rational<1>>,boost::units::dimensionless_type>>::type,boost::units::list<boost::units::static_rational<1>,boost::units::detail::expand_dimensions<0>::apply<boost::units::dimensionless_type,boost::units::dimensionless_type>::type>>,boost::units::list<boost::units::list<boost::units::static_rational<1>,boost::units::list<boost::units::detail::calculate_base_dimension_coefficients_func<false>::apply<boost::units::dimensionless_type>::type,boost::units::detail::expand_dimensions<0>::apply<boost::units::dimensionless_type,boost::units::dimensionless_type>::type>>,boost::units::dimensionless_type>> 1> ] 1> C:\cygwin\home\Christopher\svn\dnamodel\include\boost/units/detail/linear_algebra.hpp(538) : see reference to class template instantiation 'boost::units::detail::determine_extra_equations<RemainingColumns,is_done>::apply<RowsBegin,TotalColumns,Result>' being compiled 1> with 1> [ 1> RemainingColumns=2, 1> is_done=false, 1> RowsBegin=boost::units::list<boost::units::list<boost::units::detail::calculate_base_dimension_coefficients_func<false>::apply<boost::units::list<boost::units::dim<boost::units::time_base_dimension,boost::units::static_rational<1>>,boost::units::dimensionless_type>>::type,boost::units::list<boost::units::static_rational<1>,boost::units::detail::expand_dimensions<0>::apply<boost::units::dimensionless_type,boost::units::dimensionless_type>::type>>,boost::units::list<boost::units::list<boost::units::static_rational<1>,boost::units::list<boost::units::detail::calculate_base_dimension_coefficients_func<false>::apply<boost::units::dimensionless_type>::type,boost::units::detail::expand_dimensions<0>::apply<boost::units::dimensionless_type,boost::units::dimensionless_type>::type>>,boost::units::dimensionless_type>>, 1> TotalColumns=2, 1> Result=boost::units::list<boost::units::list<boost::units::detail::calculate_base_dimension_coefficients_func<false>::apply<boost::units::list<boost::units::dim<boost::units::time_base_dimension,boost::units::static_rational<1>>,boost::units::dimensionless_type>>::type,boost::units::list<boost::units::static_rational<1>,boost::units::detail::expand_dimensions<0>::apply<boost::units::dimensionless_type,boost::units::dimensionless_type>::type>>,boost::units::list<boost::units::list<boost::units::static_rational<1>,boost::units::list<boost::units::detail::calculate_base_dimension_coefficients_func<false>::apply<boost::units::dimensionless_type>::type,boost::units::detail::expand_dimensions<0>::apply<boost::units::dimensionless_type,boost::units::dimensionless_type>::type>>,boost::units::dimensionless_type>> 1> ] 1> C:\cygwin\home\Christopher\svn\dnamodel\include\boost/units/detail/linear_algebra.hpp(828) : see reference to class template instantiation 'boost::units::detail::make_square_and_invert<Matrix>' being compiled 1> with 1> [ 1> Matrix=boost::units::list<boost::units::list<boost::units::detail::calculate_base_dimension_coefficients_func<false>::apply<boost::units::list<boost::units::dim<boost::units::time_base_dimension,boost::units::static_rational<1>>,boost::units::dimensionless_type>>::type,boost::units::list<boost::units::static_rational<1>,boost::units::detail::expand_dimensions<0>::apply<boost::units::dimensionless_type,boost::units::dimensionless_type>::type>>,boost::units::list<boost::units::list<boost::units::static_rational<1>,boost::units::list<boost::units::detail::calculate_base_dimension_coefficients_func<false>::apply<boost::units::dimensionless_type>::type,boost::units::detail::expand_dimensions<0>::apply<boost::units::dimensionless_type,boost::units::dimensionless_type>::type>>,boost::units::dimensionless_type>> 1> ] 1> C:\cygwin\home\Christopher\svn\dnamodel\include\boost/units/detail/linear_algebra.hpp(1032) : see reference to class template instantiation 'boost::units::detail::normalize_units<T>' being compiled 1> with 1> [ 1> T=boost::units::list<boost::units::scaled_base_unit<boost::units::si::second_base_unit,boost::units::scale<10,boost::units::static_rational<-12>>>,boost::units::list<md::dalton_base_unit,boost::units::dimensionless_type>> 1> ] [snip] ****** END snippet of compile errors ********* Environment details: I am developing in Visual Studio Express 9.0 on Windows XP at the moment. The version of boost::units I am using is from subversion on January 3, 2009. I would feel better if I could reuse several of the base_units from SI. Just reusing length and not the others seems painfully sloppy. I welcome any comments that might nudge me in the right direction. Thanks in advance for any help. --Chris Bruns