Skip to content

Commit 6139f34

Browse files
akeesteMShabara
andauthored
Add WEC-Sim power performance example (#395)
This PR adds an example that uses MHKiT and WEC-Sim together to do a power performance assessment: - utilize resource characterization in PacWave notebook - write MCR cases for WEC-Sim - load WEC performance - assess WEC performance Right now I represent the resource using the 32-cluster results in the PacWave notebook and irregular waves in WEC-Sim. I was considering writing all 2000+ wave cases in that example to a WEC-Sim MCR file so that we can demonstrate `mhkit.wave.performance.power_performance_workflow()` but decided it would be too expensive, even if regular waves were used in WEC-Sim. I also have a half-finished example that shows how to write a directional spectra (like in `directional_waves.ipynb`) for WEC-Sim. But the WEC-Sim 'full directional spectra" feature needs documentation and an example first. The PR also includes a function to make the xarray representation of WEC-Sim data a lot more useful --------- Co-authored-by: Mohamed Shabara <[email protected]>
1 parent e20a06f commit 6139f34

File tree

6 files changed

+362
-14
lines changed

6 files changed

+362
-14
lines changed

examples/data/wave/mcr_mhkit.mat

968 Bytes
Binary file not shown.
447 Bytes
Binary file not shown.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
,Te,Hm0,weights,Tp,J
2+
0,7.974491297618696,1.2539695860020375,0.05886124580653463,9.294278901653492,6031.115427233068
3+
1,10.79453302186441,2.6414034469857426,0.03521606218803187,12.581040818023789,37004.325097576424
4+
2,6.901978594273449,1.9531216543907912,0.05200095798403432,8.044264095889801,12516.214518733921
5+
3,12.66762764506827,7.310116435425513,0.00506999304134946,14.764134784461854,367451.94558109366
6+
4,12.893701222644763,2.262294477484095,0.016045936894736042,15.027623802616274,36455.136139003094
7+
5,10.557620939325757,4.754296605622727,0.017311356620466345,12.30491950970368,116784.36178931354
8+
6,8.766663943128838,2.73937982438113,0.04364638972455223,10.217557043273704,31825.667988697423
9+
7,6.53740270831686,1.3055783411566657,0.05074593316072677,7.619350475893777,5272.441394386226
10+
8,9.666290858496115,1.340694087456887,0.03707009242331086,11.266073261650485,8443.64982080737
11+
9,12.787307070448522,3.9203973577543567,0.016464374526706786,14.903621294229048,107680.98651108926
12+
10,11.605879274797648,1.8210155555767638,0.022322848153721837,13.52666582144248,19446.169168239652
13+
11,7.584081846459788,1.8787353416133314,0.05462400212242149,8.839256231305114,12827.143860504848
14+
12,10.175410691413894,6.133932424424177,0.008836092568956415,11.859453020295915,188189.68918703857
15+
13,9.31935707393927,4.587432323814967,0.018817182816956667,10.861721531397752,95155.06836750833
16+
14,7.228995507990213,1.2566906096446477,0.05769191323850427,8.425402689965283,5449.034308785218
17+
15,5.646966933450414,1.3391066651568038,0.03211811510063545,6.581546542483,4750.825174993035
18+
16,7.615979550190041,2.634620209981552,0.03649712691597074,8.876433042179535,25339.645267361964
19+
17,9.460406027609398,3.381146758779285,0.03382351193072047,11.026114251293006,52508.67094062201
20+
18,16.000440812915354,3.044223392703431,0.00429464920004312,18.64853241598526,87446.89573211693
21+
19,10.55034307463129,1.5636343648164452,0.03026542177097602,12.296437149919917,12622.321615858824
22+
20,11.817436231011273,2.9829231066038613,0.021717088244806053,13.77323570047934,53748.08982906377
23+
21,12.10112174926391,5.305727115709343,0.014539688517578586,14.10387150263859,177305.22043219427
24+
22,10.400034982693642,3.5882967612571264,0.03231245358261764,12.121252893582334,65405.27202789767
25+
23,9.132539594795865,2.0115676174654173,0.04813020643130128,10.643985541720124,17913.12934373824
26+
24,9.880295706786633,2.4629082845730914,0.05063441840994648,11.515496161755983,29157.316024586242
27+
25,8.321803224944599,2.0030803613144172,0.054171203145458285,9.699071357744288,16104.920042215257
28+
26,6.131352093023454,1.794448837319034,0.03542910867558095,7.146098010516846,9295.960215551484
29+
27,11.430726526098171,3.9798910657304747,0.025330550582418253,13.322525088692508,90734.82138245075
30+
28,14.263809369877091,2.781733273746191,0.009358726934992883,16.624486445078194,66183.17957100201
31+
29,13.744160648067846,5.465225456257929,0.007517584283470983,16.01883525415833,240475.8375064763
32+
30,8.735251181801084,1.270630004139555,0.047066226355940106,10.180945433334598,6821.344536640248
33+
31,8.301748078970448,3.6767674099266174,0.02206953864653263,9.675697061737118,54122.886646054554
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {},
6+
"source": [
7+
"# MHKiT WEC-Sim Example\n",
8+
"\n",
9+
"This notebook demonstrates using the MHKiT wave module and [WEC-Sim](http://wec-sim.github.io/WEC-Sim/index.html) together to perform a resource characterization study in MHKiT, simulate representative cases with WEC-Sim, and visualize the results in MHKiT to estimate MAEP (Mean Annual Energy Production).\n",
10+
"\n",
11+
" 1. Characterize the available resource at a location\n",
12+
" - See the PacWave example notebook\n",
13+
" 2. Write a WEC-Sim batch file for the given clusters\n",
14+
" 3. Simulate the device *in WEC-Sim*.\n",
15+
" - Ensure that the spectra used in WEC-Sim is identical to the one used in MHKiT.\n",
16+
" 4. Load WEC-Sim batch results\n",
17+
" 5. Assess results and visualize quantities of interest\n",
18+
"\n",
19+
"This example uses WEC-Sim to simulate the [Oscillating Surge Wave Energy Converter (OSWEC)](https://wec-sim.github.io/WEC-Sim/main/user/tutorials.html#oscillating-surge-wec-oswec), a flap-type device.\n",
20+
"\n",
21+
"Start by importing MHKiT and the necessary python packages (e.g.`scipy.io`, `matplotlib.pyplot`, `pandas`, `numpy`)."
22+
]
23+
},
24+
{
25+
"cell_type": "code",
26+
"execution_count": 1,
27+
"metadata": {},
28+
"outputs": [
29+
{
30+
"ename": "ModuleNotFoundError",
31+
"evalue": "No module named 'mhkit'",
32+
"output_type": "error",
33+
"traceback": [
34+
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
35+
"\u001b[1;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
36+
"Cell \u001b[1;32mIn[1], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mmhkit\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m wave\n\u001b[0;32m 2\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mscipy\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mio\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01msio\u001b[39;00m\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mmatplotlib\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpyplot\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m \u001b[38;5;21;01mplt\u001b[39;00m\n",
37+
"\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'mhkit'"
38+
]
39+
}
40+
],
41+
"source": [
42+
"from mhkit import wave\n",
43+
"import scipy.io as sio\n",
44+
"import matplotlib.pyplot as plt\n",
45+
"import pandas as pd\n",
46+
"import numpy as np"
47+
]
48+
},
49+
{
50+
"cell_type": "markdown",
51+
"metadata": {},
52+
"source": [
53+
"## 1. Characterize the available resource at a location\n",
54+
"\n",
55+
"This example will use an abbreviated version of `PacWave_resource_characterization_example.ipynb`. \n",
56+
"For full details on downloading, calculating, and visualizing the k-means clusters representation of the site's wave resouce, see that example.\n",
57+
"\n",
58+
"We select the N=32 cluster as it's total energy flux is closet to the total energy flux of the site considering all wave conditions. We will load the PacWave example output, which can be easily saved after running the example with the command `results[32].to_csv(\"pacwave_cluster_32.csv\")`. We will start this example by reading in that csv output and formatting it for WEC-Sim."
59+
]
60+
},
61+
{
62+
"cell_type": "code",
63+
"execution_count": 2,
64+
"metadata": {},
65+
"outputs": [
66+
{
67+
"ename": "NameError",
68+
"evalue": "name 'pd' is not defined",
69+
"output_type": "error",
70+
"traceback": [
71+
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
72+
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
73+
"Cell \u001b[1;32mIn[2], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m results \u001b[38;5;241m=\u001b[39m \u001b[43mpd\u001b[49m\u001b[38;5;241m.\u001b[39mread_csv(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdata/wave/pacwave_cluster_32.csv\u001b[39m\u001b[38;5;124m\"\u001b[39m, index_col\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m0\u001b[39m)\n\u001b[0;32m 2\u001b[0m results\n",
74+
"\u001b[1;31mNameError\u001b[0m: name 'pd' is not defined"
75+
]
76+
}
77+
],
78+
"source": [
79+
"results = pd.read_csv(\"data/wave/pacwave_cluster_32.csv\", index_col=0)\n",
80+
"results"
81+
]
82+
},
83+
{
84+
"cell_type": "markdown",
85+
"metadata": {},
86+
"source": [
87+
"## 2. Write a WEC-Sim batch file for the given clusters\n",
88+
"\n",
89+
"[WEC-Sim MCR](https://wec-sim.github.io/WEC-Sim/main/user/advanced_features.html#multiple-condition-runs-mcr) (multiple condition run) files should contain a structure `mcr` that contains two variables: `header` and `cases`. Each column of `header` and `cases` denotes a variable and it's value respectively. Each row is another simulation. WEC-Sim defines waves using the significant wave height and peak period. We will isolate these values from the results of the cluster analysis and create a dictionary that is written to the `.mat` file."
90+
]
91+
},
92+
{
93+
"cell_type": "code",
94+
"execution_count": 3,
95+
"metadata": {
96+
"scrolled": true
97+
},
98+
"outputs": [
99+
{
100+
"ename": "NameError",
101+
"evalue": "name 'results' is not defined",
102+
"output_type": "error",
103+
"traceback": [
104+
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
105+
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
106+
"Cell \u001b[1;32mIn[3], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m ws_mcr_cases \u001b[38;5;241m=\u001b[39m \u001b[43mresults\u001b[49m[[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHm0\u001b[39m\u001b[38;5;124m\"\u001b[39m,\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mTp\u001b[39m\u001b[38;5;124m\"\u001b[39m]]\n\u001b[0;32m 2\u001b[0m ws_mcr_header \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39marray([\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mwaves.height\u001b[39m\u001b[38;5;124m\"\u001b[39m,\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mwaves.period\u001b[39m\u001b[38;5;124m\"\u001b[39m], dtype\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mobject\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[0;32m 3\u001b[0m ws_mcr_out \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mmcr\u001b[39m\u001b[38;5;124m'\u001b[39m: {\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mheader\u001b[39m\u001b[38;5;124m'\u001b[39m: ws_mcr_header, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcases\u001b[39m\u001b[38;5;124m'\u001b[39m: ws_mcr_cases}}\n",
107+
"\u001b[1;31mNameError\u001b[0m: name 'results' is not defined"
108+
]
109+
}
110+
],
111+
"source": [
112+
"ws_mcr_cases = results[[\"Hm0\",\"Tp\"]]\n",
113+
"ws_mcr_header = np.array([\"waves.height\",\"waves.period\"], dtype='object')\n",
114+
"ws_mcr_out = {'mcr': {'header': ws_mcr_header, 'cases': ws_mcr_cases}}\n",
115+
"sio.savemat('mcr_mhkit.mat', ws_mcr_out)"
116+
]
117+
},
118+
{
119+
"cell_type": "markdown",
120+
"metadata": {},
121+
"source": [
122+
"## 3. Simulate the device *in WEC-Sim*\n",
123+
"\n",
124+
"Now that the MCR file is created, we need to go simulate WEC performance in these wave conditions using WEC-Sim. To recreate the data used in the next step, use the created MCR file with WEC-Sim's [OSWEC example](https://github.com/WEC-Sim/WEC-Sim/tree/main/examples/OSWEC). For an accurate comparison to the power calculated in the resource characterization, we should ensure that the WEC-Sim cases use irregular JONSWAP wave spectra as in the PacWave example.\n",
125+
"\n",
126+
"For convenience in this demonstration, we enforce OSWEC model stability in the extreme wave conditions by arbitrarily applying a large PTO stiffness and damping:\n",
127+
"```\n",
128+
"pto(1).stiffness = 1e5;\n",
129+
"pto(1).damping = 5e7;\n",
130+
"```\n",
131+
"\n",
132+
"To reduce the amount of extranenous data saved for this example, we limit the WEC-Sim output to the PTO's power output in the `userDefinedFunctions.m` script:\n",
133+
"```\n",
134+
"if exist('imcr','var')\n",
135+
" if imcr == 1\n",
136+
" nmcr = size(mcr.cases,1);\n",
137+
" power = nan(1, nmcr);\n",
138+
" end\n",
139+
"\n",
140+
" iRampEnd = simu.rampTime./simu.dtOut + 1;\n",
141+
" power(imcr) = -mean(output.ptos(1).powerInternalMechanics(iRampEnd:end,5));\n",
142+
"\n",
143+
" if imcr == nmcr\n",
144+
" % Save output\n",
145+
" save('mcr_mhkit_power.mat', 'power');\n",
146+
" end\n",
147+
"end\n",
148+
"```"
149+
]
150+
},
151+
{
152+
"cell_type": "markdown",
153+
"metadata": {},
154+
"source": [
155+
"## 4. Load WEC-Sim batch results\n",
156+
"\n",
157+
"Note that in this example we do not save the entire WEC-Sim `output` structure for each case. See the `wecsim_example.ipynb` for information on loading that WEC-Sim data. Here the output is one array of average power output that we will load and compare to the resource characterization.\n",
158+
"\n",
159+
"Note that the power output \\[W\\] is significantly larger than the energy flux \\[W/m\\] due to the width of the OSWEC. "
160+
]
161+
},
162+
{
163+
"cell_type": "code",
164+
"execution_count": 4,
165+
"metadata": {
166+
"scrolled": true
167+
},
168+
"outputs": [
169+
{
170+
"ename": "NameError",
171+
"evalue": "name 'sio' is not defined",
172+
"output_type": "error",
173+
"traceback": [
174+
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
175+
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
176+
"Cell \u001b[1;32mIn[4], line 5\u001b[0m\n\u001b[0;32m 2\u001b[0m filename \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m./data/wave/mcr_mhkit_power.mat\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 4\u001b[0m \u001b[38;5;66;03m# Load data using the `wecsim.read_output` function which returns a dictionary of Datasets.\u001b[39;00m\n\u001b[1;32m----> 5\u001b[0m wecsim_data \u001b[38;5;241m=\u001b[39m \u001b[43msio\u001b[49m\u001b[38;5;241m.\u001b[39mloadmat(filename)\n\u001b[0;32m 6\u001b[0m results[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mP\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m wecsim_data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mpower\u001b[39m\u001b[38;5;124m'\u001b[39m][\u001b[38;5;241m0\u001b[39m]\n\u001b[0;32m 7\u001b[0m results\n",
177+
"\u001b[1;31mNameError\u001b[0m: name 'sio' is not defined"
178+
]
179+
}
180+
],
181+
"source": [
182+
"# Relative location and filename of simulated WEC-Sim data (run with mooring)\n",
183+
"filename = \"./data/wave/mcr_mhkit_power.mat\"\n",
184+
"\n",
185+
"# Load data using the `wecsim.read_output` function which returns a dictionary of Datasets.\n",
186+
"wecsim_data = sio.loadmat(filename)\n",
187+
"results['P'] = wecsim_data['power'][0]\n",
188+
"results"
189+
]
190+
},
191+
{
192+
"cell_type": "markdown",
193+
"metadata": {},
194+
"source": [
195+
"## 5. Assess results and visualize quantities of interest\n",
196+
"Now that we have loaded the OSWEC's modeled power, we can assess it's performance relative to the incoming wave and calculate the mean annual energy production (MAEP) using MHKiT."
197+
]
198+
},
199+
{
200+
"cell_type": "code",
201+
"execution_count": 5,
202+
"metadata": {},
203+
"outputs": [
204+
{
205+
"ename": "NameError",
206+
"evalue": "name 'wave' is not defined",
207+
"output_type": "error",
208+
"traceback": [
209+
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
210+
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
211+
"Cell \u001b[1;32mIn[5], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m results[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mCW\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[43mwave\u001b[49m\u001b[38;5;241m.\u001b[39mperformance\u001b[38;5;241m.\u001b[39mcapture_width(results[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mP\u001b[39m\u001b[38;5;124m'\u001b[39m], results[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mJ\u001b[39m\u001b[38;5;124m'\u001b[39m])\n\u001b[0;32m 2\u001b[0m oswec_width \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m18\u001b[39m\n\u001b[0;32m 3\u001b[0m results[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mCWR\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m results[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mCW\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m/\u001b[39m oswec_width\n",
212+
"\u001b[1;31mNameError\u001b[0m: name 'wave' is not defined"
213+
]
214+
}
215+
],
216+
"source": [
217+
"results['CW'] = wave.performance.capture_width(results['P'], results['J'])\n",
218+
"oswec_width = 18\n",
219+
"results['CWR'] = results['CW'] / oswec_width\n",
220+
"results"
221+
]
222+
},
223+
{
224+
"cell_type": "code",
225+
"execution_count": 6,
226+
"metadata": {},
227+
"outputs": [
228+
{
229+
"ename": "NameError",
230+
"evalue": "name 'wave' is not defined",
231+
"output_type": "error",
232+
"traceback": [
233+
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
234+
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
235+
"Cell \u001b[1;32mIn[6], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m MAEP \u001b[38;5;241m=\u001b[39m \u001b[43mwave\u001b[49m\u001b[38;5;241m.\u001b[39mperformance\u001b[38;5;241m.\u001b[39mmean_annual_energy_production_matrix(results[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mCW\u001b[39m\u001b[38;5;124m'\u001b[39m], results[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mJ\u001b[39m\u001b[38;5;124m'\u001b[39m], results[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mweights\u001b[39m\u001b[38;5;124m'\u001b[39m]) \u001b[38;5;241m/\u001b[39m \u001b[38;5;241m1000\u001b[39m \u001b[38;5;66;03m# kWh\u001b[39;00m\n\u001b[0;32m 2\u001b[0m MAEP \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mround(MAEP, \u001b[38;5;241m0\u001b[39m)\u001b[38;5;241m.\u001b[39mitem()\n\u001b[0;32m 3\u001b[0m MAEP\n",
236+
"\u001b[1;31mNameError\u001b[0m: name 'wave' is not defined"
237+
]
238+
}
239+
],
240+
"source": [
241+
"MAEP = wave.performance.mean_annual_energy_production_matrix(results['CW'], results['J'], results['weights']) / 1000 # kWh\n",
242+
"MAEP = np.round(MAEP, 0).item()\n",
243+
"MAEP"
244+
]
245+
}
246+
],
247+
"metadata": {
248+
"kernelspec": {
249+
"display_name": "Python 3 (ipykernel)",
250+
"language": "python",
251+
"name": "python3"
252+
},
253+
"language_info": {
254+
"codemirror_mode": {
255+
"name": "ipython",
256+
"version": 3
257+
},
258+
"file_extension": ".py",
259+
"mimetype": "text/x-python",
260+
"name": "python",
261+
"nbconvert_exporter": "python",
262+
"pygments_lexer": "ipython3",
263+
"version": "3.10.9"
264+
}
265+
},
266+
"nbformat": 4,
267+
"nbformat_minor": 4
268+
}

mhkit/tests/wave/io/test_wecsim.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def test_read_wecSim_cable(self):
5656
)
5757
self.assertEqual(ws_output["wave"]["elevation"].name, "elevation")
5858
self.assertEqual(
59-
ws_output["bodies"]["body1"]["position_dof1"].name, "position_dof1"
59+
ws_output["bodies"].sel(body=1, dof=1)["position"].name, "position"
6060
)
6161
self.assertEqual(len(ws_output["mooring"]), 0)
6262
self.assertEqual(len(ws_output["moorDyn"]), 0)

0 commit comments

Comments
 (0)