コード例 #1
0
    def on_calculation_exited(self, success: bool):
        """
        This is called after calculation thread has exited.

        @param success: True if calculation was successful, False otherwise
        """
        calculation_time = time.monotonic() - self.calculation_start_time

        if self.calculation_thread is not None:
            if self.calculation_thread.isRunning():
                # Skipping because another thread is now running
                # Note: This happens all the time when calculation is interrupted and restarted through ModelAccess;
                #       we see this because there is no reliable way to revoke the delayed "calculation_exited" signal
                #       after another thread has already been started
                return
            else:
                # This happens when calculation finished and no other thread was started
                self.calculation_thread = None
                Debug(
                    self,
                    f".on_calculation_exited(): Success (took {calculation_time:.2f} s)",
                    color=Theme.SuccessColor)
        else:
            Debug(
                self,
                f".on_calculation_exited(): Interrupted after {calculation_time:.2f} s",
                color=Theme.PrimaryColor)

        # Note: For some reason, most of the time we need an additional ("final-final") re-draw here; VisPy glitch?
        self.redraw()

        self.statusbar.disarm(success)
コード例 #2
0
    def recalculate(self):
        """
        Re-calculates the model.
        """
        Debug(self, ".recalculate()", color=Theme.SuccessColor)

        if self.calculation_thread is not None:
            Debug(
                self,
                ".recalculate(): WARNING: Killing orphaned calculation thread",
                color=Theme.WarningColor,
                force=True)
            self.interrupt_calculation()

        if self.initializing:
            self.initializing = False
            self.vispy_canvas.initializing = True

        self.redraw()
        self.statusbar.arm()

        # Create a new calculation thread and kick it off
        self.calculation_thread = CalculationThread(self)
        self.calculation_start_time = time.monotonic()
        self.calculation_thread.start()
コード例 #3
0
    def __init__(self, norm_id: str, comparison_id: str, _min: float,
                 _max: float, permeability: float):
        """
        Initializes the constraint.

        @param norm_id: Norm ID
        @param comparison_id: Comparison ID
        @param _min: Minimum value
        @param _max: Maximum value
        @param permeability: Relative permeability µ_r
        """
        if norm_id not in self.Norm_ID_List:
            Debug(self,
                  "Invalid norm ID",
                  color=Theme.WarningColor,
                  force=True)
            return

        if comparison_id not in self.Comparison_ID_List:
            Debug(self,
                  "Invalid comparison ID",
                  color=Theme.WarningColor,
                  force=True)
            return

        self._is_angle = norm_id in self.Norm_ID_List_Degrees

        self._norm_id = norm_id
        self._comparison_id = comparison_id

        self._min = _min
        self._max = _max

        self.permeability = permeability
コード例 #4
0
    def on_selection_changed(self, _selected, _deselected):
        """
        Gets called when the selection changed.

        @param _selected: Currently selected QItemSelection
        @param _deselected: Currently deselected QItemSelection
        """
        if self.signalsBlocked():
            Debug(self, f".on_selection_changed(): Blocked")
            return

        Debug(self, f".on_selection_changed()")
        self._selection_changed_callback()
コード例 #5
0
    def focusOutEvent(self, _event):
        """
        Gets called when the table lost focus, or when a cell item is being edited, or when a cell widget is selected.
        When not editing, this clears the selection, triggering L{on_selection_changed}

        @param _event: Event
        """
        if self.state() == QAbstractItemView.EditingState:
            Debug(self, f".focusOutEvent(): Ignored in editing mode")
        elif self.is_cell_widget_selected():
            Debug(self, f".focusOutEvent(): Ignored for cell widget")
        else:
            Debug(self, f".focusOutEvent(): Clearing selection")
            self.clearSelection()
            self.set_style(border_color="black", border_width=1)
コード例 #6
0
ファイル: Wire.py プロジェクト: shredEngineer/MagnetiCalc
    def _set_rotational_symmetry(self, parameters):
        """
        This transformation replicates and rotates this curve `count` times about an `axis` with radius `radius`.

        Note: Intended to be called from the class constructor (doesn't automatically invalidate the wire)

        @param parameters:  Dictionary containing the transformation parameters
                            (number of replications, radius, axis and offset angle)
        """
        Debug(self, "._set_rotational_symmetry()")

        axes = self.get_points_transformed().transpose()

        x, y, z = [], [], []

        axis_other_1 = (parameters["axis"] + 1) % 3
        axis_other_2 = (parameters["axis"] + 2) % 3

        for a in np.linspace(0, 2 * np.pi, parameters["count"], endpoint=False):
            b = a + parameters["offset"] * np.pi / 180
            x = np.append(x, axes[axis_other_1] * np.sin(b) - (axes[axis_other_2] + parameters["radius"]) * np.cos(b))
            y = np.append(y, axes[axis_other_1] * np.cos(b) + (axes[axis_other_2] + parameters["radius"]) * np.sin(b))
            z = np.append(z, axes[parameters["axis"]])

        axes = [x, y, z]

        # Close the resulting loop
        for i in range(3):
            axes[i] = np.append(axes[i], axes[i][0])

        self._points_transformed = np.array(axes).transpose()

        return self
コード例 #7
0
ファイル: Wire_Widget.py プロジェクト: debnoob/MagnetiCalc
    def reinitialize(self):
        """
        Re-initializes the widget.
        """
        Debug(self, ".reinitialize()")

        self.blockSignals(True)

        for i in range(3):
            self.stretch_spinbox[i].setValue(
                self.gui.config.get_point("wire_stretch")[i])

        self.rotational_symmetry_count_spinbox.setValue(
            self.gui.config.get_float("rotational_symmetry_count"))
        self.rotational_symmetry_radius_spinbox.setValue(
            self.gui.config.get_float("rotational_symmetry_radius"))

        for i, axis in enumerate(["X", "Y", "Z"]):
            if i == self.gui.config.get_int("rotational_symmetry_axis"):
                self.rotational_symmetry_axis_combobox.setCurrentIndex(i)

        self.rotational_symmetry_offset_spinbox.setValue(
            self.gui.config.get_float("rotational_symmetry_offset"))
        self.slicer_limit_spinbox.setValue(
            self.gui.config.get_float("wire_slicer_limit"))
        self.dc_spinbox.setValue(self.gui.config.get_float("wire_dc"))

        self.blockSignals(False)

        # Initially load wire from configuration
        self.set_wire(recalculate=False,
                      readjust_sampling_volume=False,
                      invalidate_self=False)
コード例 #8
0
    def clear_rows(self):
        """
        Clears all table rows.
        """
        Debug(self, f".clear_rows()")

        self.setRowCount(0)
コード例 #9
0
ファイル: Wire.py プロジェクト: shredEngineer/MagnetiCalc
    def invalidate(self):
        """
        Resets data, hiding from display.
        """
        Debug(self, ".invalidate()", color=(128, 0, 0))

        self._points_sliced = None
        self._length = None
コード例 #10
0
    def invalidate(self):
        """
        Resets data, hiding from display.
        """
        Debug(self, ".invalidate()", color=(128, 0, 0))

        self._colors = None
        self._limits = None
コード例 #11
0
    def add_constraint(self, constraint):
        """
        Adds some constraint to this volume's point generator.

        @param constraint: Constraint
        """
        Debug(self, f".add_constraint()")

        self.constraints.append(constraint)
コード例 #12
0
ファイル: Config.py プロジェクト: shredEngineer/MagnetiCalc
    def set_defaults(self):
        """
        Sets the default key-value pairs. Creates empty "User" section if not present.
        """
        self._config["DEFAULT"] = Config.Default

        if "User" not in self._config:
            Debug(self, ".set_defaults(): Creating empty User section")
            self._config["User"] = {}
コード例 #13
0
    def __init__(self):
        """
        Initializes parameters class.
        """
        Debug(self, ": Init")

        self._energy = None
        self._self_inductance = None
        self._magnetic_dipole_moment = None
コード例 #14
0
    def invalidate(self):
        """
        Resets data, hiding from display.
        """
        Debug(self, ".invalidate()", color=(128, 0, 0))

        self._energy = None
        self._self_inductance = None
        self._magnetic_dipole_moment = None
コード例 #15
0
    def select_cell(self, row=None, column=None):
        """
        Selects a cell.
        Any parameter may be left set to None in order to load its value from the selection model.

        @param row: Row
        @param column: Column
        """
        if row is None:
            row = self.selectionModel().currentIndex().row()

        if column is None:
            column = self.selectionModel().currentIndex().column()

        if row == -1 or column == -1:
            Debug(self,
                  f".select_cell({row}, {column}): WARNING: Skipped",
                  color=Theme.WarningColor,
                  force=True)
            return

        item = self.item(row, column)

        if item is None:

            # Select cell widget
            Debug(self,
                  f".select_cell({row}, {column}): Selecting cell widget")
            # widget = self.cellWidget(row, column)

            self.blockSignals(True)
            self.setCurrentCell(row, column)
            self.setFocus()
            self.blockSignals(False)

        else:

            # Select cell item
            Debug(self, f".select_cell({row}, {column}): Selecting cell item")

            item.setSelected(True)
            self.scrollToItem(item, QAbstractItemView.PositionAtCenter)
            self.selectionModel().setCurrentIndex(
                self.selectedIndexes()[0], QItemSelectionModel.SelectCurrent)
コード例 #16
0
    def get_result(self):
        """
        Calculates the field at every point of the sampling volume.

        @return: (Total number of limited points, field) if successful, None if interrupted
        """
        Debug(self, ".get_result()", color=Theme.PrimaryColor)

        total_limited = 0
        vectors = []

        # Fetch resulting vectors
        for i in range(len(self._sampling_volume_points)):

            tup = BiotSavart_JIT.worker(self._type, self._distance_limit,
                                        self._length_scale,
                                        self._current_elements,
                                        self._sampling_volume_points[i])

            total_limited += tup[0]

            vector = tup[1] * self._sampling_volume_permeabilities[i]

            vectors.append(vector)

            # Signal progress update, handle interrupt (every 16 iterations to keep overhead low)
            if i & 0xf == 0:
                self._progress_callback(100 * (i + 1) /
                                        len(self._sampling_volume_points))

                if QThread.currentThread().isInterruptionRequested():
                    Debug(self,
                          ".get_result(): Interruption requested, exiting now",
                          color=Theme.PrimaryColor)
                    return None

        if self._type == 0 or self._type == 1:
            # Field is A-field or B-field
            vectors = np.array(vectors) * self._dc * Constants.mu_0 / 4 / np.pi

        self._progress_callback(100)

        return total_limited, vectors
コード例 #17
0
    def on_row_deleted(self, row: int):
        """
        Gets called when a row was deleted.

        @param row: Row
        """
        Debug(self, f".on_row_deleted({row})")

        self._row_deleted_callback(row)
        self.select_last_row()
コード例 #18
0
    def calculate_parameters(self, progress_callback):
        """
        Calculates the parameters.

        @param progress_callback: Progress callback
        @return: True (currently non-interruptable)
        """
        Debug(self, ".calculate_parameters()", color=Theme.PrimaryColor)
        return self.parameters.recalculate(self.wire, self.sampling_volume,
                                           self.field, progress_callback)
コード例 #19
0
    def calculate_wire(self, progress_callback):
        """
        Calculates the wire.

        @param progress_callback: Progress callback
        @return: True if successful, False if interrupted
        """
        Debug(self, ".calculate_wire()", color=Theme.PrimaryColor)
        self.invalidate(do_sampling_volume=True, do_field=True, do_metric=True)
        return self.wire.recalculate(progress_callback)
コード例 #20
0
    def invalidate(self):
        """
        Resets data, hiding from display.
        """
        Debug(self, ".invalidate()", color=(128, 0, 0))

        self._points = None
        self._permeabilities = None
        self._labeled_indices = None
        self._neighbor_indices = None
コード例 #21
0
ファイル: Config.py プロジェクト: shredEngineer/MagnetiCalc
    def set_filename(self, filename: str):
        """
        Sets the filename for the current session.

        @param filename: Filename
        """
        self._filename = os.path.join(
            os.path.dirname(os.path.dirname(__file__)), filename)
        Debug(self,
              ".set_filename: file://" + self._filename.replace(" ", "%20"),
              force=True)
コード例 #22
0
    def reinitialize(self):
        """
        Re-initializes the constraint editor.
        """
        Debug(self, ".reinitialize()")

        # Initially load the constraints
        self.reload_constraints()
        self.update_table()

        self.clear_changed()
コード例 #23
0
    def quit(self):
        """
        Quits the application.
        """
        if self.calculation_thread != QThread.currentThread():
            Debug(self, ".quit()")
            if self.calculation_thread is not None:
                self.interrupt_calculation()
        else:
            Debug(
                self,
                ".quit(): Called from calculation thread (assertion failed)")

        self.config.close()

        print()
        print("Goodbye!")

        # Unregister exit handler (used by Assert_Dialog to exit gracefully)
        atexit.unregister(self.quit)
コード例 #24
0
    def on_combobox_cell_edited(self, combobox, row, column):
        """
        Gets called when a combobox cell has been edited.

        @param combobox: QCombobox
        @param row: Row
        @param column: Column
        """
        Debug(self, f".on_combobox_cell_edited()")

        self._cell_edited_callback(combobox.currentText(), row, column)
コード例 #25
0
ファイル: Config.py プロジェクト: shredEngineer/MagnetiCalc
    def remove_key(self, key):
        """
        Removes a key from the configuration.

        @param key: Key
        """
        if not self._config.remove_option("User", key):
            Debug(self,
                  f".remove_key({key}): WARNING: No such key",
                  color=Theme.WarningColor,
                  force=True)
コード例 #26
0
    def interrupt_calculation(self):
        """
        Kills any running calculation.
        """
        if self.calculation_thread is None:
            Debug(
                self,
                ".interrupt_calculation: WARNING: No calculation thread to interrupt",
                color=Theme.WarningColor,
                force=True)
            return

        if self.calculation_thread.isRunning():
            Debug(self,
                  ".interrupt_calculation(): Requesting interruption",
                  color=Theme.PrimaryColor)
            self.calculation_thread.requestInterruption()

            if self.calculation_thread.wait(5000):
                Debug(self,
                      ".interrupt_calculation(): Exited gracefully",
                      color=Theme.PrimaryColor)
            else:
                Assert_Dialog(False, "Failed to terminate calculation thread")
                if self.calculation_thread is not None:
                    if self.calculation_thread.isRunning():
                        Debug(
                            self,
                            ".interrupt_calculation(): WARNING: Terminating ungracefully",
                            color=Theme.WarningColor,
                            force=True)
                        self.calculation_thread.terminate()
                        self.calculation_thread.wait()
        else:
            Debug(
                self,
                ".interrupt_calculation: WARNING: Calculation thread should be running",
                color=Theme.WarningColor,
                force=True)

        self.calculation_thread = None
コード例 #27
0
ファイル: Wire.py プロジェクト: shredEngineer/MagnetiCalc
    def recalculate(self, progress_callback) -> bool:
        """
        Slices wire segments into smaller ones until segment lengths equal or undershoot slicer limit.

        @param progress_callback: Progress callback
        @return: True if successful, False if interrupted
        """
        Debug(self, ".recalculate()", color=Theme.SuccessColor)

        points_sliced = []
        length = 0

        for i in range(len(self.get_points_transformed()) - 1):

            # Calculate direction and length of wire segment
            segment_direction = np.array(self.get_points_transformed()[i + 1] - self.get_points_transformed()[i])
            segment_length = np.linalg.norm(segment_direction)
            length += segment_length

            # Calculate required number of slices (subdivisions) and perform linear interpolation
            slices = np.ceil(segment_length / self._slicer_limit).astype(int)
            linear = np.linspace(0, 1, slices, endpoint=False)
            points_sliced += [self.get_points_transformed()[i] + segment_direction * j for j in linear]

            # Signal progress update, handle interrupt (every 16 iterations to keep overhead low)
            if i & 0xf == 0:
                progress_callback(100 * (i + 1) / (len(self.get_points_transformed()) - 1))

                if QThread.currentThread().isInterruptionRequested():
                    Debug(self, ".recalculate(): Interruption requested, exiting now", color=Theme.PrimaryColor)
                    return False

        # Close the loop
        points_sliced.append(self.get_points_transformed()[-1])

        self._points_sliced = np.array(points_sliced)
        self._length = length

        progress_callback(100)

        return True
コード例 #28
0
    def calculate_sampling_volume(self, label_resolution, progress_callback):
        """
        Calculates the sampling volume.

        @param label_resolution: Label resolution
        @param progress_callback: Progress callback
        @return: True if successful, False if interrupted
        """
        Debug(self, ".calculate_sampling_volume()", color=Theme.PrimaryColor)
        self.invalidate(do_field=True, do_metric=True)
        return self.sampling_volume.recalculate(label_resolution,
                                                progress_callback)
コード例 #29
0
ファイル: Config.py プロジェクト: shredEngineer/MagnetiCalc
    def save(self):
        """
        Saves the configuration to file.
        """
        Debug(self, ".save()", force=True)

        with open(self._filename, "w") as file:
            self._config.write(file)

        self._synced = True
        if self._changed_callback is not None:
            self._changed_callback()
コード例 #30
0
ファイル: Config.py プロジェクト: shredEngineer/MagnetiCalc
    def get_str(self, key: str) -> str:
        """
        Reads a configuration value. Key must be in "Default" section and may be overridden in "User" section.

        @param key: Key
        @return: Value
        """
        if self.DebugGetters:
            Debug(self, f".get_str({key})")

        value = self._config.get("User", key)
        return value