udunits.F90 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. !#################################################################
  2. !
  3. ! Fortran module around UDUnits .
  4. !
  5. ! USAGE
  6. !
  7. ! use UDUnits
  8. !
  9. ! integer :: status
  10. ! integer(UD_POINTER_KIND) :: unit, unit2
  11. ! character(len=64) :: spec, spec2
  12. ! real(8) :: slope, offset
  13. !
  14. ! ! * module initialisation
  15. !
  16. ! call UDUnits_Init( status )
  17. ! if (status/=UDUNITS_NOERR) then
  18. ! print *, trim(UDUnits_StrError(status))
  19. ! stop
  20. ! end if
  21. !
  22. ! ! * high levell routines
  23. !
  24. ! spec = 'kg/s'
  25. ! call UDUnits_Standard( spec, spec2, status )
  26. ! write (*,'("standard name of `",a,"` is `",a,"`")') trim(spec), trim(spec2)
  27. !
  28. ! spec = 'gram/cm3' ; spec2 = 'kg/m3'
  29. ! call UDUnits_ConversionFactor( spec, spec2, slope, status )
  30. ! write (*,'("conversion factor from `",a,"` to `",a,"` is ",f12.4)') trim(spec), trim(spec2), slope
  31. !
  32. ! ! * low level routines
  33. !
  34. ! call UDUnits_Make( unit, status )
  35. ! call UDUnits_Make( unit2, status )
  36. !
  37. ! call UDUnits_Decode( 'kg', unit, status )
  38. ! call UDUnits_Encode( unit, spec, status )
  39. !
  40. ! call UDUnits_Convert( unit, unit2, slope, offset, status )
  41. !
  42. ! ! * done with module
  43. !
  44. ! call UDUnits_Done( status )
  45. !
  46. ! HISTORY
  47. ! 2010 feb, Arjo Segers, JRC
  48. !
  49. !#################################################################
  50. module UDUnits
  51. use UDUnits_Inc, only : UD_POINTER_KIND
  52. implicit none
  53. ! --- in/out -----------------------------------
  54. private
  55. public :: UD_POINTER_KIND
  56. public :: UDUNITS_NOERR, UDUnits_StrError
  57. public :: UDUnits_Init, UDUnits_Done
  58. public :: UDUnits_Make
  59. public :: UDUnits_Decode, UDUnits_Encode
  60. public :: UDUnits_Convert
  61. public :: UDUnits_Standard
  62. public :: UDUnits_ConversionFactor
  63. ! --- const --------------------------------
  64. ! module name:
  65. character(len=*), parameter :: mname = 'UDUnits'
  66. ! name of environment variable with path to data file:
  67. character(len=*), parameter :: env_var = 'UDUNITS_PATH'
  68. ! ! unit should be of type : integer(UD_POINTER_KIND)
  69. ! integer, parameter :: UD_POINTER_KIND = 4
  70. ! no error:
  71. integer, parameter :: UDUNITS_NOERR = 0
  72. ! ! error codes:
  73. ! integer, parameter :: UT_EOF = 1
  74. ! integer, parameter :: UT_ENOFILE = -1
  75. ! integer, parameter :: UT_ESYNTAX = -2
  76. ! integer, parameter :: UT_EUNKNOWN = -3
  77. ! integer, parameter :: UT_EIO = -4
  78. ! integer, parameter :: UT_EINVALID = -5
  79. ! integer, parameter :: UT_ENOINIT = -6
  80. ! integer, parameter :: UT_ECONVERT = -7
  81. ! integer, parameter :: UT_EALLOC = -8
  82. ! integer, parameter :: UT_ENOROOM = -9
  83. ! integer, parameter :: UT_ENOTTIME = -10
  84. !
  85. !integer, parameter :: UT_MAXNUM_BASE_QUANTITIES = 10
  86. ! storage for latest error:
  87. integer, parameter :: error_status = -100
  88. character(len=256) :: error_message = ''
  89. ! maximum length of specifications:
  90. integer, parameter :: spec_len = 64
  91. contains
  92. ! ====================================================================
  93. ! ===
  94. ! === module routines
  95. ! ===
  96. ! ====================================================================
  97. subroutine UDUnits_Init( status )
  98. !use UDUnits_Inc, only : udunits_inc_test
  99. ! --- in/out ---------------------------------
  100. integer, intent(out) :: status
  101. ! --- const ----------------------------------
  102. character(len=*), parameter :: rname = mname//'/UDUnits_Init'
  103. ! --- external -------------------------------
  104. ! Initialize the units package:
  105. integer, external :: UTOpen
  106. ! --- local ----------------------------------
  107. character(len=256) :: UDUnits_path
  108. integer :: length
  109. ! --- begin ----------------------------------
  110. !call udunits_inc_test()
  111. ! following the manuals, the path to the udunits data file is
  112. ! taken from the environment variable UDUnits_PATH if not specified;
  113. ! this does not seem to work properly however, and therefore
  114. ! the path is explicitly taken from the environment:
  115. call Get_Environment_Variable( env_var, UDUnits_path, length, status )
  116. if (status/=0) then
  117. write (error_message,'("could not get environment variable `",a,"`")') trim(env_var)
  118. status=error_status; return
  119. end if
  120. ! Initialize the units package:
  121. status = UTOpen( trim(UDUnits_path) )
  122. if (status/=0) write (error_message,'("could not initialize from data file `",a,"`")') trim(UDUnits_path)
  123. end subroutine UDUnits_Init
  124. ! ***
  125. subroutine UDUnits_Done( status )
  126. ! --- in/out ---------------------------------
  127. integer, intent(out) :: status
  128. ! --- external -------------------------------
  129. ! --- begin ----------------------------------
  130. ! function UTFree not available in Fortran interface ...
  131. ! ok
  132. status = 0
  133. end subroutine UDUnits_Done
  134. ! ====================================================================
  135. ! ===
  136. ! === error messages
  137. ! ===
  138. ! ====================================================================
  139. function UDUnits_StrError( status )
  140. use UDUnits_Inc, only : UT_EOF, UT_ENOFILE, UT_ESYNTAX, UT_EUNKNOWN, &
  141. UT_EIO, UT_EINVALID, UT_ENOINIT, UT_ECONVERT, &
  142. UT_EALLOC, UT_ENOROOM, UT_ENOTTIME
  143. ! --- in/out ---------------------------------
  144. character(len=256) :: UDUnits_StrError
  145. integer, intent(inout) :: status
  146. ! --- const ----------------------------------
  147. character(len=*), parameter :: rname = mname//'/UDUnits_StrError'
  148. ! --- begin ----------------------------------
  149. ! display message:
  150. select case ( status )
  151. ! supported:
  152. case ( UT_EOF ) ; UDUnits_StrError = 'End of file'
  153. case ( UDUNITS_NOERR ) ; UDUnits_StrError = ''
  154. case ( UT_ENOFILE ) ; UDUnits_StrError = 'Units file does not exist'
  155. case ( UT_ESYNTAX ) ; UDUnits_StrError = 'Syntax error'
  156. case ( UT_EUNKNOWN ) ; UDUnits_StrError = 'Unknown unit specification'
  157. case ( UT_EIO ) ; UDUnits_StrError = 'I/O error while accessing the units file'
  158. case ( UT_EINVALID ) ; UDUnits_StrError = 'Invalid value'
  159. case ( UT_ENOINIT ) ; UDUnits_StrError = 'Package has not be initialized'
  160. case ( UT_ECONVERT ) ; UDUnits_StrError = 'Conversion error'
  161. case ( UT_EALLOC ) ; UDUnits_StrError = 'Memory allocation failure'
  162. case ( UT_ENOROOM ) ; UDUnits_StrError = 'No room for result'
  163. case ( UT_ENOTTIME ) ; UDUnits_StrError = 'No time value'
  164. ! other ...
  165. case ( error_status ) ; UDUnits_StrError = ''
  166. ! unknown:
  167. case default
  168. write (UDUnits_StrError,'("Unknown error status from UDUnits routine : ",i6)') status
  169. end select
  170. ! add error buffer:
  171. if ( status /= 0 ) then
  172. UDUnits_StrError = trim(UDUnits_StrError)//'; '//trim(error_message)
  173. end if
  174. end function UDUnits_StrError
  175. ! ====================================================================
  176. ! ===
  177. ! === low level routines
  178. ! ===
  179. ! ====================================================================
  180. subroutine UDUnits_Make( unit, status )
  181. ! --- in/out ---------------------------------
  182. integer(UD_POINTER_KIND), intent(out) :: unit
  183. integer, intent(out) :: status
  184. ! --- external -------------------------------
  185. ! set return status:
  186. integer(UD_POINTER_KIND), external :: UTMake
  187. ! --- begin ----------------------------------
  188. ! Create a new unit:
  189. unit = UTMake()
  190. ! set return status:
  191. status = 0
  192. if ( unit < 0 ) status = int(unit)
  193. end subroutine UDUnits_Make
  194. ! ***
  195. subroutine UDUnits_Decode( spec, unit, status )
  196. ! --- in/out ---------------------------------
  197. character(len=*), intent(in) :: spec
  198. integer(UD_POINTER_KIND), intent(in) :: unit
  199. integer, intent(out) :: status
  200. ! --- external -------------------------------
  201. ! Decode a formatted specification into a unit:
  202. integer, external :: UTDec
  203. ! --- begin ----------------------------------
  204. ! Decode a formatted specification into a unit:
  205. status = UTDec( spec, unit )
  206. if (status/=0) write (error_message,'("could not decode `",a,"`")') trim(spec)
  207. end subroutine UDUnits_Decode
  208. ! ***
  209. subroutine UDUnits_Encode( unit, spec, status )
  210. ! --- in/out ---------------------------------
  211. integer(UD_POINTER_KIND), intent(in) :: unit
  212. character(len=*), intent(out) :: spec
  213. integer, intent(out) :: status
  214. ! --- external -------------------------------
  215. ! Encode a unit into a formatted specification:
  216. integer, external :: UTEnc
  217. ! --- begin ----------------------------------
  218. ! Encode a unit into a formatted specification:
  219. status = UTEnc( unit, spec )
  220. if (status/=0) write (error_message,'("could not encode from unit into formatted specification")')
  221. end subroutine UDUnits_Encode
  222. ! ***
  223. subroutine UDUnits_Convert( unit_from, unit_to, slope, intercept, status )
  224. ! --- in/out ---------------------------------
  225. integer(UD_POINTER_KIND), intent(in) :: unit_from
  226. integer(UD_POINTER_KIND), intent(in) :: unit_to
  227. real(8), intent(out) :: slope, intercept
  228. integer, intent(out) :: status
  229. ! --- external -------------------------------
  230. ! Convert from one unit to another:
  231. integer, external :: UTCvt
  232. ! --- local ----------------------------------
  233. character(len=spec_len) :: spec_from, spec_to
  234. ! --- begin ----------------------------------
  235. ! Convert from one unit to another:
  236. status = UTCvt( unit_from, unit_to, slope, intercept )
  237. if (status/=0) then
  238. call UDUnits_Encode( unit_from, spec_from, status )
  239. if (status/=0) then
  240. write (error_message,'("could not convert units; failed to convert unit_from to specification")')
  241. status = error_status; return
  242. end if
  243. call UDUnits_Encode( unit_to, spec_to, status )
  244. if (status/=0) then
  245. write (error_message,'("could not convert from `",a,"`; failed to convert unit_to to specification")') trim(spec_from)
  246. status = error_status; return
  247. end if
  248. write (error_message,'("could not convert from `",a,"` to `",a,"`")') trim(spec_from), trim(spec_to)
  249. status = error_status; return
  250. end if
  251. end subroutine UDUnits_Convert
  252. ! ====================================================================
  253. ! ===
  254. ! === high level routines
  255. ! ===
  256. ! ====================================================================
  257. subroutine UDUnits_Standard( spec_from, spec_to, status )
  258. ! --- in/out ---------------------------------
  259. character(len=*), intent(in) :: spec_from
  260. character(len=*), intent(out) :: spec_to
  261. integer, intent(out) :: status
  262. ! --- const ----------------------------------
  263. character(len=*), parameter :: rname = mname//'/UDUnits_Standard'
  264. ! --- external -------------------------------
  265. ! Convert from one unit to another:
  266. integer, external :: UTCvt
  267. ! --- local ----------------------------------
  268. integer(UD_POINTER_KIND) :: unit_from
  269. ! --- begin ----------------------------------
  270. ! setup unit:
  271. call UDUnits_Make( unit_from, status )
  272. if (status/=0) return
  273. ! fill with secification:
  274. call UDUnits_Decode( spec_from, unit_from, status )
  275. if (status/=0) return
  276. ! extract standard name:
  277. call UDUnits_Encode( unit_from, spec_to, status )
  278. if (status/=0) return
  279. ! ok
  280. status = 0
  281. end subroutine UDUnits_Standard
  282. ! ***
  283. subroutine UDUnits_ConversionFactor( spec_from, spec_to, factor, status )
  284. ! --- in/out ---------------------------------
  285. character(len=*), intent(in) :: spec_from
  286. character(len=*), intent(in) :: spec_to
  287. real(8), intent(out) :: factor
  288. integer, intent(out) :: status
  289. ! --- const ----------------------------------
  290. character(len=*), parameter :: rname = mname//'/UDUnits_ConversionFactor'
  291. ! --- external -------------------------------
  292. ! Convert from one unit to another:
  293. integer, external :: UTCvt
  294. ! --- local ----------------------------------
  295. integer(UD_POINTER_KIND) :: unit_from
  296. integer(UD_POINTER_KIND) :: unit_to
  297. real(8) :: offset
  298. ! --- begin ----------------------------------
  299. ! input unit:
  300. call UDUnits_Make( unit_from, status )
  301. if (status/=0) return
  302. call UDUnits_Decode( spec_from, unit_from, status )
  303. if (status/=0) return
  304. ! output unit:
  305. call UDUnits_Make( unit_to, status )
  306. if (status/=0) return
  307. call UDUnits_Decode( spec_to, unit_to, status )
  308. if (status/=0) return
  309. ! Convert from one unit to another:
  310. call UDUnits_Convert( unit_from, unit_to, factor, offset, status )
  311. if (status/=0) return
  312. ! check ...
  313. if ( offset /= 0.0d0 ) then
  314. write (error_message,*) 'found conversion offset unequal to zero : ', offset
  315. status=error_status; return
  316. end if
  317. ! ok
  318. status = 0
  319. end subroutine UDUnits_ConversionFactor
  320. end module UDUnits
  321. !! ######################################################################
  322. !! ###
  323. !! ### test
  324. !! ###
  325. !! ######################################################################
  326. !
  327. !! f90 -o test.x udunits_inc.F udunits.F90 -I${UDUNITS_HOME}/include -L${UDUNITS_HOME}/lib -ludunits && ./test.x
  328. !
  329. !program test_udunits
  330. !
  331. ! use UDUnits
  332. !
  333. ! implicit none
  334. !
  335. ! integer :: status
  336. ! integer(UD_POINTER_KIND) :: unit, unit2
  337. ! character(len=64) :: spec, spec2
  338. ! real(8) :: slope, offset
  339. !
  340. ! write (*,'("begin test_udunits")')
  341. !
  342. ! write (*,'(" UD_POINTER_KIND : ",i4)') UD_POINTER_KIND
  343. !
  344. ! ! * module initialisation
  345. !
  346. ! call UDUnits_Init( status )
  347. ! if (status/=UDUNITS_NOERR) then
  348. ! print *, trim(UDUnits_StrError(status))
  349. ! stop
  350. ! end if
  351. !
  352. ! ! * high levell routines
  353. !
  354. ! spec = 'kg/s'
  355. ! call UDUnits_Standard( spec, spec2, status )
  356. ! write (*,'(" standard name of `",a,"` is `",a,"`")') trim(spec), trim(spec2)
  357. !
  358. ! spec = 'gram/cm3' ; spec2 = 'kg/m3'
  359. ! call UDUnits_ConversionFactor( spec, spec2, slope, status )
  360. ! write (*,'(" conversion factor from `",a,"` to `",a,"` is ",f12.4)') trim(spec), trim(spec2), slope
  361. !
  362. ! ! * low level routines
  363. !
  364. ! call UDUnits_Make( unit, status )
  365. ! call UDUnits_Make( unit2, status )
  366. !
  367. ! call UDUnits_Decode( 'kg', unit, status )
  368. ! call UDUnits_Encode( unit, spec, status )
  369. !
  370. ! call UDUnits_Convert( unit, unit2, slope, offset, status )
  371. !
  372. ! ! * done with module
  373. !
  374. ! call UDUnits_Done( status )
  375. !
  376. ! ! *
  377. !
  378. ! write (*,'("end test_udunits")')
  379. !
  380. !end program test_udunits