Skip to content

Commit 5cb914e

Browse files
committed
Merge branch 'release/1.0'
2 parents 07a6a8e + a62d51a commit 5cb914e

File tree

12 files changed

+600
-14
lines changed

12 files changed

+600
-14
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Makefile
66
*.sublime-project
77
*.sublime-workspace
88
.vs/
9+
.vscode/
910
.project
1011
Debug/
1112
*.sln

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${FLAGS}")
1414

1515
file(GLOB SOURCES_EXAMPLE src/*.f90 example/example_usage.f90)
1616
file(GLOB SOURCES src/*.f90)
17+
file(GLOB SOURCES_TEST src/*.f90 tests/*.f90)
1718

1819
add_executable(example ${SOURCES_EXAMPLE})
20+
add_executable(test ${SOURCES_TEST})
1921
add_library(feh ${SOURCES})

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@ Fortran error handling frameworks are few and far between, and those that do exi
1414

1515
If you wish to use the Fortran Error Handler in a project, the simplest way to do so is to include the source files (in `src/`) at the start of your compilation setup. Source files should be compiled in this order: `ErrorInstance.f90`, `ErrorHandler.f90`, `ErrorCriteria.f90`, `Result.f90`. An example [Makefile.example](./Makefile.example) is included, which can be altered according to your compiler and preferences.
1616

17-
The code can also be compiled using `cmake`, which creates an executable using the `example/example_usage.f90` script as well as a library:
17+
The code can also be compiled using `cmake`, which creates an example executable (using `example/example_usage.f90`), an executable of unit tests (using `tests/run_tests.f90`), and a library of the framework:
1818

1919
```bash
2020
$ mkdir build
2121
$ cd build
2222
$ cmake ..
2323
$ make
24+
# To run the unit tests
25+
$ ./test
26+
# To run the example
27+
$ ./example
2428
```
2529

2630
Whether the library is shared or not is specified by the `BUILD_SHARED_LIBS` variable. If you wish to build a shared library, then pass the `BUILD_SHARED_LIBS` option as on:
@@ -29,6 +33,8 @@ Whether the library is shared or not is specified by the `BUILD_SHARED_LIBS` var
2933
$ cmake .. -DBUILD_SHARED_LIBS=ON
3034
```
3135

36+
The framework has been tested using GFortran 7 upwards and Intel Fortran 18.
37+
3238
<a name="usage"></a>
3339
## Usage
3440

codemeta.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"@context": "https://raw.githubusercontent.com/codemeta/codemeta/master/codemeta.jsonld",
3+
"@type": "Code",
4+
"author": [
5+
{
6+
"@id": "0000-0001-8491-4720",
7+
"@type": "Person",
8+
"email": "[email protected]",
9+
"name": "Sam Harrison",
10+
"affiliation": "UK Centre for Ecology & Hydrology, Lancaster Environment Centre, Library Avenue, Bailrigg, Lancaster, LA1 4AP, UK"
11+
},
12+
{
13+
"@id": "0000-0003-4489-5363",
14+
"@type": "Person",
15+
"email": "[email protected]",
16+
"name": "Virginie D Keller",
17+
"affiliation": "UK Centre for Ecology & Hydrology, Maclean Building, Benson Lane, Crowmarsh Gifford, Wallingford, OX10 8BB, UK"
18+
},
19+
{
20+
"@id": "0000-0002-9876-0491",
21+
"@type": "Person",
22+
"email": "[email protected]",
23+
"name": "Richard J Williams",
24+
"affiliation": "UK Centre for Ecology & Hydrology, Maclean Building, Benson Lane, Crowmarsh Gifford, Wallingford, OX10 8BB, UK"
25+
},
26+
{
27+
"@id": "0000-0003-3764-5331",
28+
"@type": "Person",
29+
"email": "[email protected]",
30+
"name": "Michael Hutchins",
31+
"affiliation": "UK Centre for Ecology & Hydrology, Maclean Building, Benson Lane, Crowmarsh Gifford, Wallingford, OX10 8BB, UK"
32+
},
33+
{
34+
"@id": "0000-0002-3627-851X",
35+
"@type": "Person",
36+
"email": "[email protected]",
37+
"name": "Stephen Lofts",
38+
"affiliation": "UK Centre for Ecology & Hydrology, Lancaster Environment Centre, Library Avenue, Bailrigg, Lancaster, LA1 4AP, UK"
39+
}
40+
],
41+
"identifier": "",
42+
"codeRepository": "https://github.com/samharrison7/fortran-error-handler",
43+
"datePublished": "2020-06-05",
44+
"dateModified": "2020-06-05",
45+
"dateCreated": "2020-06-05",
46+
"description": "Comprehensive error framework for applications requiring functional and robust error handling, utilising the power of modern object-oriented Fortran.",
47+
"keywords": "Fortran, error handling",
48+
"license": "MIT",
49+
"title": "Fortran Error Handler",
50+
"version": "v1.0.0"
51+
}

doc/Result.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,10 @@ For example:
2828
```fortran
2929
type(ErrorInstance) :: randomError
3030
type(ErrorInstance) :: anotherError
31-
type(Result) :: rNoData
3231
type(Result0D) :: r0D
3332
type(Result1D) :: r1D
3433
3534
randomError = ErrorInstance(42, "A random error.")
36-
rNoData = Result(error=randomError)
3735
r0D = Result(data=1, errors=[randomError,anotherError])
3836
r1D = Result(data=[1,2], error=randomError)
3937
```

paper.md

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,41 @@ authors:
88
orcid: 0000-0001-8491-4720
99
affiliation: 1
1010
- name: Virginie D Keller
11-
orcid:
11+
orcid: 0000-0003-4489-5363
1212
affiliation: 2
1313
- name: Richard J Williams
14-
orcid:
14+
orcid: 0000-0002-9876-0491
1515
affiliation: 2
1616
- name: Michael Hutchins
17-
orcid:
17+
orcid: 0000-0003-3764-5331
1818
affiliation: 2
1919
- name: Stephen Lofts
20-
orcid:
21-
affilication: 1
20+
orcid: 0000-0002-3627-851X
21+
affiliation: 1
2222
affiliations:
2323
- name: UK Centre for Ecology & Hydrology, Lancaster Environment Centre, Library Avenue, Bailrigg, Lancaster, LA1 4AP, UK
2424
index: 1
2525
- name: UK Centre for Ecology & Hydrology, Maclean Building, Benson Lane, Crowmarsh Gifford, Wallingford, OX10 8BB, UK
2626
index: 2
27-
date: 9 May 2020
27+
date: 5 June 2020
2828
bibliography: paper.bib
2929
---
3030

3131
# Summary
3232

3333
Despite the rise of interpreted programming languages like Python and R in scientific programming, compiled languages are still the de-facto choice for computationally intensive modelling tasks, such as in climate sciences and theoretical physics. Fortran remains the top choice of many scientists, and Modern Fortran has brought great flexibility to the language in terms of object-oriented paradigms and polymorphism.
3434

35-
Any modelling code needs robust error checking, yet Fortran error handling frameworks to provide these utilities are few and far between, and those that do exist often implement only part of the error handling process, or rely on pre-processors [@popper:2012, @lucking:2015]. Here, we present what we believe is the most comprehensive Fortran error handling framework to date, providing a universal and comprehensive solution for applications requiring functional and robust error handling, utilising the power of modern object-oriented Fortran.
35+
Any modelling code needs robust error checking, yet Fortran error handling frameworks to provide these utilities are few and far between, and those that do exist often implement only part of the error handling process, or rely on pre-processors [@poppe:2012; @lucking:2015]. Here, we present what we believe is the most comprehensive Fortran error handling framework to date, providing a universal and comprehensive solution for applications requiring functional and robust error handling, utilising the power of modern object-oriented Fortran.
3636

3737
The framework implements the whole error handling process, including separate utilities for:
38-
- Managing the error handling environment, through an `ErrorHandler` class. This allows for queuing and triggering error events, storing pre-defined custom errors and customising error reporting.
39-
- Working with separate error instances, through an `ErrorInstance` class. Error instances include error metadata such as error code, message and whether the error is critical or just a warning. In addition, error instances (optionally) include a user-defined trace of where the error originated, enabling rapid debugging.
40-
- Passing errors between routines, through a `Result` class. Result objects contain a list of error instances as well as a data component, enabling errors to be returned from functions at the same time as the data which the function would traditionally return.
41-
- Common error checking criteria, through an `ErrorCriteria` class. This defines a number of common criteria used for error checking, such as equality and non-zero assertions.
38+
39+
* Managing the error handling environment, through an `ErrorHandler` class. This allows for queuing and triggering error events, storing pre-defined custom errors and customising error reporting.
40+
41+
* Working with separate error instances, through an `ErrorInstance` class. Error instances include error metadata such as error code, message and whether the error is critical or just a warning. In addition, error instances (optionally) include a user-defined trace of where the error originated, enabling rapid debugging.
42+
43+
* Passing errors between routines, through a `Result` class. Result objects contain a list of error instances as well as a data component, enabling errors to be returned from functions at the same time as the data which the function would traditionally return.
44+
45+
* Common error checking criteria, through an `ErrorCriteria` class. This defines a number of common criteria used for error checking, such as equality and non-zero assertions.
4246

4347
These classes are extensible to enable, for example, custom error criteria by extending the `ErrorCriteria` class, or the passing of custom data types through extension of the `Result` object. They are designed to be modular, such that individual elements can be used separately to implement individual parts of the error handling process. The use of the whole framework enables robust error checking for the largest and most complex models and software.
4448

tests/assert.f90

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
!> Testing utilities for assertion
2+
module assert
3+
use util
4+
implicit none
5+
6+
interface assertEqual
7+
module procedure :: assertEqualInteger
8+
module procedure :: assertEqualReal
9+
module procedure :: assertEqualDp
10+
module procedure :: assertEqualQp
11+
module procedure :: assertEqualLogical
12+
module procedure :: assertEqualString
13+
module procedure :: assertEqualComplex
14+
end interface
15+
16+
contains
17+
18+
subroutine exitOut(msg)
19+
character(len=*) :: msg
20+
write(*, '(a)') trim(msg)
21+
error stop 1
22+
end subroutine
23+
24+
subroutine assertEqualInteger(a, b, msg)
25+
integer, intent(in) :: a, b
26+
character(len=*), optional :: msg
27+
if (a /= b) then
28+
if (present(msg)) write(*, '(a)') msg
29+
call exitOut("Assertion error: Values " // trim(str(a)) // " and " // trim(str(b)) // " are not equal.")
30+
end if
31+
end subroutine
32+
33+
subroutine assertEqualReal(a, b, msg)
34+
real, intent(in) :: a, b
35+
character(len=*), optional :: msg
36+
if (a /= b) then
37+
if (present(msg)) write(*, '(a)') msg
38+
call exitOut("Assertion error: Values " // trim(str(a)) // " and " // trim(str(b)) // " are not equal.")
39+
end if
40+
end subroutine
41+
42+
subroutine assertEqualDp(a, b, msg)
43+
real(dp), intent(in) :: a, b
44+
character(len=*), optional :: msg
45+
if (a /= b) then
46+
if (present(msg)) write(*, '(a)') msg
47+
call exitOut("Assertion error: Values " // trim(str(a)) // " and " // trim(str(b)) // " are not equal.")
48+
end if
49+
end subroutine
50+
51+
subroutine assertEqualQp(a, b, msg)
52+
real(qp), intent(in) :: a, b
53+
character(len=*), optional :: msg
54+
if (a /= b) then
55+
if (present(msg)) write(*, '(a)') msg
56+
call exitOut("Assertion error: Values " // trim(str(a)) // " and " // trim(str(b)) // " are not equal.")
57+
end if
58+
end subroutine
59+
60+
subroutine assertEqualLogical(a, b, msg)
61+
logical, intent(in) :: a, b
62+
character(len=*), optional :: msg
63+
if (a .neqv. b) then
64+
if (present(msg)) write(*, '(a)') msg
65+
call exitOut("Assertion error: Values " // trim(str(a)) // " and " // trim(str(b)) // " are not equal.")
66+
end if
67+
end subroutine
68+
69+
subroutine assertEqualString(a, b, msg)
70+
character(len=*), intent(in) :: a, b
71+
character(len=*), optional :: msg
72+
if (trim(a) /= trim(b)) then
73+
if (present(msg)) write(*, '(a)') msg
74+
call exitOut("Assertion error: Values '" // trim(a) // "' and '" // trim(b) // "' are not equal.")
75+
end if
76+
end subroutine
77+
78+
subroutine assertEqualComplex(a, b, msg)
79+
complex, intent(in) :: a, b
80+
character(len=*), optional :: msg
81+
if (a /= b) then
82+
if (present(msg)) write(*, '(a)') msg
83+
call exitOut("Assertion error: Values " // trim(str(a)) // " and " // trim(str(b)) // " are not equal.")
84+
end if
85+
end subroutine
86+
87+
end module

tests/run_tests.f90

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
program run_test
2+
use tests_Result
3+
use tests_ErrorInstance
4+
use tests_ErrorCriteria
5+
implicit none
6+
7+
write(*, '(a)') "Starting tests for ErrorInstance..."
8+
call createErrorWithDefaultCode()
9+
call createErrorWithMessageHello()
10+
call createErrorWithCode42()
11+
call createCriticalError()
12+
call createNonCriticalError()
13+
call addMessageToTrace()
14+
call addTwoMessagesToTrace()
15+
call addTraceMessageSayingHello()
16+
17+
write(*, '(a)') "Starting tests for Result..."
18+
call createResultWithNoError()
19+
call createResultWith0DIntegerData()
20+
call createResultWith0DRealData()
21+
call createResultWith0DDPData()
22+
call createResultWith0DQPData()
23+
call createResultWith0DLogicalData()
24+
call createResultWith0DCharacterData()
25+
call createResultWith0DComplexData()
26+
call createResultWith1DIntegerData()
27+
call createResultWith1DRealData()
28+
call createResultWith1DDPData()
29+
call createResultWith1DQPData()
30+
call createResultWith2DIntegerData()
31+
call createResultWith2DRealData()
32+
call createResultWith2DDPData()
33+
call createResultWith2DQPData()
34+
call createResultWith3DIntegerData()
35+
call createResultWith3DRealData()
36+
call createResultWith3DDPData()
37+
call createResultWith3DQPData()
38+
call createResultWith4DIntegerData()
39+
call createResultWith4DRealData()
40+
call createResultWith4DDPData()
41+
call createResultWith4DQPData()
42+
43+
write(*, '(a)') "Starting tests for ErrorHandler and ErrorCriteria..."
44+
call createErrorHandlerWithCustomError()
45+
call checkErrorCriteriaNonZero()
46+
call checkErrorCriteriaLessThan()
47+
call checkErrorCriteriaGreaterThan()
48+
call checkErrorCriteriaLimit()
49+
call checkErrorCriteriaNotEqual()
50+
call checkErrorCriteriaEqual()
51+
call checkErrorCriteriaPositive()
52+
call checkErrorCriteriaNegative()
53+
54+
write(*, '(a)') "Tests completed successfully!"
55+
56+
end program

0 commit comments

Comments
 (0)