UDF to compute forces and moments (torques) on surface zones and print results to file
Currently, Fluent cannot print force/moment data to file, only to screen. SYNOPSIS ========= * UDF and SCHEME interface have been added to compute surface forces/moments and print results to file and/or to the GUI. * The UDF is a DEFINE_ON_DEMAND type, so that it is always executed at the end of an iteration. * This UDF has been tested in SERIAL AND PARALLEL and matches FLUENT'S output exactly. CONTENTS ========== Instructions, 1 UDF and 1 Scheme file... sample case/data files available from author INSTRUCTIONS ============== 1. Compile UDF 2. Load case/data 3. Read scheme file fm.scm This adds a component under Solve->Monitors->Force and Moment Data.... 4. Load the UDF 5. Set up case. 6. Under Solve->Monitors->Force and Moment Data.... Pick Print/ Write to send output of Force and Moment computation to GUI and/or files (force.out and moment.out), respectively. Data files are always appended! NOTE: Computation is done upon execution of the EXECUTE_ON_DEMAND function "force_and_moment" Pick surfaces (pick walls only!) Enter coordinates for moment center Click OK 6. Iterate 7. Click EXECUTE_ON_DEMAND... (force_and_moment) to see tresults (all quantities in SI units). OUTPUT FORMAT +++++++++++++++ Forces: Time Fp(x) Fp(y) Fp(z) Fv(x) Fv(y) Fv(z) Ft(x) Ft(y) Ft(z) F = Force p = pressure v = viscous t = total (x) = x-component (y) = y-component (z) = z-component This data is sent to "force.out" if the Check Box "Write to File" is checked in the GUI panel Moments: Same as for forces.... Quantities are in N-m. Output sent to file "moment.out". NOTE: output file have been hardwired into UDF. This is easily modified. /*************************/ /* UDF SOURCE CODE fm.c */ /*************************/ /* FLUENT UDF to calculate forces and moments about specified point */ /* Uses scheme file: fm.scm */ /* Last tested on FLUENT v6.0.20 */ /* AUTHOR: William Wangard, Ph.D. */ /* Last Modified: January 23, 2003 */ #include "udf.h" void fm_cross(real a[], real b[], real c[]); #define FORCE_OUTPUT_FILE "force.out" #define MOMENT_OUTPUT_FILE "moment.out" #define MAX_THREADS 200 #define pi 3.1415926 DEFINE_ON_DEMAND(force_and_moment) /* use define function hooks panel in fluent */ { int i, n, n_threads, thread_n_id, thread_id[MAX_THREADS]; cxboolean fm_write_p, fm_print_p; Domain *domain = Get_Domain(1); Thread *thread = NULL; FILE *force_output_file; FILE *moment_output_file; real r[ND_3], origin[ND_3], xc[ND_3]; real Amag, Atot=0., A_thread_part=0.0; real Force[ND_3], Force_viscous[ND_3], Force_pressure[ND_3], Force_viscous_tot[ND_3], Force_pressure_tot[ND_3], Force_tot[ND_3], A[ND_3]; real Moment[ND_3], Moment_viscous[ND_3], Moment_pressure[ND_3], Moment_tot[ND_3], Moment_viscous_tot[ND_3], Moment_pressure_tot[ND_3]; face_t f; /* get true or false value from solver - panel inputs for print and write */ #if !RP_NODE fm_write_p = RP_Get_Boolean("fm/write?"); fm_print_p = RP_Get_Boolean("fm/print?"); #endif host_to_node_boolean_1(fm_write_p); host_to_node_boolean_1(fm_print_p); /* get number of surfaces highlighted in panel */ #if !RP_NODE n_threads= RP_Get_List_Length("fm/surfaces"); for(n=0; n<n_threads; n++) { thread_id[n] = RP_Get_List_Ref_Int("fm/surfaces",n); } #endif host_to_node_int_1(n_threads); for(n=0; n<n_threads; n++) { host_to_node_int_1(thread_id[n]); } if (!(force_output_file = fopen(FORCE_OUTPUT_FILE,"a"))) Error("unable to open file"); if (!(moment_output_file = fopen(MOMENT_OUTPUT_FILE,"a"))) Error("unable to open file"); /* Initialize forces and moments */ N3V_S(Force,=,0.0); N3V_S(Force_tot,=,0.0); N3V_S(Force_viscous,=,0.0); N3V_S(Force_pressure,=,0.0); N3V_S(Force_viscous_tot,=,0.0); N3V_S(Force_pressure_tot,=,0.0); N3V_S(Moment,=,0.0); N3V_S(Moment_tot,=,0.0); N3V_S(Moment_viscous,=,0.0); N3V_S(Moment_pressure,=,0.0); N3V_S(Moment_viscous_tot,=,0.0); N3V_S(Moment_pressure_tot,=,0.0); N3V_S(origin,=,0.0); N3V_S(xc,=,0.0); N3V_S(r,=,0.0); N3V_S(A,=,0.0); /* get real value from solver - panel inputs for moment center */ #if !RP_NODE origin[0] = RP_Get_Real("moment/centerx"); origin[1] = RP_Get_Real("moment/centery"); origin[2] = RP_Get_Real("moment/centerz"); #endif for(i=0; i<3; i++) { host_to_node_real_1(origin[i]); } Message0("n ...Computing moments around (%10.3e, %10.3e, %10.3e)n", origin[0], origin[1], origin[2]); #if RP_NODE || !PARALLEL for (n=0; n<n_threads;++n) { /* get id's of threads highlighted in panel */ thread_n_id = thread_id[n]; /* get data for wall n */ thread = Lookup_Thread(domain, thread_n_id); A_thread_part = 0.0; begin_f_loop_int(f,thread) { F_AREA(A,f,thread); Amag = N3V_MAG(A); A_thread_part += Amag; } end_f_loop_int(f,thread); /* Message(" partition %i, thread %i, Area = %10.3en", myid, thread_n_id, A_thread_part); */ Atot += A_thread_part; } Atot = PRF_GRSUM1(Atot); if(rp_axi) Atot = 2*pi*PRF_GRSUM1(Atot); Message0(" ...Total area of selected threads is %10.3e (m^2)n", Atot); /* DO MOMENTS AND FORCES */ /* calculate Mx, My and Mz for each of the selected surfaces (fm/surfaces) */ if (fm_write_p || fm_print_p) { for (n=0; n<n_threads;++n) { /* get id's of threads highlighted in panel */ thread_n_id = thread_id[n]; /* get data for wall n */ thread = Lookup_Thread(domain, thread_n_id); begin_f_loop_int(f,thread) { F_AREA(A,f,thread); F_CENTROID(xc,f,thread); N3V_VV(r,=,xc,-,origin); N3V_S(Moment_viscous,=,0.0); N3V_S(Moment_pressure,=,0.0); /* Get wall shear force and pressure force on face */ N3V_VS(Force_viscous,=,F_STORAGE_R_N3V(f,thread,SV_WALL_SHEAR),*,-1.0); N3V_VS(Force_pressure, =, A, *, F_P(f,thread)); if (rp_axi) { Force_pressure[0] = 2*pi*Force_pressure[0]; Force_viscous[0] = 2*pi*Force_viscous[0]; Force_pressure[1] = 0.0; Force_viscous[1] = 0.0; } /* Compute viscous and pressure moments */ if (rp_axi) { Moment_viscous[2] = origin[1]*Force_viscous[0]; Moment_pressure[2] = origin[1]*Force_pressure[0]; } else { fm_cross(Moment_viscous,r,Force_viscous); fm_cross(Moment_pressure,r,Force_pressure); } /* Add forces */ N3V_VV(Force, =, Force_viscous, +, Force_pressure); N3V_VV(Moment,=, Moment_viscous, +, Moment_pressure); /* Accumulate viscous/pressure forces */ N3V_V(Force_viscous_tot, +=, Force_viscous); N3V_V(Force_pressure_tot, +=, Force_pressure); N3V_V(Force_tot, +=, Force); N3V_V(Moment_viscous_tot, +=, Moment_viscous); N3V_V(Moment_pressure_tot, +=, Moment_pressure); N3V_V(Moment_tot, +=, Moment); } end_f_loop_int(f,thread); } /* Accumulate terms over all COMPUTE Nodes */ /* This function also passes global summation value to each compute node */ for(i=0;i<3;i++) { Force_viscous_tot[i] = PRF_GRSUM1(Force_viscous_tot[i]); Force_pressure_tot[i] = PRF_GRSUM1(Force_pressure_tot[i]); Force_tot[i] = PRF_GRSUM1(Force_tot[i]); Moment_viscous_tot[i] = PRF_GRSUM1(Moment_viscous_tot[i]); Moment_pressure_tot[i] = PRF_GRSUM1(Moment_pressure_tot[i]); Moment_tot[i] = PRF_GRSUM1(Moment_tot[i]); } } /* PRINT RESULTS to file ? */ if (fm_print_p) { Message0(" time Fp(x) Fp(y) Fp(z) Fv(x) Fv(y) Fv(z) Ft(x) Ft(y) Ft(z)n"); Message0("%10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3en", RP_Get_Real("flow-time"), Force_pressure_tot[0], Force_pressure_tot[1], Force_pressure_tot[2], Force_viscous_tot[0], Force_viscous_tot[1], Force_viscous_tot[2], Force_tot[0], Force_tot[1], Force_tot[2]); Message0(" time Mp(x) Mp(y) Mp(z) Mv(x) Mv(y) Mv(z) Mt(x) Mt(y) Mt(z)n"); Message0("%10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3en", RP_Get_Real("flow-time"), Moment_pressure_tot[0], Moment_pressure_tot[1], Moment_pressure_tot[2], Moment_viscous_tot[0], Moment_viscous_tot[1], Moment_viscous_tot[2], Moment_tot[0], Moment_tot[1], Moment_tot[2]); } /* Write data to file ? */ if (fm_write_p) { /* Equivalent to Message0 for file output */ #if RP_NODE if (I_AM_NODE_ZERO_P) #endif { fprintf(force_output_file,"%10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3en", RP_Get_Real("flow-time"), Force_pressure_tot[0], Force_pressure_tot[1], Force_pressure_tot[2], Force_viscous_tot[0], Force_viscous_tot[1], Force_viscous_tot[2], Force_tot[0], Force_tot[1], Force_tot[2]); fprintf(moment_output_file,"%10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3e %10.3en", RP_Get_Real("flow-time"), Moment_pressure_tot[0], Moment_pressure_tot[1], Moment_pressure_tot[2], Moment_viscous_tot[0], Moment_viscous_tot[1], Moment_viscous_tot[2], Moment_tot[0], Moment_tot[1], Moment_tot[2]); } } #endif /* RP_NODE || !PARALLEL */ fclose(force_output_file); fclose(moment_output_file); } void fm_cross(real a[], real b[], real c[]) { /* Returns a = b x c for 3-vectors ONLY */ a[0] = b[1]*c[2] - b[2]*c[1]; a[1] = b[2]*c[0] - b[0]*c[2]; a[2] = b[0]*c[1] - b[1]*c[0]; return; } /********** END OF UDF *****************/ ;;;;;;;;;;;;;;;;;;;;;;;; ; SCHEME FILE fm.scm ;;;;;;;;;;;;;;;;;;;;;;; ;; Panel for moment output data ;; works with udf fm.c ;; (define (make-new-rpvar name default type) (if (not (rp-var-object name)) (rp-var-define name default type #f))) ;; ;; Create rpvars for udf, assign initial (ie: default) values ;; types: real, boolean, integer, thread-list ;; () is empty list, #f is false (off) ;; (make-new-rpvar 'fm/print? #f 'boolean) (make-new-rpvar 'fm/write? #f 'boolean) (make-new-rpvar 'fm/surfaces '() 'thread-list) (make-new-rpvar 'moment/centerx 0 'real) (make-new-rpvar 'moment/centery 0 'real) (make-new-rpvar 'moment/centerz 0 'real) ;; ;; Create a panel for the udf ;; (define gui-define-output-data-panel ;; "panel" is a variable name ;; panel #f - no panel made yet (let ((panel #f) ;; declare variables - assign values later (fm/box) (fm/print?) (fm/write?) (fm/surfaces) (moment/centerx) (moment/centery) (moment/centerz) ) ;;; ;;; Function which is called when panel is opened ;;; sets panel variables to values in make-new-rpvar section ;;; default: all options off, no surfaces selected ;;; (define (update-cb . args) ;update panel fields (cx-set-toggle-button fm/print? (rpgetvar 'fm/print?)) (cx-set-toggle-button fm/write? (rpgetvar 'fm/write?)) (cx-set-real-entry moment/centerx (rpgetvar 'moment/centerx)) (cx-set-real-entry moment/centery (rpgetvar 'moment/centery)) (cx-set-real-entry moment/centerz (rpgetvar 'moment/centerz)) ;; get list of surfaces (face threads) (cx-set-list-items fm/surfaces (map thread-name (sort-threads-by-name (get-face-threads)))) ) ;;; ;;; Function which is called when OK button is hit. ;;; assigns variable values that were set in panel after clicking "ok" ;;; (define (apply-cb . args) (rpsetvar 'fm/print? (cx-show-toggle-button fm/print?)) (rpsetvar 'fm/write? (cx-show-toggle-button fm/write?)) (rpsetvar 'moment/centerx (cx-show-real-entry moment/centerx)) (rpsetvar 'moment/centery (cx-show-real-entry moment/centery)) (rpsetvar 'moment/centerz (cx-show-real-entry moment/centerz)) (rpsetvar 'fm/surfaces (map thread-name->id (cx-show-symbol-list-selections fm/surfaces))) ) (lambda args ;;; if panel does not exist, make panel (if (not panel) (let ((table)) (set! panel (cx-create-panel "Force and Moment Settings" apply-cb update-cb)) ;; create table w/o title in panel to enable row by column format (set! table (cx-create-table panel "" 'border #f 'below 0 'right-of 0)) ;;; ;;; Create fm inputs. ;;; (set! fm/box (cx-create-table table "Force and Moment Options" 'row 0)) (set! fm/print? (cx-create-toggle-button fm/box "Print Results" 'col 1 'row 1)) (set! fm/write? (cx-create-toggle-button fm/box "Write Results to file" 'col 1 'row 2)) (set! fm/surfaces (cx-create-symbol-list fm/box "Boundary Zones" 'visible-lines 10 'multiple-selections #t 'row 3 'col 1)) (cx-set-list-items fm/surfaces (map thread-name (sort-threads-by-name (get-face-threads)))) (set! moment/centerx (cx-create-real-entry fm/box "x-moment-center" 'row 4 'col 1)) (set! moment/centery (cx-create-real-entry fm/box "y-moment-center" 'row 4 'col 2)) (set! moment/centerz (cx-create-real-entry fm/box "z-moment-center" 'row 4 'col 3)) )) ;; tell solver to display the panel (cx-show-panel panel)))) ;;; ;;; Add new panel to end of User-Defined menu. ;;; no need to modify here except panel name and menu location ;;; (cx-add-item "Monitors" "Force and Moment Data..." #U #f cx-client? gui-define-output-data-panel) ;;;;; END OF SCHEME FILE |
||
![]()
|