
Hi Michael,
Same here, I was only wondering aloud. In fact I hope Matthias will *only* provide the compile-time version in this first iteration.
So do I ;^) Thanks for the support.
Well, I have a question to Matthias concerning similar things. How does one use a unitless number? Do you use dimensionless?
There is a dimensionless tag, but quantities of dimensionless units are specialized so that they behave just like regular value_types - by definition if something is dimensionless, there is no difference in its value, irrespective of unit system. It is mainly there to allow checking for results of dimensional calculations - if you get a dimensionless unit, it came from a dimensional analysis calculation. But there is no real benefit to explicitly constructing dimensionless quantities in general; for unit computations (without associated values) it can be nice to be able to output explicitly that the result of some calculation is dimensionless.
I posted two formulas that are from a radar handbook. The first is:
h = R^2 / 2ka
where h is the beam height, R is radar range, k is the refractivity (usually 4/3), and a is the Earth's radius. R and a must be the same unit, and h is returned in that unit. How would you code this using mcs::units?
For lengths in meters, this would be : template<typename T> quantity<SI::length,T> radar_beam_height(const quantity<SI::length,T>& radar_range, const quantity<SI::length,T>& earth_radius, T k = 4.0/3.0) { return quantity<SI::length,T>(pow<2>(radar_range)/ (2.0*k*earth_radius)); } To be more general, we can specialize for arbitrary unit system: template<class System,typename T> quantity<unit<System,length_type>,T> radar_beam_height(const quantity<unit<System,length_type>,T>& radar_range, const quantity<unit<System,length_type>,T>& earth_radius, T k = 4.0/3.0) { return quantity<unit<System,length_type>,T>(pow<2>(radar_range)/ (2.0*k*earth_radius)); } This function works as long as all quantities are of the same system. You could further generalize it to work with three different types of lengths : one for the radar range, one for the earth radius, and one for the beam height: template<class return_type,class System1,class System2,typename T> return_type radar_beam_height(const quantity<unit<System1,length_type>,T>& radar_range, const quantity<unit<System2,length_type>,T>& earth_radius, T k = 4.0/3.0) { // need to decide which system to use for calculation const return_type rr(radar_range), er(earth_radius); return return_type (pow<2>(rr)/(2.0*k*er)); } This way, if your code was like this: quantity<nautical::length> radar_range(24.5*miles); quantity<SI::length> earth_radius(6371e3*meters); you could do this: quantity<CGS::length> beam_height = radar_beam_height<CGS::system> (radar_range,earth_radius); and get the value computed in centimeters.
template <typename T> T radar_beam_height(T range, T earth_radius,
I would return a quantity of length and pass arguments of length to retain type safety
quantity<SomeSystem::dimensionless> refractivity = quantity<SomeSystem::dimensionless>(4/3))
No need, dimensionless is equivalent to T. Watch out for integer division in 4/3...
const quantity<SomeSystem::dimensionless> two(2);
Again, just a scalar T is fine here.
return quantity<T>( pow(range, 2) / ( two * refractivity * range ) );
You need to use the static power function since powers/roots change the dimensions of the units, and a return quantity in the units you like: return quantity<SI::length>(pow<2>(range)/(two*refractivity*range));
With calling code like: // What's the radar beam height at 300 nautical miles using the mean Earth radius? quantity<SI::length> height = radar_beam_height(quantity<SI::length>(1028971200), quantity<SI::length>(6371008.7714));
You can make this simpler: quantity<SI::length> height = radar_beam_height(quantity<SI::length>(quantity<nautical::length> (300.0)), quantity<SI::length>(6371.0087714*kilo*meters));
The other question I have is very open-ended. An approximation for the above formula where R is given in nautical miles, and h is returned as feet is:
h = (R / 1.23)^2
How would you go about implementing that with mcs::units? I have no idea what unit 1.23 is, or how they even derived that number, other than it has the Earth radius and conversion from nautical miles to feet built-in.
Engineering approximations can be problematic in the sense that they are often completely empirical and, therefore, don't particularly respect the rules of dimensional analysis. In these cases it's best to just wrap the approximation in a function that takes the correct arguments and returns the appropriate value: quantity<imperial::length> radar_beam_height(const quantity<nautical::length>& range) { return quantity<imperial::length>(std::pow(range.value()/1.23,2.0) *imperial::feet); } If you really wanted to think about the units of your approximation in great detail, you have : feet = (nautical miles/(something))^2 so something = nautical miles/feet^(1/2) and your constant is C = 1.23 nm/ft^1/2 This is actually a good example of where heterogeneous units would be useful... Anyway, check out the attached example code for functional versions of the above. You will also need to patch conversion.hpp - there is a minor bug in it that I needed to fix.
quantity<imperial::feet> radar_beam_height (quantity<nautical::miles> range)
It's important to maintain a semantic distinction in this library : by convention, I have the template arguments refer to the type of unit (length, mass, etc...) rather than the specific unit (meters, kilograms, etc...)... An important proviso, though : because you cannot use floating point numbers as template arguments, there will be some engineering approximations that cannot be expressed in dimensionally correct form (those with floating point powers and/or roots). However, those formulas are best wrapped in a dimensionally correct wrapper function in any case, to guarantee that the intended unit is correct. Matthias