简体   繁体   中英

Generalizing your operation for a specific declared type in Fortran

I have a Structure of arrays using the declared type in Fortran

eg

 type t_data 

    integer :: N
    real, allocatable :: x(:)
    real, allocatable :: z(:)      
    real, allocatable :: y(:)

    contains 

    procedure :: copy
    procedure :: SWAP 
    procedure :: copy_element

 end type 

 ! constructor 
 interface t_data
    module procedure constructor 
 end interface  

 contains 

 subroutine copy(this, old) 
    class(t_data), intent(inout)    :: this
    type(t_data), intent(in)        :: old
    do i = 1, old% N
       this% x(i) = old% x(i) 
       etc .. 
    end do
 end subroutine 

 subroutine copy(this, old) 
    class(t_data), intent(inout)    :: this
    type(t_data), intent(in)        :: old
    do i = 1, old% N
       this% x(i) = old% x(i) 
       etc .. 
    end do
 end subroutine 

 function constructor(size_) 
    integer, intent(in) :: size_ 
    type(t_data), :: constructor
    allocate(constructor% x(size_)) 
    allocate(constructor% y(size_) ) 
    ! etc 
 end function 

 subroutine swap(this, from, i1,i2) 
    class(t_particle_data), intent(inout) :: this
    type(t_particle_data), intent(in)    :: from
    integer, intent(in)                  :: i1, i2

    this% x(i1) = from% x(i2) 
    ! etc 
 end subroutine

These are a set of examples of procedures that need to do same operations on all arrays of the declared type t_data . My question is how to make it more maintainable to tackle the situation when we for example later want to add a new component to the declared type. Currently, when I add a new array to my t_data , I need to go through all those procedures, constructors, deconstructors, and add the component.

I am asking if there is a way to make this more easier.

MY APPLICATION

Please note that these data type is used for particle simulation. Initially I allocate t_data with a large number. However, later during my simulation I might need more particles. Hence, I allocate a new t_data with more memory and copy over the old t_data up to its old size.

   subroutine NEW_ALLOC(new, old)
   type(t_data), intent(out) :: new  
   type(t_data), intent(inout) :: old  

   nsize = old% N * 2 ! allocate twice the old size 

   new = T_DATA(nsize) 
   call new% copy(old) 
   !DEALLCOte OLD
   end subroutine 

Does anybody has/is it possible to make this in a more clever way. I do not mind mixing this with C/C++?

My question is how to make it more maintainable to tackle the situation when we for example later want to add a new component to the declared type.

Here's how I would tackle the situation, and how many Fortran programmers have tackled the situation. I don't see the compelling need to have a derived type containing 3 arrays of coordinates, and approaching the problem that way does, as OP fears, require that adding another dimension to the problem requires code revision, such as adding a member array real, allocatable :: w(:) to t_data and recoding all the type-bound procedures operating on the type.

So drop that approach in favour of

TYPE t_data
   REAL, DIMENSION(:,:), ALLOCATABLE :: elements
END TYPE t_data

let's have a couple of instances for exposition

TYPE(t_data) :: t1 ,t2, t3

and we can allocate the elements member of any of these this way

ALLOCATE(t1%elements(3,10))

which could just as easily be

ALLOCATE(t1%elements(6,100))

or whatever you wish. This has the advantage over the original derived type design that the dimensions of elements can be determined at run-time. It also makes it difficult to have different lengths for each of the coordinate arrays.

Now, copying t1 is as simple as

t2 = t1

Modern Fortran even takes care of automatically allocating the elements of t2 . So I don't see any need for defining procedures for copying whole instances of t_data . As for swapping data around, slicing and dicing, this is as simple as

t2%elements(1,:) = t1%elements(2,:)

even

t2%elements(1,:) = t1%elements(1,6:1:-1)

or

t2%elements(1,:) = t1%elements(1,[1,3,5,2,4,6])

It should be obvious how to wrap these into a swap routine. But if not, ask another question.

Next, to the point about needing to allocate more space for elements during execution. First a temporary array

REAL, DIMENSION(:,:), ALLOCATABLE :: temp

then a little code like this, to double the size of elements .

ALLOCATE(temp(3,2*n))
temp(:,1:n) = t2%elements(:,1:n)
CALL MOVE_ALLOC(to=t2%elements,from=temp)

Again, you might care to wrap this into a procedure and if you need help doing that, ask for it.

Finally, the lesson of all this is not to share how I would program the problem, but to share the idea to program in Fortran .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM