.. # JUBE Benchmarking Environment
# Copyright (C) 2008-2024
# Forschungszentrum Juelich GmbH, Juelich Supercomputing Centre
# http://www.fz-juelich.de/jsc/jube
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
.. index:: advanced tutorial
Advanced tutorial
=================
.. highlight:: bash
:linenothreshold: 5
This tutorial demonstrates more detailed functions and tools of *JUBE*. If you want a basic overview you should
read the general :doc:`tutorial` first.
.. index:: schema validation, validation, dtd
Schema validation
~~~~~~~~~~~~~~~~~
To validate your *YAML* based input files you can use schema validation.
You can find ``jube.json`` in the ``contrib/schema`` folder.
To validate your *XML* based input files you can use DTD or schema validation.
You can find ``jube.dtd``, ``jube.xsd`` and ``jube.rnc`` in the ``contrib/schema`` folder.
You have to add these schema information to your input files which you want to validate.
DTD usage:
.. code-block:: xml
:linenos:
...
Schema usage:
.. code-block:: xml
:linenos:
...
RELAX NG Compact Syntax (RNC for emacs nxml-mode) usage:
In order to use the provided rnc schema file ``schema/jube.rnc`` in
emacs open an xml file and use ``C-c C-s C-f`` or ``M-x
rng-set-schema-file-and-validate`` to choose the rnc file. You can
also use ``M-x customize-variable rng-schema-locating-files`` after
you loaded nxml-mode to customize the default search paths to include
``jube.rnc``. After successful parsing emacs offers to automatically
create a ``schema.xml`` file which looks like
.. code-block:: xml
:linenos:
The next time you open the same xml file emacs will find the correct
rnc for the validation based on ``schema.xml``.
Example validation tools:
* eclipse (using DTD or schema)
* emacs (using RELAX NG)
* xmllint:
* For validation (using the DTD)::
>>> xmllint --noout --valid
* For validation (using the DTD and Schema)::
>>> xmllint --noout --valid --schema
.. index:: scripting, perl, python, shell
.. _scripting_parameter:
Scripting parameter
~~~~~~~~~~~~~~~~~~~
In some cases it is needed to create a parameter which is based on the value of another parameter.
In this case you can use a scripting parameter.
The files used for this example can be found inside ``examples/scripting_parameter``.
The input file ``scripting_parameter.xml``:
.. literalinclude:: ../examples/scripting_parameter/scripting_parameter.xml
:language: xml
The input file ``scripting_parameter.yaml``:
.. literalinclude:: ../examples/scripting_parameter/scripting_parameter.yaml
:language: yaml
In this example we see four different parameters.
* ``number`` is a normal template which will be expanded to three different :term:`workpackages `.
* ``additional_number`` is a scripting parameter which creates a new template and bases on ``number``. The ``mode`` is set to the scripting language (``python``, ``perl`` and ``shell`` are allowed).
The additional ``type`` is optional and declares the result type after evaluating the expression. The type is only used by the sort algorithm in the result step. It is not possible to
create a template of different scripting parameters. Because of this second template we will get six different :term:`workpackages `.
* ``number_mult`` is a small calculation. You can use any other existing parameters (which are used inside the same step).
* ``text`` is a normal parameter which uses the content of another parameter. For a simple concatenation parameter you do not need a scripting
parameter.
For this example we will find the following output inside the ``run.log``-file:
.. code-block:: none
====== operation ======
>>> echo "number: 1, additional_number: 1"
>>> echo "number_mult: 1, text: Number: 1"
====== operation ======
>>> echo "number: 1, additional_number: 2"
>>> echo "number_mult: 2, text: Number: 1"
====== operation ======
>>> echo "number: 2, additional_number: 2"
>>> echo "number_mult: 4, text: Number: 2"
====== operation ======
>>> echo "number: 2, additional_number: 4"
>>> echo "number_mult: 8, text: Number: 2"
====== operation ======
>>> echo "number: 4, additional_number: 4"
>>> echo "number_mult: 16, text: Number: 4"
====== operation ======
>>> echo "number: 4, additional_number: 8"
>>> echo "number_mult: 32, text: Number: 4"
Implicit Perl or Python scripting inside the ```` or any other position is not possible.
If you want to use some scripting expressions you have to create a new parameter.
.. index:: scripting, pattern, perl, python, shell
.. _scripting_pattern:
Scripting pattern
~~~~~~~~~~~~~~~~~
Similar to the :ref:`scripting_parameter`, also different patterns, or patterns and parameters can be combined.
For this a scripting pattern can be created by using the ``mode=`` attribute in the same way as it is used for the :ref:`scripting_parameter`.
All scripting patterns are evaluated at the end of the analyse part. Each scripting pattern is evaluated once. If there are multiple matches
as described in the :ref:`statistic_values` section, only the resulting statistical pattern is available (not each individual value). Scripting pattern
do not create statistic values by themselves.
In addition the ``default=`` attribute can be used to set a default pattern value, if the value can't be found during the analysis.
The files used for this example can be found inside ``examples/scripting_pattern``.
The input file ``scripting_pattern.xml``:
.. literalinclude:: ../examples/scripting_pattern/scripting_pattern.xml
:language: xml
The input file ``scripting_pattern.yaml``:
.. literalinclude:: ../examples/scripting_pattern/scripting_pattern.yaml
:language: yaml
It will create the following output:
.. code-block:: none
| value | value_pat | dep_pat | missing_pat | missing_dep_pat | missing_pat_def | missing_def_dep_pat |
|-------|-----------|---------|-------------|-----------------|-----------------|---------------------|
| 0 | 0 | 0 | | nan | 0 | 0 |
| 1 | 1 | 2 | | nan | 0 | 0 |
| 2 | 2 | 4 | | nan | 0 | 0 |
.. index:: statistic values
.. _statistic_values:
Statistic pattern values
~~~~~~~~~~~~~~~~~~~~~~~~
Normally a pattern should only match a single entry in your result files. But sometimes there are multiple
similar entries (e.g. if the benchmark uses some iteration feature).
*JUBE* will create the statistical values ``first``, ``last``, ``min``, ``max``, ``avg``, ``std``, ``cnt`` and ``sum`` automatically.
To use these values, the user have to specify the pattern name followed by ``_``,
e.g. ``pattern_name_last`` (the pattern_name itself will always be the first match).
An example for multiple matches and the statistic values can be found in ``examples/statistic``.
The input file ``statistic.xml``:
.. literalinclude:: ../examples/statistic/statistic.xml
:language: xml
The input file ``statistic.yaml``:
.. literalinclude:: ../examples/statistic/statistic.yaml
:language: yaml
It will create the following output:
.. code-block:: none
| number_pat | number_pat_last | number_pat_min | number_pat_max | number_pat_sum | number_pat_cnt | number_pat_avg | number_pat_std |
|------------|-----------------|----------------|----------------|----------------|----------------|----------------|----------------|
| 1 | 10 | 1 | 10 | 55 | 10 | 5.5 | 3.03 |
.. index:: jobsystem
Jobsystem
~~~~~~~~~
In most cases you want to submit jobs by *JUBE* to your local jobsystem. You can use the normal file access and substitution system to prepare
your jobfile and send it to the jobsystem. *JUBE* also provide some additional features.
The files used for this example can be found inside ``examples/jobsystem``.
The input jobsystem file ``job.run.in`` for *Torque/Moab* (you can easily adapt your personal jobscript):
.. literalinclude:: ../examples/jobsystem/job.run.in
:language: bash
The *JUBE* input file ``jobsystem.xml``:
.. literalinclude:: ../examples/jobsystem/jobsystem.xml
:language: xml
The *JUBE* input file ``jobsystem.yaml``:
.. literalinclude:: ../examples/jobsystem/jobsystem.yaml
:language: yaml
As you can see the jobfile is very general and several parameters will be used for replacement. By using a general jobfile and the substitution mechanism
you can control your jobsystem directly out of your *JUBE* input file.
``$$`` is used for *Shell* substitutions instead of *JUBE* substitution (see :ref:`environment-handling`).
The submit command is a normal *Shell* command so there are no special *JUBE* tags to submit a job.
There are two new attributes:
* ``done_file`` inside the ```` allows you to set a filename/path to a file which should be used by the jobfile to mark the end of execution. *JUBE* does not know when the job ends.
Normally it will return when the *Shell* command was finished. When using a jobsystem the user usually have to wait until the jobfile is executed. If *JUBE* found a
```` containing a ``done_file`` attribute *JUBE* will return directly and will not continue automatically until the ``done_file`` exists. If you want to check the current status
of your running steps and continue the benchmark process if possible you can type::
>>> jube continue bench_run
This will continue your benchmark execution (``bench_run`` is the benchmarks directory in this example). The position of the ``done_file`` is relatively seen towards the work directory.
* ``work_dir`` can be used to change the sandbox work directory of a step. In normal cases *JUBE* checks that every work directory gets a unique name. When changing the directory the user must select a
unique name by his own. For example he can use ``$jube_benchmark_id`` and ``$jube_wp_id``, which are *JUBE* :term:`internal parameters ` and will be expanded to the current benchmark and workpackage ids. Files and directories out of a given
```` will be copied into the new work directory. Other automatic links, like the dependency links, will not be created!
You will see this Output after running the benchmark:
.. code-block:: none
| stepname | all | open | wait | error | done |
|----------|-----|------|------|-------|------|
| submit | 3 | 0 | 3 | 0 | 0 |
and this output after running the ``continue`` command (after the jobs where executed):
.. code-block:: none
| stepname | all | open | wait | error | done |
|----------|-----|------|------|-------|------|
| submit | 3 | 0 | 0 | 0 | 3 |
You have to run ``continue`` multiple times if not all ``done_file`` were written when running ``continue`` for the first time.
.. index:: include
Include external data
~~~~~~~~~~~~~~~~~~~~~
As you have seen in the example before a benchmark can become very long. To structure your benchmark you can use multiple files and reuse existing
sets. There are three different include features available.
The files used for this example can be found inside ``examples/include``.
The include file ``include_data.xml``:
.. literalinclude:: ../examples/include/include_data.xml
:language: xml
The include file ``include_data.yaml``:
.. literalinclude:: ../examples/include/include_data.yaml
:language: yaml
All files which contain data to be included must use the *XML*-format. The include files can have a user specific structure (there can be no valid
*JUBE* tags like ````), but the structure must be allowed by the searching mechanism (see below). The resulting file must have a valid *JUBE* structure.
The main file ``main.xml``:
.. literalinclude:: ../examples/include/main.xml
:language: xml
The main file ``main.yaml``:
.. literalinclude:: ../examples/include/main.yaml
:language: yaml
In these file there are three different include types:
The ``init_with`` can be used inside any set definition. Inside the given file the search mechanism will search for the same set (same type, same name), will parse its structure (this must be *JUBE* valid) and copy the content to
``main.xml``. Inside ``main.xml`` you can add additional values or overwrite existing ones. If your include-set uses a different name inside your include file you can use ``init_with="filename.xml:old_name"``. It is possible to mix *YAML* based
include files with *XML* files and vice versa.
The second method is the ``