1

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.

alekepd
  • 13
  • 4
  • Even if there are ways to fix the initial solutions presented, it would be beneficial if some discussion on how to effectively overload assignments operators in this context. – alekepd Jun 23 '16 at 11:48
  • In your `select type` attempt, the complaint is about `model2(i)` being non-allocatable. Indeed, you don't reference `POBJECT` in that construct (which is associated with `model1`). You probably want to try a further `select type` on the (allocated) `model2`, and the associated thing as the assignment target. [Or something like `allocate(model2, mold=model1(indices)); select type (obj=>model2)...`] – francescalus Jun 23 '16 at 12:37
  • For your attempt with sourced allocation, it may be worth noting that there are [problems](http://stackoverflow.com/q/34384145) with gfortran's sourced allocation. Although the outcome is different, it may be worth adding explicitly the bounds in the allocation. Something like `allocate(model2(SIZE(indices)),source=model1(indices))`. – francescalus Jun 23 '16 at 19:34
  • Thanks for catching that. The error seems to persist as I didn't select over model2. I later attempted to use the nested select type statements, which work in an example like this, although easily become ugly. – alekepd Jun 24 '16 at 11:04

1 Answers1

1

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.

IanH
  • 21,026
  • 2
  • 37
  • 59
  • Yes, this does seem to work, and satisfies the question posed. I ended up overloading the assignment operator: in the base class, I have a null assignment operator, which only gets resolved to if it the additional methods extending the assignment interface in the child class aren't captured. – alekepd Jun 24 '16 at 11:07
  • This allows me to use normal assignment syntax as the compiler knows that intrinsic assignment won't get called in any context. – alekepd Jun 24 '16 at 11:08
  • In the extension to the assignment interface I still had to remove the polymorphic context with select type statements. – alekepd Jun 24 '16 at 11:09
  • Why did you use a block statement and move_alloc for the definition of model 1 here? – alekepd Jun 24 '16 at 11:12
  • In the absence of compiler support for assignment to a polymorphic allocatable object, defining a non-polymorphic allocatable and then moving that into place is a relatively clear, succinct and efficient manner for defining a polymorphic object. I don't need the temporary non-polymorphic object elsewhere, so its identifier scope is limited by BLOCK. – IanH Jun 24 '16 at 21:37
  • Be mindful that defined assignment introduces some restrictions that are not in intrinsic assignment, related to the inability to disambiguate specific procedures on the basis of the ALLOCATABLE attribute. I don't know what you mean by null assignment. – IanH Jun 24 '16 at 21:40
  • Ah. I was using allocate(linearDivisionModel::model(n)) in place of the transfer. By null assignment, I mean that while I have formally defined how general model assignment works using a method, this assignment procedure only marks objects as broken and doesn't copy data, a state which gets checked for already. Since the generic interface has both that method, and another method for any specific subclasses I add which actually assigns obejcts (e.g. linearModel to linearModel), assignments that are valid are done, while incorrect ones get caught at run time. – alekepd Jun 25 '16 at 17:53