Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

Imagine you have program X that needs the following input by the user:

fy = 355.   #Yield stress (MPa)
fu = 552.   #Tensile stress resistance (MPa), 460 MPa in engineering stress
E = 210.E3   #Young modulus (MPa)
F = -100.E3   #vertical force (N)
k2_2=70.82e6   #stiffness 2nd segment 2L, strong axis (N.mm/rad)
loos=0.0055   #looseness 2L (rad), strong axis
bmax=3.5e6   #maximum moment 2L, strong axis (N.mm)

Then the user also wants to add an initially undetermined number of variables, a group started by imp_local+str() and another group by imp_global+str().

The number of variables to generate depends on the result of reading a txt file.

The user is aware that the program cannot handle this generated variables, but looking at a macro created by the program there is maybe a chance of introducing them.

The macro generated by the program looks like:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# import modules
from math import *
from openturns import *
from phiboot import *


# define the physical model
class TP1_Simply_supported_beam_class( OpenTURNSPythonFunction ):

    def __init__( self ):
        OpenTURNSPythonFunction.__init__( self, 9, 1)
        self.logUser = LogUser()
        self.callsNumber = 0
        self.pad = 21

    def padVar(self, var):
        if type(var) == str:
            return var.ljust(self.pad)
        else:
            return str('%+.11e' % var).ljust(self.pad)

    def setLogFile(self, filename):
        self.callsNumber = 0
        self.logUser.setFile(filename)

    def closeLogFile(self):
        self.logUser.closeFile()

    # define OpenTURNS function
    def TP1_Simply_supported_beam( self, fy, fu, E, F, k2_2, loos, bmax ):
        # log header
        if self.callsNumber == 0:
            self.logUser.level1( ''.join(map(self.padVar, ['N', 'fy', 'fu', 'E', 'F', 'k2_2', 'loos', 'bmax', 'g'])) + '\n' )

        # log input vars
        self.logUser.level1( str(self.callsNumber).ljust(self.pad) + ''.join(map(self.padVar, [fy, fu, E, F, k2_2, loos, bmax]))

And the code to generate the groups of variables is:

fileData = [] #array with the input file

inputFile = open("C:/Abaqus_JOBS/Job-M1-3_4.inp", "r") #CAE INPUT FILE
#fileData = variable with all the lines of the inp file
for line in inputFile:
    fileData.append([x.strip() for x in line.split(',')])

fgenerate1=0
nTop=[]
for row,data in enumerate(fileData):
    if data[0]=="*Nset" and data[1]=="nset=TOP":
        row_Top = row
    if len(data)==3 and data[0]=="*Nset" and data[1]=="nset=TOP" and data[2]=="generate":
        fgenerate1=1
if row_Top!=0:
    for data in fileData[row_Top+1:]: # skip first row elements
        try:
            int(data[0])
        except ValueError:
            break # found bottom_row, stop iterating
        if  fgenerate1==0:   
            nTop.append(data)
        else:
            iniN=data[0]
            finN=int(data[1])+1
            inc=data[2]
            for n in range(int(iniN), int(finN), int(inc)):
                data=n
                nTop.append([str(data)])        
loc=locals()
for k,val in enumerate(nTop) : loc["imp_local"+str(k)]
for k,val in enumerate(nTop) : loc["imp_global"+str(k)]

Now that the user has generated the imp_local and imp_global variables he wants to add them to the corresponding lines in the program macro. How can he do it?

share|improve this question
What does "add them to the corresponding lines in the program macro" mean? – unutbu May 3 at 18:50
@unutbu: add them to def TP1_Simply_supported_beam( self, fy, fu, E, F, k2_2, loos, bmax ):, self.logUser.level1( ''.join(map(self.padVar, ['N', 'fy', 'fu', 'E', 'F', 'k2_2', 'loos', 'bmax', 'g'])) + '\n' ) and self.logUser.level1( str(self.callsNumber).ljust(self.pad) + ''.join(map(self.padVar, [fy, fu, E, F, k2_2, loos, bmax])) – jpcgandre May 3 at 18:51
Are we allowed to modify TP1_Simply_supported_beam_class? – unutbu May 3 at 18:58
Yes, but in the end TP1_Simply_supported_beam_class must have the same structure and must have the added variables. – jpcgandre May 3 at 19:00

2 Answers

up vote 2 down vote accepted

If I understand the situation correctly, you should avoid defining the variables in the global namespace. "Polluting" the global namespace with dynamically named variables is never useful, because it is hard to program with variables whose names you do not know until run time.

Instead, load the names and values into a new dict, newvars. In other words, the "code that generates the group of variables" should end up defining

newvars = dict(
    fy = 355.,
    fu = 552.,
    E = 210.E3,
    F = -100.E3,
    k2_2=70.82e6,
    loos=0.0055,
    bmax=3.5e6,
    )

Then, you could define

def TP1_Simply_supported_beam(self, **kwargs):
    # log header
    if self.callsNumber == 0:
        self.logUser.level1(
            ''.join(map(self.padVar, ['N'] + kwargs.keys())) + '\n')

    # log input vars
    self.logUser.level1(
        str(self.callsNumber).ljust(self.pad) + ''.join(map(self.padVar, kwargs.values())

and use it like this:

obj = TP1_Simply_supported_beam_class()
obj.TP1_Simply_supported_beam(**newvars)
share|improve this answer
Thanks for the code! I just have a question: what's kwargs? Also, the last two lines are needed to recreate class TP1_Simply_supported_beam_class( OpenTURNSPythonFunction ):? – jpcgandre May 3 at 19:21
kwargs is what I chose to call the keyword argument variable name. When you put two asterisks, **, before a parameter name in a function definition, you are using a special syntax which tells Python to make a dict of all the keyword arguments and set it equal to kwargs. When you use **newvars when calling a function, the ** tells Python to unpack the dict into keyword arguments. For full details, see the saltycrane blog. – unutbu May 3 at 20:15
To summarize: ** is the key ingredient in this suggested solution. It allows you to pass an arbitrary number of arguments to a function which do not have to be known until runtime. – unutbu May 3 at 20:17
just another clarification. shouldn't it be obj = TP1_Simply_supported_beam_class( OpenTURNSPythonFunction ) instead of obj = TP1_Simply_supported_beam_class()? Thanks – jpcgandre May 3 at 20:26

Use Yaml or XML instead of some crappy text file as input. Structure your input, and better yet write some XSD schema that gets validated. Then use Yaml to construct whatever you need inside of Python. Code abstractions and move data/metadata into validated parameter files. Almost always.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.