These are chat archives for Fortran-FOSS-Programmers/General-Discussion

Sep 2016
Stefano Zaghi
Sep 09 2016 12:52

@tomedunn Hi Tom, the week-diet was good... FURY now support temperature like conversions (with an offset) that can be defined by the friendly builtin string parser of the FURY uom and even a totally generic conversion formula that however can be defined less friendly...
for example you can now do the followings

type(uom)       :: dBm            !< dBm unit.
type(uom)       :: mW             !< mW unit.
type(uom)       :: kelvin         !< Kelvin unit.
type(uom)       :: celsius        !< Celsius unit.
type(qreal)     :: q1             !< A quantity.
type(qreal)     :: q2             !< A quantity.
type(qreal)     :: q3             !< A quantity.
type(dBm_to_mW) :: dBm2mW         !< Converter from dBm to mW. 

dBm = uom('dBm = @user mW')
mW = uom('mW')

call dBm%set_alias_conversion(reference_index=1, alias_index=2, convert=dBm2mW)

q1 = 10. * dBm
q2 = q1%to(unit=mW)
test_passed(1) = q2%stringify(format='(F4.1)')=='10.0 mW'
print "(A,L1)", '10.0 dBm = '//q2%stringify(format='(F4.1)')//', is correct? ', test_passed(1)

call q1%unset
call q2%unset
q1 = 10. * mW
q2 = q1%to(unit=dBm)
test_passed(2) = q2%stringify(format='(F4.1)')=='10.0 dBm'
print "(A,L1)", '10.0 mW = '//q2%stringify(format='(F4.1)')//', is correct? ', test_passed(2)

kelvin = uom('K')
celsius = uom('degC<=273.15 + K=celsius>')

call q1%unset
call q2%unset
q1 = 2 * kelvin
q2 = 1 * celsius
q3 = q1 - q2%to(kelvin)
test_passed(3) = q3%stringify(format='(F7.2)')=='-272.15 K'
print "(A,L1)", '2 K - 1 celsius = '//q3%stringify(format='(F7.2)')//', is correct? ', test_passed(3)

call q3%unset
q3 = q2 - q1%to(celsius)
test_passed(4) = q3%stringify(format='(F6.2)')=='272.15 degC'
print "(A,L1)", '1 celsius - 2 K = '//q3%stringify(format='(F6.2)')//', is correct? ', test_passed(4)

Note that while "celsius to kelvin" can be defined very easy by the builtin string parser, the conversion from dBm to mW does not because their conversions are not simply y= offset + factor * x, rather in this case they are

  • dBm = 10 * log(mW)
  • mW = 10**(dBm / 10)

So the user must define this generic conversion and attach to the pre-defined uom (where I have used a placeholder), see the statement

call dBm%set_alias_conversion(reference_index=1, alias_index=2, convert=dBm2mW)

The converter dBm2mW is simply a concrete extension of one (the only for now) abstract class provided by FURY, i.e.

module dBm_to_mW_converter
!< Define the converter (user-supplied) from dBm to mW.
use fury

implicit none
public :: dBm_to_mW 

type, extends(converter) :: dBm_to_mW
  !< Converter (user-supplied) from dBm to mW.
    procedure, nopass :: convert
endtype dBm_to_mW
  pure function convert(magnitude, inverse) result(converted)
  !< User-supplied conversion from dBm to mW.
  real(R_P), intent(in)           :: magnitude !< Magnitude (of the quantity) to be converted.
  logical,   intent(in), optional :: inverse   !< Activate inverse conversion.
  real(R_P)                       :: converted !< Converted magnitude.
  logical                         :: inverse_   !< Activate inverse conversion, local variable.

  inverse_ = .false. ; if (present(inverse)) inverse_ = inverse
  if (inverse_) then
    converted = 10._R_P * log10(magnitude)
    converted = 10._R_P**(magnitude / 10._R_P)
  endfunction convert
endmodule dBm_to_mW_converter

The above snippet is taken from a FURY test. Unfortunately, I was not able to find a way to pass a generic function without bothering the user: the string parsing has been discarderd because it could be result inefficient at runtime: the multiplicative-like conveters are efficient becuase after the intial parsing they are standard Fortran function (the parsing simply set the value of offset and factor) whereas a runtime evaluation of string-function could be too much overheaded.

Let me know if such an implementation can be useful for you.