fortran-lang / stdlib

Fortran Standard Library
https://stdlib.fortran-lang.org
MIT License
1.02k stars 161 forks source link

Codata constants. #800

Closed MilanSkocic closed 1 month ago

MilanSkocic commented 2 months ago

The codata constants are implemented as derived types (#347). This way, the values, the uncertainties and the units are easily available. Only the latest release is implemented i.e. 2018. The 2022 CODATA adjustment are not available yet.

The constants, as provided by the NIST, are double precision reals and I though it was useless to provide to include any other precision.

Your comments and suggestions are welcome.

jvdp1 commented 2 months ago

Related with:

jvdp1 commented 2 months ago

Thank you @MilanSkocic for this PR. I am not a user of such constants. So I will add a few possible reviewers.

A general question/comment is about the kinds provided. I think we should try to provide at least single and double precision (it should be easy with fypp). But it is maybe useless (I don't know as I never use such things).

jalvesz commented 2 months ago

Just a style comment. Maybe instead of defining the actual value of the constants with real(7294.29954142d0,dp) you could simply use 7294.29954142_dp which already declares the kind from the parameter dp, instead of asking for a conversion to the same kind.

MilanSkocic commented 2 months ago

Thank you @MilanSkocic for this PR. I am not a user of such constants. So I will add a few possible reviewers.

A general question/comment is about the kinds provided. I think we should try to provide at least single and double precision (it should be easy with fypp). But it is maybe useless (I don't know as I never use such things).

I have a dedicated repo for generating the constants for the stdlib (https://github.com/MilanSkocic/stdlib_codata) where I have tested single precisions. However, I've got overflows for some constants. I also have my package where I provided codata constants for 2018, 2014 and 2010.

In my point of view single precision are not relevant for the codata constants.

MilanSkocic commented 2 months ago

Just a style comment. Maybe instead of defining the actual value of the constants with real(7294.29954142d0,dp) you could simply use 7294.29954142_dp which already declares the kind from the parameter dp, instead of asking for a conversion to the same kind.

Right! I will make the change as soon as possible.

perazz commented 2 months ago

This is a very interesting proposal @MilanSkocic. I have a comment that does not want to mandate any specifications, but just lay out ideas on how this could be made the most productive.

have looked at the state of the art as offered from other well-established packages: Scipy offers a full range of CODATA constants. They're very easy to use because they're basically just numeric constants. So I think it would be most useful if stdlib could also provide that facility. I think there are two ways to do it:

1) Implement all overloaded operators (==, /=, >, <, +, -, *, **, /, etc.) so that these parameter derived types can be used exactly as any other real parameter. However, because they also contain units and other information (i.e. error), more tools could be available e.g. something like .approx. to check whether a value is within the uncertainty bounds of the constant.

The drawback is you need to import all operators, not only the constant:

use stdlib_codata, only: ALPHA_PARTICLE_ELECTRON_MASS_RATIO, operator(+), operator(/), assignment(=), ...

2) Also provide a "macro-like" interface to each of them, so that they can also be used as a real constant in code. for example:

interface gravitational_constant
   module procedure g_sp
   module procedure g_dp
   ...
end interface

!...
elemental real(dp) function g_dp(mold) 
   real(dp), intent(in), optional :: mold ! dp is the default kind
   g_dp = real(NEWTONIAN_CONSTANT_OF_GRAVITATION%value, kind=dp)
end function g_dp

elemental real(dp) function g_sp(mold)
   real(sp), intent(in) :: mold ! sp is not the default kind -> not optional
   g_sp = real(NEWTONIAN_CONSTANT_OF_GRAVITATION%value, kind=sp)
ed function g_sp
!etc.

so one could use them like

real(dp) :: energy = h * gravitational_constant()
real(sp) :: nrg = h * gravitational_constant(mold=0.0)
jalvesz commented 2 months ago

Or maybe use the generic facility to avoid having to declare an interface for each constant? like this: https://godbolt.org/z/17qP4TWoa

perazz commented 2 months ago

the generic facility

It's definitely useful if this class has a few methods (including a nice print, etc.). For the purpose of just returning the numeric value (and because there is no constexpr in Fortran), I think a nice named interface is more readable IMHO. Another option would be to override real and do something like real(NEWTONIAN_CONSTANT_OF_GRAVITATION,kind=dp) which is also Fortranic, but is too long in my opinion

MilanSkocic commented 2 months ago

@perazz You are right, in Scipy, some of the most common physical constants are available directly as reals. They are aliases to the values of the complete list of Codata constants which are implemented as a dictionary where keys are names and items are tuples containing the value, the uncertainty and the unit. I can implement the codata constants as derived types with the generic facility as suggested by @jalvesz in a module called stdlib_codata and add aliases to the values of the most common physical constants in a module called stdlib_constants as it is done in Scipy. In the latter, mathematical constants, such as PI (#99), could be added later.

An example of using the constants:

program test

! use of the aliases to the values of the most common codata constants and mathematical constants
use stdlib_constants, only: c, PI  

! use of other codata constants
use stdlib_codata, only: ALPHA_PARTICLE_ELECTRON_MASS_RATIO 

real :: b
b = ALPHA_PARTICLE_ELECTRON_MASS_RATIO%eval(b) ! generic facility

end program test

A more advanced use of the stdlib_codata module would allow the user to directly initialize a parameter by accessing the value of the constant.

program test
use stdlib_codata, only: ALPHA_PARTICLE_ELECTRON_MASS_RATIO
use stdlib_kinds, only: dp
real(dp), parameter :: alpha = ALPHA_PARTICLE_ELECTRON_MASS_RATIO%value
end program test
MilanSkocic commented 2 months ago

Update based on suggestions from @perazz and @jalvesz:

MilanSkocic commented 2 months ago

Do I need to create the associated documentation in /docs/specs? I think that an example would be useful in the examplefolder. Do you think that a dedicated subfolder constantsor codata is necessary or the example can be placed in an already existing subfolder?

jvdp1 commented 2 months ago

Do I need to create the associated documentation in /docs/specs?

Yes, please. It could be called stdlib_constants.md

I think that an example would be useful in the examplefolder. Do you think that a dedicated subfolder constantsor codata is necessary or the example can be placed in an already existing subfolder?

It should be in a dedicated subfolder, e.g., constants.

MilanSkocic commented 2 months ago

I have added the documentation and the example.

MilanSkocic commented 1 month ago

@perazz to_real for consistency with stdlib has been added.

MilanSkocic commented 1 month ago

Thank you @MilanSkocic for this PR. I think it is close to be merged. Here are some minor comments. Could you maybe also add some tests? At least for the DR?

No problem the tests. I was already working on.

The code for autogenerating the stdlib_codata module is available here. It is a simple parser written in Python of the raw data provided by the NIST. If needed it can be incorporated in stdlib.

MilanSkocic commented 1 month ago

I added tests for some Codata constants (values and uncertainties) that I have arbitrary chosen.

I did not implement any test for mathematical constants as I have only defined one (PI). I am not sure how the mathematical constants should be implemented i.e. either the way I did or through derived type as mentioned issue #99. @jvdp1 Let me know if you want me to remove the mathematical constant PI until a definitive decision is made.

jvdp1 commented 1 month ago

Could you also rebase your code to solve the conflicts, please?

MilanSkocic commented 1 month ago

No problem @jvdp1 . I will take into account all your comments and suggestions. I will solve the conflicts and I will let you know when it is done.

jvdp1 commented 1 month ago

Sorry. I though it was always the same value. If not, it should not be a module variable because the tests can be run in parallel. Could it be a mix of the two approaches?

Le dim. 19 mai 2024, 07:38, Milan Skocic @.***> a écrit :

@.**** commented on this pull request.

In test/constants/test_constants.f90 https://github.com/fortran-lang/stdlib/pull/800#discussion_r1605951192:

  • implicit none
  • type(error_type), allocatable, intent(out) :: error
  • real(dp) :: value, expected, diff, fac
  • fac = 1.0d0
  • expected = 0.0d0 * fac
  • value = SPEED_OF_LIGHT_IN_VACUUM%to_real(fac, uncertainty=.true.) * fac
  • diff = expected - value
  • call check(error, diff, 0.0d0)
  • if (allocated(error)) return +end subroutine
  • +subroutine test_U_STANDARD_ACCELERATION_OF_GRAVITY(error)

  • implicit none
  • type(error_type), allocatable, intent(out) :: error
  • real(dp) :: value, expected, diff, fac
  • fac = 1.0d0

I changed fac as a module level variable. I did not declare it as a parameter because I need to able to change according to the values that are being tested.

— Reply to this email directly, view it on GitHub https://github.com/fortran-lang/stdlib/pull/800#discussion_r1605951192, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD5RO7EE4AY2HZ3N6CO767TZDA3HHAVCNFSM6AAAAABGRC7NM6VHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZDANRUHE4TOMZSGA . You are receiving this because you were mentioned.Message ID: @.***>

MilanSkocic commented 1 month ago

Sorry. I though it was always the same value. If not, it should not be a

module variable because the tests can be run in parallel.

Could it be a mix of the two approaches?

Le dim. 19 mai 2024, 07:38, Milan Skocic @.***> a

écrit :

@.**** commented on this pull request.


In test/constants/test_constants.f90

https://github.com/fortran-lang/stdlib/pull/800#discussion_r1605951192:

  • implicit none
  • type(error_type), allocatable, intent(out) :: error

  • real(dp) :: value, expected, diff, fac

  • fac = 1.0d0

  • expected = 0.0d0 * fac

  • value = SPEED_OF_LIGHT_IN_VACUUM%to_real(fac, uncertainty=.true.) * fac

  • diff = expected - value

  • call check(error, diff, 0.0d0)

  • if (allocated(error)) return

+end subroutine

+

+subroutine test_U_STANDARD_ACCELERATION_OF_GRAVITY(error)

  • implicit none

  • type(error_type), allocatable, intent(out) :: error

  • real(dp) :: value, expected, diff, fac

  • fac = 1.0d0

I changed fac as a module level variable. I did not declare it as a

parameter because I need to able to change according to the values that are

being tested.

Reply to this email directly, view it on GitHub

https://github.com/fortran-lang/stdlib/pull/800#discussion_r1605951192,

or unsubscribe

https://github.com/notifications/unsubscribe-auth/AD5RO7EE4AY2HZ3N6CO767TZDA3HHAVCNFSM6AAAAABGRC7NM6VHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZDANRUHE4TOMZSGA

.

You are receiving this because you were mentioned.Message ID:

@.***>

No problem.

MilanSkocic commented 1 month ago

@jvdp1

jvdp1 commented 1 month ago

Faire point @perazz

I will not be a user of such data. But i think that they should be kept in stdlib. Both notations are fine for me

Le mar. 21 mai 2024, 17:43, Federico Perini @.***> a écrit :

@.**** commented on this pull request.

In src/stdlib_constants.fypp https://github.com/fortran-lang/stdlib/pull/800#discussion_r1608552969:

  • STEFAN_BOLTZMANN_CONSTANT, &
  • WIEN_WAVELENGTH_DISPLACEMENT_LAW_CONSTANT, &
  • RYDBERG_CONSTANT, &
  • ELECTRON_MASS, &
  • PROTON_MASS, &
  • NEUTRON_MASS, &
  • ATOMIC_MASS_CONSTANT
  • private
  • ! mathematical constants
  • :for k in KINDS

  • real(${k}$), parameter, public :: PI${k}$ = acos(-1.0${k}$) !! PI
  • :endfor

  • ! Physical constants
  • real(dp), parameter, public :: c = SPEED_OF_LIGHT_IN_VACUUM%value !! Speed of light in vacuum

When using numeric constants, the shorter their names the better imho. Because they're inside a module that only contains them, I think they're mostly harmless. In case one has overlapping names, they can always rename them as:

use stdlib_constants, only: clight => c

Of course, it's important that their names are not confusing as @awvwgk https://github.com/awvwgk is suggesting. But here they're taken from Scipy, so, probably a good reference for most people coming from the Python world.

— Reply to this email directly, view it on GitHub https://github.com/fortran-lang/stdlib/pull/800#discussion_r1608552969, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD5RO7DZ36D3LZGQH6ER4JTZDNTSLAVCNFSM6AAAAABGRC7NM6VHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZDANRYHE4TIMRTGY . You are receiving this because you were mentioned.Message ID: @.***>

MilanSkocic commented 1 month ago

Do we have an agreement to proceed without any prefix on the constants as they are defined in Scipy? @jvdp1

jvdp1 commented 1 month ago

Thank you @MilanSkocic What do you think @perazz @jalvesz @awvwgk ?

jalvesz commented 1 month ago

LGTM!! brilliant work @MilanSkocic

jvdp1 commented 1 month ago

thank you @MilanSkocic for this PR. I will merge it.

@MilanSkocic Don't hesitate to advertise this new addition on Discourse and other channels.

MilanSkocic commented 1 month ago

Thank you @jvdp1 ! I'm glad the codata constants have been merged. For sure I'll advertise the addition. It was a pleasure to contribute to the Fortran-lang community.