"""Random collection of Actions that do no warrant a separate module."""
from ...parameter import Parameter
from .base import Action, convert_str_to_parameter
from finesse.solutions import BaseSolution
import logging
LOGGER = logging.getLogger(__name__)
[docs]class SaveModelAttrSolution(BaseSolution):
"""
Attributes
----------
values : dict
Dictionary of model attribute values
"""
pass
[docs]class Plot(Action):
def __init__(self, name="abcd"):
super().__init__(name)
def _requests(self, model, memo, first=True):
pass
def _do(self, state):
raise NotImplementedError()
[docs]class Printer(Action):
def __init__(self, *args, name="printer", eval=True):
super().__init__(name)
self.args = args
self._eval = eval
def _requests(self, model, memo, first=True):
pass
def _do(self, state):
if self._eval:
out = []
for _ in self.args:
if hasattr(_, "eval"):
out.append(_.eval())
else:
out.append(_)
print(*out)
else:
print(*(_ for _ in self.args))
[docs]class PrintModel(Action):
"""An action that prints the model object being currently used to run actions."""
def __init__(self, name="print_model"):
super().__init__(name)
def _requests(self, model, memo, first=True):
pass
def _do(self, state):
print(state.model)
[docs]class StoreModelAttr(Action):
def __init__(self, *args):
super().__init__(self.__class__.__name__)
self.args = tuple(a if isinstance(a, str) else a.full_name for a in args)
def _requests(self, model, memo, first=True):
pass
def _do(self, state):
sol = SaveModelAttrSolution(self.name)
sol.values = {}
for _ in self.args:
p = state.model.get(_)
if hasattr(p, "eval"):
sol.values[_] = p.eval()
else:
sol.values[_] = p
return sol
[docs]class PrintModelAttr(Action):
"""Prints an attribute of the model being currently used.
Parameters
----------
*args : (str,)
Strings input for the attribute to print
eval: bool, optional
When `True` symbolic expressions will be evaluated before printing.
Defaults to `True`.
Examples
--------
You can print the current value of parameters and such using:
>>> PrintModelAttr("m1.R", "bs.phi")
"""
def __init__(self, *args, eval=True):
super().__init__(self.__class__.__name__)
self.args = tuple(a if isinstance(a, str) else a.full_name for a in args)
self._eval = eval
def _requests(self, model, memo, first=True):
pass
def _do(self, state):
if self._eval:
out = []
for _ in self.args:
obj = state.model.get(_)
if hasattr(obj, "eval"):
out.append(f"{_}={obj.eval()}")
else:
out.append(f"{_}={obj}")
print(*out)
else:
print(*(f"{_}={state.model.get(_)}" for _ in self.args))
[docs]class Change(Action):
"""Changes a model Parameter to some value during an analysis.
Parameters
----------
change_dict : dict, optional
Dictionary of parameter:value pairs to change.
relative : bool, optional
Whether to increment from the parameters current value or not
**kwargs
Alternative method to specify parameter:value pairs to change
Examples
--------
A simple change of a parameter between running two `noxaxis` analyses:
>>> model = finesse.script.parse("l L1 P=1")
>>> model.run('series(noxaxis(), change(L1.P=2), noxaxis())')
Or increment from the current value:
>>> model.run('series(noxaxis(), change(L1.P=1, relative=True), noxaxis())')
"""
def __init__(self, change_dict=None, *, relative=False, **kwargs):
super().__init__(None)
self.change_dict = change_dict
self.kwargs = kwargs
self.relative = relative
@property
def change_kwargs(self):
kwargs = self.kwargs or {}
if self.change_dict:
kwargs.update(self.change_dict)
return kwargs
def _requests(self, model, memo, first=True):
for el in self.change_kwargs.keys():
p = convert_str_to_parameter(model, el)
if isinstance(p, Parameter):
memo["changing_parameters"].append(el)
else:
raise TypeError(
f"{el} is not a name of a Parameter or Component in the model"
)
def _do(self, state):
for el, val in self.change_kwargs.items():
p = convert_str_to_parameter(state.model, el)
if self.relative:
p.value += val
else:
p.value = val
[docs]class Execute(Action):
"""An action that will execute the function passed to it when it is run.
Parameters
----------
do_fn : function
A function that takes an AnalysisState, and the name of the Exec action
as its only arguments. If this function returns a :class:`BaseSolution`
object then it will be added to the simulations solution to return to the
user.
parameters : list, optional
A list of parameters that will be changed by do_fn, if any.
name : str
The name to give this action.
Examples
--------
A simple function to execute might use a pattern such as this, which generates
a Solution that is returned back to the user.
>>> from finesse.solutions import SimpleSolution
>>> def my_action(state, name):
... sol = SimpleSolution(name)
... return sol
The :class:`SimpleSolution` object is just an object you can store anything you
want in. You can extract any state information about the simulation or model and
store it here. This allows you to probe and store details that might not be
available as a detector, for example.
"""
def __init__(self, do_fn, parameters=None, name="execute"):
super().__init__(name)
self.do_fn = do_fn
self.parameters = parameters
def _do(self, state):
sol = self.do_fn(state, self.name)
if isinstance(sol, BaseSolution):
return sol
def _requests(self, model, memo, first=True):
if self.parameters is not None:
memo["changing_parameters"].extend(self.parameters)
[docs]class UpdateMaps(Action):
"""Update any maps that might be changing in the simulation."""
def __init__(self, name="update_maps", *args, **kwargs):
super().__init__(name)
self.args = args
self.kwargs = kwargs
def _requests(self, model, memo, first=True):
return None
def _do(self, state):
state.sim.update_map_data()
[docs]class LogModelAttribute(Action):
def __init__(self, *attrs):
super().__init__("print_parmeter")
self.attrs = attrs
def _requests(self, model, memo, first=True):
pass
def _do(self, state):
LOGGER.info(*(f"{_}={state.model.get(str(_))}" for _ in self.attrs))
[docs]class Scale(Action):
"""Action for scaling simulation outputs by some fixed amount. Included for
compatibility with legacy Finesse code. New users should apply any desired scalings
manually from Python.
Parameters
----------
detectors : dict
A dictionary of `detector name: scaling factor` mappings.
"""
def __init__(self, scales: dict, **kwargs):
super().__init__(None)
self.kwargs = kwargs
self.scales = scales
def _requests(self, model, memo, first=True):
pass
def _do(self, state):
sol = state.previous_solution
for det, fac in self.scales.items():
sol._outputs[det][()] *= fac
[docs]class MakeTransparent(Action):
"""Action to make all provided surfaces transparent. Simply sets the reflectivity to
zero and transmitivity to one.
Parameters
----------
surfaces : list
A list of surface component names to be made transparent.
"""
def __init__(self, surfaces, name="make transparent"):
super().__init__(name)
self.surfaces = surfaces
def _do(self, state):
for name, el in state.sim.model.elements.items():
if name in self.surfaces:
el.set_RTL(R=0, T=1)
def _requests(self, model, memo, first=True):
for name, el in model.elements.items():
if name in self.surfaces:
memo["changing_parameters"].extend(
[el.R.full_name, el.T.full_name, el.L.full_name]
)