Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Numpy docstrings #65

Merged
merged 10 commits into from
Jun 15, 2023
3 changes: 0 additions & 3 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ jobs:
- name: Check formatting with black
run: |
poetry run black --check .
- name: Docformatter
run: |
poetry run docformatter --check -r blobmodel/

pytest:

Expand Down
235 changes: 211 additions & 24 deletions blobmodel/blobs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""This module defines a Blob class and related functions for discretizing and manipulating blobs."""

import warnings
from typing import Tuple, Union
from nptyping import NDArray
Expand All @@ -7,7 +9,7 @@


class Blob:
"""A single blob."""
"""Define a single blob."""

def __init__(
self,
Expand All @@ -16,8 +18,8 @@ def __init__(
amplitude: float,
width_prop: float,
width_perp: float,
velocity_x: float,
velocity_y: float,
v_x: float,
v_y: float,
pos_x: float,
pos_y: float,
t_init: float,
Expand All @@ -26,14 +28,51 @@ def __init__(
perp_shape_parameters: dict = None,
blob_alignment: bool = True,
) -> None:

"""
Initialize a single blob.

Parameters
----------
blob_id : int
Identifier for the blob.
blob_shape : AbstractBlobShape
Shape of the blob.
amplitude : float
Amplitude of the blob.
width_prop : float
Width of the blob in the propagation direction.
width_perp : float
Width of the blob in the perpendicular direction.
v_x : float
Velocity of the blob in the x-direction.
v_y : float
Velocity of the blob in the y-direction.
pos_x : float
Initial position of the blob in the x-direction.
pos_y : float
Initial position of the blob in the y-direction.
t_init : float
Initial time of the blob.
t_drain : Union[float, NDArray]
Time scale for the blob to drain.
prop_shape_parameters : dict
Additional shape parameters for the propagation direction.
perp_shape_parameters : dict
Additional shape parameters for the perpendicular direction.
blob_alignment : bool, optional
If blob_aligment == True, the blob shapes are rotated in the propagation direction of the blob
If blob_aligment == False, the blob shapes are independent of the propagation direction

"""
self.int = int
self.blob_id = blob_id
self.blob_shape = blob_shape
self.amplitude = amplitude
self.width_prop = width_prop
self.width_perp = width_perp
self.v_x = velocity_x
self.v_y = velocity_y
self.v_x = v_x
self.v_y = v_y
self.pos_x = pos_x
self.pos_y = pos_y
self.t_init = t_init
Expand All @@ -45,7 +84,7 @@ def __init__(
{} if perp_shape_parameters is None else perp_shape_parameters
)
self.blob_alignment = blob_alignment
self.theta = cmath.phase(self.v_x + self.v_y * 1j) if blob_alignment else 0.0
self._theta = cmath.phase(self.v_x + self.v_y * 1j) if blob_alignment else 0.0

def discretize_blob(
self,
Expand All @@ -58,13 +97,27 @@ def discretize_blob(
) -> NDArray:
"""
Discretize blob on grid. If one_dimensional the perpendicular pulse shape is ignored.
The following blob shapes are implemented:
gauss: 2D gaussian function
exp: one sided exponential in x and gaussian in y

Returns
-------
discretized blob on 3d array with dimensions x,y and t : np.array
Parameters
----------
x : NDArray
Grid coordinates in the x-direction.
y : NDArray
Grid coordinates in the y-direction.
t : NDArray
Time coordinates.
Ly : float
Length of domain in the y-direction.
periodic_y : bool, optional
Flag indicating periodicity in the y-direction (default: False).
one_dimensional : bool, optional
Flag indicating a one-dimensional blob (default: False).

Returns
-------
discretized_blob : NDArray
Discretized blob on a 3D array with dimensions (x, y, t).

"""
# If one_dimensional, then Ly should be 0.
assert (one_dimensional and Ly == 0) or not one_dimensional
Expand All @@ -73,17 +126,17 @@ def discretize_blob(
warnings.warn("blob width big compared to Ly")

x_perp, y_perp = self._rotate(
origin=(self.pos_x, self.pos_y), x=x, y=y, angle=-self.theta
origin=(self.pos_x, self.pos_y), x=x, y=y, angle=-self._theta
)
if not periodic_y or one_dimensional:
return self._single_blob(
x_perp, y_perp, t, Ly, periodic_y, one_dimensional=one_dimensional
)
if self.theta == 0:
if self._theta == 0:
number_of_y_propagations = 0
else:
x_border = (Ly - self.pos_y) / np.sin(self.theta)
adjusted_Ly = Ly / np.sin(self.theta)
x_border = (Ly - self.pos_y) / np.sin(self._theta)
adjusted_Ly = Ly / np.sin(self._theta)
prop_dir = (
self._prop_dir_blob_position(t)
if type(t) in [int, float] # t has dimensionality = 0, used for testing
Expand All @@ -103,8 +156,8 @@ def discretize_blob(
Ly,
periodic_y,
number_of_y_propagations,
x_offset=Ly * np.sin(self.theta),
y_offset=Ly * np.cos(self.theta),
x_offset=Ly * np.sin(self._theta),
y_offset=Ly * np.cos(self._theta),
one_dimensional=one_dimensional,
)
+ self._single_blob(
Expand All @@ -114,15 +167,15 @@ def discretize_blob(
Ly,
periodic_y,
number_of_y_propagations,
x_offset=-Ly * np.sin(self.theta),
y_offset=-Ly * np.cos(self.theta),
x_offset=-Ly * np.sin(self._theta),
y_offset=-Ly * np.cos(self._theta),
one_dimensional=one_dimensional,
)
)

def _single_blob(
self,
x_perp: NDArray,
x_prop: NDArray,
y_perp: NDArray,
t: NDArray,
Ly: float,
Expand All @@ -132,11 +185,41 @@ def _single_blob(
y_offset: NDArray = 0,
one_dimensional: bool = False,
) -> NDArray:
"""
Calculate the discretized blob for a single blob instance.

Parameters
----------
x_prop : NDArray
Propagation direction coordinates.
y_perp : NDArray
Perpendicular coordinates.
t : NDArray
Time coordinates.
Ly : float
Length of domain in the y-direction.
periodic_y : bool
Flag indicating periodicity in the y-direction.
number_of_y_propagations : NDArray, optional
Number of times the blob propagates through the domain in y-direction (default: 0).
x_offset : NDArray, optional
Offset in the x-direction (default: 0).
y_offset : NDArray, optional
Offset in the y-direction (default: 0).
one_dimensional : bool, optional
Flag indicating a one-dimensional blob (default: False).

Returns
-------
blob : NDArray
Discretized blob.

"""
return (
self.amplitude
* self._drain(t)
* self._propagation_direction_shape(
x_perp + x_offset,
x_prop + x_offset,
t,
Ly,
periodic_y,
Expand All @@ -156,6 +239,20 @@ def _single_blob(
)

def _drain(self, t: NDArray) -> NDArray:
"""
Calculate the drain factor for the blob.

Parameters
----------
t : NDArray
Time coordinates.

Returns
-------
drain_factor : NDArray
Drain factor.

"""
if isinstance(self.t_drain, (int, float)):
return np.exp(-(t - self.t_init) / float(self.t_drain))
return np.exp(-(t - self.t_init) / self.t_drain[np.newaxis, :, np.newaxis])
Expand All @@ -168,11 +265,33 @@ def _propagation_direction_shape(
periodic_y: bool,
number_of_y_propagations: NDArray,
) -> NDArray:
"""
Calculate the shape in the propagation direction.

Parameters
----------
x : NDArray
Coordinates in the x-direction.
t : NDArray
Time coordinates.
Ly : float
Length of domain in the y-direction.
periodic_y : bool
Flag indicating periodicity in the y-direction.
number_of_y_propagations : NDArray
Number of times the blob propagates through the domain in y-direction.

Returns
-------
shape : NDArray
Shape in the propagation direction.

"""
if periodic_y:
x_diffs = (
x
- self._prop_dir_blob_position(t)
+ number_of_y_propagations * Ly * np.sin(self.theta)
+ number_of_y_propagations * Ly * np.sin(self._theta)
)
else:
x_diffs = x - self._prop_dir_blob_position(t)
Expand All @@ -189,11 +308,31 @@ def _perpendicular_direction_shape(
periodic_y: bool,
number_of_y_propagations: NDArray,
) -> NDArray:
"""
Calculate the shape in the perpendicular direction.

Parameters
----------
y : NDArray
Coordinates in the y-direction.
Ly : float
Length of domain in the y-direction.
periodic_y : bool
Flag indicating periodicity in the y-direction.
number_of_y_propagations : NDArray
Number of times the blob propagates through the domain in y-direction.

Returns
-------
shape : NDArray
Blob shape in the perpendicular direction.

"""
if periodic_y:
y_diffs = (
y
- self._perp_dir_blob_position(t)
+ number_of_y_propagations * Ly * np.cos(self.theta)
+ number_of_y_propagations * Ly * np.cos(self._theta)
)
else:
y_diffs = y - self._perp_dir_blob_position(t)
Expand All @@ -203,13 +342,41 @@ def _perpendicular_direction_shape(
)

def _prop_dir_blob_position(self, t: NDArray) -> NDArray:
"""
Calculate the position of the blob in the propagation direction.

Parameters
----------
t : NDArray
Time coordinates.

Returns
-------
position : NDArray
Position of the blob in the propagation direction.

"""
return (
self.pos_x + (self.v_x**2 + self.v_y**2) ** 0.5 * (t - self.t_init)
if self.blob_alignment
else self.pos_x + self.v_x * (t - self.t_init)
)

def _perp_dir_blob_position(self, t: NDArray) -> float:
"""
Return the position of the blob in the perpendicular direction.

Parameters
----------
t : NDArray
Time coordinates.

Returns
-------
position : float
Position of the blob in the perpendicular direction.

"""
return (
self.pos_y
if self.blob_alignment
Expand All @@ -219,6 +386,26 @@ def _perp_dir_blob_position(self, t: NDArray) -> float:
def _rotate(
self, origin: Tuple[float, float], x: NDArray, y: NDArray, angle: float
) -> Tuple[NDArray, NDArray]:
"""
Rotate the coordinates around a given origin point.

Parameters
----------
origin : Tuple[float, float]
Origin point of rotation.
x : NDArray
Coordinates in the x-direction.
y : NDArray
Coordinates in the y-direction.
angle : float
Rotation angle.

Returns
-------
rotated_coordinates : Tuple[NDArray, NDArray]
Rotated coordinates.

"""
ox, oy = origin
px, py = x, y

Expand Down
Loading