Skip to content

Commit

Permalink
Merge pull request #134 from GazzolaLab/update-0.2.4
Browse files Browse the repository at this point in the history
Update 0.2.4
  • Loading branch information
skim0119 authored Jul 15, 2022
2 parents 4f8c986 + 9e16e3d commit 112f464
Show file tree
Hide file tree
Showing 36 changed files with 2,289 additions and 225 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:

strategy:
matrix:
os: [ubuntu-latest] # [ubuntu-latest, macos-latest] # Run macos tests if really required, since they charge 10 times more for macos
os: [ubuntu-latest, macos-latest] #, windows-latest] # Run macos tests if really required, since they charge 10 times more for macos
python-version: [3.6, 3.7, 3.8]

# Steps represent a sequence of tasks that will be executed as part of the job
Expand Down
19 changes: 18 additions & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Release Note (version 0.2.3)
# Release Note (version 0.2.4)

## What's Changed

* Refactor EndPointForcesSinusoidal example and test cases by @armantekinalp in https://github.com/GazzolaLab/PyElastica/pull/110
* Fix save_every condition in ExportCallBack by @mstoelzle in https://github.com/GazzolaLab/PyElastica/pull/125
* Fix and update contact examples by @armantekinalp in https://github.com/GazzolaLab/PyElastica/pull/109
* Update rigid body rod contact by @armantekinalp in https://github.com/GazzolaLab/PyElastica/pull/117
* Update rigid body rod contact friction by @armantekinalp in https://github.com/GazzolaLab/PyElastica/pull/124
* Update ExportCallback by @skim0119 in https://github.com/GazzolaLab/PyElastica/pull/130

## New Contributors

* @mstoelzle made their first contribution in https://github.com/GazzolaLab/PyElastica/pull/125

---

# Release Note (version 0.2.3)

## Developer Note
The major updates are knot theory module added to the Cosserat rod as *mixin*, and muscular snake example is added.
Expand All @@ -8,6 +24,7 @@ The major updates are knot theory module added to the Cosserat rod as *mixin*, a
- #70: Knot theory module to compute topological quantities.
- #71: Reorganize rod constructor warning messages and collect messages in log.
- #72: Muscular snake example is added.

---

# Release Note (version 0.2.2)
Expand Down
4 changes: 2 additions & 2 deletions docs/api/connections.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ Compatibility
=============================== ==== ===========
Connection / Contact / Joints Rod Rigid Body
=============================== ==== ===========
FreeJoint ✅
FreeJoint ✅
ExternalContact ✅ ❌
FixedJoint ✅
FixedJoint ✅
HingeJoint ✅ ❌
SelfContact ✅ ❌
=============================== ==== ===========
Expand Down
7 changes: 6 additions & 1 deletion docs/api/external_forces.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ External force and environmental interaction are represented as force/torque bou
UniformForces
UniformTorques
MuscleTorques
EndpointForcesSinusoidal

.. rubric:: Available Interaction

Expand All @@ -40,11 +41,12 @@ Compatibility
Forcing Rod Rigid Body
========================== ======= ============
NoForces ✅ ✅
EndpointForces ✅
EndpointForces ✅
GravityForces ✅ ✅
UniformForces ✅ ✅
UniformTorques ✅ ✅
MuscleTorques ✅ ❌
EndpointForcesSinusoidal ✅ ❌
========================== ======= ============

========================== ======= ============
Expand Down Expand Up @@ -78,6 +80,9 @@ Built-in External Forces
.. autoclass:: MuscleTorques
:special-members: __init__

.. autoclass:: EndpointForcesSinusoidal
:special-members: __init__

Built-in Environment Interactions
---------------------------------
.. automodule:: elastica.interaction
Expand Down
92 changes: 69 additions & 23 deletions elastica/callback_functions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
__doc__ = """ Module contains callback classes to save simulation data for rod-like objects """
__all__ = ["CallBackBaseClass", "MyCallBack", "ExportCallBack"]

import warnings
import os
import sys
import numpy as np
import logging

from collections import defaultdict


class CallBackBaseClass:
Expand Down Expand Up @@ -111,48 +113,61 @@ class ExportCallBack(CallBackBaseClass):
def __init__(
self,
step_skip: int,
path: str,
filename: str,
directory: str,
method: str,
initial_file_count: int = 0,
save_every: int = 1e8,
file_save_interval: int = 1e8,
):
"""
Parameters
----------
step_skip : int
Collect data at each step_skip steps.
path : str
Path to save the file. If directories are prepended,
they must exist. The filename depends on the method.
The path is not expected to include extension.
Interval to collect simulation data into buffer.
The data will be collected at every `dt * step_skip`
interval.
filename : str
Name of the file without extension. The extension will be
determined depend on the method. File will be saved with the
name <filename>_<number>.<extension>.
directory : str
Directory to save the file. If directory doesn't exist, it will
be created. During the save, any existing files in this directory
could be overwritten.
method : str
Method name. Only the name in AVAILABLE_METHOD is
allowed.
initial_file_count : int
Initial file count index that will be appended
save_every : int
Save the file every save_every steps. (default=1e8)
file_save_interval : int
Interval, in steps, to export/save collected buffer
as file. (default = 1e8)
"""
# Assertions
MIN_STEP_SKIP = 100
if step_skip <= MIN_STEP_SKIP:
warnings.warn(f"We recommend step_skip at least {MIN_STEP_SKIP}")
logging.warning(
f"We recommend (step_skip={step_skip}) at least {MIN_STEP_SKIP}"
)
assert (
method in ExportCallBack.AVAILABLE_METHOD
), f"The exporting method ({method}) is not supported. Please use one of {ExportCallBack.AVAILABLE_METHOD}."
assert os.path.exists(path), "The export path does not exist."

# Create directory
if os.path.exists(directory):
logging.warning(
f"The directory ({directory}) already exists. Previously saved data could be overwritten."
)
os.makedirs(directory, exist_ok=True)

# Argument Parameters
self.step_skip = step_skip
self.save_path = path
self.save_path = os.path.join(directory, filename) + "_{:02d}.{}"
self.method = method
self.file_count = initial_file_count
self.save_every = save_every
self.file_save_interval = file_save_interval

# Data collector
from collections import defaultdict

self.buffer = defaultdict(list)
self.buffer_size = 0

Expand All @@ -161,16 +176,19 @@ def __init__(
import pickle

self._pickle = pickle
self._ext = "pkl"
elif method == ExportCallBack.AVAILABLE_METHOD[1]:
from numpy import savez

self._savez = savez
self._ext = "npz"
elif method == ExportCallBack.AVAILABLE_METHOD[2]:
import tempfile
import pickle

self._tempfile = tempfile.NamedTemporaryFile(delete=False)
self._pickle = pickle
self._ext = "pkl"

def make_callback(self, system, time, current_step: int):
"""
Expand Down Expand Up @@ -200,14 +218,19 @@ def make_callback(self, system, time, current_step: int):
+ sys.getsizeof(velocity)
+ sys.getsizeof(director)
)
if (
self.buffer_size > ExportCallBack.FILE_SIZE_CUTOFF
or (current_step + 1) % self.save_every == 0
):
self._dump()

if (
self.buffer_size > self.FILE_SIZE_CUTOFF
or (current_step + 1) % self.file_save_interval == 0
):
self._dump()

def _dump(self, **kwargs):
file_path = f"{self.save_path}_{self.file_count}.dat"
"""
Dump dictionary buffer (self.buffer) to a file and clear
the buffer.
"""
file_path = self.save_path.format(self.file_count, self._ext)
data = {k: np.array(v) for k, v in self.buffer.items()}
if self.method == ExportCallBack.AVAILABLE_METHOD[0]:
# pickle
Expand All @@ -224,3 +247,26 @@ def _dump(self, **kwargs):
self.file_count += 1
self.buffer_size = 0
self.buffer.clear()

def get_last_saved_path(self) -> str:
"""
Return last saved file path. If no file has been saved,
return None
"""
if self.file_count == 0:
return None
else:
return self.save_path.format(self.file_count - 1, self._ext)

def close(self):
"""
Save residual buffer
"""
if self.buffer_size:
self._dump()

def clear(self):
"""
Alias to `close`
"""
self.close()
Loading

0 comments on commit 112f464

Please sign in to comment.