Skip to content

Commit

Permalink
feat (memorymananger) Create iterators for the ListType and MemoryLis…
Browse files Browse the repository at this point in the history
…tType (#1765)

* Create iterator for the ListType and MemoryListType. Use them in the MemoryManager and MemoryManangerExt

* Update mf5to6 make files

* Reimplement the get method in the memorylist to use the name and path of a variable.

* Extract ListNodeType to separate class. Add iterator method to the List class.

* Make iterator variables allocatable

* Add unit ListIterator unittests

* Add unit test for the MemoryContainerIterator

* Fix msvs  error

* Apply review comments

* Fix spelling errors

* Fix format errors
  • Loading branch information
Manangka authored Jun 12, 2024
1 parent bce795d commit 607f59f
Show file tree
Hide file tree
Showing 23 changed files with 711 additions and 189 deletions.
132 changes: 132 additions & 0 deletions autotest/TestListIterator.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
module TestListIterator
use KindModule, only: I4B
use testdrive, only: error_type, unittest_type, new_unittest, check
use ListNodeModule, only: ListNodeType
use ListIteratorModule, only: ListIteratorType
use IteratorModule, only: IteratorType

implicit none
private
public :: collect_listiterator

contains

subroutine collect_listiterator(testsuite)
type(unittest_type), allocatable, intent(out) :: testsuite(:)

testsuite = [ &
new_unittest("constructor", test_constructor), &
new_unittest("iterate_through_list", test_iterate_through_list), &
new_unittest("empty_list", test_empty_list) &
]
end subroutine collect_listiterator

!> @brief Test the initial state of the iterator
!!
!! When the iterator is created with a non-empty list:
!! - it should indicate that it has a next value
!! - it should return null when retrieving the current value
!<
subroutine test_constructor(error)
type(error_type), allocatable, intent(out) :: error
!- Locals
class(IteratorType), allocatable :: itr
type(ListNodeType), target :: firstNode
type(ListNodeType), pointer :: firstNodePtr

!- Arrange.
firstNodePtr => firstNode

!- Act.
itr = ListIteratorType(firstNodePtr)

!- Assert.
call check(error,.not. associated(itr%value()))
if (allocated(error)) return

call check(error, itr%has_next())
if (allocated(error)) return
end subroutine test_constructor

!> @brief Iterate through a list
!!
!! This test creates an iterator for a list of 3 nodes.
!! It iterates though it and validates the expected values
!<
subroutine test_iterate_through_list(error)
type(error_type), allocatable, intent(out) :: error
!- Locals
class(IteratorType), allocatable :: itr
type(ListNodeType), pointer :: firstNodePtr

type(ListNodeType), target :: firstNode
type(ListNodeType), target :: secondNode
type(ListNodeType), target :: thirdNode

integer(I4B), target :: expected_value1 = 2
integer(I4B), target :: expected_value2 = 6
integer(I4B), target :: expected_value3 = 567
integer(I4B) :: expected_values(3)

integer(I4B) :: itr_count = 0
integer(I4B), pointer :: value_ptr

!- Arrange.
expected_values = [expected_value1, expected_value2, expected_value3]

firstNode%value => expected_value1
firstNode%nextNode => secondNode

secondNode%value => expected_value2
secondNode%nextNode => thirdNode

thirdNode%value => expected_value3
thirdNode%nextNode => null()

firstNodePtr => firstNode
itr = ListIteratorType(firstNodePtr)

!- Act.
do while (itr%has_next())
call itr%next()
itr_count = itr_count + 1
!- Assert.
select type (val => itr%value())
type is (integer(I4B))
value_ptr => val

call check(error, value_ptr == expected_values(itr_count))
if (allocated(error)) return
end select
end do

end subroutine test_iterate_through_list

!> @brief Test the initial state of the iterator with an empty list
!!
!! When the iterator is created it with an empty list:
!! - It should indicate that it has no next value
!! - It should return null when retrieving the current value
!<
subroutine test_empty_list(error)
type(error_type), allocatable, intent(out) :: error
!- Locals
class(IteratorType), allocatable :: itr
type(ListNodeType), pointer :: firstNodePtr

!- Arrange.
firstNodePtr => null()

!- Act.
itr = ListIteratorType(firstNodePtr)

!- Assert.
call check(error,.not. itr%has_next())
if (allocated(error)) return

call check(error,.not. associated(itr%value()))
if (allocated(error)) return

end subroutine test_empty_list

end module TestListIterator
75 changes: 75 additions & 0 deletions autotest/TestMemoryContainerIterator.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
module TestMemoryContainerIterator
use KindModule, only: I4B, LGP
use MemoryContainerIteratorModule, only: MemoryContainerIteratorType
use MemoryListModule, only: MemoryListType
use MemoryTypeModule, only: MemoryType
use testdrive, only: error_type, unittest_type, new_unittest, check

contains
subroutine collect_memorycontaineriterator(testsuite)
type(unittest_type), allocatable, intent(out) :: testsuite(:)

testsuite = [ &
new_unittest("iterate_through_container", &
test_iterate_through_container) &
]
end subroutine collect_memorycontaineriterator

!> @brief Iterate through a MemoryContainer
!!
!! This test creates an iterator for a container containing 3 MemoryTypes.
!! It iterates though the container and validates that each type is reached.
!!
!! Because the order of the iterator doesn't have to match the order in which
!! the MemoryTypes have been added an 'iterated' array is used. A flag in the
!! array is set to true and at the end it is validated that the entire array
!! is set to to true (indicating that all memory types have been reached)
!<
subroutine test_iterate_through_container(error)
type(error_type), allocatable, intent(out) :: error
!- Locals
type(MemoryListType) :: memory_container
type(MemoryContainerIteratorType), allocatable :: itr

type(MemoryType), target :: mt1
type(MemoryType), target :: mt2
type(MemoryType), target :: mt3

type(MemoryType), pointer :: current_mt
integer(I4B) :: mt_index = 0

logical(LGP) :: iterated(3) = .false.

!- Arrange.
mt1%name = "TestName1"
mt2%name = "TestName2"
mt3%name = "TestName3"

current_mt => mt1
call memory_container%add(current_mt)

current_mt => mt2
call memory_container%add(current_mt)

current_mt => mt3
call memory_container%add(current_mt)

itr = memory_container%iterator()

!- Act.
current_mt => null()
do while (itr%has_next())
call itr%next()
current_mt => itr%value()

read (current_mt%name(len_trim(current_mt%name):), '(i1)') mt_index
iterated(mt_index) = .true.
end do

!- Assert.
call check(error, all(iterated .eqv. .true.))
if (allocated(error)) return

end subroutine test_iterate_through_container

end module TestMemoryContainerIterator
2 changes: 2 additions & 0 deletions autotest/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ if test_drive.found() and not fc_id.contains('intel')
'HashTable',
'InputOutput',
'List',
'ListIterator',
'MathUtil',
'MemoryContainerIterator',
'Message',
'Sim',
'SwfUtils',
Expand Down
5 changes: 5 additions & 0 deletions autotest/tester.f90
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ program tester
use TestHashTable, only: collect_hashtable
use TestInputOutput, only: collect_inputoutput
use TestList, only: collect_list
use TestListIterator, only: collect_listiterator
use TestMathUtil, only: collect_mathutil
use TestMemoryContainerIterator, only: collect_memorycontaineriterator
use TestMessage, only: collect_message
use TestSim, only: collect_sim
use TestSwfUtils, only: collect_swfutils
Expand All @@ -27,7 +29,10 @@ program tester
new_testsuite("HashTable", collect_hashtable), &
new_testsuite("InputOutput", collect_inputoutput), &
new_testsuite("List", collect_list), &
new_testsuite("ListIterator", collect_listiterator), &
new_testsuite("MathUtil", collect_mathutil), &
new_testsuite("MemoryContainerIterator", &
collect_memorycontaineriterator), &
new_testsuite("Message", collect_message), &
new_testsuite("Sim", collect_sim), &
new_testsuite("SwfUtils", collect_swfutils), &
Expand Down
62 changes: 33 additions & 29 deletions make/makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,42 @@ include ./makedefaults

# Define the source file directories
SOURCEDIR1=../src
SOURCEDIR2=../src/Exchange
SOURCEDIR3=../src/Idm
SOURCEDIR4=../src/Idm/selector
SOURCEDIR5=../src/Timing
SOURCEDIR2=../src/Distributed
SOURCEDIR3=../src/Exchange
SOURCEDIR4=../src/Idm
SOURCEDIR5=../src/Idm/selector
SOURCEDIR6=../src/Model
SOURCEDIR7=../src/Model/Connection
SOURCEDIR8=../src/Model/Discretization
SOURCEDIR9=../src/Model/ModelUtilities
SOURCEDIR10=../src/Model/GroundWaterFlow
SOURCEDIR11=../src/Model/Geometry
SOURCEDIR12=../src/Model/TransportModel
SOURCEDIR13=../src/Model/GroundWaterTransport
SOURCEDIR14=../src/Model/SurfaceWaterFlow
SOURCEDIR15=../src/Model/ParticleTracking
SOURCEDIR16=../src/Model/GroundWaterEnergy
SOURCEDIR9=../src/Model/Geometry
SOURCEDIR10=../src/Model/GroundWaterEnergy
SOURCEDIR11=../src/Model/GroundWaterFlow
SOURCEDIR12=../src/Model/GroundWaterTransport
SOURCEDIR13=../src/Model/ModelUtilities
SOURCEDIR14=../src/Model/ParticleTracking
SOURCEDIR15=../src/Model/SurfaceWaterFlow
SOURCEDIR16=../src/Model/TransportModel
SOURCEDIR17=../src/Solution
SOURCEDIR18=../src/Solution/ParticleTracker
SOURCEDIR19=../src/Solution/LinearMethods
SOURCEDIR18=../src/Solution/LinearMethods
SOURCEDIR19=../src/Solution/ParticleTracker
SOURCEDIR20=../src/Solution/PETSc
SOURCEDIR21=../src/Distributed
SOURCEDIR21=../src/Timing
SOURCEDIR22=../src/Utilities
SOURCEDIR23=../src/Utilities/TimeSeries
SOURCEDIR23=../src/Utilities/ArrayRead
SOURCEDIR24=../src/Utilities/Idm
SOURCEDIR25=../src/Utilities/Idm/mf6blockfile
SOURCEDIR26=../src/Utilities/ArrayRead
SOURCEDIR27=../src/Utilities/Memory
SOURCEDIR28=../src/Utilities/Matrix
SOURCEDIR29=../src/Utilities/Vector
SOURCEDIR30=../src/Utilities/Observation
SOURCEDIR31=../src/Utilities/OutputControl
SOURCEDIR32=../src/Utilities/Libraries
SOURCEDIR33=../src/Utilities/Libraries/rcm
SOURCEDIR34=../src/Utilities/Libraries/sparskit2
SOURCEDIR35=../src/Utilities/Libraries/sparsekit
SOURCEDIR36=../src/Utilities/Libraries/blas
SOURCEDIR37=../src/Utilities/Libraries/daglib
SOURCEDIR26=../src/Utilities/Libraries
SOURCEDIR27=../src/Utilities/Libraries/blas
SOURCEDIR28=../src/Utilities/Libraries/daglib
SOURCEDIR29=../src/Utilities/Libraries/rcm
SOURCEDIR30=../src/Utilities/Libraries/sparsekit
SOURCEDIR31=../src/Utilities/Libraries/sparskit2
SOURCEDIR32=../src/Utilities/Matrix
SOURCEDIR33=../src/Utilities/Memory
SOURCEDIR34=../src/Utilities/Observation
SOURCEDIR35=../src/Utilities/OutputControl
SOURCEDIR36=../src/Utilities/TimeSeries
SOURCEDIR37=../src/Utilities/Vector

VPATH = \
${SOURCEDIR1} \
Expand Down Expand Up @@ -100,8 +100,12 @@ $(OBJDIR)/InputOutput.o \
$(OBJDIR)/TableTerm.o \
$(OBJDIR)/Table.o \
$(OBJDIR)/MemoryHelper.o \
$(OBJDIR)/InputDefinition.o \
$(OBJDIR)/ListNode.o \
$(OBJDIR)/Iterator.o \
$(OBJDIR)/Memory.o \
$(OBJDIR)/ListIterator.o \
$(OBJDIR)/InputDefinition.o \
$(OBJDIR)/MemoryContainerIterator.o \
$(OBJDIR)/List.o \
$(OBJDIR)/utl-hpcidm.o \
$(OBJDIR)/swf-zdgidm.o \
Expand Down
4 changes: 4 additions & 0 deletions msvs/mf6core.vfproj
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@
<File RelativePath="..\src\Utilities\Matrix\SparseMatrix.f90"/></Filter>
<Filter Name="Memory">
<File RelativePath="..\src\Utilities\Memory\Memory.f90"/>
<File RelativePath="..\src\Utilities\Memory\MemoryContainerIterator.f90"/>
<File RelativePath="..\src\Utilities\Memory\MemoryHelper.f90"/>
<File RelativePath="..\src\Utilities\Memory\MemoryList.f90"/>
<File RelativePath="..\src\Utilities\Memory\MemoryManager.f90"/>
Expand Down Expand Up @@ -522,9 +523,12 @@
<File RelativePath="..\src\Utilities\HeadFileReader.f90"/>
<File RelativePath="..\src\Utilities\HGeoUtil.f90"/>
<File RelativePath="..\src\Utilities\InputOutput.f90"/>
<File RelativePath="..\src\Utilities\Iterator.f90"/>
<File RelativePath="..\src\Utilities\Iunit.f90"/>
<File RelativePath="..\src\Utilities\kind.f90"/>
<File RelativePath="..\src\Utilities\List.f90"/>
<File RelativePath="..\src\Utilities\ListIterator.f90"/>
<File RelativePath="..\src\Utilities\ListNode.f90"/>
<File RelativePath="..\src\Utilities\ListReader.f90"/>
<File RelativePath="..\src\Utilities\LongLineReader.f90"/>
<File RelativePath="..\src\Utilities\MathUtil.f90"/>
Expand Down
3 changes: 2 additions & 1 deletion src/Model/Connection/ConnectionBuilder.f90
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ module ConnectionBuilderModule
use KindModule, only: I4B, LGP
use SimModule, only: store_error, count_errors, ustop
use SimVariablesModule, only: iout
use ListModule, only: ListType, isEqualIface, ListNodeType
use ListModule, only: ListType, isEqualIface
use ListNodeModule, only: ListNodeType
use BaseSolutionModule, only: BaseSolutionType
use NumericalSolutionModule, only: NumericalSolutionType
use BaseExchangeModule, only: BaseExchangeType, GetBaseExchangeFromList
Expand Down
38 changes: 38 additions & 0 deletions src/Utilities/Iterator.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module IteratorModule
use KindModule, only: LGP
implicit none
private

public :: IteratorType

type, abstract :: IteratorType
contains
procedure(has_next_if), deferred :: has_next
procedure(next_if), deferred :: next
procedure(value_if), deferred :: value

end type IteratorType

abstract interface

function has_next_if(this) result(res)
import IteratorType
import LGP
class(IteratorType) :: this
logical(LGP) :: res
end function

subroutine next_if(this)
import IteratorType
class(IteratorType) :: this
end subroutine

function value_if(this) result(res)
import IteratorType
class(IteratorType) :: this
class(*), pointer :: res
end function

end interface

end module IteratorModule
Loading

0 comments on commit 607f59f

Please sign in to comment.