This article describes how to set dependent parameters when running a
pyFoamRunParameterVariation.py, e.g. set the time step size as a function of the mesh spacing.
First, the basic usage is described with an example scenario. Afterwards, more information is given on possible pitfalls, additional options and how PyFoam invokes the script.
Let’s assume you already have an existing parameter study consisting of a parameter file and a templated OpenFOAM test case. The time step size is already parametrized, meaning there is a controlDict.template (Remember, .template is PyFoam’s default extension for parametrized files) file containing
deltaT @!delta_T!@ for the parametrized time step (Note: there are different choices to denote the tokens which are to be replaced, so it might as well be
|-delta_T-| or something different).
However, rather to manually specify a set of time step sizes in the parameter file, e.g.
delta_T(0.1 0.01 0.001); you want want evaluate a stability criterion which depends on your mesh resolution and set the time step accordingly. This is where
derivedParameters.py comes into play, described in the following steps:
derivedParameters.py file in your template test case.
In this Python file, implement your stability criterion in form of function, e.g.
def computeTimeStepSize(values): dt = someFunctionOf(values) return dt
Here, “values” is dictionary containing the current parameter vector.
Set a variable, named after the corresponding token (so here it must be “delta_T”), to the values given by function defined above:
delta_T = computeTimeStepSize(locals())
Note that calling “locals()” returns a dictionary with the current parameter vector.
pyFoamRunParameterVariation should now set the time step according to the function
computeTimeStepSize(values) (See below for the complete example code snippet).
--allow-derived-changesBy default, PyFoam will complain if you explicitly specify a parameter in your parameter file and try to overwrite it in the
derivedParameter.pyscript and cancel the parameter study. You can allow overwriting by calling
pyFoamRunParameterVariationwith the option
--derived-parameters-script=myScript.pyBy default, PyFoam will look for a file named
derivedParameters.py. If you want to use a different file name, you can pass the option
--derived-parameters-script=myScript.pyspecifying the name of your script.
Calling “locals()” in the global scope of
derivedParameters.py will give you a dictionary containing the current parameter vector. However, calling “locals()” inside a function definition will not work. Thus, you need to define your functions to accept at least one parameter to pass the parameter dictionary. If we stick to the example to the example above and assume that the mesh spacing is given by the key word “delta_x” in the parameter file, the complete
derivedParameters.py script could look like this:
def computeTimeStepSize(values): import math dx = values["delta_x"] dt = dx/math.pi return dt delta_T = computeTimeStepSize(locals())
For whatever reason, importing a module in the global scope of this script, e.g
import math def computeTimeStepSize(values): return math.pi
results in an error stating that the name math is not known.
Moving the imports inside the function itself solves this problem.
derivedParameter.py script is read as a string and then executed using Python’s built-in
exec function ( exec(object, globals, locals), see here for details. PyFoam passes the current parameter vector as the “locals” argument (That’s why you need “locals()” inside the derivedParameters.py script to access them and importing modules as usual does not work). The source code related to this functionality can be found in the file
PrepareCase.py of PyFoam. If you look it up, don’t be confused that it calls “exec_(…)”. “exec_(…)” is just a wrapper function whose definition depends on your Python version. In case of Python3, nothing more than calling Python3’s built-in “exec(…)” function happens.
If you use PyFoam not only for the test case setup but also to run the solver, you may encounter an error like this:
“Error in /usr/bin/pyFoamRunParameterVariation.py : Error in PyFoamParser: ‘Illegal character ‘<’ in line 36 (pos: 654)’ NONE”
The cause is a line in the file
computeDeltaT <function computeDeltaT at 0x7f13ae8ba378>;
It seems that PyFoam writes all varibales defined in derivedParameters.py to this file, including those declaring functions.
Here is how to fix this:
Go to the PyFoam source folder (e.g. /usr/lib/python3.6/site-packages/PyFoam if you are using Arch Linux and installed PyFoam via pip).
In the Application directory, open
Add the function given below as member function of the class ‘RunParameterVariation’
Illegal character fix
def removeFunctionEntries(self, folder): # PyFoam crashes when in encounters characters like "<", "'" in the # PyFoamPrepareCaseParameters file. Unfortunately, this is written # for some unknown reason to the file if a function is declared # in derivedParameters.py # This function removes lines containing the keyword 'function' prepareCaseParameters = open(path.join(folder.name,"PyFoamPrepareCaseParameters"), 'r') outputBuffer = "" for line in prepareCaseParameters: if "function" in line: line = "" outputBuffer += line prepareCaseParameters.close() prepareCaseParameters = open(path.join(folder.name,"PyFoamPrepareCaseParameters"), 'w') prepareCaseParameters.write(outputBuffer) prepareCaseParameters.close()
Add a call to this member function after
resetCustomers() and before initalization of run as shown below:
resetCustomCounter() # ------ insert line below ------- self.removeFunctionEntries(workCase) run=AnalyzedRunner(BoundingLogAnalyzer(progress=self.opts.progress,
The argument ‘workCase’ contains the path of the current variation.