Boost logo

Boost Users :

Subject: [Boost-users] [Units] Molecular units base_units trouble: please help
From: Christopher Bruns (cmbruns_at_[hidden])
Date: 2009-01-03 20:39:34


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


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net