"""A factory pattern for instantiating Resolution objects."""
import warnings
from functools import singledispatchmethod
from pathlib import Path
from typing import Any
from MDMC.common.factory import ModuleFactory
from MDMC.resolution.resolution import Resolution
[docs]
class ResolutionFactory(ModuleFactory[Resolution]):
"""
Factory class for resolution window functions.
Any function in `MDMC.resolution` s can be instantiated using this factory.
"""
registry: dict[str, Resolution] = {}
curr_path = Path(__file__).parent
curr_pack = __package__
exclude = (curr_path / "__init__.py", curr_path / "resolution_factory.py")
[docs]
@classmethod
def create_instance(cls,
resolution: dict | float | str | None,
*args: Any) -> Resolution:
"""
Create a Resolution object from a dictionary.
Parameters
----------
resolution : dict, float, or str
A parameter specifying the resolution. Should be a one-line dict
giving the resolution type and parameters, e.g. a Lorentzian resolution
with FWHM of 3 is specified {'lorentzian': 3.0}.
If a float is given, resolution is assumed to be Gaussian with FWHM
of that float.
If a str is given, the string is assumed to be a file path to a vanadium
run used to define a resolution.
Returns
-------
~MDMC.resolution.Resolution
A resolution object with the desired properties.
"""
resolution = cls._standardise_input(resolution)
function_name = list(resolution.keys())[0].title() + 'Resolution'
function_res = list(resolution.values())[0]
if function_name == "FileResolution":
return cls.create(function_name, function_res, *args)
return cls.create(function_name, function_res)
@singledispatchmethod
@staticmethod
def _standardise_input(resolution) -> dict:
"""
Ensure that resolution is a one-line dictionary.
Fixes 'lazy' input, e.g. if resolution
was input as a string or number.
Parameters
----------
resolution: Any
The input to the resolution factory.
Returns
-------
dict
If ``resolution`` is a dict, returns the first item of the dict.
If ``resolution`` was a float, returns ``{'gaussian': resolution}``
If ``resolution`` was a string, returns ``{'file': resolution}``
Raises
------
NotImplementedError
If ``resolution`` is not a dict, string or float.
Warns
-----
SyntaxWarning
If ``resolution`` is a dict with multiple lines, or a float.
"""
raise NotImplementedError("Format of resolution function not recognised."
" It should be a one-line dictionary, but can also"
" be a number (for Gaussian resolution), a string"
" (for resolution from file), or explicitly stated as"
" None if resolution application is not needed.")
@_standardise_input.register
@staticmethod
def _(resolution: dict) -> dict:
if len(resolution) > 1:
warnings.warn("The resolution dict should only have one line; ignoring"
" all lines except the first."
" Dictionaries are technically unordered, so"
" this may cause unintended behaviour!", SyntaxWarning)
res = {list(resolution.keys())[0]: list(resolution.values())[0]}
else:
res = resolution
if list(resolution.keys())[0].lower() == "from_file":
res = {"file": list(resolution.values())[0]}
return res
@_standardise_input.register
@staticmethod
def _(resolution: str) -> dict:
return {'file': resolution}
@_standardise_input.register(int)
@_standardise_input.register(float)
@staticmethod
def _(resolution: float) -> dict:
warnings.warn("Assuming energy resolution is Gaussian. To change this,"
" input energy resolution as {'function': 'value'}, where"
" 'function' is your desired resolution approximation function.",
SyntaxWarning)
return {'gaussian': float(resolution)}
@_standardise_input.register
@staticmethod
def _(resolution: None) -> dict:
return {'null': 0}
ResolutionFactory.scan()