Modern Fortran contains various object-oriented ideas, including a concepts of "destructors" through the FINAL keyword.
MODULE mobject
TYPE :: tobject
! Data declarations
CONTAINS
FINAL :: finalize
END TYPE
CONTAINS
SUBROUTINE finalize(object)
TYPE(tobject) object
...
END SUBROUTINE
END MODULE
However, is this feature reliable? Notably, I noticed inconsistencies about when, and whether at all, it will be called, with major differences between Intel Fortran 19 and GFortan 7, 8:
GFortran fails to destroy objects stored inside arrays.
Intel Fortran:
performs spurious and potentially superfluos destructions upon assignment, potentially even on memory containing junk data, and
performs a spurious call to the destructor upon returning from a function.
I noticed no difference between gfortran-7.4.0 and gfortran-8.2.1.2.
These inconsistencies raise some questions about the practical usability of destructors for me. Is either of the behaviors fully conforming with the standard? Is the standard unclear on this? Does the standard maybe contain clauses, that lead to unintuitive behavior?
Detailed analysis (code see below)
PROGRAM Block. Gfortran will not call the destructor for instances declared in the PROGRAM block, while Ifort will (see run1 in example).
Scalar objects. For instances declared as scalars, both Gfortran and IFort will call the destructor, if the variable has seen any form of initialization. Intel Fortran however, when assigning a function return value, will call it also
on the uninitialzied object on the stack before overwriting it with the data from the function, and
seemingly at the end of the newObject function.
This can however be guarded against by explicitly checking whether the
object is initialized, before performing any cleanup.
This means, that the programmer has to explicitly check, if the instance has been initialized.
Objects in arrays. If the object is contain in an array, and the array goes out of scope,
Gfortran will not invoke the destructor.
Intel Fortran may invoke the destructor, depending on how a given array member was initialized.
It makes no difference, whether the array is declared allocatable.
Allocatable array initialized by assignment. When using the modern feature, where assignment to an allocatable array implies allocation, the same holds, except that there are no uninitialzied instances upon which IntelFortran can call the destructor.
Allocatable/Pointers from functions.
GFortran doesn't call the destructor at the end of the function returning the an allocatable object or a pointer to an object, and instead calls it when the value is deallocated in the client code, explicitly or by going out of scope for allocatables. That's what I expected.
Intel Fortran calls in some additional cases:
When the object is declared allocatable, but not when it is a pointer, Intel Fortran invokes the destructor on the local value of the function upon exiting the function.
When initializing the object inside the function with implied allocation (var = newObject(...)), or in the case of the pointer variant, with explicit allocation (allocate(var); var = newObject(...)), the destructor is invoked on uninitialized memory, visible in run5MoveAlloc and run6MovePtr from %name containing junk data. This can be resolved by using the allocate(var); call var%init(...) pattern instead.
Testing Code
!! -- Makefile ---------------------------------------------------
!! Runs the code with various compilers.
SHELL = bash
FC = NO_COMPILER_SPECIFIED
COMPILERS = gfortran-7 gfortran-8 ifort
PR = #echo$(n)pr -m -t -w 100
define n
endef
all:
rm -rf *.mod *.bin
$(foreach FC, $(COMPILERS), $(n)\
rm -rf *.mod && \
$(FC) destructor.f90 -o $(FC).bin && \
chmod +x $(FC).bin)
$(PR) $(foreach FC, $(COMPILERS), <(head -1 <($(FC) --version)))
$(info)
$(foreach N,0 1 2 3 4 5 6,$(n) \
$(PR) $(foreach FC, $(COMPILERS), <(./$(FC).bin $(N))))
!! -- destructor.f90 ---------------------------------------------
module mobject
implicit none
private
public tobject, newObject
type :: tobject
character(32) :: name = "<undef>"
contains
procedure :: init
final :: finalize
end type tobject
contains
subroutine init(object, name)
class(tobject), intent(inout) :: object
character(*), intent(in) :: name
print *, "+ ", name
object%name = name
end subroutine init
function newObject(name)
type(tobject) :: newObject
character(*), intent(in) :: name
call new%init(name)
end function newObject
subroutine finalize(object)
type(tobject) :: object
print *, "- ", object%name
end subroutine finalize
end module mobject
module mrun
use mobject
implicit none
contains
subroutine run1()
type(tobject) :: o1_uninit, o2_field_assigned, o3_tobject, o4_new, o6_init
type(tobject), allocatable :: o5_new_alloc, o7_init_alloc
print *, ">>>>> run1"
o2_field_assigned%name = "o2_field_assigned"
o3_tobject = tobject("o3_tobject")
o4_new = newObject("o4_new")
o5_new_alloc = newObject("o5_new_alloc")
call o6_init%init("o6_init")
allocate(o7_init_alloc)
call o7_init_alloc%init("o7_init_alloc")
print *, "<<<<< run1"
end subroutine run1
subroutine run2Array()
type(tobject) :: objects(4)
print *, ">>>>> run2Array"
objects(1)%name = "objects(1)_uninit"
objects(2) = tobject("objects(2)_tobject")
objects(3) = newObject("objects(3)_new")
call objects(4)%init("objects(4)_init")
print *, "<<<<< run2Array"
end subroutine run2Array
subroutine run3AllocArr()
type(tobject), allocatable :: objects(:)
print *, ">>>>> run3AllocArr"
allocate(objects(4))
objects(1)%name = "objects(1)_uninit"
objects(2) = tobject("objects(2)_tobject")
objects(3) = newObject("objects(3)_new")
call objects(4)%init("objects(4)_init")
print *, "<<<<< run3AllocArr"
end subroutine run3AllocArr
subroutine run4AllocArrAssgn()
type(tobject), allocatable :: objects(:)
print *, ">>>>> run4AllocArrAssgn"
objects = [ &
tobject("objects(1)_tobject"), &
newObject("objects(2)_new") ]
print *, "<<<<< run4AllocArrAssgn"
end subroutine run4AllocArrAssgn
subroutine run5MoveAlloc()
type(tobject), allocatable :: o_alloc
print *, ">>>>> run5MoveAlloc"
o_alloc = getAlloc()
print *, "<<<<< run5MoveAlloc"
end subroutine run5MoveAlloc
function getAlloc() result(object)
type(tobject), allocatable :: object
print *, ">>>>> getAlloc"
allocate(object)
object = newObject("o_alloc")
print *, "<<<<< getAlloc"
end function getAlloc
subroutine run6MovePtr()
type(tobject), pointer :: o_pointer
print *, ">>>>> run6MovePtr"
o_pointer => getPtr()
deallocate(o_pointer)
print *, "<<<<< run6MovePtr"
end subroutine run6MovePtr
function getPtr() result(object)
type(tobject), pointer :: object
print *, ">>>>> getPtr"
allocate(object)
object = newObject("o_pointer")
print *, "<<<<< getPtr"
end function getPtr
end module mrun
program main
use mobject
use mrun
implicit none
type(tobject) :: object
character(1) :: argument
print *, ">>>>> main"
call get_command_argument(1, argument)
select case (argument)
case("1")
call run1()
case("2")
call run2Array()
case("3")
call run3AllocArr()
case("4")
call run4AllocArrAssgn()
case("5")
call run5MoveAlloc()
case("6")
call run6MovePtr()
case("0")
print *, "####################";
print *, ">>>>> runDirectlyInMain"
object = newObject("object_in_main")
print *, "<<<<< runDirectlyInMain"
case default
print *, "Incorrect commandline argument"
end select
print *, "<<<<< main"
end program main
Output of the testing code
>> make
rm -rf *.mod *.bin
rm -rf *.mod && gfortran-7 destructor.f90 -o gfortran-7.bin && chmod +x gfortran-7.bin
rm -rf *.mod && gfortran-8 destructor.f90 -o gfortran-8.bin && chmod +x gfortran-8.bin
rm -rf *.mod && ifort destructor.f90 -o ifort.bin && chmod +x ifort.bin
pr -m -t -w 100 <(head -1 <(gfortran-7 --version)) <(head -1 <(gfortran-8 --version)) <(head -1 <(ifort --version))
GNU Fortran (SUSE Linux) 7.4.0 GNU Fortran (SUSE Linux) 8.2.1 2 ifort (IFORT) 19.0.4.243 2019041
pr -m -t -w 100 <(./gfortran-7.bin 0) <(./gfortran-8.bin 0) <(./ifort.bin 0)
>>>>> main >>>>> main >>>>> main
#################### #################### ####################
>>>>> runDirectlyInMain >>>>> runDirectlyInMain >>>>> runDirectlyInMain
+ object_in_main + object_in_main + object_in_main
<<<<< runDirectlyInMain <<<<< runDirectlyInMain - <undef>
<<<<< main <<<<< main - object_in_main
<<<<< runDirectlyInMain
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 1) <(./gfortran-8.bin 1) <(./ifort.bin 1)
>>>>> main >>>>> main >>>>> main
>>>>> run1 >>>>> run1 >>>>> run1
+ o4_new + o4_new - <undef>
+ o5_new_alloc + o5_new_alloc + o4_new
+ o6_init + o6_init - <undef>
+ o7_init_alloc + o7_init_alloc - o4_new
<<<<< run1 <<<<< run1 + o5_new_alloc
- o7_init_alloc - o7_init_alloc - o5_new_alloc
- o6_init - o6_init + o6_init
- o5_new_alloc - o5_new_alloc + o7_init_alloc
- o4_new - o4_new <<<<< run1
- o3_tobject - o3_tobject - <undef>
- o2_field_assigned - o2_field_assigned - o2_field_assigned
<<<<< main <<<<< main - o3_tobject
- o4_new
- o6_init
- o5_new_alloc
- o7_init_alloc
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 2) <(./gfortran-8.bin 2) <(./ifort.bin 2)
>>>>> main >>>>> main >>>>> main
>>>>> run2Array >>>>> run2Array >>>>> run2Array
+ objects(3)_new + objects(3)_new - <undef>
+ objects(4)_init + objects(4)_init + objects(3)_new
<<<<< run2Array <<<<< run2Array - <undef>
<<<<< main <<<<< main - objects(3)_new
+ objects(4)_init
<<<<< run2Array
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 3) <(./gfortran-8.bin 3) <(./ifort.bin 3)
>>>>> main >>>>> main >>>>> main
>>>>> run3AllocArr >>>>> run3AllocArr >>>>> run3AllocArr
+ objects(3)_new + objects(3)_new - <undef>
+ objects(4)_init + objects(4)_init + objects(3)_new
<<<<< run3AllocArr <<<<< run3AllocArr - <undef>
<<<<< main <<<<< main - objects(3)_new
+ objects(4)_init
<<<<< run3AllocArr
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 4) <(./gfortran-8.bin 4) <(./ifort.bin 4)
>>>>> main >>>>> main >>>>> main
>>>>> run4AllocArrAssgn >>>>> run4AllocArrAssgn >>>>> run4AllocArrAssgn
+ objects(2)_new + objects(2)_new + objects(2)_new
<<<<< run4AllocArrAssgn <<<<< run4AllocArrAssgn - objects(2)_new
<<<<< main <<<<< main <<<<< run4AllocArrAssgn
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 5) <(./gfortran-8.bin 5) <(./ifort.bin 5)
>>>>> main >>>>> main >>>>> main
>>>>> run5MoveAlloc >>>>> run5MoveAlloc >>>>> run5MoveAlloc
>>>>> getAlloc >>>>> getAlloc >>>>> getAlloc
+ o_alloc + o_alloc + o_alloc
<<<<< getAlloc <<<<< getAlloc - `4�\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
<<<<< run5MoveAlloc <<<<< run5MoveAlloc - o_alloc
- o_alloc - o_alloc <<<<< getAlloc
<<<<< main <<<<< main - o_alloc
<<<<< run5MoveAlloc
- o_alloc
<<<<< main
pr -m -t -w 100 <(./gfortran-7.bin 6) <(./gfortran-8.bin 6) <(./ifort.bin 6)
>>>>> main >>>>> main >>>>> main
>>>>> run6MovePtr >>>>> run6MovePtr >>>>> run6MovePtr
>>>>> getPtr >>>>> getPtr >>>>> getPtr
+ o_pointer + o_pointer + o_pointer
<<<<< getPtr <<<<< getPtr - `��\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
- o_pointer - o_pointer - o_pointer
<<<<< run6MovePtr <<<<< run6MovePtr <<<<< getPtr
<<<<< main <<<<< main - o_pointer
<<<<< run6MovePtr
<<<<< main
TLDR: There are known outstanding issues in Gfortran. Intel claims full support. Some compilers claim no support.
The question about reliability and usability in general is quite subjective, because one has to consider many points that are unique to you (Do you need to support multiple compilers? Do you need to support their older versions? Which ones exactly? How critical it is if some entity is not finalized?).
You pose some claims which are hard to answer without actual code examples and may be a topic for a separate complete questions and answers. Gfortran publishes the current status of implementation of Fortran 2003 and 2008 features in this bug report https://gcc.gnu.org/bugzilla/show_bug.cgi?id=37336 (the link points to a meta-bug that points to several individual issues tracked in the bugzilla). It is known and that the feature is not finished and that there are outstanding issues. Most notably (at least for me), function results are not being finalized. An overview of the status of other compilers (simplified to Y/N/paritally) is at http://fortranwiki.org/fortran/show/Fortran+2003+status and used to be periodically updated in Fortran Forum articles.
I can't speak about those alleged spurious finalizations of Intel Fortran. If you identified a bug in your compiler, you should file a bug report with your vendor. Intel is generally quite responsive.
Some individual issues could be answered, though. You will likely find separate Q/As about them. But:
Gfortran will not call the destructor for instances declared in the PROGRAM block, while Ifort will (see run1 in example).
Variables declared in the main program implicitly acquire the save attribute according to the standard. The compiler is not supposed to generate any automatic finalization.
Intel Fortran however, when assigning a function return value, will call it also
As pointed out in the Gfortran bugzilla, gfortran does not finalize function result variables yet.
This means, that the programmer has to explicitly check, if the instance has been initialized.
I am afraid there is no such concept in the Fortran standard. I have no idea what " if the variable has seen any form of initialization" could mean. Note that an initializer function is a function like any other.
When using the modern feature, where assignment to an allocatable array implies allocation, the same holds, except that there are no uninitialzied instances upon which IntelFortran can call the destructor.
Not sure what that actually means. There isno such "initialization" in Fortran. Perhaps function results again?
Allocatable/Pointers from functions.
As pointed out several times, function results are not properly finalized in the current versions of Gfortran.
If you want any of the points to be answered in detail, you really have to ask a specific question. This one is too broad for that. The help/instructions for this site contain "Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the [ask] for help clarifying this question."
Related
I parallelized three nested-loops with MPI. When I ran the code, an error popped up, saying 'srun: error: Unable to create step for job 20258899: More processors requested than permitted'
Here is the script that I used to submit job.
#!/bin/bash
#SBATCH --partition=workq
#SBATCH --job-name="code"
#SBATCH --nodes=2
#SBATCH --time=1:00:00
#SBATCH --exclusive
#SBATCH --err=std.err
#SBATCH --output=std.out
#---#
module switch PrgEnv-cray PrgEnv-intel
export OMP_NUM_THREADS=1
#---#
echo "The job "${SLURM_JOB_ID}" is running on "${SLURM_JOB_NODELIST}
#---#
srun --ntasks=1000 --cpus-per-task=${OMP_NUM_THREADS} --hint=nomultithread ./example_parallel
I paste my code below. Would anyone please tell me what problem is with my code? Is the MPI that I used wrong or not? Thank you very much.
PROGRAM THREEDIMENSION
USE MPI
IMPLICIT NONE
INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(p=15,r=14)
INTEGER :: i, j, k, le(3)
REAL (KIND=dp), ALLOCATABLE :: kp(:,:,:,:), kpt(:,:), col1(:), col2(:)
REAL (KIND=dp) :: su, co, tot
INTEGER :: world_size, world_rank, ierr
INTEGER :: world_comm_1st, world_comm_2nd, world_comm_3rd
INTEGER :: th3_dimension_size, th3_dimension_size_max, th3_dimension_rank
INTEGER :: th2_dimension_size, th2_dimension_size_max, th2_dimension_rank
INTEGER :: th1_dimension_size, th1_dimension_size_max, th1_dimension_rank
INTEGER :: proc_1st_dimension_len, proc_2nd_dimension_len, proc_3rd_last_len, proc_i, proc_j, proc_k
REAL (KIND=dp) :: t0, t1
CALL MPI_INIT(ierr)
CALL MPI_COMM_SIZE(MPI_COMM_WORLD, world_size, ierr)
CALL MPI_COMM_RANK(MPI_COMM_WORLD, world_rank, ierr)
IF (world_rank == 0) THEN
t0 = MPI_WTIME()
END IF
le(1) = 1000
le(2) = 600
le(3) = 900
ALLOCATE (kp(le(1),le(2),le(3),3))
ALLOCATE (kpt(le(3),3))
ALLOCATE (col1(le(1)))
ALLOCATE (col2(le(2)))
DO i = 1, le(1), 1
DO j = 1, le(2), 1
DO k = 1, le(3), 1
kp(i,j,k,1) = DBLE(i+j+j+1)
kp(i,j,k,2) = DBLE(i+j+k+2)
kp(i,j,k,3) = DBLE(i+j+k+3)
END DO
END DO
END DO
proc_1st_dimension_len = (world_size - 1) / le(1) + 1
proc_2nd_dimension_len = (world_size - 1 / (le(1) + le(2))) + 1
proc_3rd_last_len = MOD(world_size - 1, le(1)+le(2)) + 1
IF (world_rank <= proc_3rd_last_len*proc_2nd_dimension_len*proc_1st_dimension_len) THEN
proc_i = MOD(world_rank,proc_1st_dimension_len)
proc_j = world_rank / proc_1st_dimension_len
proc_k = world_rank / (proc_1st_dimension_len*proc_2nd_dimension_len)
ELSE
proc_i = MOD(world_rank-proc_3rd_last_len,proc_1st_dimension_len-1)
proc_j = (world_rank-proc_3rd_last_len) / proc_1st_dimension_len-1
proc_k = (world_rank-proc_3rd_last_len) / (proc_2nd_dimension_len*proc_2nd_dimension_len-1)
END IF
CALL MPI_BARRIER(MPI_COMM_WORLD,ierr)
CALL MPI_COMM_SPLIT(MPI_COMM_WORLD,proc_i,world_rank,world_comm_1st,ierr)
CALL MPI_COMM_SIZE(world_comm_1st,th1_dimension_size,ierr)
CALL MPI_COMM_RANK(world_comm_1st,th1_dimension_rank,ierr)
CALL MPI_COMM_SPLIT(MPI_COMM_WORLD,proc_j,world_rank,world_comm_2nd,ierr)
CALL MPI_COMM_SIZE(world_comm_2nd,th2_dimension_size,ierr)
CALL MPI_COMM_RANK(world_comm_2nd,th2_dimension_rank,ierr)
CALL MPI_COMM_SPLIT(MPI_COMM_WORLD,proc_k,world_rank,world_comm_3rd,ierr)
CALL MPI_COMM_SIZE(world_comm_3rd,th3_dimension_size,ierr)
CALL MPI_COMM_RANK(world_comm_3rd,th3_dimension_rank,ierr)
CALL MPI_BARRIER(MPI_COMM_WORLD,ierr)
CALL MPI_ALLREDUCE(th1_dimension_size,th1_dimension_size_max,1,MPI_INT,MPI_MAX,MPI_COMM_WORLD,ierr)
CALL MPI_ALLREDUCE(th2_dimension_size,th2_dimension_size_max,1,MPI_INT,MPI_MAX,MPI_COMM_WORLD,ierr)
IF (world_rank == 0) THEN
OPEN (UNIT=3, FILE='out.dat', STATUS='UNKNOWN')
END IF
col1 = 0.0
DO i = 1, le(1), 1
IF (MOD(i-1,th1_dimension_size_max) /= th1_dimension_rank) CYCLE
col2 = 0.0
DO j = 1, le(2), 1
IF (MOD(j-1,th2_dimension_size_max) /= th2_dimension_rank) CYCLE
kpt = kp(i,j,:,:)
su = 0.0
DO k = 1, le(3), 1
IF(MOD(k-1,th1_dimension_size*th2_dimension_size) /= th3_dimension_rank) CYCLE
CALL CAL(kpt(k,3),co)
su = su + co
END DO
CALL MPI_BARRIER(world_comm_3rd,ierr)
CALL MPI_REDUCE(su,col2(j),1,MPI_DOUBLE,MPI_SUM,0,world_comm_3rd,ierr)
END DO
CALL MPI_BARRIER(world_comm_2nd,ierr)
CALL MPI_REDUCE(col2,col1(i),le(2),MPI_DOUBLE,MPI_SUM,0,world_comm_2nd,ierr)
END DO
CALL MPI_BARRIER(world_comm_1st,ierr)
tot = 0.0
IF (th1_dimension_rank == 0) THEN
CALL MPI_REDUCE(col1,tot,le(1),MPI_DOUBLE,MPI_SUM,0,world_comm_1st,ierr)
WRITE (UNIT=3, FMT=*) tot
CLOSE (UNIT=3)
END IF
DEALLOCATE (kp)
DEALLOCATE (kpt)
DEALLOCATE (col1)
DEALLOCATE (col2)
IF (world_rank == 0) THEN
t1 = MPI_WTIME()
WRITE (UNIT=3, FMT=*) 'Total time:', t1 - t0, 'seconds'
END IF
CALL MPI_FINALIZE (ierr)
STOP
END PROGRAM THREEDIMENSION
SUBROUTINE CAL(arr,co)
IMPLICIT NONE
INTEGER, PARAMETER :: dp=SELECTED_REAL_KIND(p=15,r=14)
INTEGER :: i
REAL (KIND=dp) :: arr(3), co
co = 0.0d0
co = co + (arr(1) ** 2 + arr(2) * 3.1d1) / (arr(3) + 5.0d-1)
RETURN
END SUBROUTINE CAL
With the #SBATCH directives in the header of the file, you request two nodes explicitly, and, as you do not specify --ntasks, you get the default of one task per node, so you implicitly request two tasks.
Then, when the job starts, your srun line tries to "use" 1000 tasks. You should have a line
#SBATCH --ntasks=1000
in the header as suggested per #Gilles. The srun command will inherit from that 1000 tasks by default so there is no need to specify it there in this case.
Also, if ${OMP_NUM_THREADS} were not 1, you would have to specify the --cpu-per-tasks in the header as a SBATCH directive otherwise you will face the same error.
I have a fortran.f file and whant to compile it in Linux. I dont't know what I am doing wrong. I get the following error in my subroutine:
VHImpUmat.f:476:20:
sv%Fm = get_Fm(T) ! $F_M(\Tb)$ limit stress obliquity (depends on $\theta$)
1
Error: Return type mismatch of function ‘get_fm’ at (1) (UNKNOWN/REAL(8))
AVHImpUmat.f:476:14:
sv%Fm = get_Fm(T) ! $F_M(\Tb)$ limit stress obliquity (depends on $\theta$)
1
Error: Function ‘get_fm’ at (1) has no IMPLICIT type
My subroutine:
subroutine stiffness_and_derivatives(T,sv,mat,d,msg)
use tools_lt
use constitutive_names
implicit none
type (MATERIALCONSTANTS),intent(in) :: mat
type (STATEVARIABLES),intent(inout) :: sv
type (DERIVATIVES), intent(inout) :: d
type (MESSAGE),intent(inout) :: msg
character*40 :: whereIam
real(8), intent(in) :: T(3,3)
real(8), dimension(3,3,3,3,3,3) :: c,ctransp
real(8) :: trT3,fac
sv%Fm = get_Fm(T) ! $F_M(\Tb)$ limit stress obliquity (depends on $\theta$)
sv%That = hated(T) ! $\hat {\Tb} = \Tb / \tr \Tb$
sv%LLhat= sv%Fm*sv%Fm*Idelta+mat%az2*(sv%That .out. sv%That) ! linear hp stiffness $ \hat{\cE} = a^2 \left[ \left(\Frac{F_M}{a}\right)^2 \cI + \hTb \hTb \right] $
sv%LL = -( sv%trT/(3.0d0*mat%Cs) )* sv%LLhat ! $ \cE = \frac{-\tr\Tb}{3 \kappa} \hat{\cE}$
!----- dLLhatdT ----------
trT3 = sv%trT**3 ! $\tr^3 \Tb$
fac = mat%az2 / trT3
c = (Idelta .out. T) ! $c_{ijmnkl}= I_{ijmn}T_{kl}$
ctransp = tpose35i46(c) ! $c^T= c_{ijklmn}$
d%dLLhatdT = fac * ( sv%trT*ctransp + sv%trT*(T .out. Idelta)
& - 2.0d0*( T .out. ( T .out. delta) ) ) ! $ \hat E_{ijklmn}'=a^2\left(\dfrac{ T_{rr} I_{ijmn}T_{kl} + T_{rr} T_{ij}I_{klmn}-2 T_{ij}T_{kl} \delta_{mn} }{ (T_{rr})^3} + 2 \dfrac{F_M}{a} I_{ijkl}F'_{M\, mn} \right)$
! $F'_M \approx 0$ is assumed
d%dLLdT = -(1.0d0/(3.0d0*mat%Cs) )*((sv%LLhat .out. delta) ! $\cE_{ }' = \frac{-1}{3 \kappa} \hat\cE \oneb + \dfrac{-\tr \Tb}{3\kappa}\hat\cE'$
& + sv%trT*d%dLLhatdT )
end subroutine stiffness_and_derivatives
In your subroutine, you have the statement implicit none. This is very good and is considered good programming practice.
With this, you must specifically declare any user functions with their return type just like you declare your variables.
We really cannot see what kind of variable sv%Fm is since it is likely defined in one of those modules you use. For the sake of answering, lets say the Fm component of sv is a real(8) (hint in the error message): You would declare the function like this:
real(8) :: get_fm
You would do this at the top with the rest of the variable declarations.
The second error message Error: Function ‘get_fm’ at (1) has no IMPLICIT type essentially tells you you did not declare the return type of your function when there is no implicit typing.
The first error message Error: Return type mismatch of function ‘get_fm’ at (1) (UNKNOWN/REAL(8)) always lists 2 types. The first type is in the using program unit and the second type is the return value type in the function itself. Since you did not declare the function in your subroutine, it reports 'unknown' for that one. If, for example, you accidentally declared it as an integer function, it would have (integer/real(8)) there and that is a little more self-explanatory about the type mismatch.
So adding the 1 declaration makes both errors go away.
I am using an old fortran program given to me to open a netcdf file, read its contents, perform some calculations and interpolation, and write the data to another file format. I have very little experience in fortran, so please any help would be deeply appreciated.
The program is compiled successfully:
ifort -c -CB -CU -ftrapuv -par_report0 -vec_report0 -heap-arrays -O0 -stand f90 -check all -traceback -fstack-protector -assume protect_parens -implicitnone -debug -gen-interfaces -check arg_temp_created -ftrapuv -g -convert big_endian -I/opt/cray/netcdf/4.3.0/INTEL/130/include/ CAM_netcdf_to_WRF_intermediate.f90 ; ifort CAM_netcdf_to_WRF_intermediate.o -L/opt/cray/netcdf/4.3.0/INTEL/130/lib -lnetcdf -lnetcdff
The program crashes, running out of bounds while trying to read in the netcdf file:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7657d33 in nf_open_ (A1=0x18 <Address 0x18 out of bounds>, A2=0x4e04bc <__NLITPACK_19>,
A3=0x7fffffff90ec, C1=128) at fort-control.c:27
27 fort-control.c: No such file or directory.
Running GDB, using 'bt full':
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7657d33 in nf_open_ (A1=0x18 <Address 0x18 out of bounds>, A2=0x4e04bc <__NLITPACK_19>,
A3=0x7fffffff90ec, C1=128) at fort-control.c:27
27 fort-control.c: No such file or directory.
(gdb) bt full
#0 0x00007ffff7657d33 in nf_open_ (A1=0x18 <Address 0x18 out of bounds>,
A2=0x4e04bc <__NLITPACK_19>, A3=0x7fffffff90ec, C1=128) at fort-control.c:27
B1 = 0x0
B3 = 5113020
#1 0x00007ffff76630ac in NETCDF::nf90_open (
path=<error reading variable: Cannot access memory at address 0x18>, mode=0, ncid=-858993460,
chunksize=<error reading variable: Cannot access memory at address 0x0>,
cache_size=<error reading variable: Cannot access memory at address 0x0>,
cache_nelems=<error reading variable: Cannot access memory at address 0x0>,
cache_preemption=<error reading variable: Cannot access memory at address 0x0>,
comm=<error reading variable: Cannot access memory at address 0x0>,
info=<error reading variable: Cannot access memory at address 0x0>, .tmp.PATH.len_V$ffc=128)
at netcdf4_file.f90:64
nf90_open = -144388088
ret = 0
preemption_out = 0
nelems_out = -1
size_out = 0
preemption_in = 32767
nelems_in = -134664192
size_in = 32767
The program is below:
program CAM_netcdf_to_WRF_intermediate
use netcdf
implicit none
! Declarations:
integer, parameter :: outfile_diagnostics = 16
integer, parameter :: infile_CAM_files_and_dates = 15
character(len=24) :: HDATE
! dimensions:
integer, parameter :: nx_CAM=288,ny_CAM=192,nz_CAM=26 &
,nfields=5,nfields2d=9,nfields2d_to_read=5 &
,nz_soil=4,nz_CLM=1,nfields_soil=2
integer, parameter :: nz_WRF=38
character(len=128) :: netcdf_cam_filename,netcdf_clm_filename,netcdf_pop_filename
character(len=128) :: netcdf_ice_filename
integer :: iEOF
logical :: EOF
! open outpuf log file:
open(outfile_diagnostics,form='formatted',file="Output/CCSM2WRF.log")
! read the first date and netcdf file name from the input file:
open(infile_CAM_files_and_dates,form='formatted',file="Input/CCSM2WRF.input")
read(infile_CAM_files_and_dates,*,iostat=iEOF) netcdf_cam_filename,netcdf_clm_filename,&
netcdf_pop_filename,netcdf_ice_filename,hdate
if (iEOF<0) then;
print *, "EOF True"
EOF=.true.;
else;
print *, "EOF False"
EOF=.false.;
end if
call dummy_read(nz_WRF,hdate,outfile_diagnostics,netcdf_cam_filename &
,netcdf_clm_filename,netcdf_pop_filename &
,netcdf_ice_filename,nx_CAM,ny_CAM,nz_CAM)
stop
end program CAM_netcdf_to_WRF_intermediate
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
SUBROUTINE HANDLE_ERR(STATUS)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
use netcdf
implicit none
INTEGER STATUS
IF (STATUS .NE. NF90_NOERR) THEN
PRINT *, NF90_STRERROR(STATUS)
STOP 'Stopped'
ENDIF
END SUBROUTINE HANDLE_ERR
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Subroutine dummy_read &
(nz_WRF,outfile_diagnostics,netcdf_cam_filename &
,netcdf_clm_filename,netcdf_pop_filename,netcdf_ice_filename &
,nx_CAM,ny_CAM,nz_CAM)
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
use netcdf
implicit none
integer :: nz_WRF
integer :: nx_CAM,ny_CAM,nz_CAM
character(len=128) :: filename
character(len=24) :: HDATE
integer :: outfile_diagnostics
integer :: STATUS, NCID, NCID_clm, NCID_pop, NCID_ice
character(len=128) :: netcdf_cam_filename, netcdf_clm_filename, netcdf_pop_filename
character(len=128) :: netcdf_ice_filename
! open output files for metgrid in WRF/WPS intermediate format:
write(filename,'("Output/FILE:",A13)') hdate(1:13)
write(outfile_diagnostics,*) "output intermediate file filename=",filename
open(10,form='unformatted',file=filename)
write(filename,'("Output/SST:",A13)') hdate(1:13)
write(outfile_diagnostics,*) "output intermediate SST file filename=",filename
open(11,form='unformatted',file=filename)
STATUS = NF90_OPEN(netcdf_cam_filename, 0, NCID)
! STATUS = NF90_OPEN(path = "Inputdata/ind/cam_CCSM4_historical_197909-197912-1979090100.nc", mode= 0, ncid = NCID)
IF (STATUS .NE. NF90_NOERR) CALL HANDLE_ERR(STATUS)
print *, "first status conditional statement"
STATUS = NF90_OPEN(netcdf_clm_filename, 0, NCID_clm)
IF (STATUS .NE. NF90_NOERR) CALL HANDLE_ERR(STATUS)
STATUS = NF90_OPEN(netcdf_pop_filename, 0, NCID_pop)
IF (STATUS .NE. NF90_NOERR) CALL HANDLE_ERR(STATUS)
STATUS = NF90_OPEN(netcdf_ice_filename, 0, NCID_ice)
IF (STATUS .NE. NF90_NOERR) CALL HANDLE_ERR(STATUS)
status=NF90_CLOSE(NCID)
status=NF90_CLOSE(NCID_clm)
status=NF90_CLOSE(NCID_pop)
status=NF90_CLOSE(NCID_ice)
print *, "Leaving dummy, going to MAIN"
return
end Subroutine dummy_read
The open statement works if I hard-code the path of the netcdf file (see the commented out line within the dummy_read subroutine). Printing out the netcdf_cam_filename within main returns a valid string, however printing out the string within the dummy_read subroutine returns an empty string. I am uncertain why the netcdf_cam_filename string is not making it into the subroutine correctly.
Please ask if you need additional information. I only posted pieces of the code that I think applies to the error. Thanks in advance.
Your subroutine call is mismatched to the actual definition.
Your call to dummy_read is:
call dummy_read(nz_WRF,hdate,outfile_diagnostics,netcdf_cam_filename &
,netcdf_clm_filename,netcdf_pop_filename &
,netcdf_ice_filename,nx_CAM,ny_CAM,nz_CAM)
While your declaration of dummy_read is:
Subroutine dummy_read &
(nz_WRF,outfile_diagnostics,netcdf_cam_filename &
,netcdf_clm_filename,netcdf_pop_filename,netcdf_ice_filename &
,nx_CAM,ny_CAM,nz_CAM)
Or shown a different way:
call dummy_read(nz_WRF,hdate, outfile_diagnostics,netcdf_cam_filename,netcdf_clm_filename,netcdf_pop_filename,netcdf_ice_filename,nx_CAM,ny_CAM,nz_CAM)
Subroutine dummy_read(nz_WRF,outfile_diagnostics,netcdf_cam_filename,netcdf_clm_filename,netcdf_pop_filename,netcdf_ice_filename,nx_CAM, ny_CAM,nz_CAM)
Which results in an argument mismatch. The dummy argument outfile_diagnostics is associated with the actual argument hdate and so on. You are passing 10 arguments to a subroutine declared to take 9.
You might wonder why the compiler produced an executable in such a case rather than producing an error. This is because you are calling the procedure with an implicit interface and Fortran trusts you to do the right thing. Fortran can detect argument mismatches but to do so you need to provide an explicit interface. Aside from explicitly declaring the interface, the easiest ways to do this are to either make the procedure a module procedure (by putting the subroutine into a module) or an internal procedure (by putting the procedure in the main program after a contains statement).
You can also ask the compiler to provide high levels of warnings to avoid this problem. Compiling with gfortran with -Wall produces this warning with your code:
call dummy_read(nz_WRF,hdate,outfile_diagnostics,netcdf_cam_filename &
1
Warning: Type mismatch in argument 'outfile_diagnostics' at (1); passed CHARACTER(1) to INTEGER(4)
Adiitionally, ifort provides the option -gen-interfaces flag that will automatically generate modules to contain external procedures. I would however consider this a tool to help port code to newer language standards than something to rely on.
I have put a check in error made in the input as:
integer :: lsp
chksp:do
write(*,*) "#Enter Number"
read(*,*,iostat=istat)lsp
if (istat==0) then
exit chksp
else
write(*,*)"Number can only be integer. Re-enter!"
end if
end do chksp
The problem is, it can detect error if a character value in enteres, instead of a numeric value; but it cannot detect error, if a real value is entered, instead of a integer.
Any way to force it detect integer only?
NB: May be problem with ifort; gfortran is happy with the code.
You can specify the format to request an integer:
program enter_int
implicit none
integer :: ierror, intVal
do
write(*,*) "Enter an integer number"
read(*,'(i10)',iostat=ierror) intval
if ( ierror == 0 ) then
exit
endif
write(*,*) 'An error occured - please try again'
enddo
write(*,*) 'I got: ', intVal
end program
Then, providing a float fails.
Something like the following?
ian#ian-pc:~/test/stackoverflow$ cat read.f90
Program readit
Integer :: val
Integer :: iostat
val = -9999
Do
Read( *, '( i20 )', iostat = iostat ) val
If( iostat == 0 ) Then
Write( *, * ) 'val = ', val
Else
Write( *, * ) 'oh dear!!'
End If
End Do
End Program readit
ian#ian-pc:~/test/stackoverflow$ nagfor -o read read.f90
NAG Fortran Compiler Release 5.3.1(907)
[NAG Fortran Compiler normal termination]
ian#ian-pc:~/test/stackoverflow$ ./read
10
val = 10
10.0
oh dear!!
safs
oh dear!!
123dfs23
oh dear!!
^C
ian#ian-pc:~/test/stackoverflow$ gfortran -o read read.f90
ian#ian-pc:~/test/stackoverflow$ ./read
10
val = 10
10.0
oh dear!!
dsfs
oh dear!!
^C
ian#ian-pc:~/test/stackoverflow$
I want to write a namelist with multiple items (hence multiple lines) to a character variable. The following code runs well when compiled with gfortran, but returns a write error when compiled with ifort:
program test
implicit none
type testtype
real*8 :: x
character(len=32) :: str
logical :: tf
end type testtype
type(testtype) :: thetype
integer :: iostat
character(len=1000) :: mystr(10)
namelist /THENAMELIST/ thetype
integer :: i
thetype%x = 1.0d0
thetype%str="This is a string."
thetype%tf = .true.
mystr=""
write(*,nml=THENAMELIST,delim="QUOTE")
write(mystr,THENAMELIST,iostat=iostat,delim="QUOTE")
write(*,*)"Iostat:",iostat
do i = 1, size(mystr)
write(*,*)i,trim(mystr(i))
end do
end program test
The output is the following:
> ifort -o test test.f90 ; ./test
&THENAMELIST
THETYPE%X = 1.00000000000000 ,
THETYPE%STR = "This is a string. ",
THETYPE%TF = T
/
Iostat: 66
1 &THENAMELIST THETYPE%X= 1.00000000000000 ,
2
3
4
5
6
7
8
9
10
Intel's list of run-time error messages tells me: "severe (66): Output statement overflows record".
For over completeness, using gfortran I of course get
> gfortran -o test test.f90 ; ./test
&THENAMELIST
THETYPE%X= 1.0000000000000000 ,
THETYPE%STR="This is a string. ",
THETYPE%TF=T,
/
Iostat: 0
1 &THENAMELIST
2 THETYPE%X= 1.0000000000000000 ,
3 THETYPE%STR="This is a string. ",
4 THETYPE%TF=T,
5 /
6
7
8
9
10
I have searched all over the internet, and learned that the internal file cannot be a scalar character variable, but that's about as much as I found. GFortran does accept a scalar variable and just writes newlines in that variable, but that, I guess, is non-standard fortran.
The compilers I used are:
gfortran GNU Fortran (MacPorts gcc48 4.8-20130411_0) 4.8.1 20130411 (prerelease)
ifort (IFORT) 12.0.5 20110719 (on mac)
ifort (IFORT) 13.1.1 20130313 (on GNU/Linux)
My question is: what is the error in my syntax, or how else can I write a namelist to an internal file, without having to patch the problem by writing to an actual external scratch file and read that into my variable (which is what I do now, but which is slow for large namelists)?