Skip to content

Commit

Permalink
Merge pull request #28 from zerothi/fyppify
Browse files Browse the repository at this point in the history
Use fypp for the declarations
  • Loading branch information
zerothi authored Feb 27, 2023
2 parents 2c4534e + 246ca7c commit 14f9104
Show file tree
Hide file tree
Showing 47 changed files with 1,283 additions and 1,560 deletions.
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ include $(TOP_DIR)/src/Makefile.inc
$(LIBRARIES): $(OBJECTS)

# Create target
lib: settings.bash fdict.inc $(LIBRARIES)
lib: fdict.inc fdict.fypp $(LIBRARIES)


# Include the makefile in the test directory
Expand Down Expand Up @@ -80,4 +80,3 @@ dist-fdict:
gzip fdict-$(PROJECT_VERSION).tar

dist: dist-fdict

24 changes: 11 additions & 13 deletions Makefile.project
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ _SMEKA_project = 1

# Step this version upon new versions
PROJECT_MAJOR = 0
PROJECT_MINOR = 8
PROJECT_MICRO = 1
PROJECT_VERSION = $(PROJECT_MAJOR).$(PROJECT_MINOR).$(PROJECT_MICRO)
PROJECT_MINOR = 9
PROJECT_PATCH = 0
PROJECT_VERSION = $(PROJECT_MAJOR).$(PROJECT_MINOR).$(PROJECT_PATCH)

# These are constant default
PROJECT_NAME = fdict
Expand All @@ -16,6 +16,10 @@ FDICT_LIB ?= fdict$(LIB_SUFFIX)
FDICT_LIB_STATIC ?= lib$(FDICT_LIB).a
FDICT_LIB_SHARED ?= lib$(FDICT_LIB).so

FYPPFLAGS += -DPROJECT_MAJOR=$(PROJECT_MAJOR)
FYPPFLAGS += -DPROJECT_MINOR=$(PROJECT_MINOR)
FYPPFLAGS += -DPROJECT_PATCH=$(PROJECT_PATCH)

# Define custom options for fdict
STATIC ?= 1
# If the library was built previously,
Expand All @@ -38,6 +42,8 @@ ifeq ($(SHARED),1)
PIC = 1
endif

# Add dependency of libraries
install: $(LIBRARIES) fdict.inc fdict.fypp

# Create custom installation of modules
.NOTPARALLEL: install-mod
Expand All @@ -46,24 +52,16 @@ install-mod: $(F_MODS)
$(INSTALL) -m $(_OCT_rwxrxrx) -d $(DESTDIR)$(FMOD_DIR)
$(INSTALL) -m $(_OCT_rwrr) $(F_MODS) $(DESTDIR)$(FMOD_DIR)

.NOTPARALLEL: install-settings
smeka-install: install-settings
install-settings: settings.bash smeka-install-init-dir
$(INSTALL) -m $(_OCT_rwrr) settings.bash $(DESTDIR)$(BIN_DIR)

.NOTPARALLEL: install-header
smeka-install: install-header
install-header: fdict.inc smeka-install-init-dir
$(INSTALL) -m $(_OCT_rwrr) fdict.inc $(DESTDIR)$(INC_DIR)
$(INSTALL) -m $(_OCT_rwrr) fdict.inc fdict.fypp $(DESTDIR)$(INC_DIR)


# Force the deletion of both libraries
clean:
-$(RM) $(RM_FLAG_FORCE) $(FDICT_LIB_STATIC) $(FDICT_LIB_SHARED) settings.bash fdict.inc

-$(RM) $(RM_FLAG_FORCE) $(FDICT_LIB_STATIC) $(FDICT_LIB_SHARED) fdict.inc fdict.fypp

settings.bash:
VPATH="$(TOP_DIR)" $(TOP_DIR)/setup.sh --default

.PHONY: doc
doc: source
Expand Down
185 changes: 125 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ that can contain _any_ data-type allowed by the variable module.

## Downloading and installation ##

Installing fdict requires a download of the library
Installing fdict requires a download of the library
hosted at [github](https://github.com/) at [fdict@git].

Extract and create an `setup.make` file for compilation, a minimal
Expand All @@ -28,7 +28,7 @@ Extract and create an `setup.make` file for compilation, a minimal
FC=gfortran
FFLAGS = -g

Type `make` and a library called `libfdict.a` is created.
Type `make` and a library called `libfdict.a` is created.
Subsequently the installation may be performed by:

make PREFIX=/papth/to/fdict install
Expand All @@ -44,80 +44,143 @@ To link fdict to your program the following can be used in a `Makefile`
FDICT_LIBS = -L$(FDICT_PATH) -lfdict
FDICT_INC = -I$(FDICT_PATH)

For parent programs that uses `fdict` there are 2 ways of knowing which `fdict`
version one is using:

1. A simple header file (like C-preprocessor statements), this information
is found in `fdict.inc`
2. A `fypp` compatible include file which contains library version and
which data types are included in the built library, see the file `fdict.fypp`


The file `fdict.inc` may be included in projects which exposes the following
definitions:

_FDICT_MAJOR_ 0
_FDICT_MINOR_ 8
_FDICT_MICRO_ 1
_FDICT_VERSION_ 0.8.1
_FDICT_MINOR_ 9
_FDICT_PATCH_ 0
_FDICT_VERSION_ 0.9.0

which may be used in functional codes to utilize the correct interfaces.
This is mainly meant as a feature usable when the fdict interface and
e.g. modules change names.

Alternatively the `fdict.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

#### Controlling interface parameters ####
The `fdict.fypp` file is handy when you are already relying on `fypp`
whereas the regular `fdict.inc` header files are easy to use in standard
fortran source compilation.

__Typically not needed__: allows for customization of different interfaces.

By default the number of dimensions allowed by the library is 3, i.e.
there is no interface created for `real a(:,:,:,:)`, etc. However,
to accomodate arbitrary dimensions you can call a _setup_ script
which initializes a different number of dimensions, which can
be controlled individually.

Run `./setup.sh` to get options regarding the setup.
#### Controlling interfaces ####

For instance, if you require interfaces for `real` and `real(kind(0.d0))`
up to 4 dimensions and all others up to 3 dimensions you can do this
__Typically not needed__: allows for customization of different interfaces.

# -A == all data-types, s = single, d = double
./setup.sh -A 3 -s 4 -d 4
# -R is a shorthand for both -s and -d
./setup.sh -A 3 -R 4
By default the number of dimensions allowed by the library is 5, i.e.
there is no interface created for `real a(:,:,:,:,:,:)`, etc. However,
to accomodate arbitrary dimensions you must define constants in your
`setup.make` file.

There are several fine-tuning options that allows creating more or fewer
interfaces. As the number of dimensions increases, so does the library
size. If only a specific maximum range of ranks are required, it might
be beneficial to reduce maximum ranks to the used range.


Currently the `fdict` library supports the types listed in the below table:

| Type | Precision format (GNU) | C-type | Default |
|-------------------|--------------------------|----------------------|---------|
| `type(variable_t)`| | --- | yes |
| `character(len=1)`| | `char` | yes |
| `integer` | `selected_int_kind(2)` | `byte` | no |
| `integer` | `selected_int_kind(4)` | `short` | no |
| `integer` | `selected_int_kind(9)` | `int` | yes |
| `integer` | `selected_int_kind(18)` | `long` | yes |
| `real` | `selected_real_kind(6)` | `float` | yes |
| `real` | `selected_real_kind(15)` | `double` | yes |
| `real` | `selected_real_kind(18)` | `ext. double` | no |
| `real` | `selected_real_kind(30)` | `quad` | no |
| `complex` | `selected_real_kind(6)` | `float complex` | yes |
| `complex` | `selected_real_kind(15)` | `double complex` | yes |
| `complex` | `selected_real_kind(18)` | `ext. double complex`| no |
| `complex` | `selected_real_kind(30)` | `quad complex` | no |
| `logical` | `selected_int_kind(2)` | `byte` | no |
| `logical` | `selected_int_kind(4)` | `short` | no |
| `logical` | `selected_int_kind(9)` | `int` | yes |
| `logical` | `selected_int_kind(18)` | `long` | no |
| `type(c_ptr)` | | `void *` | no |
| `type(c_funptr)` | | (procedure) `void *` | no |

In the `Default` column one can see which data-types are enabled by default. The most
commonly 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

By default `fdict` generates the kind specifications from the `selected_*_kind` routines,
however, if one wishes to use the `iso_fortran_env` module simply add `FYPPFLAGS += -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 listed
FYPPFLAGS += -DMAXRANK=n

# integer(*) types maximum rank
FYPPFLAGS += -DMAXRANK_INT=n

# real(*) types maximum rank
FYPPFLAGS += -DMAXRANK_REAL=n

# complex(*) types maximum rank
FYPPFLAGS += -DMAXRANK_CMPLX=n

# logical(*) types maximum rank
FYPPFLAGS += -DMAXRANK_LOG=n

# type(c_ptr), type(c_funptr) types maximum rank
FYPPFLAGS += -DMAXRANK_ISO_C=n


### variable ###

Using this module one gains access to a generic type variable which
can contain _any_ data format.
It currently supports the following data-types:

| Type | Precision format | C-type | `which` |
|-------------------|----------------------------|----------------------|---------|
| `type(variable_t)`| | --- | `VAR` |
| `character(len=1)`| | `char` | `a` |
| `integer` | `selected_int_kind(4)` | `short` | `h` |
| `integer` | `selected_int_kind(9)` | `int` | `i` |
| `integer` | `selected_int_kind(18)` | `long` | `l` |
| `real` | `selected_real_kind(p=6)` | `float` | `r` |
| `real` | `selected_real_kind(p=15)` | `double` | `d` |
| `complex` | `selected_real_kind(p=6)` | `float complex` | `c` |
| `complex` | `selected_real_kind(p=15)` | `double complex` | `z` |
| `type(c_ptr)` | | `void *` | `cp` |
| `type(c_funptr)` | | (procedure) `void *` | `fp` |


Basically it is used like this:
can contain _any_ data format.

use variable
integer :: a(3)
type(variable_t) :: v
a = 2
call assign(v,a)
a = 3
call assign(a,v)
It is used like this:

Also the variable contains an abbreviation for assigning pointers to
use variable
integer :: a(3
type(variable_t) :: v
a = 2
call assign(v,a)
a = 3
call assign(a,v)

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

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

To delete a variable do:

Expand Down Expand Up @@ -146,7 +209,7 @@ Here it may be beneficial to lookup the type of data:
type(variable_t) :: v
a(:) = 2
call associate(v,a)
if ( which(v) == 'i1' ) then ! signal integer of 1D (i0 for scalar)
if ( which(v) == which(a) ) then ! signal integer of 1D (i0 for scalar)
call assign(a, v)
end if

Expand All @@ -161,8 +224,10 @@ Here it may be beneficial to lookup the type of data:
end if
... etc ...

However, it may be better to explicitly check the type using `which`. The return values from
`which` are listed in the above table.
However, it may be better to explicitly check the type using `which`.
For consistency and API changes, it is encouraged to use `which(<type>)` to
ensure 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 ###
Expand All @@ -171,7 +236,7 @@ Using `type(variable_t)` it becomes easy to create dictionaries in fortran.

Using this module we implement a dictionary which can contain _any_ data
format using a `key:val` based formalism. The underlying data structure is a
linked list sorted according to hash-values of the keys. Hence searching
linked list sorted according to hash-values of the keys. Hence searching
for specific elements in the dictionary is _extremely_ fast. Searching a
dictionary with 100 keys 300000 times takes less than 0.04 seconds on
a Haswell laptop.
Expand All @@ -188,14 +253,14 @@ To extend a dictionary one uses the concatenating format:
dict = dict // ('Hello'.kv.'world') // ('No'.kv.'world')

Again as is used by the `type(variable_t)` one can with benefit use `.kvp.` to create
the dictionary value by pointers instead of copying the content.
the 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.
will change the value in the dictionary.
Note that one can easily create memory leaks with dictionaries:

use dictionary
Expand Down
Loading

0 comments on commit 14f9104

Please sign in to comment.