⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cfd.cpp

📁 A C++ class library for scientific computing
💻 CPP
字号:
/* * This is a small 4th-order Computational Fluid Dynamics solver.  It * illustrates multicomponent arrays (i.e. vector fields) and stencil * objects.  It is implemented in "C++Tran" style for simplicity. * * Incompressible flow is assumed. * * The simulation is of water in a closed cube.  The water is initially * quiescent.  At a single layer near the bottom of the cube, a forcing * function simulates the effect of a (highly idealized) propeller. * * Thanks to Stephen Smith (ACL/LANL) for help with the math. *//* * This is a "work in progress".  Things I am unhapppy about: *   - arrays, geometry, and boundary conditions are separate objects. *     This results in a certain amount of kludginess.  For example, *     the stencil operators take geometry as a 2nd parameter. *     On the plus side, one geometry object is shared among many *     arrays. *   - The new stencil objects are nice, but declaring a new stencil *     object for every single equation seems like overkill.  It would *     be nice if stencil operators could be "expression templatized" *     so that these equations could be moved to the appropriate place *     in the code.  This probably wouldn't be too much work. *   - Stencil objects can't take scalar arguments, so all the scalars *     have to be globals.  Big ugh.  There is a fix for this, *     but it hasn't been implemented yet. *   - There are serious bugs, this simulation doesn't work very well. */#define NO_STIRRING#undef NO_GRAVITY#include <blitz/array-old.h>     #include <blitz/array/cgsolve.h>#ifdef BZ_HAVE_STD	#include <fstream>#else	#include <fstream.h>#endifBZ_USING_NAMESPACE(blitz)/* * The current implementation of stencil objects forces these variables * to be placed in global scope.  Ugh.  This restriction will be removed * eventually. */double rho;                        // Density of fluiddouble recip_rho;                  // 1/rhodouble eta;                        // Kinematic viscositydouble time_now;                   // Elapsed secondsdouble delta_t;                    // Time stepdouble volume;                     // Volume of a celldouble airPressure;                // Air pressure (Pa)double spatialStep;                // Grid element sizedouble gravity;                    // Acceleration due to gravitydouble gravityPressureGradient;    // Pressure gradient due to gravity/* * The "geometry" object specifies how an array is mapped into real-world * space.  In this case, "UniformCubicGeometry" is used, which means that * the real-world grid is orthogonal, regularly spaced, with the same spatial * step in each dimension. */UniformCubicGeometry<3> geom;      // Geometry/* * Some typedefs to make life easier. */typedef TinyVector<double,3> vector3d;typedef Array<vector3d,3> vectorField;typedef Array<double,3>   scalarField;/* * Function prototypes */void record(vectorField& V);void snapshot(scalarField&);void snapshot(vectorField&);/*********************************************************************** * * Stencil objects involve these arrays: * * V       Velocity (vector field) * nextV   Velocity field at next time step (vector field) * advect  Advection (vector field) * P       Pressure (scalar field) * force   Force (vector field) * rhs     Right-hand side of the pressure equation (scalar field) * * These stencil operators are used: * * grad3DVec4        4th-order gradient of a vector field (Jacobian matrix) * Laplacian3DVec4   4th-order Laplacian of a vector field * grad3D4           4th-order gradient of a scalar field * div3DVec4         4th-order divergence of a vector field * ***********************************************************************//***********          Advection Calculation Stencil         ************/BZ_DECLARE_STENCIL2(calc_advect, advect, V)    advect = product(grad3DVec4(V,geom), *V);BZ_END_STENCIL/***********          Timestep the velocity field           ************ * This is a 63-point stencil.  For example, Laplacian3DVec4 turns into * a 45-point stencil: each 2nd derivative is a 5-point stencil, and * there are 9 of these derivatives to take the Laplacian of a 3D vector * field. */BZ_DECLARE_STENCIL5(timestep, V, nextV, P, advect, force)    nextV = *V + delta_t * ( recip_rho * (      eta * Laplacian3DVec4(V,geom) - grad3D4(P, geom) + *force) - *advect);BZ_END_STENCIL/***********     Calculate the RHS of the pressure eqn      ************/BZ_DECLARE_STENCIL2(calc_P_rhs, rhs, advect)    rhs = - rho * div3DVec4(advect, geom);BZ_END_STENCIL/***********    Pressure equation update for the solver     ************/BZ_DECLARE_STENCIL2(P_solver_update, result, P)    result += Laplacian3D4(P,geom);BZ_END_STENCIL/* * Allocate arrays and set their initial state */void setup(const int N, vectorField& V, vectorField& nextV, scalarField& P,    scalarField& P_rhs, vectorField& advect, vectorField& force){    // A 1m x 1m x 1m domain    spatialStep = 1.0 / (N - 1);    geom = UniformCubicGeometry<3>(spatialStep);    // Allocate arrays    allocateArrays(shape(N,N,N), advect, V, nextV, force);  // vector fields    allocateArrays(shape(N,N,N), P, P_rhs);                 // scalar fields    // Since incompressibility is assumed, pressure only shows up as    // derivative terms in the equations.  We choose airPressure = 0    // as an arbitrary datum.    airPressure = 0;             // Pa    rho = 1000;                  // density of fluid, kg/m^3    recip_rho = 1.0 / rho;       // inverse of density    eta = 1.0e-6;                // kinematic viscosity of fluid, m^2/s    gravity = 9.81;              // m/s^2    delta_t = 0.001;             // initial time step, in seconds    volume = pow3(spatialStep);  // cubic volume associated with grid point    // Kludge: Set eta high, so that the flow will spread faster.    // This means the cube is filled with molasses, rather than water.    eta *= 1000;       // Initial conditions: quiescent    V = 0.0;     P_rhs = 0.0;    advect = 0.0;    nextV = 0.0;    // Initial pressure condition: gravity causes a linear increase    // in pressure with depth.  Note that tensor::k means the index    // associated with the z axis (they are labelled i, j, k).#ifdef NO_GRAVITY    gravityPressureGradient = 0.0;    P = 0.0;#else    gravityPressureGradient = spatialStep * gravity * rho;#ifdef BZ_HAVE_NAMESPACES    P = airPressure + tensor::k * gravityPressureGradient;#else    P = airPressure + k * gravityPressureGradient;#endif#endif    // Set up the forcing function: gravity plus a stirring force    // at the bottom    double gravity_z = gravity * rho;    const int x = 0, y = 1, z = 2;    force[x] = 0.0;    force[y] = 0.0;#ifdef NO_GRAVITY    force[z] = 0.0;#else    force[z] = gravity_z;    #endif#ifndef NO_STIRRING    // Centre of the stirring    int centrex = int(2 * N / 3.0);    int centrey = int(2 * N / 3.0);    int centrez = int(4 * N / 5.0);    const double stirRadius = 1.0 / 3.0;    vector3d zaxis(0,0,1);    // Loop through the 2D slice where the stirring occurs    for (int i=force.lbound(firstDim); i <= force.ubound(firstDim); ++i)    {      for (int j=force.lbound(secondDim); j <= force.ubound(secondDim); ++j)      {         // Vector from the centre of the stirring to the current         // coordinate         vector3d r((i-centrex) * spatialStep, (j-centrey) * spatialStep, 0.0);         if (norm(r) < stirRadius)         {             // The cross product of the z-axis and the vector3d to this             // coordinate yields the direction of the force.  Multiply             // by gravity to get a reasonable magnitude (max force =             // 5 * gravity)             force(i,j,centrez) += cross(zaxis, r)                  * (5 * gravity_z / stirRadius);         }      }    }#endif // NO_STIRRING}// Calculate a simple check on a vector fieldvoid record(vectorField& V){    // Calculate the magnitude of a field    const int x=0, y=1, z=2;    double magx = sum(pow2(V[x])) / V.numElements();    double magy = sum(pow2(V[y])) / V.numElements();    double magz = sum(pow2(V[z])) / V.numElements();    cout << "norm = [" << magx        << " " << magy << " " << magz << " ]" << endl;}// Boundary conditions for the pressure fieldclass PressureBCs {public:    PressureBCs(double gravityPressureGradient)      : gravityPressureGradient_(gravityPressureGradient)    {  }            void applyBCs(scalarField& P) const    {        // Apply the Neumann boundary condition that grad(P) dot surface = 0        int xN = P.ubound(firstDim);        int yN = P.ubound(secondDim);        int zN = P.ubound(thirdDim);        Range all = Range::all();        // lower x        P(0,all,all) = P(2,all,all);        P(1,all,all) = P(2,all,all);        // upper x        P(xN,all,all) = P(xN-2,all,all);        P(xN-1,all,all) = P(xN-2,all,all);        // lower y        P(all,0,all) = P(all,2,all);        P(all,1,all) = P(all,2,all);        // upper y        P(all,yN,all) = P(all,yN-2,all);        P(all,yN-1,all) = P(all,yN-2,all);        // lower z        P(all,all,0) = P(all,all,2) - 2*gravityPressureGradient_;        P(all,all,1) = P(all,all,2) - gravityPressureGradient_;        // upper z        P(all,all,zN) = P(all,all,zN-2) + 2*gravityPressureGradient_;        P(all,all,zN-1) = P(all,all,zN-2) + gravityPressureGradient_;    }private:    double gravityPressureGradient_;};/* * One iteration: calculate advection, solve the pressure equation, * then time step. */void iterate(vectorField& V, vectorField& nextV, scalarField& P,    scalarField& P_rhs, vectorField& advect, vectorField& force){    // Calculate advection term    applyStencil(calc_advect(), advect, V);    // Solve to find the pressure    applyStencil(calc_P_rhs(), P_rhs, advect);    conjugateGradientSolver(P_solver_update(), P, P_rhs, 1e-8,         PressureBCs(gravityPressureGradient));    // Time step    applyStencil(timestep(), V, nextV, P, advect, force);    cycleArrays(V, nextV);    // Record some information about this time step    cout << "Velocity field: "; record(V);}/* * Adjust the time step according to the CFL stability criterion */void adjustTimeStep(vectorField& V){    // Find maximum velocity magnitude    double maxV = 0.0;    // NEEDS_WORK: Blitz should provide a norm(vectorField) function.    // This is ugly.    for (int i=V.lbound(0); i <= V.ubound(0); ++i)      for (int j=V.lbound(1); j <= V.ubound(1); ++j)        for (int k=V.lbound(2); k <= V.ubound(2); ++k)        {            double normV = norm(V(i,j,k));            if (normV > maxV)                maxV = normV;        }    cout << "Maximum velocity is " << maxV << " m/s" << endl;    maxV += 1e-10;   // Avoid divide-by-zero    // Steve K: need to have spatialStep^2 / diffusion constant    // diffusion constant = eta * recip_rho    delta_t = 0.3 * spatialStep / maxV;    const double maxTimeStep = 0.01;    if (delta_t > maxTimeStep)        delta_t = maxTimeStep;    cout << "Set time step to " << delta_t << " s" << endl;}/* *  Main program loop */int main(){    vectorField V, nextV;        // Velocity fields    scalarField P, P_rhs;        // Pressure fields    vectorField advect;          // Advection field    vectorField force;           // Forcing function    const int N = 50;            // Arrays are NxNxN    setup(N, V, nextV, P, P_rhs, advect, force);    const int nIters = 999;    // Stirring motion will stop at this time    const double forceTurnOff = 25000.0;  // seconds    for (int i=0; i < nIters; ++i)    {        cout << "Iteration " << i << " Time = " << time_now << " s" << endl;        iterate(V, nextV, P, P_rhs, advect, force);        // Update the current time, turn off the force when we pass        // forceTurnOff        double oldtime_now = time_now;        time_now += delta_t;        if ((time_now > forceTurnOff) && (oldtime_now <= forceTurnOff))            force = 0.0;        // Dump some state        snapshot(V);        snapshot(P);        // Adjust the time step for the next iteration        adjustTimeStep(V);    }    return 0;}void snapshot(scalarField& P){    static int snapshotNum = 0;    ++snapshotNum;    char filename[128];    sprintf(filename, "pressure%03d.m", snapshotNum);    ofstream ofs(filename);    int N = P.length(firstDim);    int k = N/2;    ofs << "P" << snapshotNum << " = [ ";    for (int i=0; i < N; ++i)    {        for (int j=0; j < N; ++j)        {            float value = P(k,j,N-i-1);            ofs << value << " ";        }        if (i < N-1)            ofs << ";" << endl;    }    ofs << "];" << endl;}void snapshot(vectorField& P){    static int snapshotNum = 0;    ++snapshotNum;    char filename[128];    sprintf(filename, "velocity%03d.m", snapshotNum);    ofstream ofs(filename);    int N = P.length(firstDim);    int k = N/2;    ofs << "P" << snapshotNum << " = [ ";    for (int i=0; i < N; ++i)    {        for (int j=0; j < N; ++j)        {            float value = norm(P(k,j,N-i-1));            ofs << value << " ";        }        if (i < N-1)            ofs << ";" << endl;    }    ofs << "];" << endl;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -