A lot of parameters to be passed in subroutine

A lot of parameters to be passed in subroutine

Hello,

Would you please advise me how it is better to write code in fortran.
In my programs I use many subprograms like this:

 pure subroutine SofcElStateUpdate( &
    ! input
    OptionTerm, ActiveAreaCm2, ElThick, AnSigmaP, CtSigmaP, OhmCoeff, OhmTMult,   & 
    CtActivCoeff1, CtDiffCoeff1, OhmConstCm2, T, FuelP, FuelMFrac, OxidantP,   & 
    OxidantMFrac, OptionCalc, FuelG, OxidantG, &
    ! output
    AnGasP, AnGasG, AnGasMFrac, CtGasP, CtGasG, CtGasMFrac, Alpha,   & 
    YSZConduction, MeanPO2Ct, MeanPInertCt, OhmCm2YSZ, OhmCm2Rest, OhmCm2CtActiv, OhmCm2CtDiff,   & 
    EMF0, EMF, VoltLossYSZ, VoltLossRest, VoltLossConst, VoltLossCtActiv, VoltLossCtDiff,   & 
    VoltLossMass, Volt, Current, CurrentLim, CurrentCm2, CurrentCm2Lim, CurrentCm2LimDiff,   & 
    Pow, PowCm2, DeltaH, DeltaS, DeltaG, KPDTerm, KPDVolt,   & 
    RInner, RLoad, KR, QHeat, QHeatEntropy, QHeatOhm, SGasUtil,  O2Util, O2ElToAnIn )
  real, intent(in) :: &
    OptionTerm, ActiveAreaCm2, ElThick, AnSigmaP, CtSigmaP, OhmCoeff, OhmTMult,   & 
    CtActivCoeff1, CtDiffCoeff1, OhmConstCm2, T, FuelP, FuelMFrac, OxidantP,   & 
    OxidantMFrac, OptionCalc, FuelG, OxidantG
  real, intent(out) :: &
    AnGasP, AnGasG, AnGasMFrac, CtGasP, CtGasG, CtGasMFrac, Alpha,   & 
    YSZConduction, MeanPO2Ct, MeanPInertCt, OhmCm2YSZ, OhmCm2Rest, OhmCm2CtActiv, OhmCm2CtDiff,   & 
    EMF0, EMF, VoltLossYSZ, VoltLossRest, VoltLossConst, VoltLossCtActiv, VoltLossCtDiff,   & 
    VoltLossMass, Volt, Current, CurrentLim, CurrentCm2, CurrentCm2Lim, CurrentCm2LimDiff,   & 
    Pow, PowCm2, DeltaH, DeltaS, DeltaG, KPDTerm, KPDVolt,   & 
    RInner, RLoad, KR, QHeat, QHeatEntropy, QHeatOhm, SGasUtil,  O2Util, O2ElToAnIn
    .......
end subroutine 

As you can see there is a lot of variables to be passed to the subprogram. Moreover there is
a need to repeat declaration of all variables. Logically it would be better if fortran let the following form of declaration:

 pure subroutine SofcElStateUpdate( &
  real, intent(in) :: &
    OptionTerm, ActiveAreaCm2, ElThick, AnSigmaP, CtSigmaP, OhmCoeff, OhmTMult,   & 
    CtActivCoeff1, CtDiffCoeff1, OhmConstCm2, T, FuelP, FuelMFrac, OxidantP,   & 
    OxidantMFrac, OptionCalc, FuelG, OxidantG,
 
  real, intent(out) :: &
    AnGasP, AnGasG, AnGasMFrac, CtGasP, CtGasG, CtGasMFrac, Alpha,   & 
    YSZConduction, MeanPO2Ct, MeanPInertCt, OhmCm2YSZ, OhmCm2Rest, OhmCm2CtActiv, OhmCm2CtDiff,   & 
    EMF0, EMF, VoltLossYSZ, VoltLossRest, VoltLossConst, VoltLossCtActiv, VoltLossCtDiff,   & 
    VoltLossMass, Volt, Current, CurrentLim, CurrentCm2, CurrentCm2Lim, CurrentCm2LimDiff,   & 
    Pow, PowCm2, DeltaH, DeltaS, DeltaG, KPDTerm, KPDVolt,   & 
    RInner, RLoad, KR, QHeat, QHeatEntropy, QHeatOhm, SGasUtil,   & 
    O2Util, O2ElToAnIn   )
     
    .......
end subroutine 

In this form there is no duplicating of variable declaration.

However the problem is as following:

Using pure procedures and implicite declaration as well as atributes like intent(in) intent(out) let compose safe programs. Compiler can warn that all output data will be initialized, that all input data will be not changed and so on. And if the subroutine is called as:

 call SofcElStateUpdate( OptionTerm = 1, ActiveAreaCm2 = ActiveAreaCm2, ElThick = ElThick , AnSigmaP = SigmaP, &
                       CtSigmaP = SigmaP  ..... )  

so it is guaranteed that all variables are positioned correctly.

But what is to do with so many variables in the interface of the subroutine?
Is it a bad practice to use such subprograms with a lot of variables to be passed?
I now that using types (structures) or global variables leads to the fact that no more garanty can be given if all input data are initialized, if all output data became defined and so on.

thanks in advance.

7 posts / 0 nouveau(x)
Dernière contribution
Reportez-vous à notre Notice d'optimisation pour plus d'informations sur les choix et l'optimisation des performances dans les produits logiciels Intel.

I can't address all of your questions, but heres the short form of advice.

In the old days of classical FORTRAN (before Fortran 90) this problem (excessive argument list) would have been solved using COMMON blocks--i.e., groups of related variables (related in the sense that they tend to be needed in the same subroutines) would be collected and specified as a named group for storage. Then the common group name is simply given at the start of any subroutine that needs any of the variables.

But this was not always so simple, and somewhat error prone, The method still works, but as of Fortran 90 there are modules that make the same idea easier to implement, maintain, and less error prone. Basically the same groups of related variables are declared in a module ExampleModuleName. Then, in any subroutine that needs any of the varables in this group, you simply add the statement USE ExampleModuleName. There are some additional features that enhance this method, including initialization control of the variables, exposing only a selected subset of the list to the subroutine, and changing variable names if necessary.

Read any book on modern Fortran and it will discuss this technique in depth.

Another method is to use internal procedures. You declare the variables in the outer procedure and then call the inner procedure without passing arguments - the inner procedure can "uplevel access" variables in the parent.

Steve

I thought about the modules, types and common bloks. Use of data from the module or structured data is connected with the difficulty of understanding what data needs to be specified, and which will be calculated. If the subroutine SofcElStateUpdate descriebed above is used so it is exactly clear what kind of data are the input and what are the output, and this is controlled on compiler level, not by only human attention. If data in the module are used so you can only rely on human attention.

As others said, there may be better solutions with respect to assuring correctness.  Current ifort invokes automatic generation of interface checks in Visual Studio GUI mode; it might be awkward to maintain explicit interface blocks.

From the point of view of performance, calling procedures with long argument lists in inner loops is bad.  The fill buffers of current architectures will not be used efficiently, while the long argument lists will likely prevent the compiler from saving the day by inter-procedural optimization.  The preceding thread doesn't give information to know whether these objections would apply.

io77: You can improve the situation a bit by putting all input variables into one module, all output variables into another module, and so on.

Of course, there are limits to what you can do in this way if the same variables are presently used as input values in some calls and output values in other calls. Overall, you are better off choosing a combination of tactics to reduce the number of arguments being passed to subroutines. As you have yourself observed, no single tactic will do all the things that you desire.

I would like to suggest that you consider using a type declaration. This way the subroutine could handle multiple such types and do so concurrently if written correctly. This gives you much more flexibility.

Jim Dempsey

www.quickthreadprogramming.com

Connectez-vous pour laisser un commentaire.