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