Module ctsimu.scenario.parameter
Parameter value, includes unit conversion and handling of parameter drifts.
Expand source code
# -*- coding: UTF-8 -*-
"""
Parameter value, includes unit conversion and handling of parameter drifts.
"""
import numbers
import warnings
from ..helpers import *
from .drift import Drift
class Parameter:
"""Parameter value, includes unit conversion and
handling of parameter drifts.
Attributes
----------
standard_value : float or str or bool
The parameter's standard value, i.e. its value without any drifts.
current_value : float or str or bool
The parameter's current value for the given frame number,
as set in `Parameter.set_frame()`.
native_unit : str
The parameter's native unit.
Possible values: `None`, `"mm"`, `"rad"`, `"deg"`, `"s"`, `"mA"`, `"kV"`, `"g/cm^3"`, `"lp/mm"`, `"deg/s"`, `"C"`, `"bool"`, `"string"`.
preferred_unit : str
The unit by which the value is represented in a JSON scenario file.
drifts : list
A list of subsequent parameter drifts, all of them
are objects of class `ctsimu.scenario.drift.Drift`.
value_has_changed : bool
`True` if the parameter's value has changed since the last value
has been acknowledged. A parameter value can be acknowledged by
calling the method `Parameter.acknowledge_change()`.
simple : bool
`True` if the parameter is represented
as a single value in the JSON file instead of a
parameter object.
"""
def __init__(self, standard_value=0, native_unit:str=None, simple:bool=False, valid_values:list=None, _root=None):
"""A new parameter object must be assigned a valid native unit
to enable the JSON parser to convert the drift values from the
JSON file, if necessary.
Optionally, a standard value can be passed to the constructor.
The standard value is the "actual" value defined for this
parameter in the JSON file. If a JSON object is used to set up
this parameter, the standard value provided in the constructor
is overwritten by the value given in the JSON file.
Parameters
----------
standard_value : float or str or bool
The parameter's standard value, i.e. its value without any drifts.
native_unit : str
The parameter's native unit.
Possible values: `None`, `"mm"`, `"rad"`, `"deg"`, `"s"`, `"mA"`, `"kV"`, `"g/cm^3"`, `"lp/mm"`, `"deg/s"`, `"C"`, `"bool"`, `"string"`.
simple : bool
Set to `True` if the parameter is represented
as a single value in the JSON file instead of a
parameter object.
valid_values : list
A list of acceptable standard values for the parameter.
"""
self._root = _root # root scenario object
self.fail_value = standard_value # used when set_from_json or set_standard_value fails
self.standard_value = None # is set later in self.set_standard_value()
self.current_value = None # is set later in self.set_standard_value()
self.simple = simple # if JSON uses a single value, not a parameter object
self.native_unit = None
self.set_native_unit(native_unit)
self.preferred_unit = None # Unit from/for the JSON scenario file.
self.uncertainty = None
self.preferred_uncertainty_unit = None
self.drifts = []
self.value_has_changed = True
self.valid_values = valid_values
self.set_standard_value(standard_value)
self.reset()
def __str__(self):
return f"{self.current_value}"
def reset(self):
"""Delete all drifts and set the parameter's current value
to the standard value.
"""
self.drifts = []
self.current_value = self.standard_value
self.value_has_changed = True
def json_dict(self) -> dict:
"""Create a CTSimU JSON dictionary for this parameter.
Returns
-------
json_dict : dict
"""
if self.simple is True:
# Parameter is represented by a single value.
return self.standard_value
else:
jd = dict()
jd["value"] = self.standard_value
# Add unit if necessary
unit_factor = 1
if self.native_unit not in native_units_to_omit_in_json_file:
unit = self.native_unit
if self.preferred_unit is not None:
unit = self.preferred_unit
# Convert value to preferred unit:
jd["value"] = convert_to_preferred_unit(
preferred_unit=self.preferred_unit,
native_unit=self.native_unit,
value=jd["value"])
jd["unit"] = unit
# Uncertainty
if self.uncertainty is not None:
jd["uncertainty"] = dict()
jd["uncertainty"]["value"] = self.uncertainty
if self.native_unit not in native_units_to_omit_in_json_file:
uncertainty_unit = self.native_unit
if self.preferred_uncertainty_unit is not None:
uncertainty_unit = self.preferred_uncertainty_unit
# Convert uncertainty to preferred unit:
jd["uncertainty"]["value"] = convert_to_preferred_unit(
preferred_unit=self.preferred_uncertainty_unit,
native_unit=self.native_unit,
value=jd["uncertainty"]["value"])
jd["uncertainty"]["unit"] = uncertainty_unit
if len(self.drifts) > 0:
jd["drifts"] = list()
for drift in self.drifts:
jd["drifts"].append(drift.json_dict())
return jd
def changed(self) -> bool:
"""Has the parameter changed since the last acknowledged change?
(See function `acknowledge_change`).
Returns
-------
value_has_changed : bool
`True` if the value has changed since last acknowledgment,
otherwise `False`.
"""
return value_has_changed
def is_zero(self) -> bool:
"""Check if this parameter is and always will be zero.
Returns
-------
is_zero : bool
"""
if (self.get_standard_value() == 0) and (self.has_drifts() is False):
return True
return False
def has_drifts(self) -> bool:
"""Does the parameter specify any drift?
Returns
-------
has_drifts : bool
`True` if the parameter specifies any drift, otherwise `False`.
"""
return (len(self.drifts) > 0)
def maximum_value(self, n_frames:int, reconstruction:bool=False) -> float:
"""Get the parameter's maximum value during the evolution of `n_frames`, given drifts.
Parameters
----------
n_frames : int
Number of frames in the CT scan.
reconstruction : bool
Obey only those drifts that are labeled as known to
the reconstruction software?
Returns
-------
maximum_value : float
Maximum value of this parameter, considering the parameter
drifts throughout the CT scan.
"""
if self.has_drifts():
if (self.native_unit != "string") and (self.native_unit != "bool") and (self.standard_value is not None):
total_drift_max = self.get_total_drift_for_frame(
0,
n_frames,
reconstruction
)
for f in range(n_frames):
total_drift_for_frame = self.get_total_drift_for_frame(
f,
n_frames,
reconstruction
)
if total_drift_for_frame > total_drift_max:
total_drift_max = total_drift_for_frame
return self.standard_value + total_drift_max
return standard_value
def minimum_value(self, n_frames:int, reconstruction:bool=False) -> float:
"""Get the parameter's minimum value during the evolution of `n_frames`, given drifts.
Parameters
----------
n_frames : int
Number of frames in the CT scan.
reconstruction : bool
Obey only those drifts that are labeled as known to
the reconstruction software?
Returns
-------
minimum_value : float
Minimum value of this parameter, considering the parameter
drifts throughout the CT scan.
"""
if self.has_drifts():
if (self.native_unit != "string") and (self.native_unit != "bool") and (self.standard_value is not None):
total_drift_min = self.get_total_drift_for_frame(
0,
n_frames,
reconstruction
)
for f in range(n_frames):
total_drift_for_frame = self.get_total_drift_for_frame(
f,
n_frames,
reconstruction
)
if total_drift_for_frame < total_drift_min:
total_drift_min = total_drift_for_frame
return self.standard_value + total_drift_min
return standard_value
def set_native_unit(self, native_unit:str):
"""Set the parameter's native unit.
Parameters
----------
native_unit : str
New native unit for the parameter.
Possible values: `None`, `"mm"`, `"rad"`, `"deg"`, `"s"`, `"mA"`, `"kV"`, `"g/cm^3"`, `"lp/mm"`, `"deg/s"`, `"C"`, `"bool"`, `"string"`.
"""
if is_valid_native_unit(native_unit):
self.native_unit = native_unit
def set_simple(self, simple:bool):
"""Set this parameter to "simple" mode (or not).
In simple mode, the parameter is represented in the JSON
file as a single value instead of a parameter object (with value,
unit, uncertainty and drifts).
Parameters
----------
simple : bool
Set to `True` if the parameter is represented
as a single value in the JSON file instead of a
parameter object.
"""
self.simple = simple
def set(self, value):
"""Set the parameter's standard value.
Automatically sets the current value to the standard value.
Parameters
----------
value : float or str or bool
New standard value for the parameter.
"""
self.set_standard_value(value)
def set_standard_value(self, value):
"""Set the parameter's standard value.
Automatically sets the current value to the standard value.
Parameters
----------
value : float or str or bool
New standard value for the parameter.
"""
if self.valid_values is None:
self.standard_value = value
self.current_value = value
elif isinstance(self.valid_values, list):
if value in self.valid_values:
# self.valid_values list contains the value
self.standard_value = value
self.current_value = value
else:
warnings.warn(f"Parameter: given value '{value}' not in list of valid values. Must be one of: {self.valid_values}. Using fail value instead: {self.fail_value}", UserWarning)
self.standard_value = self.fail_value
self.current_value = self.fail_value
else:
if value == self.valid_values:
# self.valid_values seems to be a single value
self.standard_value = value
self.current_value = value
else:
warnings.warn(f"Parameter: given value '{value}' not in list of valid values. Must be: {self.valid_values}. Using fail value instead: {self.fail_value}", UserWarning)
self.standard_value = self.fail_value
self.current_value = self.fail_value
def get_standard_value(self) -> float | str | bool:
"""Get parameter's standard value.
Returns
-------
standard_value : float or str or bool
The parameter's standard value.
"""
return self.standard_value
def get(self) -> float | str | bool:
"""Get parameter's current value.
Returns
-------
current_value : float or str or bool
The parameter's current value.
"""
return self.current_value
def get_current_value(self) -> float | str | bool:
"""Get parameter's current value.
Returns
-------
current_value : float or str or bool
The parameter's current value.
"""
return self.current_value
def acknowledge_change(self, new_change_state:bool=False):
"""Acknowledge a change of the parameter due to a drift.
After the acknowledgment, the method `changed`
will return the `new_change_state` value (standard: `False`).
Parameters
----------
new_change_state : bool, optional
The new state to be returned by the method `changed`.
Standard value: `False`.
"""
self.value_has_changed = new_change_state
def set_frame_and_get_value(self, frame:float, n_frames:int=1, reconstruction:bool=False) -> float | str | bool:
"""Set the new frame number, return the new current value.
Parameters
----------
frame : float
Current frame number.
n_frames : int
Total number of frames in scan.
reconstruction : bool
Obey only those drifts that are labeled as known to the
reconstruction software?
Returns
-------
current_value : float or str or bool
The parameter's current value for the given `frame` number.
"""
self.set_frame(frame, n_frames, reconstruction)
return self.current_value
def get_total_drift_for_frame(self, frame:float, n_frames:int, reconstruction:bool=False) -> float | str | bool:
"""Calculate the total drift value from all drift components,
for the given `frame` out of a total of `n_frames`,
depending on whether all drifts are applied or only
drifts known to the reconstruction software.
Parameters
----------
frame : float
Current frame number.
n_frames : int
Total number of frames in CT scan.
reconstruction : bool
Obey only those drifts that are labeled as known to the
reconstruction software?
Returns
-------
total_drift_value : float or str or bool
The parameter's total (accumulated) drift value
for the given `frame` number.
"""
total_drift = 0
for d in self.drifts:
if reconstruction is True:
if d.known_to_reconstruction is False:
# Skip this drift if it is unknown to the reconstruction,
# but we only want to obey drifts that are actually known
# to the reconstruction...
continue
if (self.native_unit == "string") or (self.native_unit == "bool"):
# A string-type parameter can only be one string,
# nothing is added, and the drifts array should only
# contain one element. Otherwise, the last drift is the
# one that has precedence. Same for bool.
total_drift = d.get_value_for_frame(frame=frame, n_frames=n_frames)
else:
# Add up all drift values for requested frame:
total_drift += d.get_value_for_frame(frame=frame, n_frames=n_frames)
return total_drift
def add_drift(self, drift:'Drift'):
"""Add a drift object to this parameter.
Parameters
----------
drift : ctsimu.scenario.drift.Drift
"""
if isinstance(drift, Drift):
drift._root = self._root
self.drifts.append(drift)
else:
raise Exception("add_drift: given 'drift' object is not of class 'Drift'.")
def add_drift_json(self, drift_dict:dict):
"""Generate a `ctsimu.scenario.drift.Drift` object from a JSON structure
that defines a drift and add it to the parameter's internal list of
drifts to handle.
Parameters
----------
drift_dict : dict
Dictionary for a CTSimU drift structure.
"""
d = Drift(native_unit=self.native_unit, preferred_unit=self.preferred_unit, _root=self._root)
d.set_from_json(drift_dict)
self.add_drift(d)
def set_from_json(self, json_parameter_object:dict) -> bool:
"""Set up this parameter from a CTSimU parameter structure.
The proper `native_unit` must be set up correctly before
running this function.
Parameters
----------
json_parameter_object : dict
A CTSimU parameter structure.
Returns
-------
success : bool
`True` on success, `False` if an error occurred.
"""
self.reset()
success = False
if isinstance(json_parameter_object, numbers.Number):
# Parameter is given as a single number, not as a
# parameter structure (with value, unit, drift, uncertainty)
if self.native_unit != "string":
self.set_standard_value(json_parameter_object)
success = True
elif isinstance(json_parameter_object, str):
# Parameter is given as a single string, not as a
# parameter structure (with value, unit, drift, uncertainty)
if self.native_unit == "string":
self.set_standard_value(json_parameter_object)
success = True
elif isinstance(json_parameter_object, bool):
# Parameter is given as a boolean.
if self.native_unit == "bool":
self.set_standard_value(json_convert_to_native_unit(self.native_unit, json_parameter_object))
success = True
elif isinstance(json_parameter_object, dict):
# Hopefully a valid parameter structure...
# Value, automatically converted to parameter's native unit:
if json_exists_and_not_null(json_parameter_object, ["value"]):
if not object_value_is_null(json_parameter_object):
self.set_standard_value(json_convert_to_native_unit(self.native_unit, json_parameter_object))
success = True
self.current_value = self.standard_value
# Unit
self.preferred_unit = get_value(json_parameter_object, ["unit"], self.native_unit)
# Uncertainty:
if json_exists_and_not_null(json_parameter_object, ["uncertainty"]):
unc = json_extract(json_parameter_object, ["uncertainty"])
if isinstance(unc, dict):
# Since file format 1.0, uncertainties are value/unit dicts:
self.uncertainty = json_convert_to_native_unit(
native_unit=self.native_unit,
value_and_unit=unc,
fallback_json_unit=self.preferred_unit
)
self.preferred_uncertainty_unit = get_value(json_parameter_object, ["uncertainty", "unit"], self.preferred_unit)
elif isinstance(unc, numbers.Number):
# Before file format 1.0, "uncertainty" and "uncertainty_unit" where
# separate properties of a parameter:
self.preferred_uncertainty_unit = get_value(json_parameter_object, ["uncertainty_unit"], self.preferred_unit)
self.uncertainty = convert_to_native_unit(
given_unit=self.preferred_uncertainty_unit,
native_unit=self.native_unit,
value=unc
)
else:
self.uncertainty = None
else:
self.uncertainty = None
if self.uncertainty == 0:
self.uncertainty = None
# Drifts:
if json_exists_and_not_null(json_parameter_object, ["drifts"]):
drifts = json_extract(json_parameter_object, ["drifts"])
if isinstance(drifts, list):
# An array of drift structures.
for d in drifts:
self.add_drift_json(d)
elif isinstance(drifts, dict):
# A single drift structure (apparently).
# This is not standard behavior, but let's support it.
self.add_drift_json(drifts)
return success
def set_parameter_from_key(self, json_object:dict, key_sequence:list) -> bool:
"""Tries to find a valid parameter object at the given
`key_sequence` in the given `json_object`. Sets the parameter
if possible and returns `True` on success, `False` otherwise.
Parameters
----------
json_object : dict
Piece of a CTSimU JSON structure.
key_sequence : list
List of strings that identify the key sequence where the
parameter is found in the given JSON structure.
Returns
-------
success : bool
`True` on success, `False` if an error occurred.
"""
if json_exists_and_not_null(json_object, key_sequence):
if self.set_from_json(json_extract(json_object, key_sequence)):
return True
return False
def set_parameter_from_possible_keys(self, json_object:dict, key_sequences:list) -> bool:
"""Searches the JSON object for each key sequence in the given list
of `key_sequences`. The first sequence that can be found is taken to
set up the parameter.
Parameters
----------
json_object : dict
Piece of a CTSimU JSON structure.
key_sequences : list
A list of possible key sequences, i.e., a list of lists of strings.
Returns
-------
success : bool
`True` on success (i.e., at least one of the key sequences
identified a valid parameter in the given JSON structure),
`False` if an error occurred.
"""
for keyseq in key_sequences:
if self.set_parameter_from_key(json_object, keyseq):
# Returned successfully, so we are done here.
return True
return False
def set_frame(self, frame:float, n_frames:int, reconstruction:bool=False) -> bool:
"""Prepares the `current_value` for the given `frame` number
(assuming a total of `n_frames`). This takes into account all drifts
(or only the ones known to reconstruction).
Parameters
----------
frame : float
Current frame number.
n_frames : int
Total number of frames in scan.
reconstruction : bool
`True`: obey only those drifts that are labeled as known to the
reconstruction software.
`False`: obey all drifts.
Returns
-------
value_has_changed : bool
If the value has changed from the previous value (e.g. due to drifts).
"""
new_value = self.standard_value
total_drift = self.get_total_drift_for_frame(frame, n_frames, reconstruction)
if self.native_unit == "string":
if isinstance(total_drift, str):
# Only assign new value if `total_drift` is actually a string:
new_value = total_drift
else:
if self.standard_value is not None:
new_value = self.standard_value + total_drift
# Check if the value has changed when compared to the previous value:
value_has_changed = False
if self.current_value != new_value:
value_has_changed = True
self.current_value = new_value
return value_has_changed
Classes
class Parameter (standard_value=0, native_unit: str = None, simple: bool = False, valid_values: list = None)
-
Parameter value, includes unit conversion and handling of parameter drifts.
Attributes
standard_value
:float
orstr
orbool
- The parameter's standard value, i.e. its value without any drifts.
current_value
:float
orstr
orbool
- The parameter's current value for the given frame number,
as set in
Parameter.set_frame()
. native_unit
:str
- The parameter's native unit.
Possible values:
None
,"mm"
,"rad"
,"deg"
,"s"
,"mA"
,"kV"
,"g/cm^3"
,"lp/mm"
,"deg/s"
,"C"
,"bool"
,"string"
. preferred_unit
:str
- The unit by which the value is represented in a JSON scenario file.
drifts
:list
- A list of subsequent parameter drifts, all of them
are objects of class
Drift
. value_has_changed
:bool
True
if the parameter's value has changed since the last value has been acknowledged. A parameter value can be acknowledged by calling the methodParameter.acknowledge_change()
.simple
:bool
True
if the parameter is represented as a single value in the JSON file instead of a parameter object.
A new parameter object must be assigned a valid native unit to enable the JSON parser to convert the drift values from the JSON file, if necessary.
Optionally, a standard value can be passed to the constructor. The standard value is the "actual" value defined for this parameter in the JSON file. If a JSON object is used to set up this parameter, the standard value provided in the constructor is overwritten by the value given in the JSON file.
Parameters
standard_value
:float
orstr
orbool
- The parameter's standard value, i.e. its value without any drifts.
native_unit
:str
- The parameter's native unit.
Possible values:
None
,"mm"
,"rad"
,"deg"
,"s"
,"mA"
,"kV"
,"g/cm^3"
,"lp/mm"
,"deg/s"
,"C"
,"bool"
,"string"
. simple
:bool
- Set to
True
if the parameter is represented as a single value in the JSON file instead of a parameter object. valid_values
:list
- A list of acceptable standard values for the parameter.
Expand source code
class Parameter: """Parameter value, includes unit conversion and handling of parameter drifts. Attributes ---------- standard_value : float or str or bool The parameter's standard value, i.e. its value without any drifts. current_value : float or str or bool The parameter's current value for the given frame number, as set in `Parameter.set_frame()`. native_unit : str The parameter's native unit. Possible values: `None`, `"mm"`, `"rad"`, `"deg"`, `"s"`, `"mA"`, `"kV"`, `"g/cm^3"`, `"lp/mm"`, `"deg/s"`, `"C"`, `"bool"`, `"string"`. preferred_unit : str The unit by which the value is represented in a JSON scenario file. drifts : list A list of subsequent parameter drifts, all of them are objects of class `ctsimu.scenario.drift.Drift`. value_has_changed : bool `True` if the parameter's value has changed since the last value has been acknowledged. A parameter value can be acknowledged by calling the method `Parameter.acknowledge_change()`. simple : bool `True` if the parameter is represented as a single value in the JSON file instead of a parameter object. """ def __init__(self, standard_value=0, native_unit:str=None, simple:bool=False, valid_values:list=None, _root=None): """A new parameter object must be assigned a valid native unit to enable the JSON parser to convert the drift values from the JSON file, if necessary. Optionally, a standard value can be passed to the constructor. The standard value is the "actual" value defined for this parameter in the JSON file. If a JSON object is used to set up this parameter, the standard value provided in the constructor is overwritten by the value given in the JSON file. Parameters ---------- standard_value : float or str or bool The parameter's standard value, i.e. its value without any drifts. native_unit : str The parameter's native unit. Possible values: `None`, `"mm"`, `"rad"`, `"deg"`, `"s"`, `"mA"`, `"kV"`, `"g/cm^3"`, `"lp/mm"`, `"deg/s"`, `"C"`, `"bool"`, `"string"`. simple : bool Set to `True` if the parameter is represented as a single value in the JSON file instead of a parameter object. valid_values : list A list of acceptable standard values for the parameter. """ self._root = _root # root scenario object self.fail_value = standard_value # used when set_from_json or set_standard_value fails self.standard_value = None # is set later in self.set_standard_value() self.current_value = None # is set later in self.set_standard_value() self.simple = simple # if JSON uses a single value, not a parameter object self.native_unit = None self.set_native_unit(native_unit) self.preferred_unit = None # Unit from/for the JSON scenario file. self.uncertainty = None self.preferred_uncertainty_unit = None self.drifts = [] self.value_has_changed = True self.valid_values = valid_values self.set_standard_value(standard_value) self.reset() def __str__(self): return f"{self.current_value}" def reset(self): """Delete all drifts and set the parameter's current value to the standard value. """ self.drifts = [] self.current_value = self.standard_value self.value_has_changed = True def json_dict(self) -> dict: """Create a CTSimU JSON dictionary for this parameter. Returns ------- json_dict : dict """ if self.simple is True: # Parameter is represented by a single value. return self.standard_value else: jd = dict() jd["value"] = self.standard_value # Add unit if necessary unit_factor = 1 if self.native_unit not in native_units_to_omit_in_json_file: unit = self.native_unit if self.preferred_unit is not None: unit = self.preferred_unit # Convert value to preferred unit: jd["value"] = convert_to_preferred_unit( preferred_unit=self.preferred_unit, native_unit=self.native_unit, value=jd["value"]) jd["unit"] = unit # Uncertainty if self.uncertainty is not None: jd["uncertainty"] = dict() jd["uncertainty"]["value"] = self.uncertainty if self.native_unit not in native_units_to_omit_in_json_file: uncertainty_unit = self.native_unit if self.preferred_uncertainty_unit is not None: uncertainty_unit = self.preferred_uncertainty_unit # Convert uncertainty to preferred unit: jd["uncertainty"]["value"] = convert_to_preferred_unit( preferred_unit=self.preferred_uncertainty_unit, native_unit=self.native_unit, value=jd["uncertainty"]["value"]) jd["uncertainty"]["unit"] = uncertainty_unit if len(self.drifts) > 0: jd["drifts"] = list() for drift in self.drifts: jd["drifts"].append(drift.json_dict()) return jd def changed(self) -> bool: """Has the parameter changed since the last acknowledged change? (See function `acknowledge_change`). Returns ------- value_has_changed : bool `True` if the value has changed since last acknowledgment, otherwise `False`. """ return value_has_changed def is_zero(self) -> bool: """Check if this parameter is and always will be zero. Returns ------- is_zero : bool """ if (self.get_standard_value() == 0) and (self.has_drifts() is False): return True return False def has_drifts(self) -> bool: """Does the parameter specify any drift? Returns ------- has_drifts : bool `True` if the parameter specifies any drift, otherwise `False`. """ return (len(self.drifts) > 0) def maximum_value(self, n_frames:int, reconstruction:bool=False) -> float: """Get the parameter's maximum value during the evolution of `n_frames`, given drifts. Parameters ---------- n_frames : int Number of frames in the CT scan. reconstruction : bool Obey only those drifts that are labeled as known to the reconstruction software? Returns ------- maximum_value : float Maximum value of this parameter, considering the parameter drifts throughout the CT scan. """ if self.has_drifts(): if (self.native_unit != "string") and (self.native_unit != "bool") and (self.standard_value is not None): total_drift_max = self.get_total_drift_for_frame( 0, n_frames, reconstruction ) for f in range(n_frames): total_drift_for_frame = self.get_total_drift_for_frame( f, n_frames, reconstruction ) if total_drift_for_frame > total_drift_max: total_drift_max = total_drift_for_frame return self.standard_value + total_drift_max return standard_value def minimum_value(self, n_frames:int, reconstruction:bool=False) -> float: """Get the parameter's minimum value during the evolution of `n_frames`, given drifts. Parameters ---------- n_frames : int Number of frames in the CT scan. reconstruction : bool Obey only those drifts that are labeled as known to the reconstruction software? Returns ------- minimum_value : float Minimum value of this parameter, considering the parameter drifts throughout the CT scan. """ if self.has_drifts(): if (self.native_unit != "string") and (self.native_unit != "bool") and (self.standard_value is not None): total_drift_min = self.get_total_drift_for_frame( 0, n_frames, reconstruction ) for f in range(n_frames): total_drift_for_frame = self.get_total_drift_for_frame( f, n_frames, reconstruction ) if total_drift_for_frame < total_drift_min: total_drift_min = total_drift_for_frame return self.standard_value + total_drift_min return standard_value def set_native_unit(self, native_unit:str): """Set the parameter's native unit. Parameters ---------- native_unit : str New native unit for the parameter. Possible values: `None`, `"mm"`, `"rad"`, `"deg"`, `"s"`, `"mA"`, `"kV"`, `"g/cm^3"`, `"lp/mm"`, `"deg/s"`, `"C"`, `"bool"`, `"string"`. """ if is_valid_native_unit(native_unit): self.native_unit = native_unit def set_simple(self, simple:bool): """Set this parameter to "simple" mode (or not). In simple mode, the parameter is represented in the JSON file as a single value instead of a parameter object (with value, unit, uncertainty and drifts). Parameters ---------- simple : bool Set to `True` if the parameter is represented as a single value in the JSON file instead of a parameter object. """ self.simple = simple def set(self, value): """Set the parameter's standard value. Automatically sets the current value to the standard value. Parameters ---------- value : float or str or bool New standard value for the parameter. """ self.set_standard_value(value) def set_standard_value(self, value): """Set the parameter's standard value. Automatically sets the current value to the standard value. Parameters ---------- value : float or str or bool New standard value for the parameter. """ if self.valid_values is None: self.standard_value = value self.current_value = value elif isinstance(self.valid_values, list): if value in self.valid_values: # self.valid_values list contains the value self.standard_value = value self.current_value = value else: warnings.warn(f"Parameter: given value '{value}' not in list of valid values. Must be one of: {self.valid_values}. Using fail value instead: {self.fail_value}", UserWarning) self.standard_value = self.fail_value self.current_value = self.fail_value else: if value == self.valid_values: # self.valid_values seems to be a single value self.standard_value = value self.current_value = value else: warnings.warn(f"Parameter: given value '{value}' not in list of valid values. Must be: {self.valid_values}. Using fail value instead: {self.fail_value}", UserWarning) self.standard_value = self.fail_value self.current_value = self.fail_value def get_standard_value(self) -> float | str | bool: """Get parameter's standard value. Returns ------- standard_value : float or str or bool The parameter's standard value. """ return self.standard_value def get(self) -> float | str | bool: """Get parameter's current value. Returns ------- current_value : float or str or bool The parameter's current value. """ return self.current_value def get_current_value(self) -> float | str | bool: """Get parameter's current value. Returns ------- current_value : float or str or bool The parameter's current value. """ return self.current_value def acknowledge_change(self, new_change_state:bool=False): """Acknowledge a change of the parameter due to a drift. After the acknowledgment, the method `changed` will return the `new_change_state` value (standard: `False`). Parameters ---------- new_change_state : bool, optional The new state to be returned by the method `changed`. Standard value: `False`. """ self.value_has_changed = new_change_state def set_frame_and_get_value(self, frame:float, n_frames:int=1, reconstruction:bool=False) -> float | str | bool: """Set the new frame number, return the new current value. Parameters ---------- frame : float Current frame number. n_frames : int Total number of frames in scan. reconstruction : bool Obey only those drifts that are labeled as known to the reconstruction software? Returns ------- current_value : float or str or bool The parameter's current value for the given `frame` number. """ self.set_frame(frame, n_frames, reconstruction) return self.current_value def get_total_drift_for_frame(self, frame:float, n_frames:int, reconstruction:bool=False) -> float | str | bool: """Calculate the total drift value from all drift components, for the given `frame` out of a total of `n_frames`, depending on whether all drifts are applied or only drifts known to the reconstruction software. Parameters ---------- frame : float Current frame number. n_frames : int Total number of frames in CT scan. reconstruction : bool Obey only those drifts that are labeled as known to the reconstruction software? Returns ------- total_drift_value : float or str or bool The parameter's total (accumulated) drift value for the given `frame` number. """ total_drift = 0 for d in self.drifts: if reconstruction is True: if d.known_to_reconstruction is False: # Skip this drift if it is unknown to the reconstruction, # but we only want to obey drifts that are actually known # to the reconstruction... continue if (self.native_unit == "string") or (self.native_unit == "bool"): # A string-type parameter can only be one string, # nothing is added, and the drifts array should only # contain one element. Otherwise, the last drift is the # one that has precedence. Same for bool. total_drift = d.get_value_for_frame(frame=frame, n_frames=n_frames) else: # Add up all drift values for requested frame: total_drift += d.get_value_for_frame(frame=frame, n_frames=n_frames) return total_drift def add_drift(self, drift:'Drift'): """Add a drift object to this parameter. Parameters ---------- drift : ctsimu.scenario.drift.Drift """ if isinstance(drift, Drift): drift._root = self._root self.drifts.append(drift) else: raise Exception("add_drift: given 'drift' object is not of class 'Drift'.") def add_drift_json(self, drift_dict:dict): """Generate a `ctsimu.scenario.drift.Drift` object from a JSON structure that defines a drift and add it to the parameter's internal list of drifts to handle. Parameters ---------- drift_dict : dict Dictionary for a CTSimU drift structure. """ d = Drift(native_unit=self.native_unit, preferred_unit=self.preferred_unit, _root=self._root) d.set_from_json(drift_dict) self.add_drift(d) def set_from_json(self, json_parameter_object:dict) -> bool: """Set up this parameter from a CTSimU parameter structure. The proper `native_unit` must be set up correctly before running this function. Parameters ---------- json_parameter_object : dict A CTSimU parameter structure. Returns ------- success : bool `True` on success, `False` if an error occurred. """ self.reset() success = False if isinstance(json_parameter_object, numbers.Number): # Parameter is given as a single number, not as a # parameter structure (with value, unit, drift, uncertainty) if self.native_unit != "string": self.set_standard_value(json_parameter_object) success = True elif isinstance(json_parameter_object, str): # Parameter is given as a single string, not as a # parameter structure (with value, unit, drift, uncertainty) if self.native_unit == "string": self.set_standard_value(json_parameter_object) success = True elif isinstance(json_parameter_object, bool): # Parameter is given as a boolean. if self.native_unit == "bool": self.set_standard_value(json_convert_to_native_unit(self.native_unit, json_parameter_object)) success = True elif isinstance(json_parameter_object, dict): # Hopefully a valid parameter structure... # Value, automatically converted to parameter's native unit: if json_exists_and_not_null(json_parameter_object, ["value"]): if not object_value_is_null(json_parameter_object): self.set_standard_value(json_convert_to_native_unit(self.native_unit, json_parameter_object)) success = True self.current_value = self.standard_value # Unit self.preferred_unit = get_value(json_parameter_object, ["unit"], self.native_unit) # Uncertainty: if json_exists_and_not_null(json_parameter_object, ["uncertainty"]): unc = json_extract(json_parameter_object, ["uncertainty"]) if isinstance(unc, dict): # Since file format 1.0, uncertainties are value/unit dicts: self.uncertainty = json_convert_to_native_unit( native_unit=self.native_unit, value_and_unit=unc, fallback_json_unit=self.preferred_unit ) self.preferred_uncertainty_unit = get_value(json_parameter_object, ["uncertainty", "unit"], self.preferred_unit) elif isinstance(unc, numbers.Number): # Before file format 1.0, "uncertainty" and "uncertainty_unit" where # separate properties of a parameter: self.preferred_uncertainty_unit = get_value(json_parameter_object, ["uncertainty_unit"], self.preferred_unit) self.uncertainty = convert_to_native_unit( given_unit=self.preferred_uncertainty_unit, native_unit=self.native_unit, value=unc ) else: self.uncertainty = None else: self.uncertainty = None if self.uncertainty == 0: self.uncertainty = None # Drifts: if json_exists_and_not_null(json_parameter_object, ["drifts"]): drifts = json_extract(json_parameter_object, ["drifts"]) if isinstance(drifts, list): # An array of drift structures. for d in drifts: self.add_drift_json(d) elif isinstance(drifts, dict): # A single drift structure (apparently). # This is not standard behavior, but let's support it. self.add_drift_json(drifts) return success def set_parameter_from_key(self, json_object:dict, key_sequence:list) -> bool: """Tries to find a valid parameter object at the given `key_sequence` in the given `json_object`. Sets the parameter if possible and returns `True` on success, `False` otherwise. Parameters ---------- json_object : dict Piece of a CTSimU JSON structure. key_sequence : list List of strings that identify the key sequence where the parameter is found in the given JSON structure. Returns ------- success : bool `True` on success, `False` if an error occurred. """ if json_exists_and_not_null(json_object, key_sequence): if self.set_from_json(json_extract(json_object, key_sequence)): return True return False def set_parameter_from_possible_keys(self, json_object:dict, key_sequences:list) -> bool: """Searches the JSON object for each key sequence in the given list of `key_sequences`. The first sequence that can be found is taken to set up the parameter. Parameters ---------- json_object : dict Piece of a CTSimU JSON structure. key_sequences : list A list of possible key sequences, i.e., a list of lists of strings. Returns ------- success : bool `True` on success (i.e., at least one of the key sequences identified a valid parameter in the given JSON structure), `False` if an error occurred. """ for keyseq in key_sequences: if self.set_parameter_from_key(json_object, keyseq): # Returned successfully, so we are done here. return True return False def set_frame(self, frame:float, n_frames:int, reconstruction:bool=False) -> bool: """Prepares the `current_value` for the given `frame` number (assuming a total of `n_frames`). This takes into account all drifts (or only the ones known to reconstruction). Parameters ---------- frame : float Current frame number. n_frames : int Total number of frames in scan. reconstruction : bool `True`: obey only those drifts that are labeled as known to the reconstruction software. `False`: obey all drifts. Returns ------- value_has_changed : bool If the value has changed from the previous value (e.g. due to drifts). """ new_value = self.standard_value total_drift = self.get_total_drift_for_frame(frame, n_frames, reconstruction) if self.native_unit == "string": if isinstance(total_drift, str): # Only assign new value if `total_drift` is actually a string: new_value = total_drift else: if self.standard_value is not None: new_value = self.standard_value + total_drift # Check if the value has changed when compared to the previous value: value_has_changed = False if self.current_value != new_value: value_has_changed = True self.current_value = new_value return value_has_changed
Methods
def acknowledge_change(self, new_change_state: bool = False)
-
Acknowledge a change of the parameter due to a drift. After the acknowledgment, the method
changed
will return thenew_change_state
value (standard:False
).Parameters
new_change_state
:bool
, optional- The new state to be returned by the method
changed
. Standard value:False
.
Expand source code
def acknowledge_change(self, new_change_state:bool=False): """Acknowledge a change of the parameter due to a drift. After the acknowledgment, the method `changed` will return the `new_change_state` value (standard: `False`). Parameters ---------- new_change_state : bool, optional The new state to be returned by the method `changed`. Standard value: `False`. """ self.value_has_changed = new_change_state
def add_drift(self, drift: Drift)
-
Expand source code
def add_drift(self, drift:'Drift'): """Add a drift object to this parameter. Parameters ---------- drift : ctsimu.scenario.drift.Drift """ if isinstance(drift, Drift): drift._root = self._root self.drifts.append(drift) else: raise Exception("add_drift: given 'drift' object is not of class 'Drift'.")
def add_drift_json(self, drift_dict: dict)
-
Generate a
Drift
object from a JSON structure that defines a drift and add it to the parameter's internal list of drifts to handle.Parameters
drift_dict
:dict
- Dictionary for a CTSimU drift structure.
Expand source code
def add_drift_json(self, drift_dict:dict): """Generate a `ctsimu.scenario.drift.Drift` object from a JSON structure that defines a drift and add it to the parameter's internal list of drifts to handle. Parameters ---------- drift_dict : dict Dictionary for a CTSimU drift structure. """ d = Drift(native_unit=self.native_unit, preferred_unit=self.preferred_unit, _root=self._root) d.set_from_json(drift_dict) self.add_drift(d)
def changed(self) ‑> bool
-
Has the parameter changed since the last acknowledged change? (See function
acknowledge_change
).Returns
value_has_changed
:bool
True
if the value has changed since last acknowledgment, otherwiseFalse
.
Expand source code
def changed(self) -> bool: """Has the parameter changed since the last acknowledged change? (See function `acknowledge_change`). Returns ------- value_has_changed : bool `True` if the value has changed since last acknowledgment, otherwise `False`. """ return value_has_changed
def get(self) ‑> float | str | bool
-
Get parameter's current value.
Returns
current_value
:float
orstr
orbool
- The parameter's current value.
Expand source code
def get(self) -> float | str | bool: """Get parameter's current value. Returns ------- current_value : float or str or bool The parameter's current value. """ return self.current_value
def get_current_value(self) ‑> float | str | bool
-
Get parameter's current value.
Returns
current_value
:float
orstr
orbool
- The parameter's current value.
Expand source code
def get_current_value(self) -> float | str | bool: """Get parameter's current value. Returns ------- current_value : float or str or bool The parameter's current value. """ return self.current_value
def get_standard_value(self) ‑> float | str | bool
-
Get parameter's standard value.
Returns
standard_value
:float
orstr
orbool
- The parameter's standard value.
Expand source code
def get_standard_value(self) -> float | str | bool: """Get parameter's standard value. Returns ------- standard_value : float or str or bool The parameter's standard value. """ return self.standard_value
def get_total_drift_for_frame(self, frame: float, n_frames: int, reconstruction: bool = False) ‑> float | str | bool
-
Calculate the total drift value from all drift components, for the given
frame
out of a total ofn_frames
, depending on whether all drifts are applied or only drifts known to the reconstruction software.Parameters
frame
:float
- Current frame number.
n_frames
:int
- Total number of frames in CT scan.
reconstruction
:bool
- Obey only those drifts that are labeled as known to the reconstruction software?
Returns
total_drift_value
:float
orstr
orbool
- The parameter's total (accumulated) drift value
for the given
frame
number.
Expand source code
def get_total_drift_for_frame(self, frame:float, n_frames:int, reconstruction:bool=False) -> float | str | bool: """Calculate the total drift value from all drift components, for the given `frame` out of a total of `n_frames`, depending on whether all drifts are applied or only drifts known to the reconstruction software. Parameters ---------- frame : float Current frame number. n_frames : int Total number of frames in CT scan. reconstruction : bool Obey only those drifts that are labeled as known to the reconstruction software? Returns ------- total_drift_value : float or str or bool The parameter's total (accumulated) drift value for the given `frame` number. """ total_drift = 0 for d in self.drifts: if reconstruction is True: if d.known_to_reconstruction is False: # Skip this drift if it is unknown to the reconstruction, # but we only want to obey drifts that are actually known # to the reconstruction... continue if (self.native_unit == "string") or (self.native_unit == "bool"): # A string-type parameter can only be one string, # nothing is added, and the drifts array should only # contain one element. Otherwise, the last drift is the # one that has precedence. Same for bool. total_drift = d.get_value_for_frame(frame=frame, n_frames=n_frames) else: # Add up all drift values for requested frame: total_drift += d.get_value_for_frame(frame=frame, n_frames=n_frames) return total_drift
def has_drifts(self) ‑> bool
-
Does the parameter specify any drift?
Returns
has_drifts
:bool
True
if the parameter specifies any drift, otherwiseFalse
.
Expand source code
def has_drifts(self) -> bool: """Does the parameter specify any drift? Returns ------- has_drifts : bool `True` if the parameter specifies any drift, otherwise `False`. """ return (len(self.drifts) > 0)
def is_zero(self) ‑> bool
-
Check if this parameter is and always will be zero.
Returns
is_zero
:bool
Expand source code
def is_zero(self) -> bool: """Check if this parameter is and always will be zero. Returns ------- is_zero : bool """ if (self.get_standard_value() == 0) and (self.has_drifts() is False): return True return False
def json_dict(self) ‑> dict
-
Create a CTSimU JSON dictionary for this parameter.
Returns
json_dict
:dict
Expand source code
def json_dict(self) -> dict: """Create a CTSimU JSON dictionary for this parameter. Returns ------- json_dict : dict """ if self.simple is True: # Parameter is represented by a single value. return self.standard_value else: jd = dict() jd["value"] = self.standard_value # Add unit if necessary unit_factor = 1 if self.native_unit not in native_units_to_omit_in_json_file: unit = self.native_unit if self.preferred_unit is not None: unit = self.preferred_unit # Convert value to preferred unit: jd["value"] = convert_to_preferred_unit( preferred_unit=self.preferred_unit, native_unit=self.native_unit, value=jd["value"]) jd["unit"] = unit # Uncertainty if self.uncertainty is not None: jd["uncertainty"] = dict() jd["uncertainty"]["value"] = self.uncertainty if self.native_unit not in native_units_to_omit_in_json_file: uncertainty_unit = self.native_unit if self.preferred_uncertainty_unit is not None: uncertainty_unit = self.preferred_uncertainty_unit # Convert uncertainty to preferred unit: jd["uncertainty"]["value"] = convert_to_preferred_unit( preferred_unit=self.preferred_uncertainty_unit, native_unit=self.native_unit, value=jd["uncertainty"]["value"]) jd["uncertainty"]["unit"] = uncertainty_unit if len(self.drifts) > 0: jd["drifts"] = list() for drift in self.drifts: jd["drifts"].append(drift.json_dict()) return jd
def maximum_value(self, n_frames: int, reconstruction: bool = False) ‑> float
-
Get the parameter's maximum value during the evolution of
n_frames
, given drifts.Parameters
n_frames
:int
- Number of frames in the CT scan.
reconstruction
:bool
- Obey only those drifts that are labeled as known to the reconstruction software?
Returns
maximum_value
:float
- Maximum value of this parameter, considering the parameter drifts throughout the CT scan.
Expand source code
def maximum_value(self, n_frames:int, reconstruction:bool=False) -> float: """Get the parameter's maximum value during the evolution of `n_frames`, given drifts. Parameters ---------- n_frames : int Number of frames in the CT scan. reconstruction : bool Obey only those drifts that are labeled as known to the reconstruction software? Returns ------- maximum_value : float Maximum value of this parameter, considering the parameter drifts throughout the CT scan. """ if self.has_drifts(): if (self.native_unit != "string") and (self.native_unit != "bool") and (self.standard_value is not None): total_drift_max = self.get_total_drift_for_frame( 0, n_frames, reconstruction ) for f in range(n_frames): total_drift_for_frame = self.get_total_drift_for_frame( f, n_frames, reconstruction ) if total_drift_for_frame > total_drift_max: total_drift_max = total_drift_for_frame return self.standard_value + total_drift_max return standard_value
def minimum_value(self, n_frames: int, reconstruction: bool = False) ‑> float
-
Get the parameter's minimum value during the evolution of
n_frames
, given drifts.Parameters
n_frames
:int
- Number of frames in the CT scan.
reconstruction
:bool
- Obey only those drifts that are labeled as known to the reconstruction software?
Returns
minimum_value
:float
- Minimum value of this parameter, considering the parameter drifts throughout the CT scan.
Expand source code
def minimum_value(self, n_frames:int, reconstruction:bool=False) -> float: """Get the parameter's minimum value during the evolution of `n_frames`, given drifts. Parameters ---------- n_frames : int Number of frames in the CT scan. reconstruction : bool Obey only those drifts that are labeled as known to the reconstruction software? Returns ------- minimum_value : float Minimum value of this parameter, considering the parameter drifts throughout the CT scan. """ if self.has_drifts(): if (self.native_unit != "string") and (self.native_unit != "bool") and (self.standard_value is not None): total_drift_min = self.get_total_drift_for_frame( 0, n_frames, reconstruction ) for f in range(n_frames): total_drift_for_frame = self.get_total_drift_for_frame( f, n_frames, reconstruction ) if total_drift_for_frame < total_drift_min: total_drift_min = total_drift_for_frame return self.standard_value + total_drift_min return standard_value
def reset(self)
-
Delete all drifts and set the parameter's current value to the standard value.
Expand source code
def reset(self): """Delete all drifts and set the parameter's current value to the standard value. """ self.drifts = [] self.current_value = self.standard_value self.value_has_changed = True
def set(self, value)
-
Set the parameter's standard value. Automatically sets the current value to the standard value.
Parameters
value
:float
orstr
orbool
- New standard value for the parameter.
Expand source code
def set(self, value): """Set the parameter's standard value. Automatically sets the current value to the standard value. Parameters ---------- value : float or str or bool New standard value for the parameter. """ self.set_standard_value(value)
def set_frame(self, frame: float, n_frames: int, reconstruction: bool = False) ‑> bool
-
Prepares the
current_value
for the givenframe
number (assuming a total ofn_frames
). This takes into account all drifts (or only the ones known to reconstruction).Parameters
frame
:float
- Current frame number.
n_frames
:int
- Total number of frames in scan.
reconstruction
:bool
True
: obey only those drifts that are labeled as known to the reconstruction software.False
: obey all drifts.
Returns
value_has_changed
:bool
- If the value has changed from the previous value (e.g. due to drifts).
Expand source code
def set_frame(self, frame:float, n_frames:int, reconstruction:bool=False) -> bool: """Prepares the `current_value` for the given `frame` number (assuming a total of `n_frames`). This takes into account all drifts (or only the ones known to reconstruction). Parameters ---------- frame : float Current frame number. n_frames : int Total number of frames in scan. reconstruction : bool `True`: obey only those drifts that are labeled as known to the reconstruction software. `False`: obey all drifts. Returns ------- value_has_changed : bool If the value has changed from the previous value (e.g. due to drifts). """ new_value = self.standard_value total_drift = self.get_total_drift_for_frame(frame, n_frames, reconstruction) if self.native_unit == "string": if isinstance(total_drift, str): # Only assign new value if `total_drift` is actually a string: new_value = total_drift else: if self.standard_value is not None: new_value = self.standard_value + total_drift # Check if the value has changed when compared to the previous value: value_has_changed = False if self.current_value != new_value: value_has_changed = True self.current_value = new_value return value_has_changed
def set_frame_and_get_value(self, frame: float, n_frames: int = 1, reconstruction: bool = False) ‑> float | str | bool
-
Set the new frame number, return the new current value.
Parameters
frame
:float
- Current frame number.
n_frames
:int
- Total number of frames in scan.
reconstruction
:bool
- Obey only those drifts that are labeled as known to the reconstruction software?
Returns
current_value
:float
orstr
orbool
- The parameter's current value for the given
frame
number.
Expand source code
def set_frame_and_get_value(self, frame:float, n_frames:int=1, reconstruction:bool=False) -> float | str | bool: """Set the new frame number, return the new current value. Parameters ---------- frame : float Current frame number. n_frames : int Total number of frames in scan. reconstruction : bool Obey only those drifts that are labeled as known to the reconstruction software? Returns ------- current_value : float or str or bool The parameter's current value for the given `frame` number. """ self.set_frame(frame, n_frames, reconstruction) return self.current_value
def set_from_json(self, json_parameter_object: dict) ‑> bool
-
Set up this parameter from a CTSimU parameter structure. The proper
native_unit
must be set up correctly before running this function.Parameters
json_parameter_object
:dict
- A CTSimU parameter structure.
Returns
success
:bool
True
on success,False
if an error occurred.
Expand source code
def set_from_json(self, json_parameter_object:dict) -> bool: """Set up this parameter from a CTSimU parameter structure. The proper `native_unit` must be set up correctly before running this function. Parameters ---------- json_parameter_object : dict A CTSimU parameter structure. Returns ------- success : bool `True` on success, `False` if an error occurred. """ self.reset() success = False if isinstance(json_parameter_object, numbers.Number): # Parameter is given as a single number, not as a # parameter structure (with value, unit, drift, uncertainty) if self.native_unit != "string": self.set_standard_value(json_parameter_object) success = True elif isinstance(json_parameter_object, str): # Parameter is given as a single string, not as a # parameter structure (with value, unit, drift, uncertainty) if self.native_unit == "string": self.set_standard_value(json_parameter_object) success = True elif isinstance(json_parameter_object, bool): # Parameter is given as a boolean. if self.native_unit == "bool": self.set_standard_value(json_convert_to_native_unit(self.native_unit, json_parameter_object)) success = True elif isinstance(json_parameter_object, dict): # Hopefully a valid parameter structure... # Value, automatically converted to parameter's native unit: if json_exists_and_not_null(json_parameter_object, ["value"]): if not object_value_is_null(json_parameter_object): self.set_standard_value(json_convert_to_native_unit(self.native_unit, json_parameter_object)) success = True self.current_value = self.standard_value # Unit self.preferred_unit = get_value(json_parameter_object, ["unit"], self.native_unit) # Uncertainty: if json_exists_and_not_null(json_parameter_object, ["uncertainty"]): unc = json_extract(json_parameter_object, ["uncertainty"]) if isinstance(unc, dict): # Since file format 1.0, uncertainties are value/unit dicts: self.uncertainty = json_convert_to_native_unit( native_unit=self.native_unit, value_and_unit=unc, fallback_json_unit=self.preferred_unit ) self.preferred_uncertainty_unit = get_value(json_parameter_object, ["uncertainty", "unit"], self.preferred_unit) elif isinstance(unc, numbers.Number): # Before file format 1.0, "uncertainty" and "uncertainty_unit" where # separate properties of a parameter: self.preferred_uncertainty_unit = get_value(json_parameter_object, ["uncertainty_unit"], self.preferred_unit) self.uncertainty = convert_to_native_unit( given_unit=self.preferred_uncertainty_unit, native_unit=self.native_unit, value=unc ) else: self.uncertainty = None else: self.uncertainty = None if self.uncertainty == 0: self.uncertainty = None # Drifts: if json_exists_and_not_null(json_parameter_object, ["drifts"]): drifts = json_extract(json_parameter_object, ["drifts"]) if isinstance(drifts, list): # An array of drift structures. for d in drifts: self.add_drift_json(d) elif isinstance(drifts, dict): # A single drift structure (apparently). # This is not standard behavior, but let's support it. self.add_drift_json(drifts) return success
def set_native_unit(self, native_unit: str)
-
Set the parameter's native unit.
Parameters
native_unit
:str
- New native unit for the parameter.
Possible values:
None
,"mm"
,"rad"
,"deg"
,"s"
,"mA"
,"kV"
,"g/cm^3"
,"lp/mm"
,"deg/s"
,"C"
,"bool"
,"string"
.
Expand source code
def set_native_unit(self, native_unit:str): """Set the parameter's native unit. Parameters ---------- native_unit : str New native unit for the parameter. Possible values: `None`, `"mm"`, `"rad"`, `"deg"`, `"s"`, `"mA"`, `"kV"`, `"g/cm^3"`, `"lp/mm"`, `"deg/s"`, `"C"`, `"bool"`, `"string"`. """ if is_valid_native_unit(native_unit): self.native_unit = native_unit
def set_parameter_from_key(self, json_object: dict, key_sequence: list) ‑> bool
-
Tries to find a valid parameter object at the given
key_sequence
in the givenjson_object
. Sets the parameter if possible and returnsTrue
on success,False
otherwise.Parameters
json_object
:dict
- Piece of a CTSimU JSON structure.
key_sequence
:list
- List of strings that identify the key sequence where the parameter is found in the given JSON structure.
Returns
success
:bool
True
on success,False
if an error occurred.
Expand source code
def set_parameter_from_key(self, json_object:dict, key_sequence:list) -> bool: """Tries to find a valid parameter object at the given `key_sequence` in the given `json_object`. Sets the parameter if possible and returns `True` on success, `False` otherwise. Parameters ---------- json_object : dict Piece of a CTSimU JSON structure. key_sequence : list List of strings that identify the key sequence where the parameter is found in the given JSON structure. Returns ------- success : bool `True` on success, `False` if an error occurred. """ if json_exists_and_not_null(json_object, key_sequence): if self.set_from_json(json_extract(json_object, key_sequence)): return True return False
def set_parameter_from_possible_keys(self, json_object: dict, key_sequences: list) ‑> bool
-
Searches the JSON object for each key sequence in the given list of
key_sequences
. The first sequence that can be found is taken to set up the parameter.Parameters
json_object
:dict
- Piece of a CTSimU JSON structure.
key_sequences
:list
- A list of possible key sequences, i.e., a list of lists of strings.
Returns
success
:bool
True
on success (i.e., at least one of the key sequences identified a valid parameter in the given JSON structure),False
if an error occurred.
Expand source code
def set_parameter_from_possible_keys(self, json_object:dict, key_sequences:list) -> bool: """Searches the JSON object for each key sequence in the given list of `key_sequences`. The first sequence that can be found is taken to set up the parameter. Parameters ---------- json_object : dict Piece of a CTSimU JSON structure. key_sequences : list A list of possible key sequences, i.e., a list of lists of strings. Returns ------- success : bool `True` on success (i.e., at least one of the key sequences identified a valid parameter in the given JSON structure), `False` if an error occurred. """ for keyseq in key_sequences: if self.set_parameter_from_key(json_object, keyseq): # Returned successfully, so we are done here. return True return False
def set_simple(self, simple: bool)
-
Set this parameter to "simple" mode (or not). In simple mode, the parameter is represented in the JSON file as a single value instead of a parameter object (with value, unit, uncertainty and drifts).
Parameters
simple
:bool
- Set to
True
if the parameter is represented as a single value in the JSON file instead of a parameter object.
Expand source code
def set_simple(self, simple:bool): """Set this parameter to "simple" mode (or not). In simple mode, the parameter is represented in the JSON file as a single value instead of a parameter object (with value, unit, uncertainty and drifts). Parameters ---------- simple : bool Set to `True` if the parameter is represented as a single value in the JSON file instead of a parameter object. """ self.simple = simple
def set_standard_value(self, value)
-
Set the parameter's standard value. Automatically sets the current value to the standard value.
Parameters
value
:float
orstr
orbool
- New standard value for the parameter.
Expand source code
def set_standard_value(self, value): """Set the parameter's standard value. Automatically sets the current value to the standard value. Parameters ---------- value : float or str or bool New standard value for the parameter. """ if self.valid_values is None: self.standard_value = value self.current_value = value elif isinstance(self.valid_values, list): if value in self.valid_values: # self.valid_values list contains the value self.standard_value = value self.current_value = value else: warnings.warn(f"Parameter: given value '{value}' not in list of valid values. Must be one of: {self.valid_values}. Using fail value instead: {self.fail_value}", UserWarning) self.standard_value = self.fail_value self.current_value = self.fail_value else: if value == self.valid_values: # self.valid_values seems to be a single value self.standard_value = value self.current_value = value else: warnings.warn(f"Parameter: given value '{value}' not in list of valid values. Must be: {self.valid_values}. Using fail value instead: {self.fail_value}", UserWarning) self.standard_value = self.fail_value self.current_value = self.fail_value