oopfortrangfortranfortran2008

Copying subsets of polymorphic arrays into target polymorphic arrays


I have two derived type polymorphic arrays (obj1 and obj2) in a subroutine. Based on the use of the subroutine, while the types of the two arrays may differ, both arrays are the same type; eg both type A or both type B. In the sample code below, I'm only showing one subtype of the abstract class (model), while in reality, I want this to work on multiple subtypes. Furthermore, in the production code, model1's elements have been modified before this copy.

program test

        use env_kindtypes,              only: si, dp

        use abs_obj_model,              only: model
        use obj_linearDivisionModel,    only: linearDivisionModel

        implicit none

        class(model),allocatable        :: model1(:), model2(:)

        allocate(linearDivisionModel::model1(10))

        !want e.g. model2 = model1([1,4,6,2])

        ![...]

Given obj1, obj2 (type A) (given as model1, model2 of type linearDivisionMode in the example code) and a set of indices, I want to transfer the specified elements from obj1 to obj2, allocating obj2 in the process.

I have tried quite a few approaches to do so, but none seem to work.

First, I've tried direct assignment using a vector subscript; this fails, complaining that direct assignment of an allocatable polymorphic array is not yet supported.

indices = [ 1 , 2 ]
model2 = model1(indices)

result:

         model2 = model1(indices)
        1
Error: Assignment to an allocatable polymorphic variable at (1) is not yet supported

Second, I tried using sourced allocation. If I try this with array slice notation, it works (but my problem is not expressible solely with ranges like this). If I try to vector index the source array, it compiles, but upon runtime I get errors from running out of memory (this isn't realistic given the system).

    allocate(model2,source=model1(indices))

runtime result:

Operating system error: Cannot allocate memory
Memory allocation failed

Error termination. Backtrace:
#0  0x434471 in __obj_lineardivisionmodel_MOD___copy_obj_lineardivisionmodel_Lineardivisionmode
    at build/processed_src/obj_linear_model.f90:462
#1  0x436c75 in cg_charge_fit
    at build/processed_src/test.f90:37
#2  0x403019 in main
    at build/processed_src/test.f90:22

Works, but isn't sufficient for my purposes.

allocate(model2,source=model1(1:2))

Third, I've been able to allocate the polymorphic array in hopes of manually transferring subelements: However, when I try to do so, I get complaints of polymorphic objects and intrinsic assignment, which I come back to at later in this post.

indices = [ 1 , 2 ]
allocate(model2(size(indices)),source=model1(1))
do i=1,size(indices)
        model2(i) = model1(indices(i))
enddo

Error: Nonallocatable variable must not be polymorphic in intrinsic assignment at (1) - check that there is a matching specific subroutine for '=' operator.

I have tried using type select statements to remove the polymorphic context, but errors remain.

select type (POBJECT => model1)
      TYPE IS (linearDivisionModel)
             allocate(linearDivisionModel::model2(size(indices)))
             do i=1,size(indices)
                      model2(i) = POBJECT(indices(i))
             enddo
end select

results:

model2(i) = model1(indices(i))
 1
Error: Nonallocatable variable must not be polymorphic in intrinsic assignment at (1) - check that there is a matching specific subroutine for '=' operator

As a work around, I hoped to use an intermediate pointer object, and to source allocation from that. Due to the f2008 standard (which is enforced here) I can't assign a pointer to a vector indexed array. Interestingly, if I create a pointer, vector index that pointer, the compiler segfaults, indicating that there make be something weird going on.

To address the compiler complaints about intrinsic assignment, I've considered writing assignment routines; however, this draws a new set of worries: the parent type both of these routines inherit from is abstract, and I cannot seem to specify a generic deferred assignment operator in that class, leading to a complex parent class which requires quite a few private methods to copy as it specifies no private variables. Furthermore, transformation between subclasses A and B is poorly defined. This still seems to be the only remaining way out, and seems complex.

How can I effectively transfer the specified polymorphic subrarrays?

I'm using gfortran version 6.1.1.


Solution

  • With complete F2008 support, this is simply an assignment statement.

    Within the constraints of that compiler, you may need to consider nested SELECT TYPE constructs, that eliminate the polymorphic nature of the left and right hand sides of the assignment.

    module my_types
      implicit none
    
      type, abstract :: model
      end type
    
      type, extends(model) :: linearDivisionModel
        character :: comp
      end type linearDivisionModel
    end module my_types
    
    program p
      use my_types
      implicit none
    
      class(model),allocatable        :: model1(:), model2(:)
      integer, allocatable :: indicies(:)
    
      ! define model1.
      block
        type(linearDivisionModel), allocatable :: tmp(:)
        allocate(tmp(10))
        tmp%comp = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
        call move_alloc(tmp, model1)
      end block
    
      indicies = [1, 2, 4, 6]
    
      ! allocate model2.
      allocate(model2(size(indicies)), mold=model1)
    
      ! define model2
      select type (model1)
      type is (linearDivisionModel)
        select type (model2)
        type is (linearDivisionModel)
          model2 = model1(indicies)
        end select
      end select
    
      ! display results.
      select type (model2)
      type is (linearDivisionModel)
        print *, model2%comp
      end select
    end program p
    

    The above appears to work with current gfortran trunk.