Skip to content

Commit

Permalink
All in-progress how-to guides
Browse files Browse the repository at this point in the history
  • Loading branch information
Rastislav Turanyi committed Jan 17, 2025
1 parent 29a3ce6 commit a244706
Show file tree
Hide file tree
Showing 7 changed files with 484 additions and 0 deletions.
21 changes: 21 additions & 0 deletions docs/source/howto.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
How-To Guides
=============

This section contains How-To guides, goal-oriented directions that will
hopefully help you achieve anything you might want to do with ResINS. If there
are any problems you are trying to solve with ResINS that we do not have a
How-To guide for, or if any of the existing How-To guides are not clear, please
do open an issue on
`our GitHub <https://github.com/pace-neutrons/resolution_functions/issues>`_.

Though, do note that these guides are provided as instructions to solve common
problems - for learning how to use ResINS, please see our
:doc:`Tutorials<tutorials>` section. The following How-To guides are currently
provided:


.. toctree::
:maxdepth: 1
:glob:

howtos/*
105 changes: 105 additions & 0 deletions docs/source/howtos/add_instrument.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
How To Add an Instrument
========================

ResINS strives to model all :term:`INS` :term:`instruments<instrument>` past,
present, and future, but it is inevitable that some will not be natively
supported *yet*. Fortunately, ResINS was designed to make adding new instruments
relatively straightforward. Though, there are two ways of doing this:

.. contents::
:backlinks: entry
:depth: 2
:local:

.. _add-personal-instrument:

How To Add an Instrument for Personal Use
-----------------------------------------

It is possible to add an :term:`instrument` only locally, outside the ResINS
ecosystem. Technically, all that is required is a dictionary, but the most
readable way is to construct a YAML file with the instrument details. This file
**must** follow the :doc:`data file spec<../dev/yaml_spec>` and include all the
necessary information. Then, ResINS can be leveraged to do the rest without
additional code:

>>> from resolution_functions import Instrument
>>> new_instrument_path = '~/instrument/instrument.yaml'
>>> version = 'version' # If the created YAML file contains multiple versions
>>> new_instrument = Instrument.from_file(new_instrument_path, version)
>>> new_instrument.name
'new_instrument'
>>> new_instrument.version
'version'

Given that the YAML file specifies a model already implemented in ResINS, and
that its parameters have been all included, the resolution function can be
computed:

>>> model = new_instrument.get_resolution_function('PyChop_fit', chopper_package='A', e_init=100, chopper_frequency=300)
>>> print(model)
PyChopModelFermi(citation=[''])

However, if a new model was created for the new instrument, more work will be
required, see :doc:`add_model`.

How to register an instrument with ResINS
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

??????????????????

How to add an instrument without creating a yaml file
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For personal use, it is possible to also specify an instrument without creating
a YAML data file for it. All that is required is a Python dictionary, though the
dictionary still **must** follow the :doc:`data file spec<../dev/yaml_spec>`
(though only the dict inside :ref:`models: key<spec-models>`):

>>> from resolution_functions import Instrument
>>> new_instrument_name = 'name'
>>> new_instrument_version = 'v1'
>>> new_instrument_default_model = 'AbINS'
>>> new_instrument_data = {'AbINS': {'function': 'polynomial_1d', 'citation': [''], 'parameters': [0, 2], 'settings': {}}}
>>> new_instrument = Instrument(new_instrument_name, new_instrument_version, new_instrument_data, new_instrument_default_model)
>>> print(new_instrument)
Instrument(name=name, version=v1)
>>> model = new_instrument.get_resolution_function()
>>> print(model)
PolynomialModel1D(citation=[''])
>>> model(100)
200.0

How To Add an Instrument to ResINS
----------------------------------

If you would like to contribute a new :term:`instrument` to ResINS (which we do
appreciate!), do open an issue on
`our GitHub <https://github.com/pace-neutrons/resolution_functions>`_
so that we can help. Otherwise, the process will start out similar to when you
would :ref:`create an instrument for personal use<add-personal-instrument>` in
that you will need to create a new YAML file for the instrument, following the
:doc:`spec<../dev/yaml_spec>`. Then, the file will have to be placed in
``resolution_functions/src/resolution_functions/instrument_data`` (of course
working on a new branch of your own fork, see Contributing Guidelines). Lastly,
to be able to use the new instrument with
:py:meth:`~resolution_functions.instrument.Instrument.from_default`,
it has to be added to
:py:data:`resolution_functions.instrument.INSTRUMENT_MAP`;
create a new entry in the dictionary with the format::

INSTRUMENT_MAP = {
'instrument_name': ('yaml_file_name', None)
}

where `instrument_name` is the official name of the instrument that you would
like users to use when creating the instrument, and `yaml_file_name` is the
name of the YAML file without the `.yaml` extension, e.g. `arcs`.

.. note::

The `None` in the example above is used for creating an alias and represents
a version name for one of the versions in the `yaml_file_name.yaml` file.
For example, the TFXA instrument is an alias for the TFXA :term:`version` of
the TOSCA :term:`instrument` and is specified as
`'TFXA': ('tosca', 'TFXA')`.
11 changes: 11 additions & 0 deletions docs/source/howtos/add_model.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
How To Add A New Model
======================

.. contents::
:backlinks: entry
:depth: 2
:local:

How to add a new model for personal use
---------------------------------------

36 changes: 36 additions & 0 deletions docs/source/howtos/add_version.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
How To Add a Version To an Instrument
=====================================

ResINS strives to model all :term:`versions<version>` of any given :term:`INS`
:term:`instrument` - old, new, and planned - but it is inevitable that some will
not be natively supported *yet*. Fortunately, while not as straightforward as
:doc:`creating a new instrument<add_instrument>`, ResINS aims to make adding
new :term:`versions<version>` as simple as possible:

.. contents::
:backlinks: entry
:depth: 2
:local:


How to add a version for personal use
-------------------------------------

Adding a :term:`version` to an :term:`instrument` only locally (without
:ref:`submitting to ResINS<howto-version-resins>`) is the more difficult task
since ResINS does not have an extension/add-ons mechanism. Therefore, to create
a version, it is necessary to resort to either creating an entire instrument or
working with Python dictionaries instead of YAML files:

Using YAML files
^^^^^^^^^^^^^^^^

To add a :term:`version` to an existing :term:`instrument`


.. _howto-version-resins:

How to add a version to ResINS
------------------------------


72 changes: 72 additions & 0 deletions docs/source/howtos/get_resolution_function.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
How To Get the Resolution Function
==================================

The short answer for getting the :term:`resolution function` of a particular
:term:`version` of a given :term:`instrument` is to use the
:py:meth:`resolution_functions.instrument.Instrument.get_resolution_function`
method, e.g.:

>>> from resolution_functions import Instrument
>>> tosca = Instrument('TOSCA')
>>> book = tosca.get_resolution_function('book', detector_bank='Forward')
>>> print(book)
ToscaBookModel(citation="")

However, this is a shared interface, and so the
:term:`configurations<configuration>` and :term:`settings<setting>` that need to
be provided differ between :term:`instruments<instrument>`,
:term:`versions<version>`, and :term:`models<model>`. Of course, the information
required can be gathered from the documentation, and the :term:`configuration`
details can be found via :doc:`programmatic means<programmatic_query>`, but the
only way of obtaining all the information simultaneously is getting the model
signature:

How To Get the Model Signature
------------------------------

The
:py:meth:`resolution_functions.instrument.Instrument.get_model_signature`
method returns a :py:class:`~inspect.Signature` (from the `inspect` module) that
contains all the information for calling the
:py:meth:`resolution_functions.instrument.Instrument.get_resolution_function`
method:

* All arguments available for the :term:`model` & :term:`version` &
:term:`instrument`
* The type annotation for that argument
* The default value for that argument
* The restrictions on that argument

This method does not distinguish between :term:`configurations<configuration>`
and :term:`settings<setting>`; it only considers
:py:meth:`resolution_functions.instrument.Instrument.get_resolution_function`
and its call signature:

>>> from resolution_functions import Instrument
>>> maps = Instrument.from_default('MAPS')
>>> sig = maps.get_model_signature()
>>> sig
<Signature (model_name: Optional[str] = 'PyChop_fit', *, chopper_package: Literal['A', 'B', 'S'] = 'A', e_init: Annotated[ForwardRef('Optional[float]'), 'restriction=[0, 2000]'] = 500, chopper_frequency: Annotated[ForwardRef('Optional[int]'), 'restriction=[50, 601, 50]'] = 400, fitting_order: 'int' = 4, _) -> resolution_functions.models.pychop.PyChopModelFermi>

The returned :py:class:`inspect.Signature` object can be then be queried using
the full capabilities afforded by this standard library implementation. For
example, the return annotation and the arguments can be queried separately:

>>> sig.return_annotation
<class 'resolution_functions.models.pychop.PyChopModelFermi'>
>>> sig.parameters
mappingproxy(OrderedDict([('model_name', <Parameter "model_name: Optional[str] = 'PyChop_fit'">), ('chopper_package', <Parameter "chopper_package: Literal['A', 'B', 'S'] = 'A'">), ('e_init', <Parameter "e_init: Annotated[ForwardRef('Optional[float]'), 'restriction=[0, 2000]'] = 500">), ('chopper_frequency', <Parameter "chopper_frequency: Annotated[ForwardRef('Optional[int]'), 'restriction=[50, 601, 50]'] = 400">), ('fitting_order', <Parameter "fitting_order: 'int' = 4">), ('_', <Parameter "_">)]))

Where the latter can then be investigated in much more detail via the
:py:class:`inspect.Parameter` interface:

>>> sig.parameters['e_init']
<Parameter "e_init: Annotated[ForwardRef('Optional[float]'), 'restriction=[0, 2000]'] = 500">
>>> sig.parameters['e_init'].name
'e_init'
>>> sig.parameters['e_init'].default
500
>>> sig.parameters['e_init'].annotation
typing.Annotated[ForwardRef('Optional[float]'), 'restriction=[0, 2000]']
>>> sig.parameters['e_init'].kind
<_ParameterKind.KEYWORD_ONLY: 3>
Loading

0 comments on commit a244706

Please sign in to comment.