From 52412c990ceaf940d6be40f66c9452da6a713483 Mon Sep 17 00:00:00 2001 From: Christian Pavese <cpav@dtu.dk> Date: Tue, 3 May 2016 09:48:15 +0200 Subject: [PATCH] Adding function to compute envelope of envelopes --- wetb/prepost/Simulations.py | 253 ++++++++++++++++++++++++++++++------ 1 file changed, 216 insertions(+), 37 deletions(-) diff --git a/wetb/prepost/Simulations.py b/wetb/prepost/Simulations.py index 75610151..894fb7d1 100755 --- a/wetb/prepost/Simulations.py +++ b/wetb/prepost/Simulations.py @@ -3294,6 +3294,181 @@ class WeibullParameters(object): self.Vstep = 2. self.shape_k = 2. +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 are fetched accordingly + + """ + + # 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 tital 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) + # Porject 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(min(upper[:,0]),min(lower[:,0])),\ + max(max(upper[:,0]),max(upper[:,0])),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 + # Porjections 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 = max(R_int[indices]) + 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 = max(R_int[indices]) + 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 = max(R_int[indices]) + 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 = max(R_int[indices]) + proj[i,0] = maxR*np.cos(sectors[i]) + proj[i,1] = maxR*np.sin(sectors[i]) + + ind = np.where(sectors==0) + proj[ind,0] = max(env[:,0]) + + ind = np.where(sectors==np.pi/2) + proj[ind,1] = max(env[:,1]) + + ind = np.where(sectors==-np.pi) + proj[ind,0] = min(env[:,0]) + + ind = np.where(sectors==-np.pi/2) + proj[ind,1] = min(env[:,1]) + + + proj[-1,:] = proj[0,:] + + return proj # FIXME: Cases has a memory leek somewhere, this whole thing needs to be # reconsidered and rely on a DataFrame instead of a dict! @@ -4891,6 +5066,34 @@ class Cases(object): return result def compute_envelope(self, sig, ch_list, 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 + ---------- + + sig : list, time-series signal + + ch_list : list, list of channels for enevelope computation + + 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 + + """ envelope= {} for ch in ch_list: @@ -4898,16 +5101,23 @@ class Cases(object): chi1 = self.res.ch_dict[ch[1]]['chi'] s0 = np.array(sig[:, chi0]).reshape(-1, 1) s1 = np.array(sig[:, chi1]).reshape(-1, 1) + # Compute a Convex Hull, the vertices number varies according to + # the shape of the poligon cloud = np.append(s0, s1, axis=1) hull = scipy.spatial.ConvexHull(cloud) closed_contour = np.append(cloud[hull.vertices,:], cloud[hull.vertices[0],:].reshape(1,2), axis=0) + + # Interpolate envelope for a given number of points if int_env: - closed_contour_int = self.int_envelope(closed_contour[:,0],\ - closed_contour[:,1],Nx=Nx) - + _,_,_,closed_contour_int = int_envelope(closed_contour[:,0], + closed_contour[:,1],Nx) + + # Based on Mx and My envelope, the other cross-sectional moments + # and forces components are identified and appended to the initial + # envelope for ich in range(2, len(ch)): chix = self.res.ch_dict[ch[ich]]['chi'] s0 = np.array(sig[hull.vertices, chix]).reshape(-1, 1) @@ -4915,8 +5125,8 @@ class Cases(object): s0 = np.append(s0, s1, axis=0) closed_contour = np.append(closed_contour, s0, axis=1) if int_env: - extra_sensor = self.int_envelope(closed_contour[:,0],\ - closed_contour[:,ich],Nx=Nx) + _,_,_,extra_sensor = int_envelope(closed_contour[:,0], + closed_contour[:,ich],Nx) es = np.atleast_2d(np.array(extra_sensor[:,1])).T closed_contour_int = np.append(closed_contour_int,es,axis=1) @@ -4924,40 +5134,9 @@ class Cases(object): envelope[ch[0]] = closed_contour_int else: envelope[ch[0]] = closed_contour + return envelope - - 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(min(upper[:,0]),min(lower[:,0])),\ - max(max(upper[:,0]),max(upper[:,0])),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_env - def envelope(self, silent=False, ch_list=[], append=''): """ Calculate envelopes and save them in a table. -- GitLab