*************************************
Contribute to Inversion Test Problems
*************************************

Reporting issues
================
Bug reports and feature requests are welcome. Please search before lodging an issue at
our Github `repository`_.


How to contribute
==================

Thank you for contributing to Inversion Test Problems! ITP is
a community driven effort to create a collection of forward
codes, simulating a wide range of physical problems. ITP relies on you to
contribute your forward code! You do not need to know all the details to get started.
Here we explain step by step how to contribute your code to ITP. If this page
does not answer all of your questions, don't hesitate to contact us via
`GitHub issues <https://github.com/inlab-geo/inversion-test-problemscofi/issues/new/choose>`_
or `Slack <https://inlab-geo.slack.com>`_.


Setting it up right
-------------------

We assume that any contributor has a Github account and established a secure
connection using the GITHUB_TOKEN or SSH. If you wish to set up a new
account or want to know how to set up a secure connection before contributing,
please see here:

`Getting started with Github <https://docs.github.com/en/get-started>`_,
and `creating a personal access token <https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token>`_.

Fork and clone the ITP repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. _fork_clone:

1. Navigate to the `GitHub repository <https://github.com/inlab-geo/inversion-test-problems>`_
2. Click the "Fork" button on top right of the page (followed by a confirmation page
   with a "Create fork" button)
3. Now you will be redirected to your own fork of Inversion Test Problems,
   where you can freely commit changes and add your code.

   .. mermaid::

     %%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'showCommitLabel': false}} }%%
       gitGraph
         commit
         commit
         branch your_own_fork
         checkout your_own_fork
         commit
         commit
         checkout main
         merge your_own_fork
         commit
         commit

4. Next, create a local copy of your fork of the ITP repository using "clone"
   (the Github equivalent of download)::

     git clone https://github.com/YOUR_GITHUB_ACCOUNT/inversion-test-problems.git

   replacing YOUR_GITHUB_ACCOUNT with your own account.



Environment setup
^^^^^^^^^^^^^^^^^
.. _env_setup:

We recommend to use a clean environment to avoid dependency issues. Here we use
conda to create the environment (`how to install conda <https://docs.conda.io/projects/conda/en/latest/user-guide/install/index.html#>`_).
Create a new environment using these commands::

    conda create -n NEW_PROBLEM scipy matplotlib

Replace NEW_PROBLEM with a sensible name. Next, activate the new environment::

    conda activate NEW_PROBLEM

Create your own Inversion Test Problem
--------------------------------------
.. _create_prob:

A new contribution to Inversion Test Problems has to conform to a consistent
file structure. The simplest way to ensure that a new contribution includes
all files is to start the creation of a new problem from the template.
Execute the following command in the ROOT folder of the cloned Github repository
of ITP (replacing NEW_PROBLEM with the previous choice)::

  python utils/_create_new_example.py NEW_PROBLEM

A new folder with the name NEW_PROBLEM is created under ROOT/contrib/ that contains
all files needed for a new contribution. Each ITP example is organised around a
central class object that contains, at minimum, a set of functions with names
that are shared by all examples.

This central class object is stored in ``ROOT/contrib/NEW_PROBLEM/__init__.py``.

There are several things to keep in mind to make a contribution successful:

1. All files as generated by the template must be present in the contributed
   folder.

2. Choose a sensible, unique name for your contribution that makes it easy to
   understand what the new example is about. The name can contain more than
   one word, connected by ``_`` (i.e. NEW_PROBLEM). Spaces are not allowed.

3. The name of the new contribution (NEW_PROBLEM) must be used twice:

   - for the folder name: inversion-test-problems/contrib/NEW_PROBLEM
   - for the central class object in NEW_PROBLEM/__init__.py, i.e.::

       ```
       class new_problem():
       ...
       ```

5. Some contributions might want to include measured values. To ensure that any
   included data can be accessed on any platform, the Python package ``pkgutil``
   has to be used. A contribution can include small datasets in plain textfiles
   (ASCII or similar). Data files are stored in ``/contrib/NEW_PROBLEM/data/``
   and can be loaded using the following code::

     tmp = pkgutil.get_data(__name__, "data/datafile.txt")
     tmp2=tmp.decode("utf-8")
     self.m=np.loadtxt(StringIO(tmp2))
     del  tmp, tmp2

   The decoder might have to be adjusted to load the data successfully. For a
   full list of encodings, see
   `here
   <https://docs.python.org/3/library/codecs.html#standard-encodings/>`_.


6. The contribution can include problem-specific functions, but should always
   include the following standard functions::

     - get_model: Returns the starting model as numpy array
     - get_data: Returns reasonable values that could be measured on the recording
        locations. This can be real measured values, or prepared synthetic
        data with reasonable noise added; numpy array.
     - forward: Contains the forward calculation. Returns synthetic data values
        based on the input model and specified recording locations; numpy array.
     - plot_model: Visualises the problem. This should include a sensible visualisation
        of the model and the synthetic data.

   There are many more functions and values that a new contribution can contain, for example::

     - inversion_suggestion: A string containing inversion suggestions..
     - gradient: Returns the Jacobian of the problem, given the model and recording locations.
     - reg_param: Contains a sensible value for regularisation parameter
     - dx: Spatial resolution in x-direction
     - dt: temporal resolution
     - nt: Number of time steps
     - The possibilities are endless! Whatever information you find helpful is
        probably also helpful for the user.

7. We aim to follow `Python PEP8 style conventions <https://peps.python.org/pep-0008/>`
   to make source code readable for any user. Once your forward code is converted
   into a contribution for ITP, we recommend to use `PyLint
   <https://pypi.org/project/pylint/>`_  to enforce PEP8 coding standard in the
   new contribution.

Commit, push and pull request
-----------------------------
.. _commit_etc:


The git `add <https://git-scm.com/docs/git-add>`_ command is how you add files to
the so-called "staging" area.

The git `commit <https://git-scm.com/docs/git-commit>`_ operation captures the staged
changes of the project.

To commit a new contribution to the repository, we therefore recommend to use
the following commands::

    git add contrib/NEW_PROBLEM # Adds the new folder, but no other changes
    git commit -m "My commit message"

Please note that we aim to use
`Angular style <https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines>`_
commit messages throughout our projects. Simply speaking, we categorise our commits by
a short prefix (from ``feat``, ``fix``, ``docs``, ``style``, ``refactor``, ``perf``,
``test`` and ``chore``).

Once your changes are committed, push the commits into your remote fork::

  git push

In your remote repository under your GitHub account you should be able to see
your new commits.

Now that you've finished the coding and editing work, click on "Contribute" and
-> "Open pull request". Write a description of your example and continue as prompted.

If everything is in place, the pull request will automatically accepted and the
new inversion test problem becomes part of the python package. Thank you for
your contribution!

Jupyter Notebook
----------------

Additionally, we encourage you to add a Jupyter Notebook with an identical name
into the folder Jupyter Notebooks that contains the following:

1. An extensive description of the new Inversion Test Problem, containing
   information about (but not limited to)...:

   - the forward calculation (ie. the underlying physics) and how it was implemented.
   - which inversion method is used (and regularisation) and how it was implemented.
   - the physical unit of relevant variables, but at least of ``model`` and ``data``.
   - all changeable parameters, possibly in a list.


2. An example of the new problem being used, with a reasonable output.
