diff --git a/src/quackseq/__init__.py b/src/quackseq/__init__.py index e69de29..753203c 100644 --- a/src/quackseq/__init__.py +++ b/src/quackseq/__init__.py @@ -0,0 +1 @@ +"""The base module for the quackseq pulse programming library.""" diff --git a/src/quackseq/event.py b/src/quackseq/event.py index 921c9b7..43635ee 100644 --- a/src/quackseq/event.py +++ b/src/quackseq/event.py @@ -1,3 +1,5 @@ +"""Event class for the pulse sequence. Every pulse sequence consists of events, that are executed subsequently and have different parameters.""" + import logging from collections import OrderedDict @@ -21,7 +23,9 @@ class Event: pulse_sequence (PulseSequence): The pulse sequence the event belongs to """ - def __init__(self, name: str, duration: float | str, pulse_sequence : "PulseSequence") -> None: + def __init__( + self, name: str, duration: float | str, pulse_sequence: "PulseSequence" + ) -> None: """Initializes the event.""" self.parameters = OrderedDict() self.name = name @@ -36,9 +40,7 @@ class Event: pulse_parameters = self.pulse_sequence.pulse_parameter_options for name, pulse_parameter_class in pulse_parameters.items(): logger.debug("Adding pulse parameter %s to event %s", name, self.name) - self.parameters[name] = pulse_parameter_class( - name - ) + self.parameters[name] = pulse_parameter_class(name) logger.debug( "Created pulse parameter %s with object id %s", name, diff --git a/src/quackseq/options.py b/src/quackseq/options.py index 24ec4ef..2a06209 100644 --- a/src/quackseq/options.py +++ b/src/quackseq/options.py @@ -1,8 +1,11 @@ +"""Options for the pulse parameters. Options can be of different types, for example boolean, numeric or function. Generally pulse parameters have different values for the different events in a pulse sequence.""" + import logging from quackseq.functions import Function logger = logging.getLogger(__name__) + class Option: """Defines options for the pulse parameters which can then be set accordingly. @@ -102,6 +105,7 @@ class NumericOption(Option): is_float (bool): If the value is a float. min_value: The minimum value of the option. max_value: The maximum value of the option. + slider (bool): If the option should be displayed as a slider. This is not used for the pulseq module, but visualizations can use this information. """ super().__init__(name, value) self.is_float = is_float @@ -116,7 +120,7 @@ class NumericOption(Option): self.value_changed.emit() elif value >= self.max_value: self.value = self.max_value - self.value_changed.emit() + self.value_changed.emit() else: raise ValueError( f"Value {value} is not in the range of {self.min_value} to {self.max_value}. This should have been caught earlier." diff --git a/src/quackseq/pulsesequence.py b/src/quackseq/pulsesequence.py index 2ba3b90..21b3e9c 100644 --- a/src/quackseq/pulsesequence.py +++ b/src/quackseq/pulsesequence.py @@ -62,8 +62,10 @@ class PulseSequence: event (Event): The event to add """ if event.name in self.get_event_names(): - raise ValueError(f"Event with name {event.name} already exists in the pulse sequence") - + raise ValueError( + f"Event with name {event.name} already exists in the pulse sequence" + ) + self.events.append(event) def create_event(self, event_name: str, duration: str) -> "Event": @@ -78,8 +80,10 @@ class PulseSequence: """ event = Event(event_name, duration, self) if event.name in self.get_event_names(): - raise ValueError(f"Event with name {event.name} already exists in the pulse sequence") - + raise ValueError( + f"Event with name {event.name} already exists in the pulse sequence" + ) + self.events.append(event) return event @@ -224,7 +228,13 @@ class QuackSequence(PulseSequence): self.add_pulse_parameter_option(self.RX_READOUT, RXReadout) def add_blank_event(self, event_name: str, duration: float): - event = self.create_event(event_name, duration) + """Adds a blank event to the pulse sequence. + + Args: + event_name (str): The name of the event + duration (float): The duration of the event with a unit suffix (n, u, m) + """ + _ = self.create_event(event_name, duration) def add_pulse_event( self, @@ -234,24 +244,44 @@ class QuackSequence(PulseSequence): phase: float, shape: Function = RectFunction(), ): + """Adds a pulse event to the pulse sequence. + + Args: + event_name (str): The name of the event + duration (float): The duration of the event with a unit suffix (n, u, m) + amplitude (float): The amplitude of the transmit pulse in percent + phase (float): The phase of the transmit pulse + shape (Function): The shape of the transmit pulse + """ event = self.create_event(event_name, duration) self.set_tx_amplitude(event, amplitude) self.set_tx_phase(event, phase) self.set_tx_shape(event, shape) def add_readout_event(self, event_name: str, duration: float): + """Adds a readout event to the pulse sequence. + + Args: + event_name (str): The name of the event + duration (float): The duration of the event with a unit suffix (n, u, m) + """ event = self.create_event(event_name, duration) self.set_rx(event, True) # TX Specific functions def set_tx_amplitude(self, event, amplitude: float) -> None: - """Sets the amplitude of the transmitter. + """Sets the relative amplitude of the transmit pulse in percent (larger 0 - max 100). Args: event (Event): The event to set the amplitude for - amplitude (float): The amplitude of the transmitter + amplitude (float): The amplitude of the transmit pulse in percent """ + if amplitude <= 0 or amplitude > 100: + raise ValueError( + "Amplitude needs to be larger than 0 and smaller or equal to 100" + ) + event.parameters[self.TX_PULSE].get_option_by_name( TXPulse.RELATIVE_AMPLITUDE ).value = amplitude @@ -268,11 +298,11 @@ class QuackSequence(PulseSequence): ).value = phase def set_tx_shape(self, event, shape: Function) -> None: - """Sets the shape of the transmitter. + """Sets the shape of the transmit pulse. Args: event (Event): The event to set the shape for - shape (Any): The shape of the transmitter + shape (Function): The shape of the transmit pulse """ event.parameters[self.TX_PULSE].get_option_by_name( TXPulse.TX_PULSE_SHAPE diff --git a/src/quackseq/spectrometer/spectrometer.py b/src/quackseq/spectrometer/spectrometer.py index fc1ba99..b2530bc 100644 --- a/src/quackseq/spectrometer/spectrometer.py +++ b/src/quackseq/spectrometer/spectrometer.py @@ -1,7 +1,7 @@ -from typing import Any +"""The base class for the spectrometer used in quackseq. This class is just a skeleton and should be inherited by all spectrometer implementations.""" -class Spectrometer(): +class Spectrometer: """Base class for spectrometers. This class should be inherited by all spectrometers. @@ -15,11 +15,10 @@ class Spectrometer(): """ raise NotImplementedError - def set_frequency(self, value : float): + def set_frequency(self, value: float): """Sets the frequency of the spectrometer.""" raise NotImplementedError - def set_averages(self, value : int): + def set_averages(self, value: int): """Sets the number of averages.""" raise NotImplementedError - \ No newline at end of file diff --git a/src/quackseq/spectrometer/spectrometer_controller.py b/src/quackseq/spectrometer/spectrometer_controller.py index 9bc1927..941ac59 100644 --- a/src/quackseq/spectrometer/spectrometer_controller.py +++ b/src/quackseq/spectrometer/spectrometer_controller.py @@ -7,7 +7,8 @@ from quackseq.pulsesequence import QuackSequence logger = logging.getLogger(__name__) -class SpectrometerController(): + +class SpectrometerController: """The base class for all spectrometer controllers.""" def run_sequence(self, sequence): @@ -17,14 +18,14 @@ class SpectrometerController(): """ raise NotImplementedError - def set_frequency(self, value : float): + def set_frequency(self, value: float): """Sets the frequency of the spectrometer.""" raise NotImplementedError - def set_averages(self, value : int): + def set_averages(self, value: int): """Sets the number of averages.""" raise NotImplementedError - + def translate_rx_event(self, sequence: QuackSequence) -> tuple: """This method translates the RX event of the pulse sequence to the limr object. @@ -62,7 +63,7 @@ class SpectrometerController(): else: return None, None - + def calculate_simulation_length(self, sequence: QuackSequence) -> float: """This method calculates the simulation length based on the settings and the pulse sequence. diff --git a/src/quackseq/spectrometer/spectrometer_model.py b/src/quackseq/spectrometer/spectrometer_model.py index 87182e0..2ccebfd 100644 --- a/src/quackseq/spectrometer/spectrometer_model.py +++ b/src/quackseq/spectrometer/spectrometer_model.py @@ -2,29 +2,54 @@ import logging from collections import OrderedDict -from typing import Any from quackseq.spectrometer.spectrometer_settings import Setting logger = logging.getLogger(__name__) + class QuackSettings(OrderedDict): + """The Quack settings class makes the different settings of the spectrometer accessible as attributes. Additionally, it provides methods to get the settings by category.""" + def __getattr__(self, key): + """Gets the value of a setting by its key. + + Args: + key (str) : The key of the setting + + Returns: + The value of the setting + """ return self[key].value def __setattr__(self, key, value): + """Sets the value of a setting by its key. + + Args: + key (str) : The key of the setting + value : The value to set + """ self[key].value = value @property def categories(self): + """The categories of the settings.""" categories = [] for setting in self.values(): - if not setting.category in categories: + if setting.category not in categories: categories.append(setting.category) return categories - + def get_settings_by_category(self, category): + """Gets the settings by category. + + Args: + category (str) : The category of the settings + + Returns: + dict : The settings with the specified category + """ settings = dict() for key, setting in self.items(): @@ -33,7 +58,8 @@ class QuackSettings(OrderedDict): return settings -class SpectrometerModel(): + +class SpectrometerModel: """The base class for all spectrometer models. It contains the settings and pulse parameters of the spectrometer. @@ -48,8 +74,7 @@ class SpectrometerModel(): """Initializes the spectrometer model.""" self.settings = QuackSettings() - - def add_setting(self,name: str, setting: Setting) -> None: + def add_setting(self, name: str, setting: Setting) -> None: """Adds a setting to the spectrometer. Args: @@ -72,10 +97,10 @@ class SpectrometerModel(): """ if name in self.settings: return self.settings[name] - + raise ValueError(f"No setting with name {name} found") - - def get_setting_by_display_name(self, display_name : str) -> Setting: + + def get_setting_by_display_name(self, display_name: str) -> Setting: """Gets a setting by its display name. Args: @@ -92,7 +117,7 @@ class SpectrometerModel(): return setting raise ValueError(f"No setting with display name {display_name} found") - + @property def target_frequency(self): """The target frequency of the spectrometer in Hz. This is the frequency where the magnetic resonance experiment is performed.""" diff --git a/src/quackseq/spectrometer/spectrometer_settings.py b/src/quackseq/spectrometer/spectrometer_settings.py index 3ee710e..92f264e 100644 --- a/src/quackseq/spectrometer/spectrometer_settings.py +++ b/src/quackseq/spectrometer/spectrometer_settings.py @@ -5,7 +5,7 @@ import logging logger = logging.getLogger(__name__) -class Setting(): +class Setting: """A setting for the spectrometer is a value that is the same for all events in a pulse sequence. E.g. the Transmit gain or the number of points in a spectrum. @@ -18,23 +18,27 @@ class Setting(): Attributes: name (str) : The name of the setting + category (str) : The category of the setting description (str) : A description of the setting value : The value of the setting category (str) : The category of the setting """ - def __init__(self, name: str, category : str, description: str = None, default=None) -> None: + def __init__( + self, name: str, category: str, description: str = None, default=None + ) -> None: """Create a new setting. Args: name (str): The name of the setting. + category (str): The category of the setting. description (str): A description of the setting. default: The default value of the setting. """ self.name = name self.category = category self.description = description - self.default = default + self.default = default if default is not None: self.value = default # Update the description with the default value @@ -48,7 +52,13 @@ class NumericalSetting(Setting): """ def __init__( - self, name: str, category: str, description: str, default, min_value=None, max_value=None + self, + name: str, + category: str, + description: str, + default, + min_value=None, + max_value=None, ) -> None: """Create a new numerical setting.""" super().__init__( @@ -104,7 +114,7 @@ class FloatSetting(NumericalSetting): description: str, min_value: float = None, max_value: float = None, - slider = False + slider=False, ) -> None: """Create a new float setting.""" super().__init__(name, category, description, default, min_value, max_value) @@ -117,7 +127,7 @@ class FloatSetting(NumericalSetting): @value.setter def value(self, value): - logger.debug(f"Setting {self.name} to {value}") + logger.debug(f"Setting {self.name} to {value}") self._value = float(value) @@ -141,13 +151,12 @@ class IntSetting(NumericalSetting): description: str, min_value=None, max_value=None, - slider = False + slider=False, ) -> None: """Create a new int setting.""" super().__init__(name, category, description, default, min_value, max_value) self.slider = slider - @property def value(self): """The value of the setting. In this case, an int.""" @@ -157,7 +166,7 @@ class IntSetting(NumericalSetting): def value(self, value): logger.debug(f"Setting {self.name} to {value}") value = int(float(value)) - + self._value = value @@ -171,11 +180,12 @@ class BooleanSetting(Setting): description (str) : A description of the setting """ - def __init__(self, name: str, category : str, default: bool, description: str) -> None: + def __init__( + self, name: str, category: str, default: bool, description: str + ) -> None: """Create a new boolean setting.""" super().__init__(name, category, description, default) - @property def value(self): """The value of the setting. In this case, a bool.""" @@ -189,7 +199,6 @@ class BooleanSetting(Setting): raise ValueError("Value must be a bool") - class SelectionSetting(Setting): """A setting that is a selection from a list of options. @@ -202,7 +211,7 @@ class SelectionSetting(Setting): """ def __init__( - self, name: str, category : str, options: list, default: str, description: str + self, name: str, category: str, options: list, default: str, description: str ) -> None: """Create a new selection setting.""" super().__init__(name, category, description, default) @@ -212,7 +221,6 @@ class SelectionSetting(Setting): self.options = options - @property def value(self): """The value of the setting. In this case, a string.""" @@ -241,7 +249,9 @@ class StringSetting(Setting): description (str) : A description of the setting """ - def __init__(self, name: str, category :str, default: str, description: str) -> None: + def __init__( + self, name: str, category: str, default: str, description: str + ) -> None: """Create a new string setting.""" super().__init__(name, category, description, default)