[Units] Molecular units base_units trouble: please help
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
AMDG Christopher Bruns wrote:
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).
Your usage appears to be correct. I should have a fix later today. In Christ, Steven Watanabe
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]
You should be able to reuse radians, steradians, and kelvin directly and use scaled units for picoseconds and nanometers. You will need to create your own base units for daltons and amount in molecules.
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).
Actually, I can directly base the LENGTH dimension on the SI units by changing line 27 of Listing 1 from
snip
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.
I would do it this way: #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 typedef scaled_base_unit< si::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 typedef scaled_base_unit< si::second_base_unit, scale<10, static_rational<-12> > > picosecond_base_unit; // define quantity types typedef nanometer_base_unit::unit_type nanometer_unit; BOOST_UNITS_STATIC_CONSTANT(nanometer, nanometer_unit); BOOST_UNITS_STATIC_CONSTANT(nanometers, nanometer_unit); typedef quantity<nanometer_unit> length_t; typedef dalton_base_unit::unit_type 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 picosecond_base_unit::unit_type 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; length_t bondLength = 0.15 * nanometers; cout << "bond length = " << bondLength << endl; // nonsense, but exercises composite dimensions cout << "composite value = " << timeStep * bondLength << endl; return 0; } This code compiles and runs correctly, giving: time step = 0.002 ps carbon mass = 12 Da bond length = 0.15 nm composite value = 0.0003 nm ps In general, it's easiest to avoid explicitly creating unit systems by using the ::unit_type approach. For most cases, the unit system ought to be considered an implementation detail since the library supports heterogeneous units transparently. That is, for any given unit, the appropriate system is just the set of units needed to fully describe the unit itself... Matthias
AMDG Christopher Bruns wrote:
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).
This was definitely a bug in the library. Fixed in the trunk. In Christ, Steven Watanabe
participants (3)
-
Christopher Bruns
-
Matthias Schabel
-
Steven Watanabe