Skip to content

Commit

Permalink
Add doc on how to add wsm (#868)
Browse files Browse the repository at this point in the history
  • Loading branch information
riclarsson authored Nov 22, 2024
2 parents 876b242 + 2df611a commit 6ea1921
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 19 deletions.
1 change: 1 addition & 0 deletions doc/arts/develop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ It contains design descriptions and guidelines for the development of ARTS.

develop.classes
develop.error
develop.workspace_methods
160 changes: 160 additions & 0 deletions doc/arts/develop.workspace_methods.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
Workspace methods
#################

Workspace methods are the core interaction a user has with ARTS calculations.
They are called from the :class:`~pyarts.workspace.Workspace` object in python but are
implemented in C++ under-the-hood.

The workspace methods definition are mainly located in the ``workspace_methods.cpp``
and ``workspace_meta_methods.cpp`` files. Some workspace methods are
automatically generated elsewhere.

Workspace methods are exposed to other C++ files via the ``<workspace.h>`` header file.
Only the CMake target ``artsworkspace`` and targets that depend on it may include
``<workspace.h>``.

Defining a workspace method
===========================

You may define either a basic method or a meta-method that depends on other methods.
The easiest way to do so is to copy an existing method and modify it to suit your needs.
Please design your method input and output to allow meta-methods to be defined,
as they simplify composibility signficantly.

Basic methods
-------------

Basic methods completely define their input and output and do not explicitly
depend on other methods. The method must be manually implemented.

Basic methods are defined entirerly in ``workspace_methods.cpp``.
They are part of a map object called ``wsm_data``. The name of the
method is the key and the value is a struct with the following fields:

- ``desc`` - a description of the method as a string.
- ``author`` - the author(s) of the method as a list of strings.
- ``out`` - the output of the method as a list of strings. These must be workspace variables.
- ``gout`` - the generic output of the method as a list of strings. These must not be workspace variables.
- ``gout_type`` - the type of the generic output as a list of strings. These must be workspace groups.
- ``gout_desc`` - the description of the generic output as a list of strings.
- ``in`` - the input of the method as a list of strings. These must be workspace variables.
- ``gin`` - the generic input of the method as a list of strings. These must not be workspace variables.
- ``gin_type`` - the type of the generic input as a list of strings. These must be workspace groups.
- ``gin_value`` - the default value of the generic input as an optional workspace value.
- ``gin_desc`` - the description of the generic input as a list of strings.
- ``pass_workspace`` - a boolean indicating if the workspace should be passed to the method. If true, the first argument to the method is a ``const Workspace&``.

The expected signature of the method will depend on these fields.
A linker error will likely occur if the actual signature does not match
the expected signature.

The ``in`` and ``out`` may contain the same variable. If they do, the variable must be
initialized before the method is called because it is assumed the method is intended to
simply modify the existing value.

The fields ``gin``, ``gin_type``, ``gin_value``, and ``gin_desc`` must be the same size.
The same is true for ``gout``, ``gout_type``, and ``gout_desc``. These are user-generated
inputs and outputs, and are often used to pass information pertinent to the method itself
but not to the workspace as a whole.

Please check other workspace methods for examples by comparing their actual signature
to the expected signature to figure out how the fields should be filled in. Also check
that the documentation is generated as intended by building the ``pyarts_docs`` target.

.. tip::

All fields but ``desc`` and ``author`` are optional. If a field is not needed, it
is convenient to leave it out.

Meta methods
------------

Meta methods do not define all their input and output, but instead define a call
order into other methods. From this call order, the inputs of the user-facing
workspace method is inferred. This method should not be implemented manually.

These methods are defined in ``workspace_meta_methods.cpp``. They are defined
as part of a list called ``wsm_meta``.
A single meta method data contains:

- ``name`` - the name of the method as a string.
- ``desc`` - a description of the method as a string.
- ``author`` - the author(s) of the method as a list of strings.
- ``methods`` - the methods that the meta method depends on as a list of strings.
- ``out`` - the output of the method as a list of strings. These must be workspace variables.
- ``preset_gin`` - The preset ``gin`` values for the method as a list of workspace values.
- ``preset_gin_value`` - The preset ``gin_value`` values for the method as a list of workspace values.

.. tip::

A meta method may depend on another meta method. If it does it is important that the
meta method it depends on is defined before it in the list.

Automatic methods
-----------------

All methods that execute a workspace agenda are automatically generated.
These will be named as ``agenda_nameExecute`` and may otherwise be
treated as normal workspace method.
You need to do nothing to define these methods. But please refrain from defining
them manually as that may cause undefined naming conflicts.

The expected signature of the method ``propagation_matrix_agendaAuto`` is also
generated automatically near the end of ``workspace_methods.cpp``. It takes
its input and output from a list of other methods. Feel free to add to this
list but make sure that any naming conflicts regarding ``gin`` are resolved
before doing so. Adding a method to this list may also require changing the
actual signature (which is why the method is generated, so that a change in
the required actual signature is immediately made apparent).

The methods the begin with ``RetrievalAdd...`` are partly generated.
These methods all require a corresponding ``jacobian_targetsAdd...`` method
that fills in the ``jacobian_targets`` workspace variable. To keep that
part of the signature consistent, the additional ``RetrievalAdd...`` information
is simply appended to the the ``in``, ``out``, and ``gin``-lists of the
corresponding ``jacobian_targetsAdd...`` method using the local ``jac2ret`` lambda.

Generated files
===============

The workspace method interface generates a lot of files during the build process.
These generated files are located in the build directory and are named
as ``auto_wsm_N.cc``, where N is a number, as ``auto_wsm.cpp``, as ``auto_wsm.h``,
and as ``auto_wsmmeta.cpp`` for the C++ interfacing code. The python-binding
code is also generated as ``py_auto_wsm_N.cpp``, where N is still a number.

Workspace method naming convention
==================================

Names carry meaning. Please follow the naming convention below and
please do not hesitate to fix any naming inconsistencies you find.

Method naming
-------------

Workspace method names should be descriptive and follow the naming convention
that the main workspace variable output of the method in ``snake_case``
is followed by a short but descriptive name of what the method does with the output
in ``PascalCase``.
A general rule of thumb is to use verbs for methods that modify the workspace
variable and nouns for methods that create a new workspace variable.

For example, :func:`~pyarts.workspace.Workspace.propagation_matrixAddLines`
has a main output of :attr:`~pyarts.workspace.Workspace.propagation_matrix` and
adds line absorption to it. It needs to be preceeded by a call to
:func:`~pyarts.workspace.Workspace.propagation_matrixInit` which sets up the
propagation matrix to an initial state.

Of course, every use-case is different but please try to follow this convention.

File naming
-----------

The file that a workspace method is implemented in should be named ``m_<concept>.cc``.
The concept should be a short but descriptive name of what the methods therein do.
Multiple methods per file is allowed and encouraged, but keep them conceptually similar.
To ensure compatibility with various filesystems, please avoid using spaces
and capital letters in the filename.

Lastly, please ensure that the file is listed in the CMake target ``artsworkspace``
or it will not be compiled.
38 changes: 19 additions & 19 deletions src/workspace_meta_methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
#include "workspace_variables.h"

std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
std::vector<WorkspaceMethodInternalMetaRecord> out;
std::vector<WorkspaceMethodInternalMetaRecord> wsm_meta;

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "measurement_sensorSimple",
.desc = "Wrapper for a single simple dirac-opening sensor",
.author = {"Richard Larsson"},
.methods = {"measurement_sensorInit", "measurement_sensorAddSimple"},
.out = {"measurement_sensor"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "measurement_sensorSimpleGaussian",
.desc = "Wrapper for a single simple Gaussian-opening sensor",
.author = {"Richard Larsson"},
Expand All @@ -30,7 +30,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.out = {"measurement_sensor"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "measurement_sensorVectorGaussian",
.desc = "Wrapper for a single simple Gaussian-opening sensor",
.author = {"Richard Larsson"},
Expand All @@ -39,7 +39,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.out = {"measurement_sensor"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "disort_spectral_flux_fieldFromAgenda",
.desc = "Use Disort for clearsky calculations of spectral flux field",
.author = {"Richard Larsson"},
Expand All @@ -48,7 +48,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.out = {"disort_spectral_flux_field"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "disort_spectral_radiance_fieldFromAgenda",
.desc = "Use the disort settings agenda to calculate spectral radiance",
.author = {"Richard Larsson"},
Expand All @@ -59,7 +59,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
"disort_quadrature_weights"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "disort_spectral_flux_fieldSunlessClearsky",
.desc = "Use Disort for clearsky calculations of spectral flux field",
.author = {"Richard Larsson"},
Expand All @@ -70,7 +70,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.preset_gin = {"option"},
.preset_gin_value = {String{"SunlessClearsky"}}});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "disort_spectral_radiance_fieldSunlessClearsky",
.desc = "Use Disort for clearsky calculations of spectral flux field",
.author = {"Richard Larsson"},
Expand All @@ -83,7 +83,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.preset_gin = {"option"},
.preset_gin_value = {String{"SunlessClearsky"}}});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "spectral_radianceApplyUnitFromSpectralRadiance",
.desc = R"(Apply unit changes to spectral radiance and its Jacobian
Expand All @@ -98,7 +98,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.out = {"spectral_radiance", "spectral_radiance_jacobian"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "spectral_radianceClearskyEmission",
.desc = "Computes clearsky emission of spectral radiances",
.author = {"Richard Larsson"},
Expand All @@ -117,7 +117,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.out = {"spectral_radiance", "spectral_radiance_jacobian"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "spectral_radianceClearskyRayleighScattering",
.desc = "Computes clearsky emission of spectral radiances",
.author = {"Richard Larsson"},
Expand All @@ -140,7 +140,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.out = {"spectral_radiance", "spectral_radiance_jacobian"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "spectral_radianceClearskyTransmission",
.desc = "Computes clearsky transmission of spectral radiances",
.author = {"Richard Larsson"},
Expand All @@ -158,7 +158,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.out = {"spectral_radiance", "spectral_radiance_jacobian"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "spectral_radianceClearskyBackgroundTransmission",
.desc = "Computes clearsky transmission of spectral radiances",
.author = {"Richard Larsson"},
Expand All @@ -175,7 +175,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.out = {"spectral_radiance", "spectral_radiance_jacobian"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "atmospheric_fieldRead",
.desc = "Reads absorption file from a directory",
.author = {"Richard Larsson"},
Expand All @@ -187,7 +187,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.preset_gin_value = {Index{0}},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "UpdateModelStates",
.desc =
"Update state of the model in preparation for a forward model run",
Expand All @@ -202,7 +202,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
"measurement_sensor"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "model_state_vectorFromData",
.desc = "Get *model_state_vector* from available data",
.author = {"Richard Larsson"},
Expand All @@ -215,7 +215,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.out = {"model_state_vector"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "model_state_vector_aprioriFromData",
.desc = "Get *model_state_vector_apriori* from available data",
.author = {"Richard Larsson"},
Expand All @@ -224,7 +224,7 @@ std::vector<WorkspaceMethodInternalMetaRecord> internal_meta_methods_creator() {
.out = {"model_state_vector_apriori"},
});

out.push_back(WorkspaceMethodInternalMetaRecord{
wsm_meta.push_back(WorkspaceMethodInternalMetaRecord{
.name = "absorption_lookup_tableCalc",
.desc = R"(Get *absorption_lookup_table* from available data.
Expand All @@ -241,7 +241,7 @@ define the default atmospheric state for the absorption lookup table.
.out = {"absorption_lookup_table"},
});

return out;
return wsm_meta;
}
const std::vector<WorkspaceMethodInternalMetaRecord>& internal_meta_methods() {
static const auto out = internal_meta_methods_creator();
Expand Down

0 comments on commit 6ea1921

Please sign in to comment.