EXPORT void apply_volume_of_fluid_redistribution( Array3<double> volume_of_fluid, // volume of fluid field Array3<double> level_set_star, // level set field at new time level // after convection and reinitialization // not mass conserving Array3<double> level_set_new, // level set field at new time level // mass conserving int number_primary_cells_i, // number of primary (pressure) cells in x1 direction int number_primary_cells_j, // number of primary (pressure) cells in x2 direction int number_primary_cells_k, // number of primary (pressure) cells in x3 direction double mesh_width_x1, // grid spacing in x1 direction (uniform) double mesh_width_x2, // grid spacing in x2 direction (uniform) double mesh_width_x3, // grid spacing in x3 direction (uniform) double volume_of_fluid_tolerance, // tolerance on the value of the volume // of fluid value double vof_2_level_set_tolerance, // tolerance in the conversion from volume // of fluid value to level-set value double lower_bound_derivatives, // lower bound for the first partial derivatives // to consider it a limiting case of vanishing // partial derivatives int number_vof_2_level_set_iterations, // number of OUTER iterations in the conversion // from volume of fluid to level-set int number_iterations_ridder, // maximum number of iterations allowed in the // nonlinear root finding algorithm double time_step_mass_redistribution, // time step for the mass redistribution double redistribution_vof_tolerance, // threshold value of time-derivative // in volume of fluid redistribution equation int maximum_number_mass_redistribution_iterations, // number of iterations allowed to make // the volume of fluid field valid // these are the sweeps on the vof error double mass_redistribution_diffusion_coefficient // diffusion coefficient for mass redistribution equation ) { Array3<double> volume_of_fluid_star; // volume of fluid field, uncorrected // so with possible vapour cells and // under/overfilled cells Array3<double> volume_of_fluid_correction; // correction to the volume of fluid field // to make it valid Array3<double> invalid_vof_cells; // indication field to show what is wrong // indicator field showing cells that are either // within bounds =0 // underfilled =-1 // overfilled =+1 // vapour cells = 5 int number_cells_vof_out_of_bounds=100; // number of control volumes where the volume of fluid // function is OUTSIDE the interval [0,1] int number_cells_numerical_vapor=100; // number of control volumes where the volume of fluid // function is INSIDE the interval [0,1] // while the cell has 6 neighbours with the same sign: // the cell is NOT an interface cell int number_cells_invalid_volume_of_fluid=200; // sum of number of vapour cells and number of cells // with the volume of fluid outside [0,1]; int index_redistribution_attempt=0; // number of attempts to achieve a // valid volume of fluid field // through the redistribution algorithm double initial_mass; // the mass present in the computational // domain initial value double final_mass; // the mass present in the computational // domain final value double change_in_mass; // the change in mass present in the // domain with respect to the initial mass bool detailed_output=0; // =1, provide detailed output // =0, provide non output /* allocate memory for the volume of fluid correction, the tentative */ /* volume of fluid field, and the indicator field */ volume_of_fluid_correction.create(number_primary_cells_i+2, number_primary_cells_j+2, number_primary_cells_k+2); volume_of_fluid_star.create(number_primary_cells_i+2, number_primary_cells_j+2, number_primary_cells_k+2); invalid_vof_cells.create(number_primary_cells_i+2, number_primary_cells_j+2, number_primary_cells_k+2); /* compute the initial mass in the computational domain */ initial_mass= compute_mass_in_domain(volume_of_fluid, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, mesh_width_x1, mesh_width_x2, mesh_width_x3); std::cerr<<"initial mass before redistribution "<< initial_mass<< " \n"; /* do a number of redistribution attempts to move the error to the interface */ /* this is an iterative process, because the position of the interface is updated */ while(index_redistribution_attempt<5 && number_cells_invalid_volume_of_fluid>0) { /* copy the original volume of fluid field to the star volume of fluid field */ copy_cell_centered_field(volume_of_fluid, volume_of_fluid_star, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k); /* apply clipping to the tentative volume of fluid field */ /* in this way it can be safely converted to a level-set field */ if(index_redistribution_attempt<1) { number_cells_vof_out_of_bounds= apply_volume_of_fluid_clipping(volume_of_fluid_star, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, volume_of_fluid_tolerance); // make_vof_field_valid(level_set_star, volume_of_fluid_star, invalid_vof_cells, // number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, // volume_of_fluid_tolerance, number_cells_vof_out_of_bounds, // number_cells_numerical_vapor, number_cells_invalid_volume_of_fluid); } else { /* this function changes the volume of fluid field in the same way */ /* as apply_volume_of_fluid_clipping, but also reports on the */ /* different error cells: overfilled, underfilled and vapour cells */ make_vof_field_valid(level_set_new, volume_of_fluid_star, invalid_vof_cells, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, volume_of_fluid_tolerance, number_cells_vof_out_of_bounds, number_cells_numerical_vapor, number_cells_invalid_volume_of_fluid); } /* bring the level-set field in accordance with the clipped volume of fluid field */ /* the level-set field can only be computed from the clipped field, because only */ /* when the volume of fluid is in the closed interval [0,1], the function can be */ /* inverted. */ if(index_redistribution_attempt<1) { if(match_level_set_to_volume_of_fluid(level_set_star, volume_of_fluid_star, level_set_new, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, volume_of_fluid_tolerance, lower_bound_derivatives, number_vof_2_level_set_iterations, number_iterations_ridder, vof_2_level_set_tolerance, mesh_width_x1, mesh_width_x2, mesh_width_x3)) { std::cerr<<" match_level_set_to_volume_of_fluid was called from \n" ; std::cerr<<" apply_volume_of_fluid_redistribution line 154 \n"; exit(1); } } else { if(match_level_set_to_volume_of_fluid(level_set_new, volume_of_fluid_star, level_set_new, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, volume_of_fluid_tolerance, lower_bound_derivatives, number_vof_2_level_set_iterations, number_iterations_ridder, vof_2_level_set_tolerance, mesh_width_x1, mesh_width_x2, mesh_width_x3)) { std::cerr<<" match_level_set_to_volume_of_fluid was called from \n" ; std::cerr<<" apply_volume_of_fluid_redistribution line 171 \n"; exit(1); } } /* extend the level-set field to the virtual cells */ field_extrapolate_boundary(level_set_new, number_primary_cells_i, number_primary_cells_j,number_primary_cells_k); /* The interface location is now fixed by level_set_new */ /* The criterium to decide if a cell is a vapour cell, is based on this level-set field. */ /* This is the cause for the need to iterate the algorithm. */ /* The over and undershoots for the original volume of fluid field are computed */ number_cells_invalid_volume_of_fluid= modify_volume_of_fluid_values( level_set_new, volume_of_fluid, volume_of_fluid_correction, invalid_vof_cells, mesh_width_x1, mesh_width_x2, mesh_width_x3, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, volume_of_fluid_tolerance); if(detailed_output) { dump_redistribution_for_debugging(level_set_star, volume_of_fluid, level_set_new, invalid_vof_cells, volume_of_fluid_correction, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, mesh_width_x1, mesh_width_x2, mesh_width_x3, index_redistribution_attempt ); } /* if necessary redistribute the volume of fluid correction */ /* so we end up with a valid field */ if(number_cells_invalid_volume_of_fluid>0) { redistribute_volume_of_fluid_error(level_set_star, level_set_new, volume_of_fluid, volume_of_fluid_correction, invalid_vof_cells, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, mesh_width_x1, mesh_width_x1, mesh_width_x3, time_step_mass_redistribution, volume_of_fluid_tolerance, redistribution_vof_tolerance, maximum_number_mass_redistribution_iterations, mass_redistribution_diffusion_coefficient, index_redistribution_attempt); } /* compute the change in mass that resulted from this redistribution sweep */ change_in_mass= compute_mass_in_domain(volume_of_fluid, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, mesh_width_x1, mesh_width_x2, mesh_width_x3)-initial_mass; std::cerr<<"change in mass after redistribution step "<< index_redistribution_attempt<<" "<< change_in_mass<< " \n"; index_redistribution_attempt++; } /* if there were cells to be corrected in the last iteration there is a */ /* need to make the last level-set field comply with the last volume of */ /* fluid field. */ analyse_validity_vof_field( level_set_new, volume_of_fluid, invalid_vof_cells, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, volume_of_fluid_tolerance, number_cells_vof_out_of_bounds, number_cells_numerical_vapor, number_cells_invalid_volume_of_fluid); std::cerr<<"The analysis shows there are "<< number_cells_invalid_volume_of_fluid<< " invalid cells\n"; if(number_cells_invalid_volume_of_fluid>0) { number_cells_invalid_volume_of_fluid= modify_volume_of_fluid_values(level_set_new, volume_of_fluid, volume_of_fluid_correction, invalid_vof_cells, mesh_width_x1, mesh_width_x2, mesh_width_x3, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, volume_of_fluid_tolerance); if(match_level_set_to_volume_of_fluid(level_set_star, volume_of_fluid, level_set_new, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, volume_of_fluid_tolerance, lower_bound_derivatives, number_vof_2_level_set_iterations, number_iterations_ridder, vof_2_level_set_tolerance, mesh_width_x1, mesh_width_x2, mesh_width_x3)) { std::cerr<<" match_level_set_to_volume_of_fluid was called from \n" ; std::cerr<<" apply_volume_of_fluid_redistribution line 248 \n"; exit(1); } if(number_cells_invalid_volume_of_fluid>0) { std::cerr<< "**************************************************** \n"; std::cerr<<"ERROR \n"; std::cerr<<"Despite the application of the volume of fluid \n"; std::cerr<<"redistribution algorithm, there were still "<< number_cells_invalid_volume_of_fluid<< "\n"; std::cerr<<"cells that have a volume of fluid value \n"; std::cerr<<"outside the physically relevant interval \n"; std::cerr<<"Increase the number of iterations, or \n"; std::cerr<<"check your input. \n"; std::cerr<<"in apply_volume_of_fluid_redistribution line 241. \n"; std::cerr<<"**************************************************** \n"; exit(1); } } final_mass=compute_mass_in_domain( volume_of_fluid, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, mesh_width_x1, mesh_width_x2, mesh_width_x3); std::cerr<<"final mass after redistribution "<< final_mass << " \n"; /* deallocate temporary storage */ volume_of_fluid_star.destroy(); volume_of_fluid_correction.destroy(); invalid_vof_cells.destroy(); }
EXPORT void initialize_volume_of_fluid( Array3<double> level_set, // level set field Array3<double> volume_of_fluid, // volume of fluid field int number_primary_cells_i, // number of primary (pressure) cells in x1 direction int number_primary_cells_j, // number of primary (pressure) cells in x2 direction int number_primary_cells_k, // number of primary (pressure) cells in x3 direction double lower_bound_derivatives // lower bound for the first partial derivatives // to consider it a limiting case of vanishing // partial derivatives ) { Array3<double> d_level_set_d_x1; // first partial derivative of // the level-set field wrt x1 // second order central approximation Array3<double> d_level_set_d_x2; // first partial derivative of // the level-set field wrt x2 // second order central approximation Array3<double> d_level_set_d_x3; // first partial derivative of // the level-set field wrt x3 // second order central approximation /* allocate memory for the gradient of the level-set field */ d_level_set_d_x1.create(number_primary_cells_i+2, number_primary_cells_j+2, number_primary_cells_k+2); d_level_set_d_x2.create(number_primary_cells_i+2, number_primary_cells_j+2, number_primary_cells_k+2); d_level_set_d_x3.create(number_primary_cells_i+2, number_primary_cells_j+2, number_primary_cells_k+2); /* compute the gradient of the level-set field, necessary for the level-set/vof conversion */ compute_level_set_gradient(level_set, d_level_set_d_x1, d_level_set_d_x2, d_level_set_d_x3, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k ); /* compute the volume of fluid field */ if(compute_volume_of_fluid(level_set, d_level_set_d_x1, d_level_set_d_x2, d_level_set_d_x3, volume_of_fluid, number_primary_cells_i, number_primary_cells_j, number_primary_cells_k, lower_bound_derivatives )) { std::cerr << "**************************************************** \n"; std::cerr<<"ERROR \n"; std::cerr<<"Something went wrong in the level-set to volume of fluid conversion \n"; std::cerr<<"Aborting...\n"; std::cerr<<"in function initialize_volume_of_fluid line 97 \n"; std::cerr << "**************************************************** \n"; }; /* extend the volume of fluid field to the virtual cells */ // field_neumann_boundary(volume_of_fluid, number_primary_cells_i, // number_primary_cells_j,number_primary_cells_k); field_extrapolate_boundary(volume_of_fluid, number_primary_cells_i, number_primary_cells_j,number_primary_cells_k); /* de-allocate memory for the gradient of the level-set field */ d_level_set_d_x1.destroy(); d_level_set_d_x2.destroy(); d_level_set_d_x3.destroy(); }