This is a small modification of the basic tutorial on the simple beam model. In particular, will show how to use mathmatical expressions inside a Jinja template. For more information in encoding and decoding see the tutorials/encoder_decoder_tutorial.ipynb
notebook.
We need to import EasyVVUQ as well as ChaosPy (we use it's distributions) and matplotlib for plotting later on.
import os
import easyvvuq as uq
import chaospy as cp
import matplotlib.pyplot as plt
from easyvvuq.actions import CreateRunDirectory, Encode, Decode, CleanUp, ExecuteLocal, Actions
We will describe the parameters. This is done for validation purposes (so that input parameters outside valid ranges given an error. Also this is where you can specify default values for input parameters that you don't want to vary in the analysis. Only the type and the default value fields are mandatory.
params = {
"F": {"type": "float", "default": 1.0},
"L": {"type": "float", "default": 1.5},
"a": {"type": "float", "min": 0.7, "max": 1.2, "default": 1.0},
"D": {"type": "float", "min": 0.75, "max": 0.85, "default": 0.8},
"d": {"type": "float", "default": 0.1},
"E": {"type": "float", "default": 200000},
"power" : {"type": "float", "default": 0.5},
"outfile": {"type": "string", "default": "output.json"}
}
Below we find the only deviation from the basic tutorial, namely the use of the Jinja decoder with a mathmatical expression for the a
variable. The standard Jinja template would look like:
{"outfile": "{{outfile}}",
"F": {{F}},
"L": {{L}},
"a": {{a}},
"D": {{D}},
"d": {{d}},
"E": {{E}}
}
This is replaces every {{variable}}
with a numeric value. If that the variable appears in the vary
, the value will be drawn from the specified probability distribution. If it does not appear in vary
, the default value as specified in the params
dict will be used. The result will be a JSON file that is read by the beam model. It does not have to be a JSON file, this same principle will hold for any type of template file.
The Jinja encoder is flexible in the sense that mathematical expressions can also be used. As an example, consider the followin template:
{"outfile": "{{outfile}}",
"F": {{F}},
"L": {{L}},
"a": {{a ** power}},
"D": {{D}},
"d": {{d}},
"E": {{E}}
}
This is the same as before, except now the square root of a
is taken. Here, power
is also defined in the params
dict. Since power
is not in vary, the value of 0.5
is always used.
encoder = uq.encoders.JinjaEncoder(template_fname='beam.jinja', target_filename='input.json')
The rest of this turorial proceeds unmodified from the basic tutorial.
decoder = uq.decoders.JSONDecoder(target_filename='output.json', output_columns=['g1'])
execute = ExecuteLocal('{}/beam input.json'.format(os.getcwd()))
actions = Actions(CreateRunDirectory('/tmp', flatten=True),
Encode(encoder), execute, Decode(decoder))
Campaign is the central hub in which operations take place. It is responsible for running your simulations, gathering the results, storing them in the Database, retrieving them for analysis, etc. The Campaign in EasyVVUQ is very powerful and supports multiple applications, sampling, analysis and execution methods. It also lets you save progress and retrieve results later for analysis. Here we only look at a simple case.
campaign = uq.Campaign(name='beam', params=params, actions=actions, work_dir='/tmp')
First we need to define the input parameter distributions. We have chosen 4 of the 6 available inputs. This is partly because this means that we won't have to sample at too many points and partly because I've found that these influence the output variable the most.
vary = {
"F": cp.Normal(1, 0.1),
"L": cp.Normal(1.5, 0.01),
"a": cp.Uniform(0.7, 1.2),
"D": cp.Triangle(0.75, 0.8, 0.85)
}
We have to choose the sampler next. For this task we can use either Stochastic Collocation, Polynomial Chaos Expansion or QMC samplers. Stochastic Collocation is fast for this problem size so that is what we chose.
campaign.set_sampler(uq.sampling.PCESampler(vary=vary, polynomial_order=1))
For this tutorial we have chosen to run the simulation on the local machine. This will done in parallel with up to 8 tasks running concurrently. Alternatives are execution in the Cloud (via the ExecuteKubernetes action) or on HPC machines.
campaign.execute().collate()
The execution can take a bit since we need to generate several hundred samples. We asked it to evaluate 8 samples in parallel. You can track progress by using the progress
method. You can also check progress automatically and resume execution after it is done if you want to run this inside a script rather than interactively.
campaign.get_collation_result()
We then call the analyse method whose functionality will depend on the sampling method used. It returns an AnalysisResults object which can be used to retrieve numerical values or plot the results. In this case Sobols indices.
results = campaign.analyse(qoi_cols=['g1'])
We can plot the results in a treemap format. Each square representing the relative influence of that parameter to the variance of the output variable (vertical displacement at point a
). A square labeled higher orders
represent the influence of the interactions between the input parameters.
results.plot_sobols_treemap('g1', figsize=(10, 10))
plt.axis('off');
Alternatively you can get the Sobol index values using the method call below.
results.sobols_first('g1')
results.supported_stats()
results._get_sobols_first('g1', 'F')
results.sobols_total('g1', 'F')