Skip to content

Commit 5c19036

Browse files
Frequency Domain Linear Solver (#419)
For single DOF PTO
1 parent f06340d commit 5c19036

5 files changed

Lines changed: 196 additions & 80 deletions

File tree

tests/test_utilities.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def bem_data(f1, nfreq, ndof, ndir):
7070
Froude_Krylov_force = np.ones([nfreq, ndof, ndir], dtype=complex) + 1j
7171
inertia_matrix = np.ones([ndof, ndof])
7272
hydrostatic_stiffness = np.ones([ndof, ndof])
73-
73+
7474
data_vars = {
7575
'added_mass': (radiation_dims, added_mass),
7676
'radiation_damping': (radiation_dims, radiation_damping),
@@ -160,24 +160,24 @@ def test_plot_hydrodynamic_coefficients(bem_data,ndof):
160160
assert isinstance(fig_ex,Figure)
161161

162162
def test_plot_bode_impedance(intrinsic_impedance, ndof):
163-
fig_Zi, axes_Zi = wot.utilities.plot_bode_impedance(intrinsic_impedance)
164-
163+
fig_Zi, axes_Zi = wot.utilities.plot_bode_impedance(intrinsic_impedance)
164+
165165
assert 2*ndof*ndof == len(fig_Zi.axes)
166166
assert isinstance(fig_Zi,Figure)
167167
assert all([isinstance(ax, Axes) for ax in np.reshape(axes_Zi,-1)])
168168

169169

170170
def test_plot_power_flow(power_flows):
171-
fig_sankey, ax_sankey = wot.utilities.plot_power_flow(power_flows)
172-
171+
fig_sankey, ax_sankey = wot.utilities.plot_power_flow(power_flows)
172+
173173
assert isinstance(fig_sankey, Figure)
174-
assert isinstance(ax_sankey, Axes)
174+
assert isinstance(ax_sankey, Axes)
175175

176176
def test_calculate_power_flow(wb_bem,
177177
regular_wave,
178178
pi_controller_pto,
179179
wb_hydro_impedance):
180-
"""PI controller matches optimal for any regular wave,
180+
"""PI controller matches optimal for any regular wave,
181181
thus we check if the radiated power is equal the absorber power
182182
and if the Optimal excitation is equal the actual excitation"""
183183

@@ -196,14 +196,39 @@ def test_calculate_power_flow(wb_bem,
196196
bounds_opt=((-1e4, 0), (0, 2e4),)
197197
)
198198

199-
pflows = wot.utilities.calculate_power_flows(wec,
200-
pi_controller_pto,
201-
res,
202-
regular_wave,
199+
pflows = wot.utilities.calculate_power_flows(wec,
200+
pi_controller_pto,
201+
res,
202+
regular_wave,
203203
wb_hydro_impedance)
204204

205205
assert pflows['Absorbed'] == approx(pflows['Radiated'], rel=1e-4)
206206
assert pflows['Optimal Excitation'] == approx(pflows['Actual Excitation'], rel=1e-4)
207207

208-
209-
208+
def test_linear_solve(wb_bem, regular_wave):
209+
omega = wb_bem.omega.values
210+
gear_ratio = 12.0
211+
torque_constant = 6.7
212+
winding_resistance = 0.5
213+
winding_inductance = 0.0
214+
drivetrain_inertia = 2.0
215+
drivetrain_friction = 1.0
216+
drivetrain_stiffness = 0.0
217+
218+
drivetrain_impedance = (1j*omega*drivetrain_inertia +
219+
drivetrain_friction +
220+
1/(1j*omega)*drivetrain_stiffness)
221+
222+
winding_impedance = winding_resistance + 1j*omega*winding_inductance
223+
224+
225+
pto_impedance_11 = -1* gear_ratio**2 * drivetrain_impedance
226+
off_diag = np.sqrt(3.0/2.0) * torque_constant * gear_ratio
227+
pto_impedance_12 = -1*(off_diag+0j) * np.ones(omega.shape)
228+
pto_impedance_21 = -1*(off_diag+0j) * np.ones(omega.shape)
229+
pto_impedance_22 = winding_impedance
230+
pto_impedance = np.array([[pto_impedance_11, pto_impedance_12],
231+
[pto_impedance_21, pto_impedance_22]])
232+
233+
power, _, _, _ = wot.utilities.linear_solve(wb_bem, pto_impedance, regular_wave.isel(realization=0), np.eye(1))
234+
assert power == approx(-29.2, abs=0.05)

tests/test_waves.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ def test_time_series(self, pm_spectrum, pm_f1, pm_nfreq):
250250
direction = 0.0
251251
nrealizations = 1
252252
wave = wot.waves.long_crested_wave(pm_spectrum, nrealizations, direction)
253-
wave_ts = wot.fd_to_td(wave.sel(realization=0).values, pm_f1, pm_nfreq, False)
253+
wave_ts = wot.fd_to_td(wave.sel(realization=0).values, pm_f1, pm_nfreq, 1, False)
254254
# calculate the spectrum from the time-series
255255
t = wot.time(pm_f1, pm_nfreq)
256256
fs = 1/t[1]
@@ -279,7 +279,7 @@ def ndbc_spectrum(self,):
279279
files = [f'41013{i}2020.txt' for i in markers]
280280
spec = ws.read_ndbc_ascii([os.path.join(dir, file) for file in files])
281281
return spec.sel(time=time).interp(freq=freq)
282-
282+
283283
@pytest.fixture(scope="class")
284284
def nrealizations(self):
285285
"""Number of wave realizations."""
@@ -308,7 +308,7 @@ def test_shape(self, ndbc_spectrum, elevation, nrealizations):
308308
def test_type(self, elevation):
309309
"""Test that the elevation dataArray has the correct type."""
310310
assert np.iscomplexobj(elevation)
311-
311+
312312
def test_realizations(self, elevation):
313313
"""Test that the number of realizations is correct."""
314314
realization_out = elevation.realization.values
@@ -344,4 +344,4 @@ def test_float(self,):
344344
matrix.
345345
"""
346346
phase = wot.waves.random_phase()
347-
assert (phase < np.pi) and (phase >= -np.pi)
347+
assert (phase < np.pi) and (phase >= -np.pi)

wecopttool/core.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,7 +1278,7 @@ def fd_to_td(self, fd: ndarray) -> ndarray:
12781278
--------
12791279
fd_to_td, WEC.td_to_fd
12801280
"""
1281-
return fd_to_td(fd, self.f1, self.nfreq, True)
1281+
return fd_to_td(fd, self.f1, self.nfreq, 1, True)
12821282

12831283
def td_to_fd(
12841284
self,
@@ -1718,6 +1718,7 @@ def fd_to_td(
17181718
fd: ArrayLike,
17191719
f1: Optional[float] = None,
17201720
nfreq: Optional[int] = None,
1721+
nsubsteps: int = 1,
17211722
zero_freq: Optional[bool] = True,
17221723
) -> ndarray:
17231724
"""Convert a complex array of Fourier coefficients to a real array
@@ -1753,6 +1754,9 @@ def fd_to_td(
17531754
Fundamental frequency :python:`f1` [:math:`Hz`].
17541755
nfreq
17551756
Number of frequencies.
1757+
nsubsteps
1758+
Number of steps between the default (implied) time steps.
1759+
A value of :python:`1` corresponds to the default step length.
17561760
zero_freq
17571761
Whether the mean (DC) component is included.
17581762
@@ -1773,7 +1777,7 @@ def fd_to_td(
17731777
assert np.allclose(np.imag(fd[0, :]), 0), msg
17741778

17751779
if (f1 is not None) and (nfreq is not None):
1776-
tmat = time_mat(f1, nfreq, zero_freq=zero_freq)
1780+
tmat = time_mat(f1, nfreq, nsubsteps, zero_freq=zero_freq)
17771781
td = tmat @ complex_to_real(fd, zero_freq)
17781782
elif (f1 is None) and (nfreq is None):
17791783
n = 2*(fd.shape[0]-1)

0 commit comments

Comments
 (0)