Same output for changing input

Dear Friends,

I can’t find the error in the first subroutine MaxMin.
The output for maxmom and minmom is OK.
The output for maxposx, maxposa, minposx, minposa stays the same for whatever value of P1, P2, P3, P4.
I’m no software guru but a retired structural engineer doing this as a hobby so don’t be to hard on me. :slightly_smiling_face:

! DLL Max. moments and shear forces in 2-span beam with equal lengths
! under up to 4 moving loads
! Version        Author         Date
!    A            R.M.        14/06/2024 
!    B            R.M.        02/05/2025
module mom_m
    implicit none
    save

 contains
  ! Subroutine to calculate moments maxmin
 Subroutine MaxMin(L,P1,P2,P3,P4,a1,a2,a3,t,ch1,ar)
 !DIR$ ATTRIBUTES STDCALL, DLLEXPORT :: MaxMin
 !DIR$ ATTRIBUTES ALIAS: 'MaxMin' :: Maxmin
 !DIR$ ATTRIBUTES REFERENCE :: ch1, ar

        
   real, intent(in) :: L, P1, P2, P3, P4, a1, a2, a3, t
   real, intent(out) ::ar(6), ch1(2)
   integer :: n
   real, dimension(:,:), allocatable :: moments
   real :: maxmom
   real :: minmom
   integer :: maxpos(2)
   integer :: minpos(2)
   real :: maxposx
   real :: maxposa
   real :: minposx
   real :: minposa
   integer :: ia, ix
    n = int(2*L/t + 1)
 !  initialize
    ar = 0.0
    maxpos = 0
    minpos = 0
    maxposx = 0.0
    maxposa = 0.0
    minposx = 0.0
    minposa = 0.0

    allocate (moments(n,n))
    do ix = 1, n
       do ia = 1, n
          moments(ia, ix) = Mx((ix-1)*t, (ia-1)*t, L, P1) +  &
          Mx((ix-1)*t, (ia-1)*t - a1, L, P2) +               &
          Mx((ix-1)*t, (ia-1)*t - (a1+a2), L, P3) +          &
          Mx((ix-1)*t, (ia-1)*t - (a1+a2+a3), L, P4)
       end do
    end do
   maxmom=maxval(moments)
   minmom=minval(moments)
!	 maxpos=maxloc(moments,mask=moments.gt.0)
   maxpos = findloc(moments, maxmom)
!	 minpos=minloc(moments,mask=moments.lt.0)
   minpos = findloc(moments, minmom)
   maxposx=(maxpos(2)-1) *t
   maxposa=(maxpos(1)-1) *t
   minposx=(minpos(2)-1) *t
   minposa=(minpos(1)-1) *t
    
   ar= (/maxmom, minmom, maxposx, maxposa, minposx, minposa/)
   ch1 = minpos
 

    return

 end subroutine MaxMin
 
function DkEnd(L, P1, P2, P3, P4,a1, a2, a3, t)
 ! Function to calculate max. shear force at end support
!DIR$ ATTRIBUTES STDCALL, DLLEXPORT:: DkEnd
!DIR$ ATTRIBUTES ALIAS: 'DkEnd' :: DkEnd
 real :: DkEnd 	
    integer :: ia
    integer :: n
    real, intent(in) :: L,P1,P2,P3,P4,a1,a2,a3,t
    real:: MaxVa
    real, dimension(:), allocatable :: dka
    n = int(2*L/t + 1)
    allocate (dka(n))
           
    do ia = 1, n
        dka(ia) = Va((ia-1)*t, L, P1) +                    & 
                  Va((ia-1)*t - a1, L, P2) +               &
                  Va((ia-1)*t - (a1+a2), L, P3) +          &
                  Va((ia-1)*t - (a1+a2+a3), L, P4) 
    end do
  MaxVa = minval(dka)
  DkEnd=MaxVa
  
  
end function DkEnd

function DkMid(L, P1, P2, P3, P4,a1, a2, a3, t)
 ! Function to calculate max. shear force at mid support
!DIR$ ATTRIBUTES STDCALL, DLLEXPORT :: DkMid
!DIR$ ATTRIBUTES ALIAS: 'DkMid' :: DkMid
    real :: DkMid	
    integer :: ia
    integer :: n
    real, intent(in) :: L,P1,P2,P3,P4,a1,a2,a3,t
    real MaxVb
    real, dimension(:), allocatable :: dkb
    n = int(2*L/t + 1)
    allocate (dkb(n))
    
     do ia = 1, n
        dkb(ia) = Vb((ia-1)*t, L, P1) +                    & 
                  Vb((ia-1)*t - a1, L, P2) +               &
                  Vb((ia-1)*t - (a1+a2), L, P3) +          &
                  Vb((ia-1)*t - (a1+a2+a3), L, P4) 
     end do
     
  MaxVb = minval(dkb)
  DkMid=MaxVb
  
  
end function DkMid

real function ReacB(L, P1, P2, P3, P4,a1, a2, a3, t)
 ! Function to calculate max. reaction at mid support
!DIR$ ATTRIBUTES STDCALL, DLLEXPORT :: ReacB
!DIR$ ATTRIBUTES ALIAS: 'ReacB' :: ReacB
     integer :: ia
     integer :: n
     real, intent(in) :: L,P1,P2,P3,P4,a1,a2,a3,t
     real MaxRb
     real, dimension(:), allocatable :: recb
     n = int(2*L/t + 1)
     allocate (recb(n))
    
     do ia = 1, n
        recb(ia) = Rb((ia-1)*t, L, P1) +                    & 
                   Rb((ia-1)*t - a1, L, P2) +               &
                   Rb((ia-1)*t - (a1+a2), L, P3) +          &
                   Rb((ia-1)*t - (a1+a2+a3), L, P4) 
     end do
     
  MaxRb = maxval(recb)
  ReacB=MaxRb
  
  
end function ReacB

real function Mx(x,a,L,P)
    ! Function to calcute moment under one moving point load
!DIR$ ATTRIBUTES STDCALL, DLLEXPORT :: Mx
!DIR$ ATTRIBUTES ALIAS: 'Mx' :: Mx        
    !Calling parameters
    real :: x   ! Position where moment is calculated
    real :: a   ! Position point load
    real :: L   ! Span
    real :: P   ! Value load
    real :: b
    
    ! Evaluate Mx
    if (a < L) then
       b = L-a
    else
       a = 2.*L-a
       b = L-a
       x = 2.*L-x
    endif
    if (a < 0 .or. a > 2.*L) then
       Mx = 0.0
    else if (x <= a) then
       Mx = ((P * b) / (4. * (L ** 3.))) * ((4. * (L ** 2.)) - (a * (L + a))) * x
    else if ((x > a) .and. (x <= L)) then
       Mx = ((P * b) / (4. * (L ** 3.))) * ((4. * (L ** 2.)) - (a * (L + a))) * x - P * (x - a)
    else if (x > L) then
       Mx = -(((P * b * a) / (4 * L ** 3.)) * (L + a)) * (2. * L - x)
    end if

 end function Mx
 
  real function Va(a,L,P)   
  ! Function to calcute max. shear at end support under one moving point load 
   !DIR$ ATTRIBUTES STDCALL, DLLEXPORT :: Va
   !DIR$ ATTRIBUTES ALIAS: 'Va' :: Va

   !Calling parameters

      real :: a, L, P, b   ! Position point load, Span, load
      Va = 0.0
! Evaluate Va
if (a <= L) then
 b = L-a
  else
      a = 2*L-a
      b = a-L
endif

if (a < 0 .or. a > 2*L) then
      Va = 0.0
  else if (a <= L) then
      Va = -((P*b)/(4*L**3))*(4*L**2-a*(L+a))
  else if (a > L) then
      Va = -((P*a*b)/(4*L**3))*(L+a)
end if

end function Va

real function Vb(a,L,P)   
! Function to calcute max. shear at mid support under one moving point load    
!DIR$ ATTRIBUTES STDCALL, DLLEXPORT :: Vb
!DIR$ ATTRIBUTES ALIAS: 'Vb' :: Vb
!Calling parameters
  real :: a, L, P, b   
  Vb = 0.0
! Evaluate Vb
if (a <= L) then
      b = L-a
  else
      a = 2*L-a
      b = a-L
endif

if (a < 0 .or. a > 2*L) then
      Vb = 0.0
  else if (a <= L) then
      Vb = -((P*a)/(4*L**3))*(4*L**2+b*(L+a))
  else if (a > L) then
      Vb = -((P*a*b)/(4*L**3))*(L+a)
end if 

end function Vb

real function Rb(a,L,P)   
!Function to calcute max. reaction at mid support under one moving point load    
!DIR$ ATTRIBUTES STDCALL, DLLEXPORT :: Rb
!DIR$ ATTRIBUTES ALIAS: 'Rb' :: Rb
!Calling parameters
  real :: a, L, P, b   
  Rb = 0.0
! Evaluate rb
if (a <= L) then
      b = L-a
  else
      a = 2*L-a
      b = a-L
endif

if (a < 0 .or. a > 2*L) then
      Rb = 0.0
  else if (a <= L) then
      Rb = ((P*a)/(2*L**3))*(2*L**2+b*(L+a))
  else if (a > L) then
      Rb = ((P*a)/(2*L**3))*(2*L**2+b*(L+a))
end if 

end function Rb

subroutine poutrel(name, arp)
    !DIR$ ATTRIBUTES REFERENCE, STDCALL, DLLEXPORT :: poutrel
    !DIR$ ATTRIBUTES ALIAS: 'poutrel' :: poutrel
    !DIR$ ATTRIBUTES MIXED_STR_LEN_ARG :: poutrel
    !DIR$ ATTRIBUTES NOINLINE :: procedure

    use, intrinsic :: iso_fortran_env, only: error_unit
    implicit none

    ! Calling parameter
    character(len=*), intent(in) :: name  ! Keep as INTENT(IN) since we won't modify it
    real, intent(out) :: arp(26)

    type :: profiel
        character(10) :: name
        real :: A, h, b, tw, tf, r
    end type profiel

    integer, parameter :: NPROF = 90

    type(profiel) :: profielen(NPROF)

    ! The table is generated automatically from poutr.csv and
    ! included using a data statement
    include "profielen.fi"

    real :: A = 0.0, h = 0.0, b = 0.0, tw = 0.0, tf = 0.0, r = 0.0
    real :: Iy = 0.0, Avz = 0.0, Wy = 0.0, Wply = 0.0, iry = 0.0
    real :: Iz = 0.0, Wz = 0.0, Wplz = 0.0, irz = 0.0
    real :: Ss = 0.0, IT = 0.0, Iw = 0.0, G = 0.0
    real :: Auf = 0.0, Izuf = 0.0, irzuf = 0.0, Wzuf = 0.0, Wzpluf = 0.0, Sy = 0.0, Syr = 0.0
    real :: Mplyd = 0.0, Mplzd = 0.0, Mplwd = 0.0, Vplzd = 0.0, Vplyd = 0.0
    real, parameter :: pi = 3.1415926535

    character(len=10) :: name_to_find
    integer :: idx

    ! Initialize output array to zeros
    arp = 0.0

    ! Initialize variables
    name_to_find = trim(name)  ! Use a local copy of the input parameter

    ! Debug output
    print *, "Looking for profile: '", trim(name_to_find), "'"

    ! Search for the profile
    idx = findloc(profielen%name, name_to_find, dim=1)

    if (idx == 0) then
        print *, "Invalid input: Profile not found."
        ! Initialize with zeros in case profile not found
        arp = 0.0
        return
    end if

    ! Profile was found
    A  = profielen(idx)%A
    h  = profielen(idx)%h
    b  = profielen(idx)%b
    tw = profielen(idx)%tw
    tf = profielen(idx)%tf
    r  = profielen(idx)%r

   
    ! Shear area
    Avz = A-2*b*tf+(tw+2*r)*tf
    ! Weight per meter
    G = A*8000*1.0e-6  ! Fixed precision issue
    ! Second moment of inertia
    Iy =(1.0/12.0)*(b*h**3.0-(b-tw)*(h-2.0*tf)**3.0)+0.03*r**4.0+0.2146*r**2.0*(h-2.0*tf-0.4468*r)**2.0
    ! Second moment of inertia about z-axis
    Iz = (1.0/12.0)*(2*tf*b**3.0+(h-2*tf)*tw**3.0) + &
         0.03*r**4.0 + 0.2146*r**2.0*(tw-0.4468*r)**2.0
    ! Radius of gyration
    iry = sqrt(Iy/A)
    irz = sqrt(Iz/A)
    ! Tortional constant
    IT = (2.0/3.0)*(b-0.63*tf)*tf**3.0+1.0/3.0*(h-2*tf)*tw**3.0+2*tw/tf*(0.145+0.1*r/tf)  &
    *(((r+tw/2.0)**2.0+(r+tf)**2.0-r**2.0)/(2*r+tf))**4.0
    ! Warping constant
    Iw = ((tf*b**3.0)/24.0)*(h-tf)**2.0
    ! Length of stiff bearing
    Ss = tw+2*tf+(4.0-2.0*sqrt(2.0))*r
    ! Elastic section modulus
    Wy = 2*Iy/h
    Wz = 2*Iz/b
    ! Plastic section modulus
    Wply = (tw*h**2.0)/4.0+(b-tw)*(h-tf)*tf+((4.0-pi)/2.0)*r**2.0*(h-2.0*tf)+((3.0*pi-10.0)/3.0)*r**3.0
    Wplz = (b**2.0*tf)/2.0 + ((h-2.0*tf)/4.0)*tw**2.0+r**3.0*(10.0/3.0-pi)+(2.0-pi/2.0)*tw*r**2.0
    ! Static radius
    Sy = b*tf*((h-tf)/2.0)+(h/2.0-tf)*tw*((h-2.0*tf)/4.0)+((r**2*(4.0-pi))/4.0)*((h-2.0*tf-r)/2.0)
    Syr = Sy - tw*((h/2.0-tf-r)**2)/2.0
    ! Cross sectional values top flange top flange + 1/5 web)
    Auf = b*tf + ((h - 2*tf)/5)*tw
    Izuf = Iz/2.0
    irzuf = sqrt(Izuf/Auf)
    Wzuf = Wz /2.0
    Wzpluf = Wplz/2.0
    ! Output (indexed for easier reference)
    arp( 1) = A
    arp( 2) = h
    arp( 3) = b
    arp( 4) = tw
    arp( 5) = tf
    arp( 6) = r
    arp( 7) = Avz
    arp( 8) = G
    arp( 9) = Iy
    arp(10) = Iz
    arp(11) = iry
    arp(12) = irz
    arp(13) = IT
    arp(14) = Iw
    arp(15) = Ss
    arp(16) = Wy
    arp(17) = Wz
    arp(18) = Wply
    arp(19) = Wplz
    arp(20) = Auf
    arp(21) = Izuf
    arp(22) = irzuf
    arp(23) = Wzuf
    arp(24) = Wzpluf
    arp(25) = Sy
    arp(26) = Syr

end subroutine poutrel
  
  ! Fixed subroutine to properly use profile data

    subroutine mplst(name, name_len, Fy, pl)
        !DEC$ ATTRIBUTES DLLEXPORT :: mplst
        !DIR$ ATTRIBUTES ALIAS: 'mplst' :: mplst
        !DEC$ ATTRIBUTES STDCALL :: mplst
        !DEC$ ATTRIBUTES REFERENCE :: name
        character(len=32), intent(in) :: name  ! or 64, or whatever max you need
        integer, intent(in) :: name_len
        real, intent(in) :: Fy
        real, intent(out) :: pl(5)
  ! Local variables - must declare all at beginning
  real :: arp(26)
  real :: Wply, Wplz, h, tf, b, Avz, Auf
  real :: Mplyd, Mplzd, Mplwd, Vplzd, Vplyd
  

  print *, "mplst called with name: ", trim(name)
  print *, "name_len: ", name_len
  print *, "Fy: ", Fy

  ! First get the profile data
  call poutrel(name(1:name_len), arp)

  if (any(arp == 0.0)) then
    print *, "Warning: poutrel returned zero values"
  end if
  
  ! Get values from the arp array
  Wply = arp(18)
  Wplz = arp(19)
  h = arp(2)
  tf = arp(5)
  b = arp(3)
  Avz = arp(7)
  Auf = arp(20)
  
  ! Full plastic capacity profile calculations
  Mplyd = Wply * Fy
  Mplzd = Wplz * Fy
  Mplwd = 0.25 * ((h-tf) * b**2 * tf * Fy)
  Vplzd = (Avz * Fy) / sqrt(3.0)
  Vplyd = (2 * Auf * Fy) / sqrt(3.0)
  
  pl(1) = Mplyd
  pl(2) = Mplzd
  pl(3) = Mplwd
  pl(4) = Vplzd
  pl(5) = Vplyd
  
  end subroutine mplst

  real function test_dll(x)
  !DEC$ ATTRIBUTES DLLEXPORT :: test_dll
  !DIR$ ATTRIBUTES ALIAS: 'test_dll' :: test_dll
  !DEC$ ATTRIBUTES STDCALL :: test_dll
  real, intent(in) :: x
  test_dll = x * 2.0
  end function test_dll
    
end module mom_m
      
  ! A program to compute moments using the module
program mom
    ! Need to compile the module first before using it
    use mom_m

    ! Moments in a 2 span continuous beam with equal spans under up to 4 moving point loads.
    ! 20/05/2024 R.M. Versie A

    implicit none

    ! Declare variables
    real :: a1, a2, a3      ! Distance between point loads
    real :: P1, P2, P3, P4  ! Point loads
    real :: L               ! Length span
    real :: Fy
    real, parameter :: t = 0.05   ! Distance between studied points
    real :: ar(6), ch1(2)
    real :: arp(26), pl(5)  ! Arrays for profile data and plastic capacities
    real :: maxposx
    character(len=10) :: name = ""
    integer :: name_len     ! Declare name_len at the beginning

    ! Retrieve data of European parallel flange beams
    ! Version        Author         Date
    !    A            R.M.        21/04/2025

    ! Test values
    name = 'HEB300'
    L = 6.0
    a1 = 3.6
    a2 = 1.0
    a3 = 1.0
    P1 = 22.5
    P2 = 22.5
    P3 = 0.0
    P4 = 0.0
    Fy = 355.0

    ! Call MaxMin subroutine
    call MaxMin(L, P1, P2, P3, P4, a1, a2, a3, t, ch1, ar)
    write(*,*) 'Max Min: maxmom, minmom, maxposx, maxposa, minposx, minposa ='
    write(*,*) ar
    write(*,*) 'Max Va=', DkEnd(L, P1, P2, P3, P4, a1, a2, a3, t)
    write(*,*) 'Max Vb=', DkMid(L, P1, P2, P3, P4, a1, a2, a3, t)
    write(*,*) 'Max Rb=', ReacB(L, P1, P2, P3, P4, a1, a2, a3, t)

    ! Get beam profile values
    call poutrel(name, arp)

    print *, "maxpos= " , maxposx
    print *, "Profiel= ", name
    print *, "A= ", arp(1)
    print *, "h= ", arp(2)
    print *, "b= ", arp(3)
    print *, "tw= ", arp(4)
    print *, "tf= ", arp(5)
    print *, "r= ", arp(6)
    print *, "Avz= ", arp(7)
    print *, "G= ", arp(8)
    print *, "Iy= ", arp(9)
    print *, "Iz= ", arp(10)
    print *, "iy= ", arp(11)
    print *, "iz= ", arp(12)
    print *, "IT= ", arp(13)
    print *, "Iw= ", arp(14)
    print *, "Ss= ", arp(15)
    print *, "Wy= ", arp(16)
    print *, "Wz= ", arp(17)
    print *, "Wply= ", arp(18)
    print *, "Wplz= ", arp(19)
    print *, "Auf= ", arp(20)
    print *, "Izuf= ", arp(21)
    print *, "irzuf= ", arp(22)
    print *, "Wzuf= ", arp(23)
    print *, "Wpluf= ", arp(24)
    print *, "Sy= ", arp(25)
    print *, "Syr= ", arp(26)

    ! Calculate plastic capacities
    name_len = len_trim(name)  ! Calculate the length of the name string
    call mplst(name(1:name_len), name_len, Fy, pl)

    print *, "Mplyd= ", pl(1)
    print *, "Mplzd= ", pl(2)
    print *, "Mplwd= ", pl(3)
    print *, "Vplzd= ", pl(4)
    print *, "Vplyd= ", pl(5)

end program mom

Never mind. I should have thought better before asking
Of course the position of the maximum moment stays the same for two equal forces at the same distance whatever their value.

Roger

Instead of using these intrinsic calls to the scan through the array twice, consider the maxloc() intrinsic, which allows this to be done with a single pass. There is also a minloc() intrinsic.