1111 if the neighborhood search will be updated.
1212
1313# Keyword Arguments
14- - `moving_particles`: Indices of moving particles. Default is each particle in the system.
14+ - `moving_particles`: Indices of moving particles. Default is each particle in the system
15+ for the [`WallBoundarySystem`](@ref) and all clamped particles
16+ for the [`TotalLagrangianSPHSystem`](@ref).
1517
1618# Examples
1719```jldoctest; output = false
@@ -48,7 +50,9 @@ function PrescribedMotion(movement_function, is_moving; moving_particles=nothing
4850 return PrescribedMotion (movement_function, is_moving, moving_particles)
4951end
5052
51- function initialize! (prescribed_motion:: PrescribedMotion , initial_condition)
53+ function initialize_prescribed_motion! (prescribed_motion:: PrescribedMotion ,
54+ initial_condition,
55+ n_clamped_particles= nparticles (initial_condition))
5256 # Test `movement_function` return type
5357 pos = extract_svector (initial_condition. coordinates,
5458 Val (size (initial_condition. coordinates, 1 )), 1 )
@@ -57,23 +61,34 @@ function initialize!(prescribed_motion::PrescribedMotion, initial_condition)
5761 " Returning regular `Vector`s causes allocations and significant performance overhead."
5862 end
5963
60- # Empty `moving_particles` means all particles are moving
64+ # Empty `moving_particles` means all clamped particles are moving.
65+ # For boundaries, all particles are considered clamped.
6166 if isempty (prescribed_motion. moving_particles)
6267 # Default is an empty vector, since the number of particles is not known when
6368 # instantiating `PrescribedMotion`.
64- resize! (prescribed_motion. moving_particles, nparticles (initial_condition))
65- prescribed_motion. moving_particles .= collect (1 : nparticles (initial_condition))
69+ resize! (prescribed_motion. moving_particles, n_clamped_particles)
70+
71+ # Clamped particles for TLSPH are the last `n_clamped_particles` in the system
72+ first_particle = nparticles (initial_condition) - n_clamped_particles + 1
73+ clamped_particles = first_particle: nparticles (initial_condition)
74+ prescribed_motion. moving_particles .= collect (clamped_particles)
6675 end
76+
77+ return prescribed_motion
78+ end
79+
80+ function initialize_prescribed_motion! (:: Nothing , initial_condition,
81+ n_clamped_particles= nparticles (initial_condition))
82+ return nothing
6783end
6884
69- function (prescribed_motion:: PrescribedMotion )(system, t, semi)
70- (; coordinates, cache) = system
85+ function (prescribed_motion:: PrescribedMotion )(coordinates, velocity, acceleration,
86+ ismoving, system, semi, t)
7187 (; movement_function, is_moving, moving_particles) = prescribed_motion
72- (; acceleration, velocity) = cache
7388
74- system . ismoving[] = is_moving (t)
89+ ismoving[] = is_moving (t)
7590
76- is_moving (t) || return system
91+ is_moving (t) || return nothing
7792
7893 @threaded semi for particle in moving_particles
7994 pos_original = initial_coords (system, particle)
@@ -90,11 +105,85 @@ function (prescribed_motion::PrescribedMotion)(system, t, semi)
90105 end
91106 end
92107
93- return system
108+ return nothing
94109end
95110
96- function (prescribed_motion:: Nothing )(system:: AbstractSystem , t, semi)
97- system. ismoving[] = false
111+ """
112+ OscillatingMotion2D(; frequency, translation_vector, rotation_angle, rotation_center,
113+ rotation_phase_offset=0, tspan=(-Inf, Inf),
114+ ramp_up_tspan=(0.0, 0.0), moving_particles=nothing)
115+
116+ Create a [`PrescribedMotion`](@ref) for a 2D oscillating motion of particles.
117+ The motion is a combination of a translation and a rotation around a center point
118+ with the same frequency. Note that a phase offset can be specified for a rotation
119+ that is out of sync with the translation.
120+
121+ Create a [`PrescribedMotion`](@ref) for 2D oscillatory particle motion
122+ that combines a translation and a rotation about a specified center.
123+ Both use the same frequency; an optional phase offset allows the rotation
124+ to be out of phase with the translation.
125+
126+ # Keywords
127+ - `frequency`: Frequency of the oscillation in Hz.
128+ - `translation_vector`: Vector specifying the amplitude and direction of the translation.
129+ - `rotation_angle`: Maximum rotation angle in radians.
130+ - `rotation_center`: Center point of the rotation as an `SVector`.
131+
132+ # Keywords (optional)
133+ - `rotation_phase_offset=0`: Phase offset of the rotation in number of periods (`1` = 1 period).
134+ - `tspan=(-Inf, Inf)`: Time span in which the motion is active. Outside of this time span,
135+ particles remain at their last position.
136+ - `ramp_up_tspan`: Time span in which the motion is smoothly ramped up from zero
137+ to full amplitude.
138+ Outside of this time span, the motion has full amplitude.
139+ Default is no ramp-up.
140+ - `moving_particles`: Indices of moving particles. Default is each particle in the system
141+ for the [`WallBoundarySystem`](@ref) and all clamped particles
142+ for the [`TotalLagrangianSPHSystem`](@ref).
143+
144+ # Examples
145+ ```jldoctest; output = false, filter = r"PrescribedMotion{.*"
146+ # Oscillating motion with frequency 1 Hz, translation amplitude 1 in x-direction,
147+ # rotation angle 90 degrees (pi/2) around the origin.
148+ frequency = 1.0
149+ translation_vector = SVector(1.0, 0.0)
150+ rotation_angle = pi / 2
151+ rotation_center = SVector(0.0, 0.0)
152+
153+ motion = OscillatingMotion2D(frequency, translation_vector, rotation_angle,
154+ rotation_center)
155+
156+ # output
157+ PrescribedMotion{...}
158+ """
159+ function OscillatingMotion2D (; frequency, translation_vector, rotation_angle,
160+ rotation_center, rotation_phase_offset= 0 , tspan= (- Inf , Inf ),
161+ ramp_up_tspan= (0.0 , 0.0 ), moving_particles= nothing )
162+ @inline function movement_function (x, t)
163+ if isfinite (tspan[1 ])
164+ t = t - tspan[1 ]
165+ end
166+
167+ sin_scaled = sin (frequency * 2pi * t)
168+ translation = sin_scaled * translation_vector
169+ x_centered = x .- rotation_center
170+ sin_scaled_offset = sin (2pi * (frequency * t - rotation_phase_offset))
171+ angle = rotation_angle * sin_scaled_offset
172+ rotated = SVector (x_centered[1 ] * cos (angle) - x_centered[2 ] * sin (angle),
173+ x_centered[1 ] * sin (angle) + x_centered[2 ] * cos (angle))
174+
175+ result = rotated .+ rotation_center .+ translation
176+
177+ if ramp_up_tspan[2 ] > ramp_up_tspan[1 ] && ramp_up_tspan[1 ] <= t <= ramp_up_tspan[2 ]
178+ # Apply a smoothstep ramp-up during the ramp-up time span
179+ t_rel = (t - ramp_up_tspan[1 ]) / (ramp_up_tspan[2 ] - ramp_up_tspan[1 ])
180+ ramp_factor = 3 * t_rel^ 2 - 2 * t_rel^ 3
181+ return result * ramp_factor + (1 - ramp_factor) * x
182+ end
183+ return result
184+ end
185+
186+ is_moving (t) = tspan[1 ] <= t <= tspan[2 ]
98187
99- return system
188+ return PrescribedMotion (movement_function, is_moving; moving_particles)
100189end
0 commit comments