Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Fortran type-free variable and type-free dictionary

License

NotificationsYou must be signed in to change notification settings

zerothi/fdict

Repository files navigation

Build StatusDonate

A variable and dictionary in pure fortran for retaining any data-typeand a fast hash-table dictionary.

Usage

This module consists of two separate modules which co-exist formaintenance and usage reasons.

First, the variable module which is a type-free variable that can containany variable type, and any dimension as well.

Second, the dictionary module which contains a hash-table of variablesthat can containany data-type allowed by the variable module.

Downloading and installation

Installing fdict requires a download of the libraryhosted atgithub atfdict@git.

Installation can be done via 2 different back-ends. 1)smeka build system, or 2)CMake build.

smeka build system

Extract and create ansetup.make file for compilation, a minimalsetup.make file can look like this

FC=gfortranFFLAGS = -g

if in a build directory, also add this:

PREFIX =

Typemake and a library calledlibfdict.a is created.Subsequently the installation may be performed by:

make PREFIX=/path/to/fdict install

which installs the required files (modules and libraries) to the folder.It will also install pkg-config files for auto-detection.

CMake

CMake procedure can be done via the normal procedure:

cmake -S. -Bbuild-fdictcmake --build build-fdict

fdict should also be able to be used in a sub-project. If problemsoccur, feel free to open up an issue.

Linking to fdict

To use the dictionary you need to add include statements for themodules as well as linking to the program.

To link fdict to your program the following can be used in aMakefile

FDICT_PATH  = /path/to/fdict/parentFDICT_LIBS  = -L$(FDICT_PATH) -lfdictFDICT_INC   = -I$(FDICT_PATH)

Alternatively, one can use pkg-config for obtaining the include flags andlibraries.

For parent programs that usesfdict there are 2 ways of knowing whichfdictversion one is using:

  1. A simple header file (like C-preprocessor statements), this informationis found infdict.inc
  2. Afypp compatible include file which contains library version andwhich data types are included in the built library, see the filefdict.fypp

The filefdict.inc may be included in projects which exposes the followingdefinitions:

_FDICT_MAJOR_ 0_FDICT_MINOR_ 9_FDICT_PATCH_ 0_FDICT_VERSION_ 0.9.0

This is mainly meant as a feature usable when the fdict interface ande.g. modules change names.

Alternatively thefdict.fypp inclusion file exposes variables such as:

  • the library version numbers (as above)
  • which data-types are enabled
  • the number of ranks for each kind

Thefdict.fypp file is handy when you are already relying onfyppwhereas the regularfdict.inc header files are easy to use in standardfortran source compilation.

Controlling interfaces

Typically not needed: allows for customization of different interfaces.

By default the number of dimensions allowed by the library is 5, i.e.there is no interface created forreal a(:,:,:,:,:,:), etc. However,to accomodate arbitrary dimensions you must define constants in yoursetup.make file.

There are several fine-tuning options that allows creating more or fewerinterfaces. As the number of dimensions increases, so does the librarysize. If only a specific maximum range of ranks are required, it mightbe beneficial to reduce maximum ranks to the used range.

Currently thefdict library supports the types listed in the below table:

TypePrecision format (GNU)C-typeDefault
type(variable_t)---yes
character(len=1)charyes
integerselected_int_kind(2)byteno
integerselected_int_kind(4)shortno
integerselected_int_kind(9)intyes
integerselected_int_kind(18)longyes
realselected_real_kind(6)floatyes
realselected_real_kind(15)doubleyes
realselected_real_kind(18)ext. doubleno
realselected_real_kind(30)quadno
complexselected_real_kind(6)float complexyes
complexselected_real_kind(15)double complexyes
complexselected_real_kind(18)ext. double complexno
complexselected_real_kind(30)quad complexno
logicalselected_int_kind(2)byteno
logicalselected_int_kind(4)shortno
logicalselected_int_kind(9)intyes
logicalselected_int_kind(18)longno
type(c_ptr)void *no
type(c_funptr)(procedure)void *no

In theDefault column one can see which data-types are enabled by default. The mostcommonly used data-types are enabled.

To enable the non-default data types you can do so with (Makefile scheme):

FYPPFLAGS += -DWITH_INT8=1 # for int kind(2)FYPPFLAGS += -DWITH_INT16=1 # for int kind(4)# Note that not all compilers support extended precisions# If you experience compiler errors, this is likely the cause.FYPPFLAGS += -DWITH_REAL80=1 # for real and complex kind(18)FYPPFLAGS += -DWITH_REAL128=1 # for real and complex kind(30)FYPPFLAGS += -DWITH_LOG8=1 # for logical kind(2)FYPPFLAGS += -DWITH_LOG16=1 # for logical kind(4)FYPPFLAGS += -DWITH_LOG64=1 # for logical kind(18)FYPPFLAGS += -DWITH_ISO_C=1 # for enabling c_ptr and c_funptr

Forcmake the variables are all prefixed withFDICT_, e.g.-DFDICT_FYPPFLAGS,to ensure there are no clashes with parent programs.

By defaultfdict generates the kind specifications from theselected_*_kind routines,however, if one wishes to use theiso_fortran_env module simply addFYPPFLAGS += -DWITH_ISO_ENV.

To control the maximum ranks in the interfaces one can add these:

# type(c_ptr), type(c_funptr) and character(len=1)# are data types that are not affected by the MAXRANK variable# globally define the maximum ranks of all but the above listedFYPPFLAGS += -DMAXRANK=n# integer(*) types maximum rankFYPPFLAGS += -DMAXRANK_INT=n# real(*) types maximum rankFYPPFLAGS += -DMAXRANK_REAL=n# complex(*) types maximum rankFYPPFLAGS += -DMAXRANK_CMPLX=n# logical(*) types maximum rankFYPPFLAGS += -DMAXRANK_LOG=n# type(c_ptr), type(c_funptr) types maximum rankFYPPFLAGS += -DMAXRANK_ISO_C=n

variable

Using this module one gains access to a generic type variable whichcan containany data format.

It is used like this:

use variableinteger :: a(3type(variable_t) :: va = 2call assign(v,a)a = 3call assign(a,v)

Also the variable contains an abbreviation for assigning pointers tonot copy data, but retain data locality:

integer, target :: a(3)type(variable_t) :: va = 2call associate(v,a)a = 3! Now v contains a = 3

To delete a variable do:

use variabletype(variable_t) :: vcall delete(v)

However, when the variable is using pointers, instead the user can do

use variabletype(variable_t) :: v! preferredcall nullify(v)! orcall delete(v,dealloc=.false.)

which merely destroys the variable object and thus retains the datawhere it is. As with any other pointer arithmetic it is up to the programmerto ensure there is no memory leaks.

In some cases one does not know which data-type is being stored in a variable.Here it may be beneficial to lookup the type of data:

use variableinteger, target :: a(3)type(variable_t) :: va(:) = 2call associate(v,a)if ( which(v) == which(a) ) then ! signal integer of 1D (i0 for scalar)   call assign(a, v)end if! Another possibility is to *try* to get the valuelogical :: successinteger, target :: i1(3)real, target :: r1(3)call assign(r1, v, success=success)if ( .not. success ) then    call assign(i1, v, success=success)end if... etc ...

However, it may be better to explicitly check the type usingwhich.For consistency and API changes, it is encouraged to usewhich(<type>) toensure that the data-types are as expected. I.e.which([real(real64) ::])is the preferred way of forcing a data-type contained in a variable.

dictionary

Usingtype(variable_t) it becomes easy to create dictionaries in fortran.

Using this module we implement a dictionary which can containany dataformat using akey:val based formalism. The underlying data structure is alinked list sorted according to hash-values of the keys. Hence searchingfor specific elements in the dictionary isextremely fast. Searching adictionary with 100 keys 300000 times takes less than 0.04 seconds ona Haswell laptop.Concatenating dictionaries is also very fast.

Creating a dictionary is almost as easy as the Python equivalent:

use dictionarytype(dictionary_t) :: dictdict = ('KEY'.kv.1)

To extend a dictionary one uses the concatenating format:

dict = dict // ('Hello'.kv.'world') // ('No'.kv.'world')

Again as is used by thetype(variable_t) one can with benefit use.kvp. to createthe dictionary value by pointers instead of copying the content.Hence doing:

real :: r(4)dict = dict // ('reals'.kvp.r)r = 4

will change the value in the dictionary.Note that one can easily create memory leaks with dictionaries:

use dictionarytype(dictionary_t) :: dictdict = ('KEY'.kv.1)dict = dict // ('KEY'.kv.2)dict = ('KEY'.kv.3)

The 1st assignement is valid since the dictionary is empty.The 2nd assignment concatenates and does not produce any memory leaks.In that case the old keyKEY is deleted and the new value2 is inserted.The 3rd assignment produces a memory leak since the pointer to the originaldictionary gets lost. Be sure to callcall delete(dict) prior to singleassignments.

There are various ways to access the data in a dictionary.

  1. Accessing specific keys may be exercised using

     use dictionary type(dictionary_t) :: dict type(variable_t) :: var integer :: i real :: r logical :: success dict = ('KEY'.kv.1) call assign(r, dict, 'KEY', success=success) if ( .not. success ) call assign(i, dict, 'KEY', success=success) call assign(var, dict, 'KEY')

    Since values in dictionaries are stored usingvariable_t we have tofollow the limitations of that implementation. Therefore it may be betterto always use a temporaryvariable_t to retrieve the values stored. Thiswill remove a redundant lookup in the dictionary.

  2. Users may find the.key. and.value. operators which only acts on the firstelement of the dictionary (which may be a surprise). This is only useful for loopingdictionaries.

     use dictionary type(dictionary_t) :: dict, dict_first type(variable_t) :: var character(DICTIONARY_KEY_LENGTH) :: key integer :: i real :: r logical :: success dict = ('KEY'.kv.1) dict = dict // ('KEY1'.kv.3) ! start looping dict_first = .first. dict do while ( .not. (.empty. dict_first) )    ! now .key. and .value. could be used:    key = .key. dict_first    call assign(var, dict_first)    ! Get next dictionary entry    dict_first = .next. dict_first end while

Note that the dictionary can also containany data type.

However, if it needs to do custom data-types the programmer needs toextend the code by supplying a few custom routines.

Intrinsically the dictionary can contain dictionaries by this:

use dictionarytype(dictionary_t) :: d1, d2d1 = ('hello'.kv.'world')d2 = ('hello'.kv.'world')d1 = d1 // ('dict'.kvp.d2)

But it will be up to the user toknow the key for data types other thanintegers, reals, complex numbers, characters andc_* extension types.

Note that the dictionary contained is passed by reference, and thusif you deleted2, you will have a dangling pointer ind1.

Contributions, issues and bugs

I would advice any users to contribute as much feedback and/or PRs to furthermaintain and expand this library.

Please do not hesitate to contribute!

If you find any bugs please form abug report/issue.

If you have a fix please consider adding apull request.

License

The fdict license isMPL-2.0, see the LICENSE file.

Thanks

A big thanks goes to Alberto Garcia for contributing ideas and givingme bug reports. Without him the interface would have been much morecomplex!


[8]ページ先頭

©2009-2025 Movatter.jp