Skip to content

Commit

Permalink
Handle crashing micro simulations (#85)
Browse files Browse the repository at this point in the history
* Add first draft of simulation crash handling

* Extend handling of crashing simulations to adaptivity and corner cases

* Adapt format in crash handling to pep8

* Add tests for simulation crashes

* Adapt formatting of tests

* Update logger message for crashing simulation in the first run

Co-authored-by: Ishaan Desai <[email protected]>

* Restructure simulation crash handling and add global condition for exit

* Add first draft of simulation crash handling

* Extend handling of crashing simulations to adaptivity and corner cases

* Adapt format in crash handling to pep8

* Add tests for simulation crashes

* Adapt formatting of tests

* Add interpolation to crash handling

* Add Inverse Distance Weighting and improve crash handling

* Format with pre-commit

* Apply suggestions from code review

Co-authored-by: Ishaan Desai <[email protected]>

* Incoporate review into crash handling

* Base crash interpolation on macro parameter

* Format simulation crash handling

* Adapt error for no available neighbor

* Apply text improvement suggestions from code review

Co-authored-by: Ishaan Desai <[email protected]>

* Apply suggestions from crash handling review

* Adapt interpolation test

---------

Co-authored-by: Torben Schiz <[email protected]>
Co-authored-by: Ishaan Desai <[email protected]>
  • Loading branch information
3 people authored May 3, 2024
1 parent 5d118d9 commit 407809c
Show file tree
Hide file tree
Showing 8 changed files with 516 additions and 22 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/run-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ jobs:
pip3 install --user .
pip3 uninstall -y pyprecice
- name: Run unit tests
- name: Run micro_manager unit test
working-directory: micro-manager/tests/unit
run: python3 -m unittest test_micro_manager.py

- name: Run interpolation unit test
working-directory: micro-manager/tests/unit
run: python3 -m unittest test_interpolation.py

- name: Run micro simulation crash unit test
working-directory: micro-manager/tests/unit
run: python3 -m unittest test_micro_simulation_crash_handling.py
83 changes: 83 additions & 0 deletions micro_manager/interpolation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import numpy as np
from sklearn.neighbors import NearestNeighbors


class Interpolation:
def __init__(self, logger):

self._logger = logger

def get_nearest_neighbor_indices(
self,
coords: np.ndarray,
inter_point: np.ndarray,
k: int,
) -> np.ndarray:
"""
Get local indices of the k nearest neighbors of a point.
Parameters
----------
coords : list
List of coordinates of all points.
inter_point : list | np.ndarray
Coordinates of the point for which the neighbors are to be found.
k : int
Number of neighbors to consider.
Returns
------
neighbor_indices : np.ndarray
Local indices of the k nearest neighbors in all local points.
"""
if len(coords) < k:
self._logger.info(
"Number of desired neighbors k = {} is larger than the number of available neighbors {}. Resetting k = {}.".format(
k, len(coords), len(coords)
)
)
k = len(coords)
neighbors = NearestNeighbors(n_neighbors=k).fit(coords)

neighbor_indices = neighbors.kneighbors(
[inter_point], return_distance=False
).flatten()

return neighbor_indices

def interpolate(self, neighbors: np.ndarray, point: np.ndarray, values):
"""
Interpolate a value at a point using inverse distance weighting. (https://en.wikipedia.org/wiki/Inverse_distance_weighting)
.. math::
f(x) = (\sum_{i=1}^{n} \frac{f_i}{\Vert x_i - x \Vert^2}) / (\sum_{j=1}^{n} \frac{1}{\Vert x_j - x \Vert^2})
Parameters
----------
neighbors : np.ndarray
Coordinates at which the values are known.
point : np.ndarray
Coordinates at which the value is to be interpolated.
values :
Values at the known coordinates.
Returns
-------
interpol_val / summed_weights :
Value at interpolation point.
"""
interpol_val = 0
summed_weights = 0
# Iterate over all neighbors
for inx in range(len(neighbors)):
# Compute the squared norm of the difference between interpolation point and neighbor
norm = np.linalg.norm(np.array(neighbors[inx]) - np.array(point)) ** 2
# If interpolation point is already part of the data it is returned as the interpolation result
# This avoids division by zero
if norm < 1e-16:
return values[inx]
# Update interpolation value
interpol_val += values[inx] / norm
# Extend normalization factor
summed_weights += 1 / norm

return interpol_val / summed_weights
Loading

0 comments on commit 407809c

Please sign in to comment.