Skip to content

Commit 1af97d7

Browse files
committed
Add in manager code
1 parent 572ff14 commit 1af97d7

4 files changed

Lines changed: 280 additions & 0 deletions

File tree

meson.build

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ if pyprojectwheelbuild_enabled
4848
# These are the src with which Python interacts.
4949
# Injected with `script/inject-srcs-into-meson-build.py`
5050
srcs = files(
51+
'src/example_fgen_basic/error_v/creation_wrapper.f90',
5152
'src/example_fgen_basic/get_wavelength_wrapper.f90',
5253
)
5354

@@ -56,7 +57,9 @@ if pyprojectwheelbuild_enabled
5657
srcs_ancillary_lib = files(
5758
'src/example_fgen_basic/error_v/creation.f90',
5859
'src/example_fgen_basic/error_v/error_v.f90',
60+
'src/example_fgen_basic/error_v/error_v_manager.f90',
5961
'src/example_fgen_basic/fpyfgen/base_finalisable.f90',
62+
'src/example_fgen_basic/fpyfgen/derived_type_manager_helpers.f90',
6063
'src/example_fgen_basic/get_wavelength.f90',
6164
'src/example_fgen_basic/kind_parameters.f90',
6265
)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
!> Wrapper for interfacing `m_error_v_creation` with Python
2+
!>
3+
!> Written by hand here.
4+
!> Generation to be automated in future (including docstrings of some sort).
5+
module m_error_v_creation_w
6+
7+
! => allows us to rename on import to avoid clashes
8+
use m_error_v_creation, only: o_create_error => create_error
9+
use m_error_v, only: ErrorV
10+
11+
! The manager module, which makes this all work
12+
use m_error_v_manager, only: &
13+
error_v_manager_get_free_instance_number => get_free_instance_number, &
14+
error_v_manager_associate_pointer_with_instance => associate_pointer_with_instance
15+
! TODO: finalisation
16+
17+
implicit none
18+
private
19+
20+
public :: create_error, iget_code, iget_message
21+
22+
contains
23+
24+
subroutine create_error(inv, res_instance_index)
25+
! Needs to be subroutine to have the created instance persist I think
26+
! (we can check)
27+
! function create_error(inv) result(res_instance_index)
28+
29+
integer, intent(in) :: inv
30+
!! Input value to use to create the error
31+
32+
integer, intent(out) :: res_instance_index
33+
!! Instance index of the result
34+
!
35+
! This is the major trick for wrapping.
36+
! We return instance indexes (integers) to Python rather than the instance itself.
37+
38+
type(ErrorV), pointer :: res
39+
40+
! This is the other trick for wrapping.
41+
! We have to ensure that we have correctly associated pointers
42+
! with the derived type instances we want to 'pass' across the Python-Fortran interface.
43+
! Once we've done this, we can then set them more or less like normal derived types.
44+
res_instance_index = error_v_manager_get_free_instance_number()
45+
call error_v_manager_associate_pointer_with_instance(res_instance_index, res)
46+
47+
! Use the pointer more or less like a normal instance of the derived type
48+
res = o_create_error(inv)
49+
! Ensure that the instance index is set correctly
50+
res % instance_index = res_instance_index
51+
52+
end subroutine create_error
53+
54+
! Full set of wrapping strategies to pass different types in e.g.
55+
! https://gitlab.com/magicc/fgen/-/blob/switch-to-uv/tests/test-data/exposed_attrs/src/exposed_attrs/exposed_attrs_wrapped.f90
56+
! (we will do a full re-write of the code which generates this,
57+
! but the strategies will probably stay as they are)
58+
subroutine iget_code( &
59+
instance_index, &
60+
code &
61+
)
62+
63+
integer, intent(in) :: instance_index
64+
65+
integer, intent(out) :: code
66+
67+
type(ErrorV), pointer :: instance
68+
69+
call error_v_manager_associate_pointer_with_instance(instance_index, instance)
70+
71+
code = instance % code
72+
73+
end subroutine iget_code
74+
75+
subroutine iget_message( &
76+
instance_index, &
77+
message &
78+
)
79+
80+
integer, intent(in) :: instance_index
81+
82+
character(len=128), intent(out) :: message
83+
84+
type(ErrorV), pointer :: instance
85+
86+
call error_v_manager_associate_pointer_with_instance(instance_index, instance)
87+
88+
message = instance % message
89+
90+
end subroutine iget_message
91+
92+
end module m_error_v_creation_w
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
!> Manager of `ErrorV` (TODO: xref) across the Fortran-Python interface
2+
!>
3+
!> Written by hand here.
4+
!> Generation to be automated in future (including docstrings of some sort).
5+
!
6+
! TODO: make it possible to reallocate the number of instances
7+
module m_error_v_manager
8+
9+
use fpyfgen_derived_type_manager_helpers, only: finalise_derived_type_instance_number, &
10+
get_derived_type_free_instance_number
11+
use m_error_v, only: ErrorV
12+
13+
implicit none
14+
private
15+
16+
integer, public, parameter :: N_INSTANCES_DEFAULT = 4096
17+
!! Default maximum number of instances which can be created simultaneously
18+
!
19+
! TODO: allow reallocation if possible
20+
21+
! This is the other trick, we hold an array of instances
22+
! for tracking what is being passed back and forth across the interface.
23+
type(ErrorV), target, dimension(N_INSTANCES_DEFAULT) :: instance_array
24+
logical, dimension(N_INSTANCES_DEFAULT) :: instance_available = .true.
25+
26+
public :: get_free_instance_number, &
27+
associate_pointer_with_instance, &
28+
finalise_instance
29+
30+
contains
31+
32+
function get_free_instance_number() result(instance_index)
33+
!! Get the index of a free instance
34+
35+
integer :: instance_index
36+
!! Free instance index
37+
38+
call get_derived_type_free_instance_number( &
39+
instance_index, &
40+
N_INSTANCES_DEFAULT, &
41+
instance_available, &
42+
instance_array &
43+
)
44+
45+
end function get_free_instance_number
46+
47+
subroutine associate_pointer_with_instance(instance_index, instance_pointer)
48+
!! Associate a pointer with the instance corresponding to the given model index
49+
!!
50+
!! Stops execution if the instance has not already been initialised.
51+
52+
integer, intent(in) :: instance_index
53+
!! Index of the instance to point to
54+
55+
type(ErrorV), pointer, intent(inout) :: instance_pointer
56+
!! Pointer to associate
57+
58+
call check_index_claimed(instance_index)
59+
instance_pointer => instance_array(instance_index)
60+
61+
end subroutine associate_pointer_with_instance
62+
63+
subroutine finalise_instance(instance_index)
64+
!! Finalise an instance
65+
66+
integer, intent(in) :: instance_index
67+
!! Index of the instance to finalise
68+
69+
call check_index_claimed(instance_index)
70+
call finalise_derived_type_instance_number( &
71+
instance_index, &
72+
N_INSTANCES_DEFAULT, &
73+
instance_available, &
74+
instance_array &
75+
)
76+
77+
end subroutine finalise_instance
78+
79+
subroutine check_index_claimed(instance_index)
80+
!! Check that an index has already been claimed
81+
!!
82+
!! Stops execution if the index has not been claimed.
83+
84+
integer, intent(in) :: instance_index
85+
!! Instance index to check
86+
87+
if (instance_available(instance_index)) then
88+
! TODO: switch to errors here - will require some thinking
89+
print *, "Index ", instance_index, " has not been claimed"
90+
error stop 1
91+
end if
92+
93+
if (instance_index < 1) then
94+
! TODO: switch to errors here - will require some thinking
95+
print *, "Requested index is ", instance_index, " which is less than 1"
96+
error stop 1
97+
end if
98+
99+
if (instance_array(instance_index) % instance_index < 1) then
100+
! TODO: switch to errors here - will require some thinking
101+
print *, "Index ", instance_index, " is associated with an instance that has instance index < 1", &
102+
"instance's instance_index attribute ", instance_array(instance_index) % instance_index
103+
error stop 1
104+
end if
105+
106+
end subroutine check_index_claimed
107+
108+
end module m_error_v_manager
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
!> Helpers for derived type managers
2+
module fpyfgen_derived_type_manager_helpers
3+
4+
use fpyfgen_base_finalisable, only: BaseFinalisable, invalid_instance_index
5+
6+
implicit none
7+
private
8+
9+
public :: get_derived_type_free_instance_number, &
10+
finalise_derived_type_instance_number
11+
12+
contains
13+
14+
subroutine get_derived_type_free_instance_number(instance_index, n_instances, instance_avail, instance_array)
15+
!! Get the next available instance number
16+
!!
17+
!! If successful, `instance_index` will contain a positive value.
18+
!! If no available instances are found,
19+
!! instance_index will be set to `invalid_instance_index`.
20+
!! TODO: change the above to return a Result type instead
21+
22+
integer, intent(out) :: instance_index
23+
!! Free index
24+
!!
25+
!! If no available instances are found, set to `invalid_instance_index`.
26+
27+
integer, intent(in) :: n_instances
28+
!! Size of `instance_avail`
29+
30+
logical, dimension(n_instances), intent(inout) :: instance_avail
31+
!! Array that indicates whether each index is available or not
32+
33+
class(BaseFinalisable), dimension(n_instances), intent(inout) :: instance_array
34+
!! Array of instances
35+
36+
integer :: i
37+
38+
! Default if no available models are found
39+
instance_index = invalid_instance_index
40+
41+
do i = 1, n_instances
42+
43+
if (instance_avail(i)) then
44+
45+
instance_avail(i) = .false.
46+
instance_array(i) % instance_index = i
47+
instance_index = i
48+
return
49+
50+
end if
51+
52+
end do
53+
54+
end subroutine get_derived_type_free_instance_number
55+
56+
subroutine finalise_derived_type_instance_number(instance_index, n_instances, instance_avail, instance_array)
57+
!! Finalise the derived type with the given instance index
58+
59+
integer, intent(in) :: instance_index
60+
!! Index of the instance to finalise
61+
62+
integer, intent(in) :: n_instances
63+
!! Size of `instance_avail`
64+
65+
logical, dimension(n_instances), intent(inout) :: instance_avail
66+
!! Array that indicates whether each index is available or not
67+
68+
class(BaseFinalisable), dimension(n_instances), intent(inout) :: instance_array
69+
!! Array of instances
70+
71+
call instance_array(instance_index) % finalise()
72+
instance_array(instance_index) % instance_index = invalid_instance_index
73+
instance_avail(instance_index) = .true.
74+
75+
end subroutine finalise_derived_type_instance_number
76+
77+
end module fpyfgen_derived_type_manager_helpers

0 commit comments

Comments
 (0)