In the previous chapters we simulated some simple fluid cases and typical FSI problem. Now we may have a look at the heat transfer problem, in which heat is being transferred between solid walls and flowing fluid.
Example 6: Heat transferΒΆ
As shown in the figure, there is flowing fluid between an upper wall and a lower wall. The initial temperature, which is phi value in this case, of fluid and the upper wall is 20, while the lower wall is 40. In this case we can see how heat transfer occurs from the hot lower wall to the fluid.
We give the geometry and material parameters for modeling the walls and the fluid, including the length and height of the fluid block and wall, particle reference resolution, diffusion coefficient, etc.
/**
* @brief Define the geometry parameter.
*/
Real DL = 2.0; /**< Channel length. */
Real DH = 0.4; /**< Channel height. */
Real resolution_ref = DH / 25.0; /**< Global reference reoslution. */
Real DL_sponge = resolution_ref * 20.0; /**< Sponge region to impose inflow condition. */
/**< Boundary width, determined by specific layer of boundary particles. */
Real BW = resolution_ref * 4.0; /** Domain bounds of the system. */
BoundingBox system_domain_bounds(Vec2d(-DL_sponge - BW, -BW), Vec2d(DL + BW, DH + BW));
/** Material properties. */
Real diffusion_coff = 1.0e-3;
Real bias_diffusion_coff = 0.0;
Real alpha = Pi / 6.0;
Vec2d bias_direction(cos(alpha), sin(alpha));
/**
*@brief Material properties of the fluid.
*/
Real rho0_f = 1.0; /**< Density. */
Real U_f =1.0; /**< Characteristic velocity. */
Real c_f = 10.0 * U_f; /**< Speed of sound. */
Real Re = 100.0; /**< Reynolds number100. */
Real mu_f = rho0_f * U_f * DH / Re; /**< Dynamics viscosity. */
/**
*@brief Temperatures.
*/
Real phi_upper_wall = 20.0;
Real phi_lower_wall = 40.0;
Real phi_fluid_initial = 20.0;
and define solid wall bodies as well as fluid body with following code piece.
/** create a water block shape */
std::vector<Vecd> CreatShape()
{
//geometry
std::vector<Vecd> shape;
shape.push_back(Vecd(0.0 - DL_sponge, 0.0));
shape.push_back(Vecd(0.0 - DL_sponge, DH));
shape.push_back(Vecd(DL, DH));
shape.push_back(Vecd(DL, 0.0));
shape.push_back(Vecd(0.0 - DL_sponge, 0.0));
return shape;
}
/** create outer wall shape */
std::vector<Vecd> CreatOuterWallShape()
{
std::vector<Vecd> outer_wall_shape;
outer_wall_shape.push_back(Vecd(-DL_sponge - BW, -BW));
outer_wall_shape.push_back(Vecd(-DL_sponge - BW, DH + BW));
outer_wall_shape.push_back(Vecd(DL + BW, DH + BW));
outer_wall_shape.push_back(Vecd(DL + BW, -BW));
outer_wall_shape.push_back(Vecd(-DL_sponge - BW, -BW));
return outer_wall_shape;
}
/** create inner wall shape */
std::vector<Vecd> CreatInnerWallShape()
{
std::vector<Vecd> inner_wall_shape;
inner_wall_shape.push_back(Vecd(-DL_sponge - 2.0 * BW, 0.0));
inner_wall_shape.push_back(Vecd(-DL_sponge - 2.0 * BW, DH));
inner_wall_shape.push_back(Vecd(DL + 2.0 * BW, DH));
inner_wall_shape.push_back(Vecd(DL + 2.0 * BW, 0.0));
inner_wall_shape.push_back(Vecd(-DL_sponge - 2.0 * BW, 0.0));
return inner_wall_shape;
}
After the typical points for the geometry of different parts are given, we define bodies based on shape creating.
The ThermofluidBody
is inherited from FluidBody
, while ThermosolidBody
is inherited from SolidBody
.
/** Thermofluid body definition */
class ThermofluidBody : public FluidBody
{
public:
ThermofluidBody(SPHSystem &system, std::string body_name)
: FluidBody(system, body_name)
{
std::vector<Vecd> body_shape = CreatShape();
body_shape_ = new ComplexShape(body_name);
body_shape_->addAPolygon(body_shape, ShapeBooleanOps::add);
}
};
/** Thermosolid body definition */
class ThermosolidBody : public SolidBody
{
public:
ThermosolidBody(SPHSystem &system, std::string body_name)
: SolidBody(system, body_name)
{
std::vector<Vecd> outer_wall_shape = CreatOuterWallShape();
std::vector<Vecd> inner_wall_shape = CreatInnerWallShape();
body_shape_ = new ComplexShape(body_name);
body_shape_->addAPolygon(outer_wall_shape, ShapeBooleanOps::add);
body_shape_->addAPolygon(inner_wall_shape, ShapeBooleanOps::sub);
}
};
Initially, all particles have been set to at rest. we also setup material properties and initial condition for different bodies. The thermal relaxation between different bodies is also set. Here, we must insert a specie Phi resperenting heat being transferred between these two bodies.
/**
* Setup heat conduction material properties for diffusion fluid body
*/
class ThermofluidBodyMaterial
: public DiffusionReactionMaterial<FluidParticles, WeaklyCompressibleFluid>
{
public:
ThermofluidBodyMaterial()
: DiffusionReactionMaterial<FluidParticles, WeaklyCompressibleFluid>()
{
rho_0_ = rho0_f;
c_0_ = c_f;
mu_ = mu_f;
//add a scalar for temperature in fluid
insertASpecies("Phi");
assignDerivedMaterialParameters();
initializeDiffusion();
}
/** Initialize diffusion reaction material. */
virtual void initializeDiffusion() override {
DirectionalDiffusion* phi_diffusion
= new DirectionalDiffusion(species_indexes_map_["Phi"], species_indexes_map_["Phi"],
diffusion_coff, bias_diffusion_coff, bias_direction);
species_diffusion_.push_back(phi_diffusion);
};
};
/**
* Setup heat conduction material properties for diffusion solid body
*/
class ThermosolidBodyMaterial
: public DiffusionReactionMaterial<SolidParticles, Solid>
{
public:
ThermosolidBodyMaterial()
: DiffusionReactionMaterial<SolidParticles, Solid>()
{
//add a scalar for temperature in solid
insertASpecies("Phi");
assignDerivedMaterialParameters();
initializeDiffusion();
}
/** Initialize diffusion reaction material. */
virtual void initializeDiffusion() override {
DirectionalDiffusion* phi_diffusion
= new DirectionalDiffusion(species_indexes_map_["Phi"], species_indexes_map_["Phi"],
diffusion_coff, bias_diffusion_coff, bias_direction);
species_diffusion_.push_back(phi_diffusion);
};
};
/**
* application dependent solid body initial condition
*/
class ThermosolidBodyInitialCondition
: public DiffusionReactionInitialCondition<SolidBody, SolidParticles, Solid>
{
protected:
size_t phi_;
void Update(size_t index_i, Real dt) override
{
if (-BW <= pos_n_[index_i][1] && pos_n_[index_i][1] <= 0.0)
{
species_n_[phi_][index_i] = phi_lower_wall;
}
if (DH <= pos_n_[index_i][1] && pos_n_[index_i][1] <= DH+BW)
{
species_n_[phi_][index_i] = phi_upper_wall;
}
};
public:
ThermosolidBodyInitialCondition(SolidBody* diffusion_solid_body)
: DiffusionReactionInitialCondition<SolidBody, SolidParticles, Solid>(diffusion_solid_body) {
phi_ = material_->SpeciesIndexMap()["Phi"];
};
};
/**
* application dependent fluid body initial condition
*/
class ThermofluidBodyInitialCondition
: public DiffusionReactionInitialCondition< FluidBody, FluidParticles, WeaklyCompressibleFluid>
{
protected:
size_t phi_;
void Update(size_t index_i, Real dt) override
{
if (0 <= pos_n_[index_i][1] && pos_n_[index_i][1] <= DH)
{
species_n_[phi_][index_i] = phi_fluid_initial;
}
};
public:
ThermofluidBodyInitialCondition(FluidBody* diffusion_fluid_body)
: DiffusionReactionInitialCondition<FluidBody, FluidParticles, WeaklyCompressibleFluid > (diffusion_fluid_body) {
phi_ = material_->SpeciesIndexMap()["Phi"];
};
};
Here is the definition of heat transfer relaxation method. In this case, there are two bodies, so we use the ComplexBodyRelation
.
If there is only one body, InnerBodyRelation
works.
/**
*Set thermal relaxation between different bodies
*/
class ThermalRelaxationComplex
: public RelaxationOfAllDiffusionSpeciesRK2<FluidBody, FluidParticles, WeaklyCompressibleFluid,
RelaxationOfAllDiffussionSpeciesComplex<FluidBody, FluidParticles, WeaklyCompressibleFluid, SolidBody, SolidParticles, Solid>,
ComplexBodyRelation>
{
public:
ThermalRelaxationComplex(ComplexBodyRelation* body_complex_relation)
: RelaxationOfAllDiffusionSpeciesRK2(body_complex_relation) {};
virtual ~ThermalRelaxationComplex() {};
};
In the main function, we need to build up a SPHSystem
, in which the boundings of the whole calculation domain are defined.
Then we create the SPHBody
s of ThermofluidBody
and ThermosolidBody
by following piece of code.
Particles and materials should also be assigned to the bodies here.
/**
* @brief Creating body, materials and particles for a ThermofluidBody .
*/
ThermofluidBody *thermofluid_body = new ThermofluidBody(system, "ThermofluidBody");
ThermofluidBodyMaterial *thermofluid_body_material = new ThermofluidBodyMaterial();
DiffusionReactionParticles<FluidParticles, WeaklyCompressibleFluid>
diffusion_fluid_body_particles(thermofluid_body, thermofluid_body_material);
/**
* @brief Creating body and particles for the ThermosolidBody.
*/
ThermosolidBody *thermosolid_body = new ThermosolidBody(system, "ThermosolidBody");
ThermosolidBodyMaterial *thermosolid_body_material = new ThermosolidBodyMaterial();
DiffusionReactionParticles<SolidParticles, Solid>
diffusion_solid_body_particles(thermosolid_body, thermosolid_body_material);
Then, the topological relation of all bodies is defined by
/** topology */
InnerBodyRelation* fluid_body_inner = new InnerBodyRelation(thermofluid_body);
InnerBodyRelation* solid_body_inner = new InnerBodyRelation(thermosolid_body);
ComplexBodyRelation* fluid_body_complex = new ComplexBodyRelation(fluid_body_inner, {thermosolid_body });
Here, the fluid_body_inner
interacts with thermosolid_body
by introducing the ComplexBodyRelation
.
After creating the bodies, the method related with heat transfer will be defined. First, we setup the initial condition.
/** Case setup */
ThermosolidBodyInitialCondition thermosolid_condition(thermosolid_body);
ThermofluidBodyInitialCondition thermofluid_initial_condition(thermofluid_body);
/** Corrected strong configuration for diffusion solid body. */
solid_dynamics::CorrectConfiguration correct_configuration(solid_body_inner);
Then the main algorithm for fluid flowing and thermal transfer is defined, including the general methods: time stepping based on fluid dynamics and diffusion, fluid dynamics, and the methods for thermal relaxtion as well as boundary conditions.
/**
* @brief Algorithms of Elastic dynamics.
*/
/** Evaluation of density by summation approach. */
fluid_dynamics::DensitySummationComplex update_density_by_summation(fluid_body_complex);
/** Time step size without considering sound wave speed. */
fluid_dynamics::AdvectionTimeStepSize get_fluid_advection_time_step(thermofluid_body, U_f);
/** Time step size with considering sound wave speed. */
fluid_dynamics::AcousticTimeStepSize get_fluid_time_step(thermofluid_body);
/** Time step size calculation. */
GetDiffusionTimeStepSize<FluidBody, FluidParticles, WeaklyCompressibleFluid> get_thermal_time_step(thermofluid_body);
/** Diffusion process between two diffusion bodies. */
ThermalRelaxationComplex thermal_relaxation_complex(fluid_body_complex);
/** Pressure relaxation using verlet time stepping. */
/** Here, we do not use Riemann solver for pressure as the flow is viscous. */
fluid_dynamics::PressureRelaxationWithWall pressure_relaxation(fluid_body_complex);
fluid_dynamics::DensityRelaxationRiemannWithWall density_relaxation(fluid_body_complex);
/** Computing viscous acceleration. */
fluid_dynamics::ViscousAccelerationWithWall viscous_acceleration(fluid_body_complex);
/** Impose transport velocity. */
fluid_dynamics::TransportVelocityCorrectionComplex transport_velocity_correction(fluid_body_complex);
/** Computing vorticity in the flow. */
fluid_dynamics::VorticityInner compute_vorticity(fluid_body_inner);
The main loops are defined in the following piece of code. parallel_exec()
is the main caculation part in the loop.
Pressure, density and thermal value are updated in pressure_relaxation.parallel_exec(dt)
, density_relaxation.parallel_exec(dt)
, and thermal_relaxation_complex.parallel_exec(dt)
.
Cell linked lists and configurations will be updated in thermofluid_body->updateCellLinkedList()
and fluid_body_complex->updateConfiguration()
.
/**
* @brief Main loop starts here.
*/
while (GlobalStaticVariables::physical_time_ < End_Time)
{
Real integration_time = 0.0;
/** Integrate time (loop) until the next output time. */
while (integration_time < D_Time) {
initialize_a_fluid_step.parallel_exec();
Dt = get_fluid_advection_time_step.parallel_exec();
update_density_by_summation.parallel_exec();
viscous_acceleration.parallel_exec();
transport_velocity_correction.parallel_exec(Dt);
inner_ite_dt = 0;
Real relaxation_time = 0.0;
while (relaxation_time < Dt) {
dt = SMIN(SMIN(dt_thermal, get_fluid_time_step.parallel_exec()), Dt);
pressure_relaxation.parallel_exec(dt);
density_relaxation.parallel_exec(dt);
thermal_relaxation_complex.parallel_exec(dt);
relaxation_time += dt;
integration_time += dt;
GlobalStaticVariables::physical_time_ += dt;
parabolic_inflow.exec();
inner_ite_dt++;
}
if (number_of_iterations % screen_output_interval == 0)
{
std::cout << std::fixed << std::setprecision(9) << "N=" << number_of_iterations << " Time = "
<< GlobalStaticVariables::physical_time_
<< " Dt = " << Dt << " Dt / dt = " << inner_ite_dt << "\n";
}
number_of_iterations++;
/** Water block configuration and periodic condition. */
periodic_condition.bounding_.parallel_exec();
thermofluid_body->updateCellLinkedList();
periodic_condition.update_cell_linked_list_.parallel_exec();
fluid_body_complex->updateConfiguration();
}
tick_count t2 = tick_count::now();
/** write run-time observation into file */
compute_vorticity.parallel_exec();
fluid_observer_contact->updateConfiguration();
write_real_body_states.WriteToFile(GlobalStaticVariables::physical_time_);
write_fluid_phi.WriteToFile(GlobalStaticVariables::physical_time_);
write_fluid_velocity.WriteToFile(GlobalStaticVariables::physical_time_);
tick_count t3 = tick_count::now();
interval += t3 - t2;
}
Note that, we need to find the samller time step size between GetDiffusionTimeStepSize
and AcousticTimeStepSize
to decide data exchanging frequency.
Beside the particle position, velocity, we output the feature Phi
to value the temperature in this case as shown in the figure below.
We should mention that we can add new features to the methods related with the observer for more quantitative information the simulation.