-
Antariksh Dicholkar authoredAntariksh Dicholkar authored
envelope.py 9.64 KiB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Feb 13 12:58:25 2018
@author: dave
"""
from __future__ import print_function
from __future__ import division
from __future__ import unicode_literals
from __future__ import absolute_import
from builtins import dict
from io import open
from builtins import zip
from builtins import range
from builtins import str
from builtins import int
from future import standard_library
standard_library.install_aliases()
from builtins import object
import numpy as np
import scipy
def compute_env_of_env(envelope, dlc_list, Nx=300, Nsectors=12, Ntheta=181):
"""
The function computes load envelopes for given channels and a groups of
load cases starting from the envelopes computed for single simulations.
The output is the envelope of the envelopes of the single simulations.
This total envelope is projected on defined polar directions.
Parameters
----------
envelope : dict, dictionaries of interpolated envelopes of a given
channel (it's important that each entry of the dictonary
contains a matrix of the same dimensions). The dictonary
is organized by load case
dlc_list : list, list of load cases
Nx : int, default=300
Number of points for the envelope interpolation
Nsectors: int, default=12
Number of sectors in which the total envelope will be divided. The
default is every 30deg
Ntheta; int, default=181
Number of angles in which the envelope is interpolated in polar
coordinates.
Returns
-------
envelope : array (Nsectors x 6),
Total envelope projected on the number of angles defined in Nsectors.
The envelope is projected in Mx and My and the other cross-sectional
moments and forces are fetched accordingly (at the same time step where
the corresponding Mx and My are occuring)
"""
# Group all the single DLCs
cloud = np.zeros(((Nx+1)*len(envelope),6))
for i in range(len(envelope)):
cloud[(Nx+1)*i:(Nx+1)*(i+1),:] = envelope[dlc_list[i]]
# Compute total Hull of all the envelopes
hull = scipy.spatial.ConvexHull(cloud[:,:2])
cc = np.append(cloud[hull.vertices,:2],
cloud[hull.vertices[0],:2].reshape(1,2),axis=0)
# Interpolate full envelope
cc_x,cc_up,cc_low,cc_int= int_envelope(cc[:,0], cc[:,1], Nx=Nx)
# Project full envelope on given direction
cc_proj = proj_envelope(cc_x, cc_up, cc_low, cc_int, Nx, Nsectors, Ntheta)
env_proj = np.zeros([len(cc_proj),6])
env_proj[:,:2] = cc_proj
# Based on Mx and My, gather the remaining cross-sectional forces and
# moments
for ich in range(2, 6):
s0 = np.array(cloud[hull.vertices, ich]).reshape(-1, 1)
s1 = np.array(cloud[hull.vertices[0], ich]).reshape(-1, 1)
s0 = np.append(s0, s1, axis=0)
cc = np.append(cc, s0, axis=1)
_,_,_,extra_sensor = int_envelope(cc[:,0],cc[:,ich],Nx)
es = np.atleast_2d(np.array(extra_sensor[:,1])).T
cc_int = np.append(cc_int,es,axis=1)
for isec in range(Nsectors):
ids = (np.abs(cc_int[:,0]-cc_proj[isec,0])).argmin()
env_proj[isec,ich] = (cc_int[ids-1,ich]+cc_int[ids,ich]+\
cc_int[ids+1,ich])/3
return env_proj
def int_envelope(ch1, ch2, Nx):
"""
Function to interpolate envelopes and output arrays of same length
Number of points is defined by Nx + 1, where the + 1 is needed to
close the curve
"""
upper = []
lower = []
indmax = np.argmax(ch1)
indmin = np.argmin(ch1)
if indmax > indmin:
lower = np.array([ch1[indmin:indmax+1],ch2[indmin:indmax+1]]).T
upper = np.concatenate((np.array([ch1[indmax:],ch2[indmax:]]).T,
np.array([ch1[:indmin+1],ch2[:indmin+1]]).T),
axis=0)
else:
upper = np.array([ch1[indmax:indmin+1],ch2[indmax:indmin+1]]).T
lower = np.concatenate((np.array([ch1[indmin:],ch2[indmin:]]).T,
np.array([ch1[:indmax+1],ch2[:indmax+1]]).T),
axis=0)
int_1 = np.linspace(min(upper[:,0].min(),lower[:,0].min()),
max(upper[:,0].max(),lower[:,0].max()),Nx/2+1)
upper = np.flipud(upper)
int_2_up = np.interp(int_1,np.array(upper[:,0]),np.array(upper[:,1]))
int_2_low = np.interp(int_1,np.array(lower[:,0]),np.array(lower[:,1]))
int_env = np.concatenate((np.array([int_1[:-1],int_2_up[:-1]]).T,
np.array([int_1[::-1],int_2_low[::-1]]).T),
axis=0)
return int_1, int_2_up, int_2_low, int_env
def proj_envelope(env_x, env_up, env_low, env, Nx, Nsectors, Ntheta):
"""
Function to project envelope on given angles
Angles of projection is defined by Nsectors
Projections are obtained in polar coordinates and outputted in
cartesian
"""
theta_int = np.linspace(-np.pi,np.pi,Ntheta)
sectors = np.linspace(-np.pi,np.pi,Nsectors+1)
proj = np.zeros([Nsectors,2])
R_up = np.sqrt(env_x**2+env_up**2)
theta_up = np.arctan2(env_up,env_x)
R_low = np.sqrt(env_x**2+env_low**2)
theta_low = np.arctan2(env_low,env_x)
R = np.concatenate((R_up,R_low))
theta = np.concatenate((theta_up,theta_low))
R = R[np.argsort(theta)]
theta = np.sort(theta)
R_int = np.interp(theta_int,theta,R,period=2*np.pi)
for i in range(Nsectors):
if sectors[i]>=-np.pi and sectors[i+1]<-np.pi/2:
indices = np.where(np.logical_and(theta_int >= sectors[i],
theta_int <= sectors[i+1]))
maxR = R_int[indices].max()
proj[i+1,0] = maxR*np.cos(sectors[i+1])
proj[i+1,1] = maxR*np.sin(sectors[i+1])
elif sectors[i]==-np.pi/2:
continue
elif sectors[i]>-np.pi/2 and sectors[i+1]<=0:
indices = np.where(np.logical_and(theta_int >= sectors[i],
theta_int <= sectors[i+1]))
maxR = R_int[indices].max()
proj[i,0] = maxR*np.cos(sectors[i])
proj[i,1] = maxR*np.sin(sectors[i])
elif sectors[i]>=0 and sectors[i+1]<np.pi/2:
indices = np.where(np.logical_and(theta_int >= sectors[i],
theta_int <= sectors[i+1]))
maxR = R_int[indices].max()
proj[i+1,0] = maxR*np.cos(sectors[i+1])
proj[i+1,1] = maxR*np.sin(sectors[i+1])
elif sectors[i]==np.pi/2:
continue
elif sectors[i]>np.pi/2 and sectors[i+1]<=np.pi:
indices = np.where(np.logical_and(theta_int >= sectors[i],
theta_int <= sectors[i+1]))
maxR = R_int[indices].max()
proj[i,0] = maxR*np.cos(sectors[i])
proj[i,1] = maxR*np.sin(sectors[i])
ind = np.where(sectors==0)
proj[ind,0] = env[:,0].max()
ind = np.where(sectors==np.pi/2)
proj[ind,1] = env[:,1].max()
ind = np.where(sectors==-np.pi)
proj[ind,0] = env[:,0].min()
ind = np.where(sectors==-np.pi/2)
proj[ind,1] = env[:,1].min()
return proj
def closed_contour(cloud):
"""Return indices of the vertices of the closed convex contour that
contain all points of the (x,y) cloud of pairs.
Parameters
----------
cloud : ndarray of floats, shape (npoints, ndim)
Coordinates of points to construct a convex hull from. Ndim should be
at least 2 or higher.
cloud_extra : np.ndarray(npoints, nr_extra_channels)
Returns
-------
ivertices : ndarray(nvertices)
vertices : ndarray(nvertices, 2+nr_extra_channels)
"""
if not cloud.shape[1] >= 2:
raise IndexError('Cloud dimension should be 2 or greater')
hull = scipy.spatial.ConvexHull(cloud[:,0:2])
# indices to the vertices containing the cloud of the first 2 dimensions
ivertices = np.ndarray((len(hull.vertices)+1,), dtype=np.int32)
ivertices[0:-1] = hull.vertices
ivertices[-1] = hull.vertices[0]
# actual vertices of all dimensions in the cloud, based on the first two
# dimensions
vertices = np.ndarray((len(ivertices), cloud.shape[1]))
vertices[0:-1,:] = cloud[ivertices[0:-1],:]
vertices[-1,:] = cloud[ivertices[-1],:]
return vertices, ivertices
def compute_envelope(cloud, int_env=False, Nx=300):
"""
The function computes load envelopes for given signals and a single
load case. Starting from Mx and My moments, the other cross-sectional
forces are identified.
Parameters
----------
x, y : np.ndarray(n)
2 components of the same time signal, e.g x and y.
int_env : boolean, default=False
If the logic parameter is True, the function will interpolate the
envelope on a given number of points
Nx : int, default=300
Number of points for the envelope interpolation
Returns
-------
envelope : dictionary,
The dictionary has entries refered to the channels selected.
Inside the dictonary under each entry there is a matrix with 6
columns, each for the sectional forces and moments
"""
vertices, ivertices = closed_contour(cloud)
# interpolate to a fixed location of equally spaced vertices
if int_env:
vert_int = np.ndarray((Nx+1, cloud.shape[1]))
_,_,_,vert_int[:,0:2] = int_envelope(vertices[:,0], vertices[:,1], Nx)
for i in range(2, cloud.shape[1]):
_,_,_,extra = int_envelope(vertices[:,0], vertices[:,i], Nx)
vert_int[:,i] = extra[:,1]
vertices = vert_int
return vertices