class TestOptionsExpander(unittest.TestCase):

    def setUp(self):
        unittest.TestCase.setUp(self)

        self.ops = Options("op1")
        self.ops.beam.energy_eV = [5e3, 10e3, 15e3]

        self.expander = OptionsExpander()

    def tearDown(self):
        unittest.TestCase.tearDown(self)

    def testexpand(self):
        opss = self.expander.expand(self.ops)
        self.assertEqual(3, len(opss))

        names = list(map(attrgetter('name'), opss))
        self.assertIn('op1+energy_eV=5000.0', names)
        self.assertIn('op1+energy_eV=10000.0', names)
        self.assertIn('op1+energy_eV=15000.0', names)

    def testis_expandable(self):
        self.assertTrue(self.expander.is_expandable(self.ops))

        self.ops.beam.energy_eV = [5e3]
        self.assertFalse(self.expander.is_expandable(self.ops))
    def setUp(self):
        unittest.TestCase.setUp(self)

        self.ops = Options("op1")
        self.ops.beam.energy_eV = [5e3, 10e3, 15e3]

        self.expander = OptionsExpander()
示例#3
0
    def __init__(self):
        self._expander = OptionsExpander()

        self._beam_exporters = {}
        self._geometry_exporters = {}
        self._detector_exporters = {}
        self._limit_exporters = {}
        self._model_exporters = {}
示例#4
0
 def __init__(self):
     self._expander = OptionsExpander()
示例#5
0
class Converter(object):
    """
    Base class of all converters.

    Derived class shall modify the following class variables to specify
    the allowable classes for each of these parameters:

        * :attr:`MATERIALS`: list of allowable material classes
        * :attr:`BEAM`: list of allowable beam classes
        * :attr:`GEOMETRIES`: list of allowable geometry classes
        * :attr:`DETECTORS`: list of allowable detector classes
        * :attr:`LIMITS`: list of allowable limit classes
        * :attr:`MODELS`: dictionary of allowable models
            (key: model type, value: list of allowable models)
        * :attr:`DEFAULT_MODELS`: dictionary of default models
            (key: model type, value: default model)

    .. note::

       The keys for :attr:`MODELS` and :attr:`DEFAULT_MODELS` have to be the same.
    """

    PARTICLES = []
    MATERIALS = []
    BEAMS = []
    GEOMETRIES = []
    DETECTORS = []
    LIMITS = []
    MODELS = {}
    DEFAULT_MODELS = {}

    def __init__(self):
        self._expander = OptionsExpander()

    def _warn(self, *messages):
        message = ' '.join(messages)
        warnings.warn(message, ConversionWarning)

    def convert(self, options):
        """
        Performs two tasks:

          1. Expand the options to create single options that the Monte Carlo
             program can run
          2. Converts the beam, geometry, detectors and limits to be compatible
             with the Monte Carlo program.

        This method may raise :exc:`ConversionException` if some parameters
        cannot be converted.
        If a parameter is modified or removed during the conversion, a
        :class:`ConversionWarning` is issued.
        If no conversion is required, the parameter (and its object) are left
        intact.

        This method returns a list of options, corresponding to the expanded
        options.

        :arg options: options
        """
        list_options = []

        for options in self._expand(options):
            if not self._convert_beam(options): continue
            if not self._convert_geometry(options): continue
            if not self._convert_detectors(options): continue
            if not self._convert_limits(options): continue
            if not self._convert_models(options): continue

            list_options.append(options)

        if not list_options:
            self._warn("No options could be converted")

        return list_options

    def _expand(self, options):
        return self._expander.expand(options)

    def _convert_beam(self, options):
        clasz = options.beam.__class__
        if clasz not in self.BEAMS:
            self._warn("Cannot convert beam (%s)." % clasz.__name__,
                       "This options definition was removed.")
            return False

        if options.beam.particle not in self.PARTICLES:
            self._warn("Particle (%s) not supported" % options.beam.particle,
                       "This options definition was removed.")
            return False

        return True

    def _convert_geometry(self, options):
        # Check class
        clasz = options.geometry.__class__
        if clasz not in self.GEOMETRIES:
            self._warn("Cannot convert geometry (%s)." % clasz.__name__,
                       "This options definition was removed.")
            return False

        # Check material
        for material in options.geometry.get_materials():
            if not isinstance(material, tuple(self.MATERIALS)):
                self._warn("Invalid material (%s) in geometry (%s)" % \
                           (material.name, clasz.__name__),
                           "This options definition was removed.")
                return False

        return True

    def _convert_detectors(self, options):
        for key in list(options.detectors.keys()):
            detector = options.detectors[key]
            clasz = detector.__class__
            if clasz not in self.DETECTORS:
                options.detectors.pop(key)

                self._warn("Detector '%s' of type '%s' cannot be converted." % \
                           (key, clasz.__name__),
                           "It was removed")

        if not options.detectors:
            self._warn("No detectors in options.",
                       "This options definition was removed.")
            return False

        return True

    def _convert_limits(self, options):
        for limit in list(options.limits):
            clasz = limit.__class__
            if clasz not in self.LIMITS:
                options.limits.discard(limit)

                self._warn("Limit of type '%s' cannot be converted." % clasz.__name__,
                           "It was removed")

        if not options.limits:
            self._warn("No limits in options.",
                       "This options definition was removed.")
            return False

        return True

    def _convert_models(self, options):
        for model_type, default_model in self.DEFAULT_MODELS.items():
            models = list(options.models.iterclass(model_type))

            # Add default model if model type is missing
            if not models:
                options.models.add(default_model)

                self._warn("Set default model (%s) for model type '%s'" % \
                           (default_model, model_type))

            # Check if model is allowable
            else:
                availables = set(self.MODELS[model_type])
                classes = set(filter(inspect.isclass, availables))

                for model in models:
                    if model not in availables and \
                            not isinstance(model, tuple(classes)):
                        options.models.discard(model) # not required
                        options.models.add(default_model)

                        self._warn("Model (%s) is not allowable." % model,
                                   "It is replaced by the default model (%s)." % default_model)

        # Remove extra model types
        for model in list(options.models):
            if model.type not in self.DEFAULT_MODELS:
                options.models.discard(model)

                self._warn("Unknown model type (%s) for this converter." % model.type,
                           "Model (%s) is removed." % model)

        return True
示例#6
0
class Exporter(object, metaclass=ABCMeta):
    """
    Base class for all exporters.
    """

    def __init__(self):
        self._expander = OptionsExpander()

        self._beam_exporters = {}
        self._geometry_exporters = {}
        self._detector_exporters = {}
        self._limit_exporters = {}
        self._model_exporters = {}

    def export(self, options, dirpath, *args, **kwargs):
        """
        Exports options to a file inside the specified output directory.
        Returns the filepath of the exported options.

        :arg options: options to export
            The options must only contained a single value for each parameter.
        :arg dirpath: full path to output directory
        """
        if self._expander.is_expandable(options):
            raise ValueError("Only options with singular value can be exported")

        return self._export(options, dirpath, *args, **kwargs)

    @abstractmethod
    def _export(self, options, dirpath, *args, **kwargs):
        """
        Performs the actual export.
        """
        raise NotImplementedError

    def _run_exporters(self, options, *args, **kwargs):
        """
        Internal command to call the register export functions.
        """
        self._export_beam(options, *args, **kwargs)
        self._export_geometry(options, *args, **kwargs)
        self._export_detectors(options, *args, **kwargs)
        self._export_limits(options, *args, **kwargs)
        self._export_models(options, *args, **kwargs)

    def _export_beam(self, options, *args, **kwargs):
        """
        Exports the beam.
        If a exporter function is defined, it calls this function with the
        following arguments:

            * options object
            * beam object
            * optional arguments and keyword-arguments
        """
        clasz = options.beam.__class__
        method = self._beam_exporters.get(clasz)

        if not method:
            raise ExporterException("Could not export beam '%s'" % clasz.__name__)

        method(options, options.beam, *args, **kwargs)

    def _export_geometry(self, options, *args, **kwargs):
        """
        Exports the geometry.
        If a exporter function is defined, it calls this function with the
        following arguments:

            * options object
            * geometry object
            * optional arguments and keyword-arguments
        """
        clasz = options.geometry.__class__
        method = self._geometry_exporters.get(clasz)

        if method:
            method(options, options.geometry, *args, **kwargs)
        else:
            raise ExporterException("Could not export geometry '%s'" % clasz.__name__)

    def _export_detectors(self, options, *args, **kwargs):
        """
        Exports the detectors.
        If a exporter function is defined, it calls this function with the
        following arguments for each detector:

            * options object
            * name/key of detector
            * detector object
            * optional arguments and keyword-arguments
        """
        for name, detector in options.detectors.items():
            clasz = detector.__class__
            method = self._detector_exporters.get(clasz)

            if not method:
                raise ExporterException("Could not export detector '%s' (%s)" % \
                                        (name, clasz.__name__))

            method(options, name, detector, *args, **kwargs)

    def _export_limits(self, options, *args, **kwargs):
        """
        Exports the limit.
        If a exporter function is defined, it calls this function with the
        following arguments for each limit:

            * options object
            * limit object
            * optional arguments and keyword-arguments
        """
        for limit in options.limits:
            clasz = limit.__class__
            method = self._limit_exporters.get(clasz)

            if not method:
                raise ExporterException("Could not export limit '%s'" % \
                                        clasz.__name__)

            method(options, limit, *args, **kwargs)

    def _export_models(self, options, *args, **kwargs):
        """
        Exports the models.
        If a exporter function is defined, it calls this function with the
        following arguments for each model:

            * options object
            * model object
            * optional arguments and keyword-arguments
        """
        for model in options.models:
            type_ = model.type
            method = self._model_exporters.get(type_)

            if not method:
                raise ExporterException("Could not export model of type '%s'" % \
                                        type_.name)

            method(options, model, *args, **kwargs)

    def _export_dummy(self, options, *args, **kwargs):
        pass