Basic modeling with Python
==========================
+-----------------+--------------------------------------------+
| Problem files | :github:`Download ` |
+-----------------+--------------------------------------------+
This example illustrates how Python scripting can be used with quoFEM to
express general mathematical models without the use of a dedicated
finite element analysis engine.
The Rosenbrock function is a *test function* that is often used to
evaluate numerical optimization algorithms. It is given by the following
expression:
.. math:: g(x, y)=(a-x)^{2}+b\left(y-x^{2}\right)^{2}
.. figure:: qfem-0005.png
:width: 500px
:align: center
A forward propagation analysis will be conducted to numerically
integrate the first and second moments of a random variable whose value
is obtained by applying the Rosenbrock function to the two following
statistically independent random variables:
1. First variable, ``X``: **Uniform** distribution with a lower bound
:math:`(L_B)` of :math:`-2.0`, upper bound :math:`(U_B)` of
:math:`2.0`,
2. Second variable, ``Y``: **Uniform** distribution with a lower bound
:math:`(L_B)` of :math:`1.4`, upper bound :math:`(U_B)` of
:math:`1.6`,
UQ Workflow
-----------
The objective of this UQ problem is to numerically integrate the
following integrals with a forward propagation routine:
.. math::
\mu_g = \mathbf{E}[g(X,Y)] = \int^{1.6}_{1.4} \int^{2.0}_{-2.0} g(x,y) f_{XY} (x,y) dx dy
.. math::
\begin{align}
\sigma_g^2 &= \mathbf{E}[(g(X,Y)-\mathbf{E}[g(X,Y)])^2] \\
&= \int^{1.6}_{1.4} \int^{2.0}_{-2.0} (g(x,y)-\mathbf{E}[g(X,Y)])^2f_{XY} (x,y) dx dy
\end{align}
where :math:`f_{XY}(x,y)` is the joint probability density function of
the random variables :math:`X` and :math:`Y`.
This procedure is implemented with a Latin hypercube sampling routine in
quoFEM by entering the following inputs in the **UQ** tab:
+---------------+-------+
| **Method** | LHS |
+---------------+-------+
| **Samples** | 200 |
+---------------+-------+
| **Seed** | 949 |
+---------------+-------+
Model Files
-----------
For this forward propagation problem, we need to define a Python script
that will import a set of random variable values, apply them to the
Rosenbrock function, then write the results to a file named
``results.out`` for every random variable realization generated by
Dakota. To this end, we begin by defining our random variables as Python
objects as follows, and supplying this as a ``.py`` script in the
**Parameters File** field of the **FEM** tab:
.. code:: python
X = 0.0
Y = 0.0
Although this file will later be used as a Python module, it is
important to note that in the quoFEM workflow, it is NOT handled as a
true Python script. Rather, it can be thought of loosely as analogous to
a C "header file" in that it simply gives us an interface to Dakota from
Python. The values assigned to the variables at this point will not be
used.
**Note**: The **Parameters File** is not a true Python script; For
the time being, until further documentation about the tool is
developed, users should only use line-by-line variable definitions
in this file. Python assignments like ``X, Y = 0.0, 0.0`` are not
supported.
We can now implement our model for the **Input File** which will begin
with a star-import from the parameters file we just created. Assuming
that file was named ``params.py``, this import would look like so:
.. code:: python
from params import *
Next we define the following simple function which evaluates the
Rosenbrock function:
.. code:: python
def rosenbrock(x, y):
a = 1.
b = 100.
return (a - x)**2.0 + b*(y - x**2.)**2.
Finally, we apply our ``rosenbrock`` function to the variables we
imported from ``params``, and write the results to a file called
``results.out``. Note that throughout the forward propagation routine,
the values assigned to the variables ``X`` and ``Y`` in the ``params``
interface are varied by the workflow application.
.. code:: python
with open('results.out', 'w') as f:
result = rosenbrock(X, Y)
f.write('{:.60g}'.format(result))
The code from these steps is colleted and made available for download in
the following files:
#. `rosenbrock.py `_:
This file is a Python script which implements the Rosenbrock
function. It is supplied to the **Input Script** field of the **FEM**
tab. Because this file write directly to ``results.out``, it obviates
the need for supplying a **Postprocess Script**. When invoked in the
workflow, the Python routine is supplied a set of random variable
realizations through the star-import of the script supplied to the
**Parameters File** field.
#. `params.py `_:
This file is a Python script which defines the problem's random
variables as objects in the Python runtime. It is supplied to the
**Parameters File** field of the **FEM** tab. *The literal values
which are assigned to variables in this file will be varied at
runtime by the UQ engine.*
Results
-------
The result for a forward propagation run with a maximum of :math:`200`
iterations is:
.. math::
\mu_g = 146.519 \\
\sigma_g = 134.01