#!/usr/bin/env python
"""Module containing simulation parameter classes."""
import copy
import functools
import itertools
import operator
from collections import OrderedDict
from collections.abc import Iterable
import numpy as np
from ..util.serialize import JsonSerializable
from .configobjvalidation import (integer_numpy_array_check,
integer_scalar_or_integer_numpy_array_check,
real_numpy_array_check,
real_scalar_or_real_numpy_array_check)
# TODO: Change configobj to yaml + voluptuous (for validation)
# https://pypi.org/project/voluptuous/
try:
# noinspection PyUnresolvedReferences
from configobj import ConfigObj, Section, flatten_errors
# noinspection PyUnresolvedReferences
from validate import Validator
except ImportError: # pragma: no cover
pass
try:
import cPickle as pickle
except ImportError as e: # pragma: no cover
import pickle
__all__ = ["combine_simulation_parameters", "SimulationParameters"]
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# xxxxxxxxxxxxxxx Module Functions xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
[docs]def combine_simulation_parameters(params1, params2):
"""
Combine two SimulationParameters objects and return a new
SimulationParameters object corresponding to the union of them.
The union is only valid if both objects have the same parameters and
only the values of the unpacked parameters are different.
Parameters
----------
params1 : SimulationParameters
The first SimulationParameters object.
params2 : SimulationParameters
The second SimulationParameters object.
Returns
-------
union : SimulationParameters
The union of 'params1' and 'params2'.
"""
if set(params1.parameters.keys()) != set(params2.parameters.keys()):
raise RuntimeError('Both SimulationParameters objects must have'
' the same parameters.')
if set(params1.unpacked_parameters) != set(params2.unpacked_parameters):
raise RuntimeError('Both SimulationParameters objects must have'
' the same unpacked parameters (only the values'
' should can be different).')
fixed_parameters = params1.fixed_parameters
for key in fixed_parameters:
if params1[key] != params2[key]:
raise RuntimeError('The fixed parameters in both '
'SimulationParameters objects must have the'
' same value.')
union = SimulationParameters()
# Add the fixed parameters to the 'union' object.
for key in fixed_parameters:
union.add(key, copy.copy(params1[key]))
# Add the union of the unpacked parameters to the 'union' object.
for key in params1.unpacked_parameters:
union.add(key, np.union1d(params1[key], params2[key]))
# Set the parameters to be unpacked.
for p in params1.unpacked_parameters:
union.set_unpack_parameter(p)
return union
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# xxxxxxxxxxxxxxx SimulationParameters - START xxxxxxxxxxxxxxxxxxxxxxxxxxxx
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
[docs]class SimulationParameters(JsonSerializable):
"""Class to store the simulation parameters.
A SimulationParameters object acts as a container for all simulation
parameters. To add a new parameter to the object just call the
:meth:`add` method passing the name and the value of the parameter. The
value can be anything as long as the
:meth:`.SimulationRunner._run_simulation` method can understand it.
Alternatively, you can create a SimulationParameters object with all
the parameters with the static method :meth:`create`, which receives a
dictionary with the parameter names as keys.
Parameters can be marked to be "unpacked", as long as they are
iterable, by calling the :meth:`set_unpack_parameter` method. Different
simulations will be performed for every combination of parameters
marked to be unpacked, with the other parameters kept constant.
Examples
--------
- Create a new empty SimulationParameters object and add the individual
parameters to it by calling its :meth:`add` method.
.. code-block:: python
params = SimulationParameters()
params.add('p1', [1,2,3])
params.add('p2', ['a','b'])
params.add('p3', 15)
- Creating a new SimulationParameters object with the static
:meth:`create` function.
.. code-block:: python
p = {'p1':[1,2,3], 'p2':['a','b'],'p3':15}
params=SimulationParameters.create(p)
params.set_unpack_parameter('p1')
params.set_unpack_parameter('p2')
- We can then set some of the parameters to be unpacked with
.. code-block:: python
params.set_unpack_parameter('p1')
See also
--------
.SimulationResults : Class to store simulation results.
.SimulationRunner : Base class to implement Monte Carlo simulations.
"""
def __init__(self):
# Dictionary that will store the parameters. The key is the
# parameter name and the value is the parameter value.
self.parameters = {}
# A set to store the names of the parameters that will be unpacked.
# Note there is a property to get the parameters marked to be
# unpacked, that is, the unpacked_parameters property.
self._unpacked_parameters_set = set()
# If this SimulationParameters object was unpacked into a list of
# SimulationParameters objects then each of these new objects will
# set this variable with its index in that list. In other words, if
# this member variable in a SimulationParameters object was set to
# a non-negative integer then that SimulationParameters object is
# actually one of the unpacked variations of another
# SimulationParameters object. The original SimulationParameters
# object will be stored in the _original_sim_params member
# variable.
self._unpack_index = -1
self._original_sim_params = None
@property
def unpack_index(self):
"""Get method for the unpack_index property.
Returns
-------
int
The unpack index. Ir this SimulationParameters object is not
an unpacked SimulationParameters then -1 is returned.
"""
return self._unpack_index
@property
def unpacked_parameters(self):
"""
Names of the parameters marked to be unpacked.
Returns
-------
list[str]
"""
return sorted(self._unpacked_parameters_set)
@property
def fixed_parameters(self):
"""
Names of the parameters which are NOT marked to be unpacked.
Returns
-------
list[str]
List with the names of the fixed parameter.
"""
fixed_params = [
name for name in self.parameters.keys()
if name not in self._unpacked_parameters_set
]
return fixed_params
[docs] @staticmethod
def _create(params_dict, unpack_index=-1, original_sim_params=None):
"""
Creates a new SimulationParameters object.
This static method provides a different way to create a
SimulationParameters object, already containing the parameters in
the `params_dict` dictionary.
Parameters
----------
params_dict : dict
Dictionary containing the parameters. Each dictionary key
corresponds to a parameter's name, while the dictionary value
corresponds to the actual parameter value..
unpack_index : int
Index of the created SimulationParameters object when it is
part of the unpacked variations of another SimulationParameters
object. See :meth:`get_unpacked_params_list`.
original_sim_params : SimulationParameters
The original SimulationParameters object from which the
SimulationParameters object that will be created by this method
came from.
Returns
-------
sim_params : SimulationParameters
The corresponding SimulationParameters object.
"""
sim_params = SimulationParameters()
sim_params.parameters = copy.deepcopy(params_dict)
if unpack_index < 0:
unpack_index = -1
# pylint: disable=W0212
sim_params._unpack_index = unpack_index
# pylint: disable=W0212
sim_params._original_sim_params = original_sim_params
return sim_params
[docs] @staticmethod
def create(params_dict):
"""
Creates a new SimulationParameters object.
This static method provides a different way to create a
SimulationParameters object, already containing the parameters in
the `params_dict` dictionary.
Parameters
----------
params_dict : dict
Dictionary containing the parameters. Each dictionary key
corresponds to a parameter's name, while the dictionary value
corresponds to the actual parameter value..
Returns
-------
sim_params : SimulationParameters
The corresponding SimulationParameters object.
"""
return SimulationParameters._create(params_dict)
[docs] def add(self, name, value):
"""Adds a new parameter to the SimulationParameters object.
If there is already a parameter with the same name it will be
replaced.
Parameters
----------
name : str
Name of the parameter.
value : anything
Value of the parameter.
"""
self.parameters[name] = value
[docs] def remove(self, name):
"""
Remove the parameter with name `name` from the SimulationParameters
object
Parameters
----------
name : str
Name of the parameter to be removed.
Raises
------
KeyError
If `name` is not in parameters.
"""
del self.parameters[name]
if name in self._unpacked_parameters_set:
self._unpacked_parameters_set.remove(name)
[docs] def set_unpack_parameter(self, name, unpack_bool=True):
"""Set the unpack property of the parameter with name `name`.
The parameter `name` must be already added to the
SimulationParameters object and be an iterable.
This is used in the :class:`.SimulationRunner` class.
Parameters
----------
name : str
Name of the parameter to be unpacked.
unpack_bool : bool, optional
True activates unpacking for `name`, False deactivates it.
Raises
------
ValueError
If `name` is not in parameters or is not iterable.
"""
if name in self.parameters.keys():
if isinstance(self.parameters[name], Iterable):
if unpack_bool is True:
self._unpacked_parameters_set.add(name)
else:
self._unpacked_parameters_set.remove(name)
else:
raise ValueError("Parameter {0} is not iterable".format(name))
else:
raise ValueError("Unknown parameter: `{0}`".format(name))
def __getitem__(self, name):
"""Return the parameter with name `name`.
Easy access to a given parameter using the brackets syntax.
Parameters
----------
name : str
Name of the desired parameter.
Returns
-------
desired_param : anything
The value of the parameter with name `name`.
"""
return self.parameters[name]
def __setitem__(self, key, value):
self.parameters[key] = value
def __repr__(self): # pragma: no cover
"""
Get the object representation as a string.
Returns
-------
str
The object representation as a string.
"""
def modify_name(p_name):
"""
Add an * in p_name if it is set to be unpacked
Parameters
----------
p_name : str
The original name.
"""
if p_name in self._unpacked_parameters_set:
p_name += '*'
return p_name
repr_list = []
for name, value in self.parameters.items():
repr_list.append("'{0}': {1}".format(modify_name(name), value))
return '{%s}' % ', '.join(repr_list)
def __len__(self):
"""
Get the number of different parameters stored in the
SimulationParameters object.
Returns
-------
length : int
The number of different parameters stored in the
SimulationParameters object
"""
return len(self.parameters)
def __iter__(self): # pragma: no cover
"""
Get an iterator to the parameters in the SimulationParameters
object.
Returns
-------
iterator
An iterator to the SimulationParameters object.
"""
return iter(self.parameters)
def __eq__(self, other):
"""
Check if two SimulationParameters objects are equal.
Two simulation parameters objects are considered equal if all
parameters stored in both objects are the same, except for a
parameter object called 'rep_max'.
Parameters
----------
other: SimulationParameters
The other SimulationParameters to be compared with self.
Returns
-------
bool
True if both objects are considered to be equal, returns
False otherwise.
Notes
-----
The main usage for comparing if two SimulationParameters objects
are equal is when loading partial results in the :class:`.SimulationRunner`
class, where we need to assure we are not combining results for
different simulation parameters. The :class:`.SimulationRunner` class must
check if the loaded results parameters match the current parameters
to be simulated and thus require the "==" operator (or the "!="
operator) to be implemented.
However, it makes sense to ignore a parameter called 'rep_max',
since it is not a parameter related to a 'scenario'. It is used in
the :class:`.SimulationRunner` class to indicate the maximum number of
iterations to perform and there is no problem when its value is
different.
"""
if self is other: # pragma: no cover
return True
if not isinstance(other, self.__class__):
return False
# pylint: disable=W0212
if self._unpacked_parameters_set != other._unpacked_parameters_set:
return False
if set(self.parameters.keys()) != set(other.parameters.keys()):
return False
if self._unpack_index != other._unpack_index:
return False
for key in self.parameters.keys():
# We care about all keys, except for a key called 'rep_max'
# whose value does not matter when comparing if two
# SimulationResults objects are equal or not.
if key != "rep_max":
# noinspection PyTypeChecker
if np.any(self.parameters[key] != other.parameters[key]):
return False
# If we didn't return until we reach this point then the objects
# are equal
return True
def __ne__(self, other):
"""
Check if two SimulationParameters objects are different.
See documentation for __eq__.
Parameters
----------
other: SimulationParameters
The other SimulationParameters to be compared with self.
Returns
-------
bool
True if both objects are considered to be different, returns
False otherwise.
"""
return not self.__eq__(other)
[docs] def get_num_unpacked_variations(self):
"""
Get the number of variations when the parameters are unpacked.
If no parameter was marked to be unpacked, then return 1.
If this SimulationParameters object is actually a 'unpacked
variation' of another SimulationParameters object, return the
number of variations of the parent SimulationParameters object
instead.
Returns
-------
num : int
The number of variations when the parameters are unpacked.
"""
# If self._original_sim_params is not None, that means that this
# SimulationParameters object is in fact one of the unpacked
# variations of another SimulationParameters object. In that case,
# we return the number of unpacked variations of the original
# object.
if self._original_sim_params is not None:
return self._original_sim_params.get_num_unpacked_variations()
if len(self._unpacked_parameters_set) == 0:
return 1
# Generator for the lengths of the parameters set to be
# unpacked
gen_values = (len(self.parameters[i])
for i in self._unpacked_parameters_set)
# Just multiply all the lengths
return functools.reduce(operator.mul, gen_values)
[docs] def get_pack_indexes(self, fixed_params_dict=None):
"""
When you call the function get_unpacked_params_list you get a
list of SimulationParameters objects corresponding to all
combinations of the parameters. The function get_pack_indexes
allows you to provided all parameters marked to be unpacked except
one, and returns the indexes of the list returned by
get_unpacked_params_list that you want.
Parameters
----------
fixed_params_dict : dict[str, anything]
A dictionary with the name of the fixed parameters as keys and
the fixed value as value.
Returns
-------
indexes : np.ndarray
The desired indexes (1D numpy array or an integer).
Examples
--------
Suppose we have
>>> p={'p1':[1,2,3], 'p2':['a','b'],'p3':15}
>>> params=SimulationParameters.create(p)
>>> params.set_unpack_parameter('p1')
>>> params.set_unpack_parameter('p2')
If we call params.get_unpacked_params_list we will get a list of
six SimulationParameters objects, one for each combination of the
values of p1 and p2. That is,
>>> len(params.get_unpacked_params_list())
6
Likewise, in the simulation the :class:`.SimulationRunner` object will return
a list of results in the order corresponding to the order of the
list of parameters. The get_pack_indexes is used to get the index
of the results corresponding to a specific configuration of
parameters. Suppose now you want the results when 'p2' is varying,
but with the other parameters fixed to some specific value. For
this create a dictionary specifying all parameters except 'p2' and
call get_pack_indexes with this dictionary. You will get an array
of indexes that can be used in the results list to get the desired
results. For instance
>>> fixed={'p1':3,'p3':15}
>>> indexes = params.get_pack_indexes(fixed)
>>> index0 = indexes[0]
>>> index1 = indexes[1]
>>> unpacked_list = params.get_unpacked_params_list()
>>> unpacked_list[index0]['p1']
3
>>> unpacked_list[index0]['p3']
15
>>> unpacked_list[index1]['p1']
3
>>> unpacked_list[index1]['p3']
15
"""
if fixed_params_dict is None: # pragma: no cover
fixed_params_dict = {}
# Get the only parameter that was not fixed
varying_param = list(self._unpacked_parameters_set -
set(fixed_params_dict.keys()))
# List to store the indexes (as strings) of the fixed parameters,
# as well as ":" for the varying parameter,
param_indexes = []
# Note that self.unpacked_parameters is a sorted list, which
# guarantees a predictable order
for i in self.unpacked_parameters:
if i in varying_param:
param_indexes.append(':')
else:
fixed_param_value_index = list(self.parameters[i]).index(
fixed_params_dict[i])
param_indexes.append(str(fixed_param_value_index))
# xxxxx Get the indexes xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# For this we create a auxiliary numpy array going from 0 to the
# number of unpack variations. Then we use param_indexes to build a
# string that we can evaluate using the auxiliary numpy array in
# order to get the linear indexes.
# Get the lengths of the parameters marked to be unpacked
dimensions = [
len(self.parameters[i]) for i in self.unpacked_parameters
]
aux = np.arange(0, self.get_num_unpacked_variations())
aux.shape = dimensions
indexes = eval("aux" +
"[{0}]".format(",".join(param_indexes))).flatten()
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# if indexes.size == 1:
# indexes = indexes[0]
return indexes
[docs] def get_unpacked_params_list(self):
"""
Get a list of SimulationParameters objects, each one
corresponding to a possible combination of (unpacked) parameters.
Returns
-------
list[SimulationParameters]
A list of SimulationParameters objects.
Examples
--------
Suppose you have a SimulationParameters object with the parameters
'a', 'b', 'c' and 'd' as below
>>> simparams = SimulationParameters()
>>> simparams.add('a', 1)
>>> simparams.add('b', 2)
>>> simparams.add('c', [3, 4])
>>> simparams.add('d', [5, 6])
and the parameters 'c' and 'd' were set to be unpacked.
>>> simparams.set_unpack_parameter('c')
>>> simparams.set_unpack_parameter('d')
Then get_unpacked_params_list would return a list of four
SimulationParameters objects as below
>>> len(simparams.get_unpacked_params_list())
4
That is
.. code-block:: python
[{'a': 1, 'c': 3, 'b': 2, 'd': 5},
{'a': 1, 'c': 3, 'b': 2, 'd': 6},
{'a': 1, 'c': 4, 'b': 2, 'd': 5},
{'a': 1, 'c': 4, 'b': 2, 'd': 6}]
"""
# If unpacked_parameters is empty, return self
if not self._unpacked_parameters_set:
return [self]
# Lambda function to get an iterator to a (iterable) parameter
# given its name. This only works if self.parameters[name] is an
# iterable.
def get_iter_from_name(name):
return iter(self.parameters[name])
# Dictionary that stores the name and an iterator of a parameter
# marked to be unpacked
unpacked_params_iter_dict = OrderedDict()
# The sorted function is important to guarantee that the keys
# returned from _unpacked_parameters_set will have a predictable
# order
for i in sorted(self._unpacked_parameters_set):
unpacked_params_iter_dict[i] = get_iter_from_name(i)
keys = list(unpacked_params_iter_dict.keys())
# Using itertools.product we can convert the multiple iterators
# (for the different parameters marked to be unpacked) to a single
# iterator that returns all the possible combinations (cartesian
# product) of the individual iterators.
all_combinations = itertools.product(
*(unpacked_params_iter_dict.values()))
# Names of the parameters that don't need to be unpacked
regular_params = (set(self.parameters.keys()) -
self._unpacked_parameters_set)
# Constructs a list with dictionaries, where each dictionary
# corresponds to a possible parameters combination
unpack_params_length = len(self._unpacked_parameters_set)
all_possible_dicts_list = []
for comb in all_combinations:
new_dict = {}
# Add current combination of the unpacked parameters
for index in range(unpack_params_length):
new_dict[keys[index]] = comb[index]
# Add the regular parameters
for param in regular_params:
new_dict[param] = self.parameters[param]
all_possible_dicts_list.append(new_dict)
# Map the list of dictionaries to a list of SimulationParameters
# objects and return it.
#
# Note that since we are passing the index "i" to each new object
# in the list as well as the original SimulationParameters object
# "self", then each SimulationParameters object in the returned
# list will know its index in that list (the _unpack_index
# variable) as well as the original SimulationParameters object
# from where it came from (stored in the _original_sim_params
# variable).
sim_params_list = [
SimulationParameters._create(v, i, self)
for i, v in enumerate(all_possible_dicts_list)
]
return sim_params_list
[docs] def save_to_pickled_file(self, filename):
"""
Save the SimulationParameters object to the file `filename` using
pickle.
Parameters
----------
filename : str
Name of the file to save the parameters.
"""
with open(filename, 'wb') as output:
pickle.dump(self, output, protocol=2)
[docs] @staticmethod
def load_from_pickled_file(filename):
"""
Load the SimulationParameters from the file 'filename' previously
stored (using pickle) with `save_to_pickled_file`.
Parameters
----------
filename : str
Name of the file from where the results will be loaded.
Returns
-------
SimulationParameters
The loaded SimulationParameters object.
"""
with open(filename, 'rb') as input_file:
obj = pickle.load(input_file)
return obj
[docs] @staticmethod
def load_from_config_file(filename, spec=None, save_parsed_file=False):
"""
Load the SimulationParameters from a config file using the
configobj module.
If the config file has a parameter called ``unpacked_parameters``,
which should be a list of strings with the names of other
parameters, then these parameters will be set to be unpacked.
Parameters
----------
filename : str
Name of the file from where the results will be loaded.
spec : list[str], optional
A list of strings with the config spec. See "validation" in the
configobj module documentation for more info.
save_parsed_file : bool
If `save_parsed_file` is True, then the parsed config file will
be written back to disk. This will add any missing values in
the config file whose default values are provided in the
`spec`. This will even create the file if all default values
are provided in `spec` and the file does not exist yet.
Notes
-----
Besides the usual checks that the configobj validation has such as
``integer``, ``string``, ``option``, etc., you can also use
`real_numpy_array` for numpy float arrays. Note that when this
validation function is used you can set the array in the config
file in several ways such as
SNR=0,5,10,15:20
for instance.
"""
if spec is None:
spec = []
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
def add_params(simulation_params, config):
"""
Add the parameters in `config`.
Parameters
----------
simulation_params : SimulationParameters
The SimulationParameters object where the parameters will
be added.
config : configobj.ConfigObj | configobj.Section
A ConfigObj object or a Section object. The `config` object
can contain parameters (called scalars) or sections which
can contain either parameters or other sections.
"""
# Add scalar parameters
for v in config.scalars:
simulation_params.add(v, config[v])
for s in config.sections:
add_params(simulation_params, config[s])
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
conf_file_parser = ConfigObj(filename,
list_values=True,
configspec=spec)
# Dictionary with custom validation functions. Here we add a
# validation function for numpy float arrays.
fdict = {
'real_numpy_array':
real_numpy_array_check,
'integer_numpy_array':
integer_numpy_array_check,
'integer_scalar_or_integer_numpy_array_check':
integer_scalar_or_integer_numpy_array_check,
'real_scalar_or_real_numpy_array_check':
real_scalar_or_real_numpy_array_check
}
validator = Validator(fdict)
# The 'copy' argument indicates that if we save the ConfigObj
# object to a file after validating, the default values will also
# be written to the file.
result = conf_file_parser.validate(validator,
preserve_errors=True,
copy=True)
# Note that if there was no parsing errors, then "result" will be
# 'True'. It there was an error, then result will be a dictionary
# with each parameter as a key. The value of each key will be
# either 'True' if that parameter was parsed without error or a
# "validate.something" object (since we set preserve_errors to
# True) describing the error.
# if result != True:
# print 'Config file validation failed!'
# sys.exit(1)
# xxxxx Test if there was some error in parsing the file xxxxxxxxxx
# The flatten_errors function will return only the parameters whose
# parsing failed.
errors_list = flatten_errors(conf_file_parser, result)
if len(errors_list) != 0:
first_error = errors_list[0]
# The exception will only describe the error for the first
# incorrect parameter.
if first_error[2] is False:
msg = ("Error loading file {0}. Parameter '{1}' in section "
"'{2}' must be provided.")
raise Exception(
msg.format(filename, first_error[1], first_error[0][0]))
msg = ("Error loading file {0}. Parameter '{1}' in section "
"'{2}' is invalid. {3}")
raise Exception(
msg.format(filename, first_error[1], first_error[0][0],
str(first_error[2]).capitalize()))
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# xxxxx Finally add the parsed parameters to the params object xxxx
params = SimulationParameters()
add_params(params, conf_file_parser)
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
if save_parsed_file is True: # pragma: no cover
# xxxxx Write the parsed config file to disk xxxxxxxxxxxxxxxxxx
# This will add the default values if they are not present. If
# the file does not exist yet and all default values are
# provided in the spec then the file will be created. If some
# parameter without a default value was not provided then when
# exception would already have been thrown and we wouldn't be
# here.
conf_file_parser.write()
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# If the special parameter 'unpacked_parameters' is in the config
# file, then we will set the parameters whose name are in it to be
# unpacked
try:
unpacked_parameters_list = params['unpacked_parameters']
except KeyError:
unpacked_parameters_list = []
for name in unpacked_parameters_list:
params.set_unpack_parameter(name)
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
return params
[docs] def _to_dict(self):
"""
Convert the SimulationParameters object to a dictionary for easier
further serialization.
"""
original_sim_params = self._original_sim_params
":type: SimulationParameters"
if original_sim_params is not None:
original_sim_params = original_sim_params._to_dict()
out = {
'parameters': self.parameters,
'unpacked_parameters_set': self._unpacked_parameters_set,
'unpack_index': self.unpack_index,
'original_sim_params': original_sim_params
}
return out
[docs] @staticmethod
def _from_dict(d):
"""
Create a new SimulationParameters object from a dictionary.
Parameters
----------
d : dict
The dictionary with the data.
Returns
-------
SimulationParameters
The new SimulationParameters object.
"""
sim_params = SimulationParameters()
sim_params.parameters = d['parameters']
sim_params._unpacked_parameters_set = d['unpacked_parameters_set']
sim_params._unpack_index = d['unpack_index']
original_sim_params = d['original_sim_params']
if original_sim_params is not None:
original_sim_params = SimulationParameters._from_dict(
original_sim_params)
sim_params._original_sim_params = original_sim_params
return sim_params
# xxxxxxxxxx SimulationParameters - END xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx