diff --git a/.github/workflows/fabm.yml b/.github/workflows/fabm.yml index 6073d740..95761d2d 100644 --- a/.github/workflows/fabm.yml +++ b/.github/workflows/fabm.yml @@ -30,7 +30,7 @@ jobs: FFLAGS: -fcheck=all steps: - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ">=3.7" - name: Install Python dependencies @@ -64,7 +64,7 @@ jobs: FFLAGS: -check all steps: - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ">=3.7" - name: Install Python dependencies @@ -104,7 +104,7 @@ jobs: # nocontiguous because spurious errors occur when assigning null() to contiguous pointers steps: - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ">=3.7" - name: Install Python dependencies @@ -149,7 +149,7 @@ jobs: sudo rm -rf /usr/share/dotnet sudo rm -rf "$AGENT_TOOLSDIRECTORY" - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ">=3.7" - name: Install Python dependencies @@ -189,7 +189,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ">=3.7" - name: Install Python dependencies @@ -226,9 +226,9 @@ jobs: with: submodules: recursive - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.python-version }} - name: Install dependencies run: python -m pip install --upgrade pip build - name: Build distribution diff --git a/src/fabm.F90 b/src/fabm.F90 index 3a51e4ef..4c741481 100644 --- a/src/fabm.F90 +++ b/src/fabm.F90 @@ -401,8 +401,6 @@ function fabm_create_model(path, initialize, settings, unit) result(model) call fabm_initialize_library() allocate(model) - model%root%parameters%path = '' - model%root%couplings%path = '' if (present(settings)) then call model%settings%take_values(settings) else diff --git a/src/yaml/yaml_settings.F90 b/src/yaml/yaml_settings.F90 index 179d4534..5c7c5268 100644 --- a/src/yaml/yaml_settings.F90 +++ b/src/yaml/yaml_settings.F90 @@ -44,7 +44,8 @@ subroutine error_reporter_proc(message) character(len=:), allocatable :: long_name character(len=:), allocatable :: description class (type_yaml_node), pointer :: backing_store_node => null() - character(len=:), allocatable :: path + character(len=:), allocatable :: path ! repurposed 2024-03-28 to hold path to yaml file + class (type_settings_node), pointer :: node => null() procedure(error_reporter_proc), pointer, nopass :: error_reporter => null() class (type_value), pointer :: parent => null() integer :: display = display_inherit @@ -55,6 +56,7 @@ subroutine error_reporter_proc(message) procedure :: is_visible => value_is_visible procedure :: get_yaml_style => value_get_yaml_style procedure :: create_child + procedure :: get_path procedure :: ignore_node generic :: ignore => ignore_node procedure :: finalize => value_finalize @@ -79,6 +81,7 @@ subroutine error_reporter_proc(message) end type type, extends(type_settings_node) :: type_list_item + integer :: index type (type_list_item), pointer :: next => null() end type @@ -296,6 +299,34 @@ integer function value_get_yaml_style(self, display) value_get_yaml_style = -1 end function + function get_path(self) result(path) + class (type_value), target, intent(in) :: self + character(len=:), allocatable :: path + + class (type_value), pointer :: value + character(len=8) :: strindex + + path = '' + value => self + do while (associated(value)) + if (allocated(value%path)) then + if (len(value%path) > 0) then + path = value%path // ':' // path + return + end if + end if + if (.not. associated(value%node)) return + select type (node => value%node) + class is (type_key_value_pair) + path = '/' // node%name // path + class is (type_list_item) + write (strindex,'(i0)') node%index + path = '[' // trim(strindex) // ']' // path + end select + value => value%parent + end do + end function + recursive subroutine value_finalize(self) class (type_value), intent(inout) :: self end subroutine @@ -336,7 +367,7 @@ subroutine load(self, path, unit, error_reporter) if (present(error_reporter)) self%error_reporter => error_reporter root => yaml_parse(path, unit, error) if (error /= '') call self%report_error(trim(error)) - if (.not. allocated(self%path)) self%path = '' + self%path = trim(path) self%backing_store_node => root call settings_set_data(self) end subroutine load @@ -345,7 +376,6 @@ subroutine take_values(self, other) class (type_settings), intent(inout) :: self class (type_settings), intent(inout) :: other - if (.not. allocated(self%path)) self%path = '' self%error_reporter => other%error_reporter self%backing_store_node => other%backing_store_node other%backing_store_node => null() @@ -557,7 +587,7 @@ subroutine get(self, name) pair%key = key allocate(type_value::pair%value) pair%value%parent => self - pair%value%path = self%path//'/'//name + pair%value%node => pair if (associated(self%backing_store)) pair%value%backing_store_node => self%backing_store%get(key, case_sensitive=.false.) end if @@ -654,9 +684,9 @@ function type_real_setting_create(node, long_name, units, default, minimum, maxi if (present(scale_factor)) setting%scale_factor = scale_factor if (present(display)) setting%display = display if (present(default)) then - if (default < setting%minimum) call setting%report_error('Default value of setting '//setting%path// & + if (default < setting%minimum) call setting%report_error('Default value of setting '//setting%get_path()// & ' lies below prescribed minimum.') - if (default > setting%maximum) call setting%report_error('Default value of setting '//setting%path// & + if (default > setting%maximum) call setting%report_error('Default value of setting '//setting%get_path()// & ' exceeds prescribed maximum.') setting%has_default = .true. setting%default = default @@ -668,7 +698,7 @@ function type_real_setting_create(node, long_name, units, default, minimum, maxi if (setting%has_default) then setting%pvalue = setting%default * setting%scale_factor else - call setting%report_error('No value specified for setting '//setting%path//'; cannot continue because& + call setting%report_error('No value specified for setting '//setting%get_path()//'; cannot continue because& & this parameter does not have a default value either.') end if end if @@ -683,14 +713,14 @@ subroutine real_set_data(self, backing_store_node) select type (backing_store_node) class is (type_yaml_scalar) self%pvalue = backing_store_node%to_real(self%pvalue, success) - if (.not. success) call self%report_error(self%path//' is set to "'//trim(backing_store_node%string)// & + if (.not. success) call self%report_error(self%get_path()//' is set to "'//trim(backing_store_node%string)// & '", which cannot be interpreted as a real number.') class default - call self%report_error('Setting '//self%path//' must be a real number.') + call self%report_error('Setting '//self%get_path()//' must be a real number.') end select - if (self%pvalue < self%minimum) call self%report_error('Value specified for parameter '//self%path// & + if (self%pvalue < self%minimum) call self%report_error('Value specified for parameter '//self%get_path()// & ' lies below prescribed minimum.') - if (self%pvalue > self%maximum) call self%report_error('Value specified for parameter '//self%path// & + if (self%pvalue > self%maximum) call self%report_error('Value specified for parameter '//self%get_path()// & ' exceeds prescribed maximum.') self%pvalue = self%pvalue * self%scale_factor end subroutine @@ -780,7 +810,7 @@ function type_integer_setting_create(node, long_name, units, default, minimum, m do ioption = 1, size(options) do ioption2 = ioption + 1, size(options) if (options(ioption)%value == options(ioption2)%value) call setting%report_error( & - 'Setting '//setting%path//' has multiple options with the same integer value.') + 'Setting '//setting%get_path()//' has multiple options with the same integer value.') end do end do if (allocated(setting%options)) deallocate(setting%options) @@ -801,16 +831,16 @@ function type_integer_setting_create(node, long_name, units, default, minimum, m end do end if if (present(default)) then - if (default < setting%minimum) call setting%report_error('Default value of setting '//setting%path// & + if (default < setting%minimum) call setting%report_error('Default value of setting '//setting%get_path()// & ' lies below prescribed minimum.') - if (default > setting%maximum) call setting%report_error('Default value of setting '//setting%path// & + if (default > setting%maximum) call setting%report_error('Default value of setting '//setting%get_path()// & ' exceeds prescribed maximum.') if (allocated(setting%options)) then found = .false. do ioption = 1, size(setting%options) if (default == setting%options(ioption)%value) found = .true. end do - if (.not.found) call setting%report_error('Default value of setting '//setting%path// & + if (.not.found) call setting%report_error('Default value of setting '//setting%get_path()// & ' does not correspond to any known option.') end if setting%has_default = .true. @@ -823,7 +853,7 @@ function type_integer_setting_create(node, long_name, units, default, minimum, m if (setting%has_default) then setting%pvalue = setting%default else - call setting%report_error('No value specified for setting '//setting%path//'; cannot continue because& + call setting%report_error('No value specified for setting '//setting%get_path()//'; cannot continue because& & it does not have a default value either.') end if end if @@ -875,26 +905,26 @@ subroutine integer_set_data(self, backing_store_node) stroptions = stroptions // achar(10) // '- ' // trim(strtmp) // ' = ' // self%options(ioption)%long_name end if end do - call self%report_error(self%path//' is set to "'//trim(backing_store_node%string)// & + call self%report_error(self%get_path()//' is set to "'//trim(backing_store_node%string)// & '", which is not one of the valid options:' // stroptions) else - call self%report_error(self%path//' is set to "'//trim(backing_store_node%string)// & + call self%report_error(self%get_path()//' is set to "'//trim(backing_store_node%string)// & '", which cannot be interpreted as an integer number.') end if end if class default - call self%report_error('Setting '//self%path//' must be an integer number.') + call self%report_error('Setting '//self%get_path()//' must be an integer number.') end select - if (self%pvalue < self%minimum) call self%report_error('Value specified for setting '//self%path// & + if (self%pvalue < self%minimum) call self%report_error('Value specified for setting '//self%get_path()// & ' lies below prescribed minimum.') - if (self%pvalue > self%maximum) call self%report_error('Value specified for setting '//self%path// & + if (self%pvalue > self%maximum) call self%report_error('Value specified for setting '//self%get_path()// & ' exceeds prescribed maximum.') if (allocated(self%options)) then success = .false. do ioption = 1, size(self%options) if (self%pvalue == self%options(ioption)%value) success = .true. end do - if (.not. success) call self%report_error('Value specified for setting '//self%path// & + if (.not. success) call self%report_error('Value specified for setting '//self%get_path()// & ' does not correspond to any known option.') end if end subroutine integer_set_data @@ -971,7 +1001,7 @@ function type_logical_setting_create(node, long_name, default, description, targ if (setting%has_default) then setting%pvalue = setting%default else - call setting%report_error('No value specified for parameter '//setting%path//'; cannot continue because& + call setting%report_error('No value specified for parameter '//setting%get_path()//'; cannot continue because& & this parameter does not have a default value either.') end if end if @@ -986,10 +1016,10 @@ subroutine logical_set_data(self, backing_store_node) select type (backing_store_node) class is (type_yaml_scalar) self%pvalue = backing_store_node%to_logical(self%pvalue, success) - if (.not. success) call self%report_error(self%path//' is set to "'//trim(backing_store_node%string)// & + if (.not. success) call self%report_error(self%get_path()//' is set to "'//trim(backing_store_node%string)// & '", which cannot be interpreted as logical value (true or false).') class default - call self%report_error('Setting '//self%path//' must be set to a logical value (true or false).') + call self%report_error('Setting '//self%get_path()//' must be set to a logical value (true or false).') end select end subroutine @@ -1070,13 +1100,13 @@ function type_string_setting_create(node, long_name, units, default, description class is (type_yaml_scalar) setting%value = trim(yaml_node%string) class default - call setting%report_error(setting%path//' must be be a string or null.') + call setting%report_error(setting%get_path()//' must be be a string or null.') end select elseif (.not. reuse_value) then if (setting%has_default) then setting%value = setting%default else - call setting%report_error('No value specified for parameter '//setting%path//'; cannot continue because& + call setting%report_error('No value specified for parameter '//setting%get_path()//'; cannot continue because& & this parameter does not have a default value either.') end if end if @@ -1141,9 +1171,15 @@ subroutine node_set_value(self, value) class (type_value), target :: value value%parent => self%value%parent - call move_alloc(self%value%path, value%path) + value%node => self%value%node value%backing_store_node => self%value%backing_store_node - if (self%own_value) deallocate(self%value) + if (self%own_value) then + deallocate(self%value) + else + self%value%parent => null() + self%value%node => null() + self%value%backing_store_node => null() + end if self%value => value end subroutine @@ -1207,7 +1243,7 @@ recursive subroutine settings_set_data(self, populator) end if class is (type_yaml_null) class default - call self%report_error(self%path//' should be a dictionary') + call self%report_error(self%get_path()//' should be a dictionary') end select end subroutine @@ -1245,7 +1281,6 @@ subroutine list_set_data(self, backing_store_node, populator) type (type_yaml_list_item), pointer :: yaml_item type (type_list_item), pointer :: item, last_item integer :: i - character(len=8) :: strindex select type (backing_store_node) class is (type_yaml_list) @@ -1262,10 +1297,10 @@ subroutine list_set_data(self, backing_store_node, populator) i = 0 do while (associated(yaml_item)) i = i + 1 - write (strindex,'(i0)') i allocate(item) + item%index = i allocate(type_value::item%value) - item%value%path = self%path//'['//trim(strindex)//']' + item%value%node => item item%value%backing_store_node => yaml_item%node item%value%parent => self if (.not. associated(last_item)) then @@ -1279,7 +1314,7 @@ subroutine list_set_data(self, backing_store_node, populator) end do class is (type_yaml_null) class default - call self%report_error(self%path//' should be a list') + call self%report_error(self%get_path()//' should be a list') end select end subroutine @@ -1297,7 +1332,6 @@ recursive subroutine finalize(self) current => next end do self%first => null() - if (allocated(self%path)) deallocate(self%path) end subroutine finalize subroutine finalize_store(self)