def eval_seg_comp_loop(DerP, DerA, DerB, ZetaP, ZetaA, ZetaB, gamma_seg): ''' Derivative of induced velocity Q w.r.t. collocation and segment coordinates in format: [ (x,y,z) of Q, (x,y,z) of Zeta ] Warning: function optimised for performance. Variables are scaled during the execution. ''' Cfact = cfact_biot * gamma_seg RA = ZetaP - ZetaA RB = ZetaP - ZetaB RAB = ZetaB - ZetaA Vcr = libalg.cross3d(RA, RB) vcr2 = np.dot(Vcr, Vcr) # numerical radious if vcr2 < (VORTEX_RADIUS_SQ * libalg.normsq3d(RAB)): return ### other constants ra1, rb1 = libalg.norm3d(RA), libalg.norm3d(RB) rainv = 1. / ra1 rbinv = 1. / rb1 Tv = RA * rainv - RB * rbinv dotprod = np.dot(RAB, Tv) ### --------------------------------------------- cross-product derivatives # lower triangular part only vcr2inv = 1. / vcr2 vcr4inv = vcr2inv * vcr2inv diag_fact = Cfact * vcr2inv * dotprod off_fact = -2. * Cfact * vcr4inv * dotprod Dvcross = [[diag_fact + off_fact * Vcr[0]**2], [off_fact * Vcr[0] * Vcr[1], diag_fact + off_fact * Vcr[1]**2], [ off_fact * Vcr[0] * Vcr[2], off_fact * Vcr[1] * Vcr[2], diag_fact + off_fact * Vcr[2]**2 ]] ### ------------------------------------------ difference terms derivatives Vsc = Vcr * vcr2inv * Cfact Ddiff = np.array([RAB * Vsc[0], RAB * Vsc[1], RAB * Vsc[2]]) dQ_dRAB = np.array([Tv * Vsc[0], Tv * Vsc[1], Tv * Vsc[2]]) ### ---------------------------------------------- Final assembly (crucial) # ps: calling Dvcross_by_skew3d does not slow down execution. dQ_dRA=Dvcross_by_skew3d(Dvcross,-RB)\ +np.dot(Ddiff, der_runit(RA,rainv,-rainv**3)) dQ_dRB=Dvcross_by_skew3d(Dvcross, RA)\ -np.dot(Ddiff, der_runit(RB,rbinv,-rbinv**3)) DerP += dQ_dRA + dQ_dRB # w.r.t. P DerA -= dQ_dRAB + dQ_dRA # w.r.t. A DerB += dQ_dRAB - dQ_dRB # w.r.t. B
def biot_segment(zetaP,zetaA,zetaB,gamma=1.0): ''' Induced velocity of segment A_>B of circulation gamma over point P. ''' # differences ra=zetaP-zetaA rb=zetaP-zetaB rab=zetaB-zetaA ra_norm,rb_norm=libalg.norm3d(ra),libalg.norm3d(rb) vcross=libalg.cross3d(ra,rb) vcross_sq=np.dot(vcross,vcross) # numerical radious if vcross_sq<(VORTEX_RADIUS_SQ*libalg.normsq3d(rab)): return np.zeros((3,)) q=((cfact_biot*gamma/vcross_sq)*\ ( np.dot(rab,ra)/ra_norm - np.dot(rab,rb)/rb_norm)) * vcross return q
def biot_panel_fast(zetaC,ZetaPanel,gamma=1.0): ''' Induced velocity over point ZetaC of a panel of vertices coordinates ZetaPanel and circulaiton gamma, where: ZetaPanel.shape=(4,3)=[vertex local number, (x,y,z) component] ''' Cfact=cfact_biot*gamma q=np.zeros((3,)) R_list = zetaC-ZetaPanel Runit_list=[R_list[ii]/libalg.norm3d(R_list[ii]) for ii in svec] for aa,bb in LoopPanel: RAB=ZetaPanel[bb,:]-ZetaPanel[aa,:] # segment vector Vcr = libalg.cross3d(R_list[aa],R_list[bb]) vcr2=np.dot(Vcr,Vcr) if vcr2<(VORTEX_RADIUS_SQ*libalg.normsq3d(RAB)): continue q+=( (Cfact/vcr2)*np.dot(RAB,Runit_list[aa]-Runit_list[bb]) ) *Vcr return q
def eval_panel_fast_coll(zetaP, ZetaPanel, gamma_pan=1.0): ''' Computes derivatives of induced velocity w.r.t. coordinates of target point, zetaP, coordinates. Returns two elements: - DerP: derivative of induced velocity w.r.t. ZetaP, with: DerP.shape=(3,3) : DerC[ Uind_{x,y,z}, ZetaC_{x,y,z} ] The function is based on eval_panel_fast, but does not perform operations required to compute the derivatives w.r.t. the panel coordinates. ''' DerP = np.zeros((3, 3)) ### ---------------------------------------------- Compute common variables # these are constants or variables depending only on vertices and P coords Cfact = cfact_biot * gamma_pan # distance vertex ii-th from P R_list = zetaP - ZetaPanel r1_list = [libalg.norm3d(R_list[ii]) for ii in svec] r1inv_list = [1. / r1_list[ii] for ii in svec] Runit_list = [R_list[ii] * r1inv_list[ii] for ii in svec] Der_runit_list = [ der_runit(R_list[ii], r1inv_list[ii], -r1inv_list[ii]**3) for ii in svec ] ### ------------------------------------------------- Loop through segments for aa, bb in LoopPanel: RAB = ZetaPanel[bb, :] - ZetaPanel[aa, :] # segment vector Vcr = libalg.cross3d(R_list[aa], R_list[bb]) vcr2 = np.dot(Vcr, Vcr) if vcr2 < (VORTEX_RADIUS_SQ * libalg.normsq3d(RAB)): continue Tv = Runit_list[aa] - Runit_list[bb] dotprod = np.dot(RAB, Tv) ### ----------------------------------------- cross-product derivatives # lower triangular part only vcr2inv = 1. / vcr2 vcr4inv = vcr2inv * vcr2inv diag_fact = Cfact * vcr2inv * dotprod off_fact = -2. * Cfact * vcr4inv * dotprod Dvcross = [[diag_fact + off_fact * Vcr[0]**2], [ off_fact * Vcr[0] * Vcr[1], diag_fact + off_fact * Vcr[1]**2 ], [ off_fact * Vcr[0] * Vcr[2], off_fact * Vcr[1] * Vcr[2], diag_fact + off_fact * Vcr[2]**2 ]] ### ---------------------------------------- difference term derivative Vsc = Vcr * vcr2inv * Cfact Ddiff = np.array([RAB * Vsc[0], RAB * Vsc[1], RAB * Vsc[2]]) ### ------------------------------------------ Final assembly (crucial) # dQ_dRA=Dvcross_by_skew3d(Dvcross,-R_list[bb])\ # +np.dot(Ddiff, Der_runit_list[aa] ) # dQ_dRB=Dvcross_by_skew3d(Dvcross, R_list[aa])\ # -np.dot(Ddiff, Der_runit_list[bb] ) DerP += Dvcross_by_skew3d(Dvcross,RAB)+\ +np.dot(Ddiff, Der_runit_list[aa]-Der_runit_list[bb] ) return DerP
def eval_panel_fast(zetaP, ZetaPanel, gamma_pan=1.0): ''' Computes derivatives of induced velocity w.r.t. coordinates of target point, zetaP, and panel coordinates. Returns two elements: - DerP: derivative of induced velocity w.r.t. ZetaP, with: DerP.shape=(3,3) : DerC[ Uind_{x,y,z}, ZetaC_{x,y,z} ] - DerVertices: derivative of induced velocity wrt panel vertices, with: DerVertices.shape=(4,3,3) : DerVertices[ vertex number {0,1,2,3}, Uind_{x,y,z}, ZetaC_{x,y,z} ] The function is based on eval_panel_comp, but minimises operationsby recycling variables. ''' DerP = np.zeros((3, 3)) DerVertices = np.zeros((4, 3, 3)) ### ---------------------------------------------- Compute common variables # these are constants or variables depending only on vertices and P coords Cfact = cfact_biot * gamma_pan # distance vertex ii-th from P R_list = zetaP - ZetaPanel r1_list = [libalg.norm3d(R_list[ii]) for ii in svec] r1inv_list = [1. / r1_list[ii] for ii in svec] Runit_list = [R_list[ii] * r1inv_list[ii] for ii in svec] Der_runit_list = [ der_runit(R_list[ii], r1inv_list[ii], -r1inv_list[ii]**3) for ii in svec ] ### ------------------------------------------------- Loop through segments for aa, bb in LoopPanel: RAB = ZetaPanel[bb, :] - ZetaPanel[aa, :] # segment vector Vcr = libalg.cross3d(R_list[aa], R_list[bb]) vcr2 = np.dot(Vcr, Vcr) if vcr2 < (VORTEX_RADIUS_SQ * libalg.normsq3d(RAB)): continue Tv = Runit_list[aa] - Runit_list[bb] dotprod = np.dot(RAB, Tv) ### ----------------------------------------- cross-product derivatives # lower triangular part only vcr2inv = 1. / vcr2 vcr4inv = vcr2inv * vcr2inv diag_fact = Cfact * vcr2inv * dotprod off_fact = -2. * Cfact * vcr4inv * dotprod Dvcross = [[diag_fact + off_fact * Vcr[0]**2], [ off_fact * Vcr[0] * Vcr[1], diag_fact + off_fact * Vcr[1]**2 ], [ off_fact * Vcr[0] * Vcr[2], off_fact * Vcr[1] * Vcr[2], diag_fact + off_fact * Vcr[2]**2 ]] ### ---------------------------------------- difference term derivative Vsc = Vcr * vcr2inv * Cfact Ddiff = np.array([RAB * Vsc[0], RAB * Vsc[1], RAB * Vsc[2]]) ### ---------------------------------------------------- RAB derivative dQ_dRAB = np.array([Tv * Vsc[0], Tv * Vsc[1], Tv * Vsc[2]]) ### ------------------------------------------ Final assembly (crucial) dQ_dRA=Dvcross_by_skew3d(Dvcross,-R_list[bb])\ +np.dot(Ddiff, Der_runit_list[aa] ) dQ_dRB=Dvcross_by_skew3d(Dvcross, R_list[aa])\ -np.dot(Ddiff, Der_runit_list[bb] ) DerP += dQ_dRA + dQ_dRB # w.r.t. P DerVertices[aa, :, :] -= dQ_dRAB + dQ_dRA # w.r.t. A DerVertices[bb, :, :] += dQ_dRAB - dQ_dRB # w.r.t. B # ### collocation point only # DerP +=Dvcross_by_skew3d(Dvcross,RA-RB)+np.dot(Ddiff, # der_runit(RA,rainv,minus_rainv3)-der_runit(RB,rbinv,minus_rbinv3)) return DerP, DerVertices