Interpolation of profiles from data

As noted in the example on constraining with time series, if you want to your simulated turbulence to look like your measured time series when the simulation point is near a measured point, you need to have profile functions that will recreate the values from the data when needed. PyConTurb has a custom interpolator and profiles to handle this. The interpolator is demonstrated in another example; this one will focues on the profile functions that interpolate from the constraining data in con_tc.

This example has the following sections:

Preliminaries: imports and definitions

We first import required functions and set a few notebook-specific functions/variables.

[1]:
%matplotlib inline
from pathlib import Path

import matplotlib.pyplot as plt  # matplotlib for some plotting
import numpy as np  # numeric python functions
import pandas as pd  # need this to load our data from the csv files

from pyconturb import TimeConstraint
from pyconturb.sig_models import data_sig
from pyconturb.spectral_models import data_spectrum
from pyconturb.wind_profiles import data_profile

from _nb_utils import plot_interp

# load the constraint file used in the constrained example, used for our "data"
data_dir = Path('.').absolute() / 'data'
con_tc = TimeConstraint(pd.read_csv(data_dir / 'con_tc.csv', index_col=0))  # load data from csv into tc
con_tc.index = con_tc.index.map(lambda x: float(x) if (x not in 'kxyz') else x)  # index cleaning

We also need to create the spatial dataframe that defines the area to be simulated. Due to dimensions assumed in the plotting function used in this example, we won’t use gen_spat_grid but will rather construct the dataframe manually.

[2]:
Y, Z = np.meshgrid([-50, 50], np.linspace(10, 140))  # 2d arrays for contours
k, x = 0*Y.flatten(), 0*Y.flatten()
spat_df = pd.DataFrame([k, x, Y.flatten(), Z.flatten()], index=['k', 'x', 'y', 'z'])

Wind profile interpolated from data

We will use the same TimeConstraint object as we did in the example on constraining data. The object was loaded in during the “Preliminaries” section above. Recall that it has constraints on \(u\), \(v\) and \(w\) at 6 heights ranging from 115 to 131 m.

Instead of using the power_profile or constant_profile functions like we did before, we can use the data_profile function. Usage of this function is detailed in the Reference Guide, but it’s just like the other profile functions except we pass in con_tc as a keyword argument.

Here is a “sanity check” comparing the mean wind speed from con_tc to the values returned by data_profile.

[3]:
# get the data profile points for interpolation
wsp_intp = data_profile(spat_df, con_tc=con_tc)
# calculate the values fron con_tc for comparison
yp, zp = np.zeros(6), con_tc.loc['z'].unique()
wspp = con_tc.get_time().filter(regex='u_').mean().values
# plot the interpolated values
plot_interp(yp, zp, wspp, Y, Z, wsp_intp)
../_images/notebooks_6_data_profile_interp_7_0.png

Sig profile interpolated from data

The usage of the data_sig function is similar to the iec_sig function, except that we must pass in con_tc as a keyword argument. Here is a sanity check similar to the one we gave for wind_profile.

[4]:
# define the points we want to query and call data_sig
plt.figure(1, figsize=(15, 4))
for k in range(3):  # loop through components
    kspat_df = spat_df.copy(deep=True)  # copy spat_df
    kspat_df.loc['k'] = k  # assign component
    sig_intp = data_sig(kspat_df, con_tc=con_tc)
    # calculate the values fron con_tc for comparison
    yp, zp = np.zeros(6), con_tc.loc['z'].unique()
    sigp = con_tc.get_time().filter(regex=f'{"uvw"[k]}_').std().values
    # plot the interpolated values
    plt.subplot(1, 3, 1 + k)
    plot_interp(yp, zp, sigp, Y, Z, sig_intp)
    plt.title(f'Component {"uvw"[k]} sig')
../_images/notebooks_6_data_profile_interp_9_0.png

Spectra interpolated from data

Lastly, there is also a function, data_spectrum, to interpolate spectral profiles from con_tc. Here’s an example of how it works.

[5]:
# define the points we want to query and call data_spectrum
f, f_idx = 0.05, 5  # frequency to look at and its index in the freq vector
plt.figure(1, figsize=(14, 10))
for k in range(3):  # loop through components
    kspat_df = spat_df.copy(deep=True)  # copy spat_df
    kspat_df.loc['k'] = k  # assign component
    spc_intp = data_spectrum(f, kspat_df, con_tc=con_tc)
    # calculate the values fron con_tc for comparison
    yp, zp = np.zeros(6), con_tc.loc['z'].unique()
    mags = 2*np.abs(np.fft.rfft(con_tc.get_time().filter(regex=f'{"uvw"[k]}_'), axis=0))**2
    spcp = mags[f_idx, :]
    # plot the interpolated values
    plt.subplot(2, 2, 1 + k)
    plot_interp(yp, zp, spcp, Y, Z, spc_intp, fmt='%.2e')
    plt.title(f'Component {"uvw"[k]} spectrum, f = {f} Hz')
../_images/notebooks_6_data_profile_interp_11_0.png

We can see that the variation with height of the interpolated spectrum matches, as desired. The scaling between the original and interpolated spectra is off, but this is because the spectra should be scaled according to the standard deviation, which we do not do in this example for brevity.