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
#include
#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
{
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 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 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 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