4
$\begingroup$

I am currently working on implementing a material model in an Abaqus UMAT. The code is structured like this

MODULE FUNCTIONS
CONTAINS

FUNCTION GET_A(...) RESULT (A)
  IMPLICIT NONE
! ... some variable declarations
  DOUBLE PRECISION :: A

! ... some code

END FUNCTION

FUNCTION GET_B(...) RESULT (B)
  IMPLICIT NONE
! ... some variable declarations
  DOUBLE PRECISION, DIMENSION(n) :: B
  DOUBLE PRECISION :: DUMMY_VAR, GET_A

  DUMMY_VAR = GET_A(...)
! ... some code

END FUNCTION


END MODULE FUNCTIONS


SUBROUTINE UMAT(...)
! ... a lot of definitions
! ... a lot of code

END SUBROUTINE UMAT

I don't think it matters, but the imaginary function 'GET_B' is supposed to return an array. If a function does not depend on another function, I can call it from the UMAT without a problem. If I try to run the schematic UMAT from above I will get a compiler error similar to

UMAT.obj : error LNK2019: unresolved external symbol get_a referenced in function functions_mp_get_b.R

I read somewhere, that I am not supposed to declare the type of 'GET_A' within 'GET_B'. When I do that and change the code to

MODULE FUNCTIONS
CONTAINS

FUNCTION GET_A(...) RESULT (A)
  IMPLICIT NONE
! ... some variable declarations
  DOUBLE PRECISION :: A

! ... some code

END FUNCTION

FUNCTION GET_B(...) RESULT (B)
  IMPLICIT NONE
! ... some variable declarations
  DOUBLE PRECISION, DIMENSION(n) :: B
  DOUBLE PRECISION :: DUMMY_VAR

  DUMMY_VAR = GET_A(...)
! ... some code

END FUNCTION


END MODULE FUNCTIONS


SUBROUTINE UMAT(...)
! ... a lot of definitions
! ... a lot of code

END SUBROUTINE UMAT

the error changes to

UMAT.f(some_line): error #6404: This name does not have a type, and must have an explicit type. [GET_A] DUMMY_VAR = GET_A(...)

What can I do to call 'GET_A' from 'GET_B' (both functions are in the some module)? Do I need to put the return type of a function before the 'FUNCTION' statement? Since I am used to using the 'RESULT (...)' statement, I usually declare the type in the function body. If it helps to post the full code here, I will do so. I just thought that putting a schematic code here will help to cause too much confusion.

Edit: I uploaded the full code to this repository on git, where I also added the .log file for the precise error. I hope somebody can help me with this.

$\endgroup$
9
  • $\begingroup$ I don't know the exact cause of the error, but in subroutine UMAT, do you use FUNCTIONS or use FUNCTIONS, only: GET_B? Or do you just call GET_B? If you aren't useing the module, it might be able to recognize GET_B, but unable to determine the interface of the internal call to GET_A. $\endgroup$
    – Tyberius
    Commented Nov 10, 2023 at 21:07
  • $\begingroup$ I use FUNCTIONS in the subroutine UMAT, but the problem seems to be unrelated to the subroutine, but rather stands on its own in the module FUNCTIONS. I had the impression that GET_B should be aware of the presence of GET_A, since they both are in the same module within the same file. $\endgroup$
    – Noco
    Commented Nov 10, 2023 at 21:24
  • $\begingroup$ I also added a link to the repository with full code and log file. $\endgroup$
    – Noco
    Commented Nov 10, 2023 at 21:35
  • $\begingroup$ Your log contains a decent number of compiler errors, but the most concerning one is UMAT_NL.f(221): error #7002: Error in opening the compiled module file. Check INCLUDE paths. [FUNCTIONS] USE FUNCTIONS . This suggests you have an issue with the compilation and the program doesn't know where to find the FUNCTIONS module $\endgroup$
    – Tyberius
    Commented Nov 10, 2023 at 22:27
  • 1
    $\begingroup$ Are GET_A and GET_B referring to GET_G01G2 and GET_DEPS respectively? Because in GET_DEPS, you are calling GET_G1G2 (notice no 0 in the name), which doesn't exist. This causes the module build to fail, which probably causes all the other failures in the log. $\endgroup$
    – Tyberius
    Commented Nov 10, 2023 at 22:53

1 Answer 1

4
$\begingroup$

Just to wrap this up, the error wound being that GET_B wasn't calling GET_A, but rather GET_A0, a typo of GET_A which didn't exist and caused the compilation failure.

Some nice little debugging take aways that I have learned (and still have to remind myself to actually follow through with):

  1. Try to reduce the bug down to as small an example as possible. This can help to avoid X/Y problems where you think the big is caused by X (function calling another function), but it's really due to Y (typo in function name).
  2. Once you have narrowed it down as much as you can, use this as your test bed for determining the bug or as an example that someone else can run to reproduce the bug. This is for similar reasons to 1, where if you narrow in too much on a possible cause, you may make it impossible for someone else to reproduce the problem.
  3. Programming bugs tend to be boring, to the point where they are easier to overlook. Far more (though by no means all) of the bugs I have created have been simple typos, transpositions of function arguments, missing or not run functions calls, etc rather than messed up algorithm design or some tricky quirk of the programming language. I'll many times jump to trying some elaborate testing to determine the cause of bug, when I would be better served just doing basic sanity checks.
$\endgroup$
3
  • 2
    $\begingroup$ Simplest in Fortran is always to have an interface in scope for called routines, and use the appropriate compiler flag to warn, or better error, when it can't find one - you declare all your variables, why not your procedures? For gfortran the flag is -Wimplicit-interface possibly coupled with -Werror $\endgroup$
    – Ian Bush
    Commented Nov 11, 2023 at 9:54
  • $\begingroup$ Fair point. To be honest I do not use Fortran for anything else but Abaqus and I must admit that my debugging routine could be better. I do not compile the subroutine manually, but I should look into having a small program to test my modules. What do you mean with I do not declare my procedures? I had the impression that declaring the result variable in a function would just do that. $\endgroup$
    – Noco
    Commented Nov 11, 2023 at 18:05
  • $\begingroup$ I mean you should provide an Interface for your procedures which is in scope at the calling point - simply having them all in a module is the best way to satisfy this requirement. This is more than just declaring the return type, if you know C or C++ think function prototypes, only more powerful. $\endgroup$
    – Ian Bush
    Commented Nov 11, 2023 at 18:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .