Skip to content

Commit 566afa5

Browse files
committed
update example cases with run-flag for documentation gallery
1 parent 9361670 commit 566afa5

File tree

12 files changed

+568
-367
lines changed

12 files changed

+568
-367
lines changed

examples/AxialStretchingCase/README.md

Whitespace-only changes.
Lines changed: 66 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,23 @@
1-
"""Axial stretching test-case
2-
3-
Assume we have a rod lying aligned in the x-direction, with high internal
4-
damping.
5-
6-
We fix one end (say, the left end) of the rod to a wall. On the right
7-
end we apply a force directed axially pulling the rods tip. Linear
8-
theory (assuming small displacements) predict that the net displacement
9-
experienced by the rod tip is Δx = FL/AE where the symbols carry their
10-
usual meaning (the rod is just a linear spring). We compare our results
11-
with the above result.
12-
13-
We can "improve" the theory by having a better estimate for the rod's
14-
spring constant by assuming that it equilibriates under the new position,
15-
with
16-
Δx = F * (L + Δx)/ (A * E)
17-
which results in Δx = (F*l)/(A*E - F). Our rod reaches equilibrium wrt to
18-
this position.
19-
20-
Note that if the damping is not high, the rod oscillates about the eventual
21-
resting position (and this agrees with the theoretical predictions without
22-
any damping : we should see the rod oscillating simple-harmonically in time).
23-
24-
isort:skip_file
251
"""
2+
Axial Stretching
3+
================
4+
5+
This case tests the axial stretching of a rod. A rod is fixed at one end and
6+
a force is applied at the other end. The rod stretches and the displacement
7+
of the tip is compared with the analytical solution.
8+
"""
9+
10+
# isort:skip_file
2611

2712
import numpy as np
2813
from matplotlib import pyplot as plt
2914

3015
import elastica as ea
3116

17+
# %%
18+
# First, we define a simulator class that inherits from the necessary mixins.
19+
# This makes it easy to add constraints, forces, and damping to the system.
20+
3221

3322
class StretchingBeamSimulator(
3423
ea.BaseSystemCollection, ea.Constraints, ea.Forcing, ea.Damping, ea.CallBacks
@@ -39,10 +28,10 @@ class StretchingBeamSimulator(
3928
stretch_sim = StretchingBeamSimulator()
4029
final_time = 200.0
4130

42-
# Options
43-
PLOT_FIGURE = True
44-
SAVE_FIGURE = False
45-
SAVE_RESULTS = False
31+
# %%
32+
# Next, we set up the test parameters for the simulation. This includes the
33+
# number of elements, the start position, direction, normal, length, radius,
34+
# density, and Young's modulus of the rod.
4635

4736
# setting up test params
4837
n_elem = 19
@@ -58,6 +47,10 @@ class StretchingBeamSimulator(
5847
poisson_ratio = 0.5
5948
shear_modulus = youngs_modulus / (poisson_ratio + 1.0)
6049

50+
# %%
51+
# Now we can create the `CosseratRod` object. We use the `straight_rod` method
52+
# to create a straight rod with the specified parameters.
53+
6154
stretchable_rod = ea.CosseratRod.straight_rod(
6255
n_elem,
6356
start,
@@ -71,16 +64,29 @@ class StretchingBeamSimulator(
7164
)
7265

7366
stretch_sim.append(stretchable_rod)
67+
68+
# %%
69+
# We then apply a boundary condition to fix one end of the rod. We use the
70+
# `OneEndFixedBC` constraint to fix the position and director of the first node.
71+
7472
stretch_sim.constrain(stretchable_rod).using(
7573
ea.OneEndFixedBC, constrained_position_idx=(0,), constrained_director_idx=(0,)
7674
)
7775

76+
# %%
77+
# A force is applied to the other end of the rod. We use the `EndpointForces`
78+
# forcing to apply a force in the x-direction.
79+
7880
end_force_x = 1.0
7981
end_force = np.array([end_force_x, 0.0, 0.0])
8082
stretch_sim.add_forcing_to(stretchable_rod).using(
8183
ea.EndpointForces, 0.0 * end_force, end_force, ramp_up_time=1e-2
8284
)
8385

86+
# %%
87+
# Damping is added to the system to help it reach a steady state. We use an
88+
# `AnalyticalLinearDamper` to add damping to the rod.
89+
8490
# add damping
8591
dl = base_length / n_elem
8692
dt = 0.1 * dl
@@ -92,6 +98,11 @@ class StretchingBeamSimulator(
9298
)
9399

94100

101+
# %%
102+
# We define a callback class to record the position and velocity of the rod
103+
# during the simulation. This is useful for post-processing the results.
104+
105+
95106
# Add call backs
96107
class AxialStretchingCallBack(ea.CallBackBaseClass):
97108
"""
@@ -125,54 +136,41 @@ def make_callback(
125136
AxialStretchingCallBack, step_skip=200, callback_params=recorded_history
126137
)
127138

139+
# %%
140+
# We finalize the simulator and create the time-stepper. The `PositionVerlet`
141+
# time-stepper is used to integrate the system.
142+
128143
stretch_sim.finalize()
129144
timestepper: ea.typing.StepperProtocol = ea.PositionVerlet()
130145
# timestepper = PEFRL()
131146

147+
# %%
148+
# The simulation is run for the specified `final_time`.
149+
132150
total_steps = int(final_time / dt)
133151
print("Total steps", total_steps)
134152
dt = final_time / total_steps
135153
time = 0.0
136154
for i in range(total_steps):
137155
time = timestepper.step(stretch_sim, time, dt)
138156

139-
if PLOT_FIGURE:
140-
# First-order theory with base-length
141-
expected_tip_disp = end_force_x * base_length / base_area / youngs_modulus
142-
# First-order theory with modified-length, gives better estimates
143-
expected_tip_disp_improved = (
144-
end_force_x * base_length / (base_area * youngs_modulus - end_force_x)
145-
)
146-
147-
fig = plt.figure(figsize=(10, 8), frameon=True, dpi=150)
148-
ax = fig.add_subplot(111)
149-
ax.plot(recorded_history["time"], recorded_history["position"], lw=2.0)
150-
ax.hlines(base_length + expected_tip_disp, 0.0, final_time, "k", "dashdot", lw=1.0)
151-
ax.hlines(
152-
base_length + expected_tip_disp_improved, 0.0, final_time, "k", "dashed", lw=2.0
153-
)
154-
if SAVE_FIGURE:
155-
fig.savefig("axial_stretching.pdf")
156-
plt.show()
157-
158-
if SAVE_RESULTS:
159-
import pickle
160-
161-
filename = "axial_stretching_data.dat"
162-
file = open(filename, "wb")
163-
pickle.dump(stretchable_rod, file)
164-
file.close()
165-
166-
tv = (
167-
np.asarray(recorded_history["time"]),
168-
np.asarray(recorded_history["velocity_norms"]),
169-
)
170-
171-
def as_time_series(v: np.ndarray) -> np.ndarray:
172-
return v.T
173-
174-
np.savetxt(
175-
"velocity_norms.csv",
176-
as_time_series(np.stack(tv)),
177-
delimiter=",",
178-
)
157+
# %%
158+
# Finally, we plot the results and compare them with the analytical solution.
159+
# The analytical solution is calculated using the first-order theory with
160+
# both the base length and the modified length.
161+
162+
# First-order theory with base-length
163+
expected_tip_disp = end_force_x * base_length / base_area / youngs_modulus
164+
# First-order theory with modified-length, gives better estimates
165+
expected_tip_disp_improved = (
166+
end_force_x * base_length / (base_area * youngs_modulus - end_force_x)
167+
)
168+
169+
fig = plt.figure(figsize=(10, 8), frameon=True, dpi=150)
170+
ax = fig.add_subplot(111)
171+
ax.plot(recorded_history["time"], recorded_history["position"], lw=2.0)
172+
ax.hlines(base_length + expected_tip_disp, 0.0, final_time, "k", "dashdot", lw=1.0)
173+
ax.hlines(
174+
base_length + expected_tip_disp_improved, 0.0, final_time, "k", "dashed", lw=2.0
175+
)
176+
plt.show()

examples/ButterflyCase/README.md

Whitespace-only changes.
Lines changed: 73 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
"""
2+
Butterfly
3+
=========
4+
5+
This case simulates the motion of a rod that is initially shaped like a
6+
butterfly. The rod is released from rest and allowed to fall under gravity.
7+
The simulation tracks the position and energy of the rod over time.
8+
"""
9+
110
import numpy as np
211
from matplotlib import pyplot as plt
312
from matplotlib.colors import to_rgb
@@ -6,6 +15,9 @@
615
import elastica as ea
716
from elastica.utils import MaxDimension
817

18+
# %%
19+
# First, we define a simulator class that inherits from the necessary mixins.
20+
921

1022
class ButterflySimulator(ea.BaseSystemCollection, ea.CallBacks):
1123
pass
@@ -14,11 +26,10 @@ class ButterflySimulator(ea.BaseSystemCollection, ea.CallBacks):
1426
butterfly_sim = ButterflySimulator()
1527
final_time = 40.0
1628

17-
# Options
18-
PLOT_FIGURE = True
19-
SAVE_FIGURE = True
20-
SAVE_RESULTS = True
21-
ADD_UNSHEARABLE_ROD = False
29+
# %%
30+
# Next, we set up the test parameters for the simulation. This includes the
31+
# number of elements, the origin, the angle of inclination, the length,
32+
# radius, density, and Young's modulus of the rod.
2233

2334
# setting up test params
2435
# FIXME : Doesn't work with elements > 10 (the inverse rotate kernel fails)
@@ -44,6 +55,10 @@ class ButterflySimulator(ea.BaseSystemCollection, ea.CallBacks):
4455
poisson_ratio = 0.5
4556
shear_modulus = youngs_modulus / (poisson_ratio + 1.0)
4657

58+
# %%
59+
# We then define the initial positions of the nodes of the rod to create the
60+
# butterfly shape.
61+
4762
positions = np.empty((MaxDimension.value(), n_elem + 1))
4863
dl = total_length / n_elem
4964

@@ -60,6 +75,9 @@ class ButterflySimulator(ea.BaseSystemCollection, ea.CallBacks):
6075
- np.sin(angle_of_inclination) * vertical_direction
6176
)
6277

78+
# %%
79+
# Now we can create the `CosseratRod` object with the specified positions.
80+
6381
butterfly_rod = ea.CosseratRod.straight_rod(
6482
n_elem,
6583
start=origin.reshape(3),
@@ -76,6 +94,11 @@ class ButterflySimulator(ea.BaseSystemCollection, ea.CallBacks):
7694
butterfly_sim.append(butterfly_rod)
7795

7896

97+
# %%
98+
# We define a callback class to record the position and energy of the rod
99+
# during the simulation.
100+
101+
79102
# Add call backs
80103
class VelocityCallBack(ea.CallBackBaseClass):
81104
"""
@@ -117,12 +140,17 @@ def make_callback(
117140
VelocityCallBack, step_skip=100, callback_params=recorded_history
118141
)
119142

143+
# %%
144+
# We finalize the simulator and create the time-stepper.
120145

121146
butterfly_sim.finalize()
122147
timestepper: ea.typing.StepperProtocol
123148
timestepper = ea.PositionVerlet()
124149
# timestepper = PEFRL()
125150

151+
# %%
152+
# The simulation is run for the specified `final_time`.
153+
126154
dt = 0.01 * dl
127155
total_steps = int(final_time / dt)
128156
print("Total steps", total_steps)
@@ -131,52 +159,43 @@ def make_callback(
131159
for i in range(total_steps):
132160
time = timestepper.step(butterfly_sim, time, dt)
133161

134-
if PLOT_FIGURE:
135-
# Plot the histories
136-
fig = plt.figure(figsize=(5, 4), frameon=True, dpi=150)
137-
ax = fig.add_subplot(111)
138-
positions_history = recorded_history["position"]
139-
# record first position
140-
first_position = positions_history.pop(0)
141-
ax.plot(first_position[2, ...], first_position[0, ...], "r--", lw=2.0)
142-
n_positions = len(positions_history)
143-
for i, pos in enumerate(positions_history):
144-
alpha = np.exp(i / n_positions - 1)
145-
ax.plot(pos[2, ...], pos[0, ...], "b", lw=0.6, alpha=alpha)
146-
# final position is also separate
147-
last_position = positions_history.pop()
148-
ax.plot(last_position[2, ...], last_position[0, ...], "k--", lw=2.0)
149-
# don't block
150-
fig.show()
151-
152-
# Plot the energies
153-
energy_fig = plt.figure(figsize=(5, 4), frameon=True, dpi=150)
154-
energy_ax = energy_fig.add_subplot(111)
155-
times = np.asarray(recorded_history["time"])
156-
te = np.asarray(recorded_history["te"])
157-
re = np.asarray(recorded_history["re"])
158-
be = np.asarray(recorded_history["be"])
159-
se = np.asarray(recorded_history["se"])
160-
161-
energy_ax.plot(times, te, c=to_rgb("xkcd:reddish"), lw=2.0, label="Translations")
162-
energy_ax.plot(times, re, c=to_rgb("xkcd:bluish"), lw=2.0, label="Rotation")
163-
energy_ax.plot(times, be, c=to_rgb("xkcd:burple"), lw=2.0, label="Bend")
164-
energy_ax.plot(times, se, c=to_rgb("xkcd:goldenrod"), lw=2.0, label="Shear")
165-
energy_ax.plot(times, te + re + be + se, c="k", lw=2.0, label="Total energy")
166-
energy_ax.legend()
167-
# don't block
168-
energy_fig.show()
169-
170-
if SAVE_FIGURE:
171-
fig.savefig("butterfly.png")
172-
energy_fig.savefig("energies.png")
173-
174-
plt.show()
175-
176-
if SAVE_RESULTS:
177-
import pickle
178-
179-
filename = "butterfly_data.dat"
180-
file = open(filename, "wb")
181-
pickle.dump(butterfly_rod, file)
182-
file.close()
162+
# %%
163+
# Finally, we plot the results. The position of the rod is plotted at
164+
# different time steps, and the energies are plotted as a function of time.
165+
166+
# Plot the histories
167+
fig = plt.figure(figsize=(5, 4), frameon=True, dpi=150)
168+
ax = fig.add_subplot(111)
169+
positions_history = recorded_history["position"]
170+
# record first position
171+
first_position = positions_history.pop(0)
172+
ax.plot(first_position[2, ...], first_position[0, ...], "r--", lw=2.0)
173+
n_positions = len(positions_history)
174+
for i, pos in enumerate(positions_history):
175+
alpha = np.exp(i / n_positions - 1)
176+
ax.plot(pos[2, ...], pos[0, ...], "b", lw=0.6, alpha=alpha)
177+
# final position is also separate
178+
last_position = positions_history.pop()
179+
ax.plot(last_position[2, ...], last_position[0, ...], "k--", lw=2.0)
180+
# don't block
181+
fig.show()
182+
183+
# Plot the energies
184+
energy_fig = plt.figure(figsize=(5, 4), frameon=True, dpi=150)
185+
energy_ax = energy_fig.add_subplot(111)
186+
times = np.asarray(recorded_history["time"])
187+
te = np.asarray(recorded_history["te"])
188+
re = np.asarray(recorded_history["re"])
189+
be = np.asarray(recorded_history["be"])
190+
se = np.asarray(recorded_history["se"])
191+
192+
energy_ax.plot(times, te, c=to_rgb("xkcd:reddish"), lw=2.0, label="Translations")
193+
energy_ax.plot(times, re, c=to_rgb("xkcd:bluish"), lw=2.0, label="Rotation")
194+
energy_ax.plot(times, be, c=to_rgb("xkcd:burple"), lw=2.0, label="Bend")
195+
energy_ax.plot(times, se, c=to_rgb("xkcd:goldenrod"), lw=2.0, label="Shear")
196+
energy_ax.plot(times, te + re + be + se, c="k", lw=2.0, label="Total energy")
197+
energy_ax.legend()
198+
# don't block
199+
energy_fig.show()
200+
201+
plt.show()

examples/CatenaryCase/README.md

Whitespace-only changes.

0 commit comments

Comments
 (0)