/* Flow adaptor class for multiphase flow methods */ #include "analysis/FlowAdaptor.h" #include "analysis/distance.h" #include "analysis/morphology.h" FlowAdaptor::FlowAdaptor(ScaLBL_ColorModel &M) { Nx = M.Dm->Nx; Ny = M.Dm->Ny; Nz = M.Dm->Nz; timestep = -1; timestep_previous = -1; phi.resize(Nx, Ny, Nz); phi.fill(0); // phase indicator field phi_t.resize(Nx, Ny, Nz); phi_t.fill(0); // time derivative for the phase indicator field } FlowAdaptor::~FlowAdaptor() { } double FlowAdaptor::ImageInit(ScaLBL_ColorModel &M, std::string Filename) { int rank = M.rank; int Nx = M.Nx; int Ny = M.Ny; int Nz = M.Nz; if (rank == 0) printf("Re-initializing fluids from file: %s \n", Filename.c_str()); M.Mask->Decomp(Filename); for (int i = 0; i < Nx * Ny * Nz; i++) M.id[i] = M.Mask->id[i]; // save what was read for (int i = 0; i < Nx * Ny * Nz; i++) M.Dm->id[i] = M.Mask->id[i]; // save what was read double *PhaseLabel; PhaseLabel = new double[Nx * Ny * Nz]; M.AssignComponentLabels(PhaseLabel); double Count = 0.0; double PoreCount = 0.0; for (int k = 1; k < Nz - 1; k++) { for (int j = 1; j < Ny - 1; j++) { for (int i = 1; i < Nx - 1; i++) { if (M.id[Nx * Ny * k + Nx * j + i] == 2) { PoreCount++; Count++; } else if (M.id[Nx * Ny * k + Nx * j + i] == 1) { PoreCount++; } } } } Count = M.Dm->Comm.sumReduce(Count); PoreCount = M.Dm->Comm.sumReduce(PoreCount); if (rank == 0) printf(" new saturation: %f (%f / %f) \n", Count / PoreCount, Count, PoreCount); ScaLBL_CopyToDevice(M.Phi, PhaseLabel, Nx * Ny * Nz * sizeof(double)); M.Dm->Comm.barrier(); ScaLBL_D3Q19_Init(M.fq, M.Np); ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, M.ScaLBL_Comm->LastExterior(), M.Np); ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, M.ScaLBL_Comm->FirstInterior(), M.ScaLBL_Comm->LastInterior(), M.Np); M.Dm->Comm.barrier(); ScaLBL_CopyToHost(M.Averages->Phi.data(), M.Phi, Nx * Ny * Nz * sizeof(double)); delete PhaseLabel; double saturation = Count / PoreCount; return saturation; } double FlowAdaptor::UpdateFractionalFlow(ScaLBL_ColorModel &M) { double MASS_FRACTION_CHANGE = 0.006; double FRACTIONAL_FLOW_EPSILON = 5e-6; if (M.db->keyExists("FlowAdaptor")) { auto flow_db = M.db->getDatabase("FlowAdaptor"); MASS_FRACTION_CHANGE = flow_db->getWithDefault("mass_fraction_factor", 0.006); FRACTIONAL_FLOW_EPSILON = flow_db->getWithDefault("fractional_flow_epsilon", 5e-6); } int Np = M.Np; double dA, dB, phi; double vx, vy, vz; double mass_a, mass_b, mass_a_global, mass_b_global; double *Aq_tmp, *Bq_tmp; double *Vel_x, *Vel_y, *Vel_z, *Phase; Aq_tmp = new double[7 * Np]; Bq_tmp = new double[7 * Np]; Phase = new double[Np]; Vel_x = new double[Np]; Vel_y = new double[Np]; Vel_z = new double[Np]; ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7 * Np * sizeof(double)); ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7 * Np * sizeof(double)); ScaLBL_CopyToHost(Vel_x, &M.Velocity[0], Np * sizeof(double)); ScaLBL_CopyToHost(Vel_y, &M.Velocity[Np], Np * sizeof(double)); ScaLBL_CopyToHost(Vel_z, &M.Velocity[2 * Np], Np * sizeof(double)); int Nx = M.Nx; int Ny = M.Ny; int Nz = M.Nz; mass_a = mass_b = 0.0; double maxSpeed = 0.0; double localMaxSpeed = 0.0; /* compute mass change based on weights */ double sum_weights_A = 0.0; double sum_weights_B = 0.0; for (int k = 1; k < Nz - 1; k++) { for (int j = 1; j < Ny - 1; j++) { for (int i = 1; i < Nx - 1; i++) { int n = M.Map(i, j, k); //double distance = M.Averages->SDs(i,j,k); if (!(n < 0)) { dA = Aq_tmp[n] + Aq_tmp[n + Np] + Aq_tmp[n + 2 * Np] + Aq_tmp[n + 3 * Np] + Aq_tmp[n + 4 * Np] + Aq_tmp[n + 5 * Np] + Aq_tmp[n + 6 * Np]; dB = Bq_tmp[n] + Bq_tmp[n + Np] + Bq_tmp[n + 2 * Np] + Bq_tmp[n + 3 * Np] + Bq_tmp[n + 4 * Np] + Bq_tmp[n + 5 * Np] + Bq_tmp[n + 6 * Np]; phi = (dA - dB) / (dA + dB); Phase[n] = phi; mass_a += dA; mass_b += dB; vx = Vel_x[n]; vy = Vel_y[n]; vz = Vel_z[n]; double local_momentum = sqrt(vx * vx + vy * vy + vz * vz); double local_weight = (FRACTIONAL_FLOW_EPSILON + local_momentum); if (phi > 0.0) { sum_weights_A += local_weight * dA; } else { sum_weights_B += local_weight * dB; } if (local_momentum > localMaxSpeed) { localMaxSpeed = local_momentum; } } } } } maxSpeed = M.Dm->Comm.maxReduce(localMaxSpeed); mass_a_global = M.Dm->Comm.sumReduce(mass_a); mass_b_global = M.Dm->Comm.sumReduce(mass_b); double sum_weights_A_global = M.Dm->Comm.sumReduce(sum_weights_A); double sum_weights_B_global = M.Dm->Comm.sumReduce(sum_weights_B); sum_weights_A_global /= (FRACTIONAL_FLOW_EPSILON + maxSpeed); sum_weights_B_global /= (FRACTIONAL_FLOW_EPSILON + maxSpeed); //double total_momentum_A = sqrt(vax_global*vax_global+vay_global*vay_global+vaz_global*vaz_global); //double total_momentum_B = sqrt(vbx_global*vbx_global+vby_global*vby_global+vbz_global*vbz_global); /* compute the total mass change */ double TOTAL_MASS_CHANGE = MASS_FRACTION_CHANGE * (mass_a_global + mass_b_global); if (fabs(TOTAL_MASS_CHANGE) > 0.1 * mass_a_global) TOTAL_MASS_CHANGE = 0.1 * mass_a_global; if (fabs(TOTAL_MASS_CHANGE) > 0.1 * mass_b_global) TOTAL_MASS_CHANGE = 0.1 * mass_b_global; double MASS_FACTOR_A = TOTAL_MASS_CHANGE / sum_weights_A_global; double MASS_FACTOR_B = TOTAL_MASS_CHANGE / sum_weights_B_global; double LOCAL_MASS_CHANGE = 0.0; for (int k = 1; k < Nz - 1; k++) { for (int j = 1; j < Ny - 1; j++) { for (int i = 1; i < Nx - 1; i++) { int n = M.Map(i, j, k); if (!(n < 0)) { phi = Phase[n]; vx = Vel_x[n]; vy = Vel_y[n]; vz = Vel_z[n]; double local_momentum = sqrt(vx * vx + vy * vy + vz * vz); double local_weight = (FRACTIONAL_FLOW_EPSILON + local_momentum) / (FRACTIONAL_FLOW_EPSILON + maxSpeed); /* impose ceiling for spurious currents */ //if (local_momentum > maxSpeed) local_momentum = maxSpeed; if (phi > 0.0) { LOCAL_MASS_CHANGE = MASS_FACTOR_A * local_weight; Aq_tmp[n] -= 0.3333333333333333 * LOCAL_MASS_CHANGE; Aq_tmp[n + Np] -= 0.1111111111111111 * LOCAL_MASS_CHANGE; Aq_tmp[n + 2 * Np] -= 0.1111111111111111 * LOCAL_MASS_CHANGE; Aq_tmp[n + 3 * Np] -= 0.1111111111111111 * LOCAL_MASS_CHANGE; Aq_tmp[n + 4 * Np] -= 0.1111111111111111 * LOCAL_MASS_CHANGE; Aq_tmp[n + 5 * Np] -= 0.1111111111111111 * LOCAL_MASS_CHANGE; Aq_tmp[n + 6 * Np] -= 0.1111111111111111 * LOCAL_MASS_CHANGE; //DebugMassA[n] = (-1.0)*LOCAL_MASS_CHANGE; } else { LOCAL_MASS_CHANGE = MASS_FACTOR_B * local_weight; Bq_tmp[n] += 0.3333333333333333 * LOCAL_MASS_CHANGE; Bq_tmp[n + Np] += 0.1111111111111111 * LOCAL_MASS_CHANGE; Bq_tmp[n + 2 * Np] += 0.1111111111111111 * LOCAL_MASS_CHANGE; Bq_tmp[n + 3 * Np] += 0.1111111111111111 * LOCAL_MASS_CHANGE; Bq_tmp[n + 4 * Np] += 0.1111111111111111 * LOCAL_MASS_CHANGE; Bq_tmp[n + 5 * Np] += 0.1111111111111111 * LOCAL_MASS_CHANGE; Bq_tmp[n + 6 * Np] += 0.1111111111111111 * LOCAL_MASS_CHANGE; //DebugMassB[n] = LOCAL_MASS_CHANGE; } } } } } if (M.rank == 0) printf("Update Fractional Flow: change mass of fluid B by %f \n", TOTAL_MASS_CHANGE / mass_b_global); // Need to initialize Aq, Bq, Den, Phi directly //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7 * Np * sizeof(double)); ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7 * Np * sizeof(double)); delete Aq_tmp; delete Bq_tmp; delete Vel_x; delete Vel_y; delete Vel_z; delete Phase; return (TOTAL_MASS_CHANGE); } void FlowAdaptor::Flatten(ScaLBL_ColorModel &M) { ScaLBL_D3Q19_Init(M.fq, M.Np); ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, M.ScaLBL_Comm->LastExterior(), M.Np); ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, M.ScaLBL_Comm->FirstInterior(), M.ScaLBL_Comm->LastInterior(), M.Np); } double FlowAdaptor::MoveInterface(ScaLBL_ColorModel &M) { double INTERFACE_CUTOFF = M.color_db->getWithDefault("move_interface_cutoff", 0.1); double MOVE_INTERFACE_FACTOR = M.color_db->getWithDefault("move_interface_factor", 10.0); ScaLBL_CopyToHost(phi.data(), M.Phi, Nx * Ny * Nz * sizeof(double)); /* compute the local derivative of phase indicator field */ double beta = M.beta; double factor = 0.5 / beta; double total_interface_displacement = 0.0; double total_interface_sites = 0.0; for (int n = 0; n < Nx * Ny * Nz; n++) { /* compute the distance to the interface */ double value1 = M.Averages->Phi(n); double dist1 = factor * log((1.0 + value1) / (1.0 - value1)); double value2 = phi(n); double dist2 = factor * log((1.0 + value2) / (1.0 - value2)); phi_t(n) = value2; if (value1 < INTERFACE_CUTOFF && value1 > -1 * INTERFACE_CUTOFF && value2 < INTERFACE_CUTOFF && value2 > -1 * INTERFACE_CUTOFF) { /* time derivative of distance */ double dxdt = 0.125 * (dist2 - dist1); /* extrapolate to move the distance further */ double dist3 = dist2 + MOVE_INTERFACE_FACTOR * dxdt; /* compute the new phase interface */ phi_t(n) = (2.f * (exp(-2.f * beta * (dist3))) / (1.f + exp(-2.f * beta * (dist3))) - 1.f); total_interface_displacement += fabs(MOVE_INTERFACE_FACTOR * dxdt); total_interface_sites += 1.0; } } ScaLBL_CopyToDevice(M.Phi, phi_t.data(), Nx * Ny * Nz * sizeof(double)); return total_interface_sites; } double FlowAdaptor::ShellAggregation(ScaLBL_ColorModel &M, const double target_delta_volume) { const RankInfoStruct rank_info(M.rank, M.nprocx, M.nprocy, M.nprocz); auto rank = M.rank; auto Nx = M.Nx; auto Ny = M.Ny; auto Nz = M.Nz; auto N = Nx * Ny * Nz; double vF = 0.f; double vS = 0.f; double delta_volume; double WallFactor = 1.0; bool USE_CONNECTED_NWP = false; DoubleArray phase(Nx, Ny, Nz); IntArray phase_label(Nx, Ny, Nz); ; DoubleArray phase_distance(Nx, Ny, Nz); Array phase_id(Nx, Ny, Nz); fillHalo fillDouble(M.Dm->Comm, M.Dm->rank_info, {Nx - 2, Ny - 2, Nz - 2}, {1, 1, 1}, 0, 1); // Basic algorithm to // 1. Copy phase field to CPU ScaLBL_CopyToHost(phase.data(), M.Phi, N * sizeof(double)); double count = 0.f; for (int k = 1; k < Nz - 1; k++) { for (int j = 1; j < Ny - 1; j++) { for (int i = 1; i < Nx - 1; i++) { if (phase(i, j, k) > 0.f && M.Averages->SDs(i, j, k) > 0.f) count += 1.f; } } } double volume_initial = M.Dm->Comm.sumReduce(count); double PoreVolume = M.Dm->Volume * M.Dm->Porosity(); /*ensure target isn't an absurdly small fraction of pore volume */ if (volume_initial < target_delta_volume * PoreVolume) { volume_initial = target_delta_volume * PoreVolume; } // 2. Identify connected components of phase field -> phase_label double volume_connected = 0.0; double second_biggest = 0.0; if (USE_CONNECTED_NWP) { ComputeGlobalBlobIDs(Nx - 2, Ny - 2, Nz - 2, rank_info, phase, M.Averages->SDs, vF, vS, phase_label, M.Dm->Comm); M.Dm->Comm.barrier(); // only operate on component "0"ScaLBL_ColorModel &M, count = 0.0; for (int k = 0; k < Nz; k++) { for (int j = 0; j < Ny; j++) { for (int i = 0; i < Nx; i++) { int label = phase_label(i, j, k); if (label == 0) { phase_id(i, j, k) = 0; count += 1.0; } else phase_id(i, j, k) = 1; if (label == 1) { second_biggest += 1.0; } } } } volume_connected = M.Dm->Comm.sumReduce(count); second_biggest = M.Dm->Comm.sumReduce(second_biggest); } else { // use the whole NWP for (int k = 0; k < Nz; k++) { for (int j = 0; j < Ny; j++) { for (int i = 0; i < Nx; i++) { if (M.Averages->SDs(i, j, k) > 0.f) { if (phase(i, j, k) > 0.f) { phase_id(i, j, k) = 0; } else { phase_id(i, j, k) = 1; } } else { phase_id(i, j, k) = 1; } } } } } // 3. Generate a distance map to the largest object -> phase_distance CalcDist(phase_distance, phase_id, *M.Dm); double temp, value; double factor = 0.5 / M.beta; for (int k = 0; k < Nz; k++) { for (int j = 0; j < Ny; j++) { for (int i = 0; i < Nx; i++) { if (phase_distance(i, j, k) < 3.f) { value = phase(i, j, k); if (value > 1.f) value = 1.f; if (value < -1.f) value = -1.f; // temp -- distance based on analytical form McClure, Prins et al, Comp. Phys. Comm. temp = -factor * log((1.0 + value) / (1.0 - value)); /// use this approximation close to the object if (fabs(value) < 0.8 && M.Averages->SDs(i, j, k) > 1.f) { phase_distance(i, j, k) = temp; } // erase the original object phase(i, j, k) = -1.0; } } } } if (rank == 0) printf("Pathway volume / next largest ganglion %f \n", volume_connected / second_biggest); if (rank == 0) printf("MorphGrow with target volume fraction change %f \n", target_delta_volume / volume_initial); double target_delta_volume_incremental = target_delta_volume; if (fabs(target_delta_volume) > 0.01 * volume_initial) target_delta_volume_incremental = 0.01 * volume_initial * target_delta_volume / fabs(target_delta_volume); delta_volume = MorphGrow(M.Averages->SDs, phase_distance, phase_id, M.Averages->Dm, target_delta_volume_incremental, WallFactor); for (int k = 0; k < Nz; k++) { for (int j = 0; j < Ny; j++) { for (int i = 0; i < Nx; i++) { if (phase_distance(i, j, k) < 0.0) phase_id(i, j, k) = 0; else phase_id(i, j, k) = 1; //if (phase_distance(i,j,k) < 0.0 ) phase(i,j,k) = 1.0; } } } CalcDist(phase_distance, phase_id, *M.Dm); // re-calculate distance // 5. Update phase indicator field based on new distnace for (int k = 0; k < Nz; k++) { for (int j = 0; j < Ny; j++) { for (int i = 0; i < Nx; i++) { double d = phase_distance(i, j, k); if (M.Averages->SDs(i, j, k) > 0.f) { if (d < 3.f) { //phase(i,j,k) = -1.0; phase(i, j, k) = (2.f * (exp(-2.f * M.beta * d)) / (1.f + exp(-2.f * M.beta * d)) - 1.f); } } } } } fillDouble.fill(phase); count = 0.f; for (int k = 1; k < Nz - 1; k++) { for (int j = 1; j < Ny - 1; j++) { for (int i = 1; i < Nx - 1; i++) { if (phase(i, j, k) > 0.f && M.Averages->SDs(i, j, k) > 0.f) { count += 1.f; } } } } double volume_final = M.Dm->Comm.sumReduce(count); delta_volume = (volume_final - volume_initial); if (rank == 0) printf("Shell Aggregation: change fluid volume fraction by %f \n", delta_volume / volume_initial); if (rank == 0) printf(" new saturation = %f \n", volume_final / (M.Mask->Porosity() * double((Nx - 2) * (Ny - 2) * (Nz - 2) * M.nprocs))); // 6. copy back to the device //if (rank==0) printf("MorphInit: copy data back to device\n"); ScaLBL_CopyToDevice(M.Phi, phase.data(), N * sizeof(double)); // 7. Re-initialize phase field and density ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, M.ScaLBL_Comm->LastExterior(), M.Np); ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, M.ScaLBL_Comm->FirstInterior(), M.ScaLBL_Comm->LastInterior(), M.Np); auto BoundaryCondition = M.BoundaryCondition; if (BoundaryCondition == 1 || BoundaryCondition == 2 || BoundaryCondition == 3 || BoundaryCondition == 4) { if (M.Dm->kproc() == 0) { ScaLBL_SetSlice_z(M.Phi, 1.0, Nx, Ny, Nz, 0); ScaLBL_SetSlice_z(M.Phi, 1.0, Nx, Ny, Nz, 1); ScaLBL_SetSlice_z(M.Phi, 1.0, Nx, Ny, Nz, 2); } if (M.Dm->kproc() == M.nprocz - 1) { ScaLBL_SetSlice_z(M.Phi, -1.0, Nx, Ny, Nz, Nz - 1); ScaLBL_SetSlice_z(M.Phi, -1.0, Nx, Ny, Nz, Nz - 2); ScaLBL_SetSlice_z(M.Phi, -1.0, Nx, Ny, Nz, Nz - 3); } } return delta_volume; } double FlowAdaptor::SeedPhaseField(ScaLBL_ColorModel &M, const double seed_water_in_oil) { srand(time(NULL)); auto rank = M.rank; auto Np = M.Np; double mass_loss = 0.f; double count = 0.f; double *Aq_tmp, *Bq_tmp; Aq_tmp = new double[7 * Np]; Bq_tmp = new double[7 * Np]; ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7 * Np * sizeof(double)); ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7 * Np * sizeof(double)); for (int n = 0; n < M.ScaLBL_Comm->LastExterior(); n++) { double random_value = seed_water_in_oil * double(rand()) / RAND_MAX; double dA = Aq_tmp[n] + Aq_tmp[n + Np] + Aq_tmp[n + 2 * Np] + Aq_tmp[n + 3 * Np] + Aq_tmp[n + 4 * Np] + Aq_tmp[n + 5 * Np] + Aq_tmp[n + 6 * Np]; double dB = Bq_tmp[n] + Bq_tmp[n + Np] + Bq_tmp[n + 2 * Np] + Bq_tmp[n + 3 * Np] + Bq_tmp[n + 4 * Np] + Bq_tmp[n + 5 * Np] + Bq_tmp[n + 6 * Np]; double phase_id = (dA - dB) / (dA + dB); if (phase_id > 0.0) { Aq_tmp[n] -= 0.3333333333333333 * random_value; Aq_tmp[n + Np] -= 0.1111111111111111 * random_value; Aq_tmp[n + 2 * Np] -= 0.1111111111111111 * random_value; Aq_tmp[n + 3 * Np] -= 0.1111111111111111 * random_value; Aq_tmp[n + 4 * Np] -= 0.1111111111111111 * random_value; Aq_tmp[n + 5 * Np] -= 0.1111111111111111 * random_value; Aq_tmp[n + 6 * Np] -= 0.1111111111111111 * random_value; Bq_tmp[n] += 0.3333333333333333 * random_value; Bq_tmp[n + Np] += 0.1111111111111111 * random_value; Bq_tmp[n + 2 * Np] += 0.1111111111111111 * random_value; Bq_tmp[n + 3 * Np] += 0.1111111111111111 * random_value; Bq_tmp[n + 4 * Np] += 0.1111111111111111 * random_value; Bq_tmp[n + 5 * Np] += 0.1111111111111111 * random_value; Bq_tmp[n + 6 * Np] += 0.1111111111111111 * random_value; } mass_loss += random_value * seed_water_in_oil; } for (int n = M.ScaLBL_Comm->FirstInterior(); n < M.ScaLBL_Comm->LastInterior(); n++) { double random_value = seed_water_in_oil * double(rand()) / RAND_MAX; double dA = Aq_tmp[n] + Aq_tmp[n + Np] + Aq_tmp[n + 2 * Np] + Aq_tmp[n + 3 * Np] + Aq_tmp[n + 4 * Np] + Aq_tmp[n + 5 * Np] + Aq_tmp[n + 6 * Np]; double dB = Bq_tmp[n] + Bq_tmp[n + Np] + Bq_tmp[n + 2 * Np] + Bq_tmp[n + 3 * Np] + Bq_tmp[n + 4 * Np] + Bq_tmp[n + 5 * Np] + Bq_tmp[n + 6 * Np]; double phase_id = (dA - dB) / (dA + dB); if (phase_id > 0.0) { Aq_tmp[n] -= 0.3333333333333333 * random_value; Aq_tmp[n + Np] -= 0.1111111111111111 * random_value; Aq_tmp[n + 2 * Np] -= 0.1111111111111111 * random_value; Aq_tmp[n + 3 * Np] -= 0.1111111111111111 * random_value; Aq_tmp[n + 4 * Np] -= 0.1111111111111111 * random_value; Aq_tmp[n + 5 * Np] -= 0.1111111111111111 * random_value; Aq_tmp[n + 6 * Np] -= 0.1111111111111111 * random_value; Bq_tmp[n] += 0.3333333333333333 * random_value; Bq_tmp[n + Np] += 0.1111111111111111 * random_value; Bq_tmp[n + 2 * Np] += 0.1111111111111111 * random_value; Bq_tmp[n + 3 * Np] += 0.1111111111111111 * random_value; Bq_tmp[n + 4 * Np] += 0.1111111111111111 * random_value; Bq_tmp[n + 5 * Np] += 0.1111111111111111 * random_value; Bq_tmp[n + 6 * Np] += 0.1111111111111111 * random_value; } mass_loss += random_value * seed_water_in_oil; } count = M.Dm->Comm.sumReduce(count); mass_loss = M.Dm->Comm.sumReduce(mass_loss); if (rank == 0) printf("Remove mass %f from %f voxels \n", mass_loss, count); // Need to initialize Aq, Bq, Den, Phi directly //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7 * Np * sizeof(double)); ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7 * Np * sizeof(double)); delete Aq_tmp; delete Bq_tmp; return (mass_loss); }