134 EXCEPTION(
"Cell population must be immersed boundary");
138 mpMesh = &(mpCellPopulation->rGetMesh());
141 unsigned num_grid_ptsX = mpMesh->GetNumGridPtsX();
142 unsigned num_grid_ptsY = mpMesh->GetNumGridPtsY();
145 mGridSpacingX = 1.0 / (
double) num_grid_ptsX;
146 mGridSpacingY = 1.0 / (
double) num_grid_ptsY;
149 c_vector<double, 2 * 2> domain_size;
150 domain_size(0) = 0.0;
151 domain_size(1) = 1.0;
152 domain_size(2) = 0.0;
153 domain_size(3) = 1.0;
155 mpBoxCollection = std::make_unique<ObsoleteBoxCollection<DIM>>(
156 mpCellPopulation->GetInteractionDistance(),
161 mpBoxCollection->SetupLocalBoxesHalfOnly();
162 mpBoxCollection->CalculateNodePairs(mpMesh->rGetNodes(), mNodePairs);
169 mpArrays = std::make_unique<ImmersedBoundary2dArrays<DIM>>(
173 mpCellPopulation->DoesPopulationHaveActiveSources()
176 mpFftInterface = std::make_unique<ImmersedBoundaryFftInterface<DIM>>(
178 &(mpArrays->rGetModifiableRightHandSideGrids()[0][0][0]),
179 &(mpArrays->rGetModifiableFourierGrids()[0][0][0]),
180 &(mpMesh->rGetModifiable2dVelocityGrids()[0][0][0]),
181 mpCellPopulation->DoesPopulationHaveActiveSources()
184 mFftNorm = (
double) num_grid_ptsX * (
double) num_grid_ptsY;
192 if(mAdditiveNormalNoise)
194 std::array<double, DIM> lower_corner;
195 lower_corner.fill(0.0);
197 std::array<double, DIM> upper_corner;
198 upper_corner.fill(1.0);
200 std::array<unsigned, DIM> num_grid_pts;
201 num_grid_pts.fill(num_grid_ptsX / mNoiseSkip);
203 std::array<bool, DIM> periodicity;
204 periodicity.fill(
true);
206 const double total_gridpts = std::accumulate(num_grid_pts.begin(),
209 std::multiplies<double>());
212 if (num_grid_ptsX % mNoiseSkip != 0)
214 WARNING(
"mNoiseSkip should perfectly divide num_grid_ptsX or adding forces will likely not work as expected.");
216 if (total_gridpts > 100.0 * 100.0)
218 WARNING(
"You probably have too many grid points for creating a random field. Increase mNoiseSkip");
222 mpRandomField = std::make_unique<UniformGridRandomFieldGenerator<DIM>>(
236 unsigned num_grid_ptsX = mpMesh->GetNumGridPtsX();
237 unsigned num_grid_ptsY = mpMesh->GetNumGridPtsY();
240 for (
auto node_iter = mpMesh->GetNodeIteratorBegin(
false);
241 node_iter != mpMesh->GetNodeIteratorEnd();
244 node_iter->ClearAppliedForce();
248 multi_array<double, 3>& r_force_grids = mpArrays->rGetModifiableForceGrids();
250 for (
unsigned dim = 0; dim < 2; dim++)
252 for (
unsigned x = 0; x < num_grid_ptsX; x++)
254 for (
unsigned y = 0; y < num_grid_ptsY; y++)
256 r_force_grids[dim][x][y] = 0.0;
262 if (mpCellPopulation->DoesPopulationHaveActiveSources())
264 multi_array<double, 3> &r_rhs_grid = mpArrays->rGetModifiableRightHandSideGrids();
266 for (
unsigned x = 0; x < num_grid_ptsX; x++)
268 for (
unsigned y = 0; y < num_grid_ptsY; y++)
270 r_rhs_grid[2][x][y] = 0.0;
309 if constexpr (DIM == 2)
311 unsigned num_grid_ptsX = mpMesh->GetNumGridPtsX();
312 unsigned num_grid_ptsY = mpMesh->GetNumGridPtsY();
318 unsigned first_idx_x;
319 unsigned first_idx_y;
321 std::vector<unsigned> x_indices(4);
322 std::vector<unsigned> y_indices(4);
324 std::vector<double> x_deltas(4);
325 std::vector<double> y_deltas(4);
327 c_vector<double, DIM> node_location;
328 c_vector<double, DIM> applied_force;
331 multi_array<double, 3>& r_force_grids = mpArrays->rGetModifiableForceGrids();
334 for (
auto elem_iter = mpMesh->GetElementIteratorBegin(
false);
335 elem_iter != mpMesh->GetElementIteratorEnd();
338 dl = mpMesh->GetAverageNodeSpacingOfElement(elem_iter->GetIndex(),
false);
340 for (
unsigned node_idx = 0; node_idx < elem_iter->GetNumNodes(); node_idx++)
342 Node<DIM> *p_node = elem_iter->GetNode(node_idx);
349 first_idx_x =
unsigned(floor(node_location[0] / mGridSpacingX)) + num_grid_ptsX - 1;
350 first_idx_y =
unsigned(floor(node_location[1] / mGridSpacingY)) + num_grid_ptsY - 1;
353 for (
unsigned i = 0; i < 4; i++)
355 x_indices[i] = (first_idx_x + i) % num_grid_ptsX;
356 y_indices[i] = (first_idx_y + i) % num_grid_ptsY;
358 x_deltas[i] = Delta1D(fabs(x_indices[i] * mGridSpacingX - node_location[0]), mGridSpacingX);
359 y_deltas[i] = Delta1D(fabs(y_indices[i] * mGridSpacingY - node_location[1]), mGridSpacingY);
363 for (
unsigned x_idx = 0; x_idx < 4; ++x_idx)
365 for (
unsigned y_idx = 0; y_idx < 4; ++y_idx)
368 weight = x_deltas[x_idx] * y_deltas[y_idx] * dl / (mGridSpacingX * mGridSpacingY);
370 r_force_grids[0][x_indices[x_idx]][y_indices[y_idx]] += applied_force[0] * weight;
371 r_force_grids[1][x_indices[x_idx]][y_indices[y_idx]] += applied_force[1] * weight;
378 for (
auto lam_iter = mpMesh->GetLaminaIteratorBegin(
false);
379 lam_iter != mpMesh->GetLaminaIteratorEnd();
382 dl = mpMesh->GetAverageNodeSpacingOfLamina(lam_iter->GetIndex(),
false);
384 for (
unsigned node_idx = 0; node_idx < lam_iter->GetNumNodes(); ++node_idx)
386 Node<DIM> *p_node = lam_iter->GetNode(node_idx);
393 first_idx_x =
unsigned(floor(node_location[0] / mGridSpacingX)) + num_grid_ptsX - 1;
394 first_idx_y =
unsigned(floor(node_location[1] / mGridSpacingY)) + num_grid_ptsY - 1;
397 for (
unsigned i = 0; i < 4; i++)
399 x_indices[i] = (first_idx_x + i) % num_grid_ptsX;
400 y_indices[i] = (first_idx_y + i) % num_grid_ptsY;
402 x_deltas[i] = Delta1D(fabs(x_indices[i] * mGridSpacingX - node_location[0]), mGridSpacingX);
403 y_deltas[i] = Delta1D(fabs(y_indices[i] * mGridSpacingY - node_location[1]), mGridSpacingY);
407 for (
unsigned x_idx = 0; x_idx < 4; x_idx++)
409 for (
unsigned y_idx = 0; y_idx < 4; y_idx++)
412 weight = x_deltas[x_idx] * y_deltas[y_idx] * dl / (mGridSpacingX * mGridSpacingY);
414 r_force_grids[0][x_indices[x_idx]][y_indices[y_idx]] += applied_force[0] * weight;
415 r_force_grids[1][x_indices[x_idx]][y_indices[y_idx]] += applied_force[1] * weight;
424 ZeroFieldSums(r_force_grids);
436 if constexpr (DIM == 2)
438 unsigned num_grid_ptsX = mpMesh->GetNumGridPtsX();
439 unsigned num_grid_ptsY = mpMesh->GetNumGridPtsY();
444 unsigned first_idx_x;
445 unsigned first_idx_y;
447 std::vector<unsigned> x_indices(4);
448 std::vector<unsigned> y_indices(4);
450 std::vector<double> x_deltas(4);
451 std::vector<double> y_deltas(4);
455 multi_array<double, 3>& r_rhs_grids = mpArrays->rGetModifiableRightHandSideGrids();
457 std::vector<std::shared_ptr<FluidSource<DIM>>>& r_element_sources = mpMesh->rGetElementFluidSources();
458 std::vector<std::shared_ptr<FluidSource<DIM>>>& r_balance_sources = mpMesh->rGetBalancingFluidSources();
461 std::vector<std::shared_ptr<FluidSource<DIM>>> combined_sources;
462 combined_sources.insert(combined_sources.end(), r_element_sources.begin(), r_element_sources.end());
463 combined_sources.insert(combined_sources.end(), r_balance_sources.begin(), r_balance_sources.end());
466 double cumulative_strength = 0.0;
467 for (
unsigned source_idx = 0; source_idx < r_element_sources.size(); source_idx++)
469 cumulative_strength += r_element_sources[source_idx]->GetStrength();
473 double balance_strength = -1.0 * cumulative_strength / (
double)r_balance_sources.size();
474 for (
unsigned source_idx = 0; source_idx < r_balance_sources.size(); source_idx++)
476 r_balance_sources[source_idx]->SetStrength(balance_strength);
480 for (
unsigned source_idx = 0; source_idx < combined_sources.size(); source_idx++)
482 std::shared_ptr<FluidSource<DIM>> this_source = combined_sources[source_idx];
485 c_vector<double, DIM> source_location = this_source->rGetLocation();
486 double source_strength = this_source->GetStrength();
489 first_idx_x =
unsigned(floor(source_location[0] / mGridSpacingX)) + num_grid_ptsX - 1;
490 first_idx_y =
unsigned(floor(source_location[1] / mGridSpacingY)) + num_grid_ptsY - 1;
493 for (
unsigned i = 0; i < 4; i ++)
495 x_indices[i] = (first_idx_x + i) % num_grid_ptsX;
496 y_indices[i] = (first_idx_y + i) % num_grid_ptsY;
498 x_deltas[i] = Delta1D(fabs(x_indices[i] * mGridSpacingX - source_location[0]), mGridSpacingX);
499 y_deltas[i] = Delta1D(fabs(y_indices[i] * mGridSpacingY - source_location[1]), mGridSpacingY);
503 for (
unsigned x_idx = 0; x_idx < 4; ++x_idx)
505 for (
unsigned y_idx = 0; y_idx < 4; ++y_idx)
508 weight = x_deltas[x_idx] * y_deltas[y_idx] / (mGridSpacingX * mGridSpacingY);
510 r_rhs_grids[2][x_indices[x_idx]][y_indices[y_idx]] += source_strength * weight;
524 unsigned num_grid_ptsX = mpMesh->GetNumGridPtsX();
525 unsigned num_grid_ptsY = mpMesh->GetNumGridPtsY();
528 unsigned reduced_size = 1 + (num_grid_ptsY/2);
531 multi_array<double, 3>& r_vel_grids = mpMesh->rGetModifiable2dVelocityGrids();
532 multi_array<double, 3>& r_force_grids = mpArrays->rGetModifiableForceGrids();
533 multi_array<double, 3>& r_rhs_grids = mpArrays->rGetModifiableRightHandSideGrids();
534 multi_array<double, 3>& r_source_gradient_grids = mpArrays->rGetModifiableSourceGradientGrids();
536 const multi_array<double, 2>& r_op_1 = mpArrays->rGetOperator1();
537 const multi_array<double, 2>& r_op_2 = mpArrays->rGetOperator2();
538 const std::vector<double>& r_sin_2x = mpArrays->rGetSin2x();
539 const std::vector<double>& r_sin_2y = mpArrays->rGetSin2y();
541 multi_array<std::complex<double>, 3>& r_fourier_grids = mpArrays->rGetModifiableFourierGrids();
542 multi_array<std::complex<double>, 2>& r_pressure_grid = mpArrays->rGetModifiablePressureGrid();
545 Upwind2d(r_vel_grids, r_rhs_grids);
548 if (mpCellPopulation->DoesPopulationHaveActiveSources())
550 double factor = 1.0 / (3.0 * mReynoldsNumber);
552 CalculateSourceGradients(r_rhs_grids, r_source_gradient_grids);
554 for (
unsigned dim = 0; dim < 2; ++dim)
556 for (
unsigned x = 0; x < num_grid_ptsX; ++x)
558 for (
unsigned y = 0; y < num_grid_ptsY; ++y)
560 r_rhs_grids[dim][x][y] = r_vel_grids[dim][x][y] + dt * (r_force_grids[dim][x][y] + factor * r_source_gradient_grids[dim][x][y] - r_rhs_grids[dim][x][y]);
567 for (
unsigned dim = 0; dim < 2; ++dim)
569 for (
unsigned x = 0; x < num_grid_ptsX; ++x)
571 for (
unsigned y = 0; y < num_grid_ptsY; ++y)
573 r_rhs_grids[dim][x][y] = (r_vel_grids[dim][x][y] + dt * (r_force_grids[dim][x][y] - r_rhs_grids[dim][x][y]));
580 mpFftInterface->FftExecuteForward();
591 if (mpCellPopulation->DoesPopulationHaveActiveSources())
593 for (
unsigned x = 0; x < num_grid_ptsX; ++x)
595 for (
unsigned y = 0; y < reduced_size; ++y)
597 r_pressure_grid[x][y] = (r_op_2[x][y] * r_fourier_grids[2][x][y] - mI * (r_sin_2x[x] * r_fourier_grids[0][x][y] / mGridSpacingX +
598 r_sin_2y[y] * r_fourier_grids[1][x][y] / mGridSpacingY)) / r_op_1[x][y];
604 for (
unsigned x = 0; x < num_grid_ptsX; ++x)
606 for (
unsigned y = 0; y < reduced_size; ++y)
608 r_pressure_grid[x][y] = -mI * (r_sin_2x[x] * r_fourier_grids[0][x][y] / mGridSpacingX +
609 r_sin_2y[y] * r_fourier_grids[1][x][y] / mGridSpacingY) / r_op_1[x][y];
615 r_pressure_grid[0][0] = 0.0;
616 r_pressure_grid[num_grid_ptsX/2][0] = 0.0;
617 r_pressure_grid[num_grid_ptsX/2][num_grid_ptsY/2] = 0.0;
618 r_pressure_grid[0][num_grid_ptsY/2] = 0.0;
624 for (
unsigned x = 0; x < num_grid_ptsX; ++x)
626 for (
unsigned y = 0; y < reduced_size; ++y)
628 r_fourier_grids[0][x][y] = (r_fourier_grids[0][x][y] - (mI * dt / (mReynoldsNumber * mGridSpacingX)) * r_sin_2x[x] * r_pressure_grid[x][y]) / (r_op_2[x][y]);
629 r_fourier_grids[1][x][y] = (r_fourier_grids[1][x][y] - (mI * dt / (mReynoldsNumber * mGridSpacingY)) * r_sin_2y[y] * r_pressure_grid[x][y]) / (r_op_2[x][y]);
634 mpFftInterface->FftExecuteInverse();
635 for (
unsigned dim = 0; dim < 2; ++dim)
637 for (
unsigned x = 0; x < num_grid_ptsX; ++x)
639 for (
unsigned y = 0; y < num_grid_ptsY; ++y)
641 r_vel_grids[dim][x][y] /= mFftNorm;
652 ZeroFieldSums(r_vel_grids);
658 multi_array<double, 3>& rField)
660 unsigned num_grid_ptsX = mpMesh->GetNumGridPtsX();
661 unsigned num_grid_ptsY = mpMesh->GetNumGridPtsY();
663 for (
unsigned dim = 0; dim < 2; ++dim)
665 double total_field_val = 0.0;
668 for (
unsigned x = 0; x < num_grid_ptsX; ++x)
670 for (
unsigned y = 0; y < num_grid_ptsY; ++y)
672 if (rField[dim][x][y] != 0.0)
674 total_field_val += rField[dim][x][y];
680 const double average_field_val = (total_field_val / count);
682 for (
unsigned x = 0; x < num_grid_ptsX; ++x)
684 for (
unsigned y = 0; y < num_grid_ptsY; ++y)
686 if (rField[dim][x][y] != 0.0)
688 rField[dim][x][y] -= average_field_val;
705 const multi_array<double, 3>& rInput,
706 multi_array<double, 3>& rOutput)
708 unsigned num_grid_ptsX = mpMesh->GetNumGridPtsX();
709 unsigned num_grid_ptsY = mpMesh->GetNumGridPtsX();
711 unsigned prev_x = num_grid_ptsX - 1;
712 unsigned prev_y = num_grid_ptsY - 1;
717 for (
unsigned x = 0; x < num_grid_ptsX; x++)
719 for (
unsigned y = 0; y < num_grid_ptsY; y++)
722 if (rInput[0][x][y] > 0)
724 rOutput[0][x][y] = rInput[0][x][y] * (rInput[0][x][y] - rInput[0][prev_x][y]) / mGridSpacingX;
725 rOutput[1][x][y] = rInput[0][x][y] * (rInput[1][x][y] - rInput[1][prev_x][y]) / mGridSpacingX;
729 rOutput[0][x][y] = rInput[0][x][y] * (rInput[0][next_x][y] - rInput[0][x][y]) / mGridSpacingX;
730 rOutput[1][x][y] = rInput[0][x][y] * (rInput[1][next_x][y] - rInput[1][x][y]) / mGridSpacingX;
734 if (rInput[1][x][y] > 0)
736 rOutput[0][x][y] += rInput[1][x][y] * (rInput[0][x][y] - rInput[0][x][prev_y]) / mGridSpacingY;
737 rOutput[1][x][y] += rInput[1][x][y] * (rInput[1][x][y] - rInput[1][x][prev_y]) / mGridSpacingY;
741 rOutput[0][x][y] += rInput[1][x][y] * (rInput[0][x][next_y] - rInput[0][x][y]) / mGridSpacingY;
742 rOutput[1][x][y] += rInput[1][x][y] * (rInput[1][x][next_y] - rInput[1][x][y]) / mGridSpacingY;
745 prev_y = (prev_y + 1) % num_grid_ptsY;
746 next_y = (next_y + 1) % num_grid_ptsY;
749 prev_x = (prev_x + 1) % num_grid_ptsX;
750 next_x = (next_x + 1) % num_grid_ptsX;
756 const multi_array<double, 3>& rRhs,
757 multi_array<double, 3>& rGradients)
759 unsigned num_grid_ptsX = mpMesh->GetNumGridPtsX();
760 unsigned num_grid_ptsY = mpMesh->GetNumGridPtsY();
763 double factor = 1.0 / (2.0 * mGridSpacingX);
766 for (
unsigned x = 0; x < num_grid_ptsX; ++x)
768 unsigned next_x = (x + 1) % num_grid_ptsX;
769 unsigned prev_x = (x + num_grid_ptsX - 1) % num_grid_ptsX;
771 for (
unsigned y = 0; y < num_grid_ptsY; ++y)
773 unsigned next_y = (y + 1) % num_grid_ptsY;
774 unsigned prev_y = (y + num_grid_ptsY - 1) % num_grid_ptsY;
776 rGradients[0][x][y] = factor * (rRhs[2][next_x][y] - rRhs[2][prev_x][y]);
777 rGradients[1][x][y] = factor * (rRhs[2][x][next_y] - rRhs[2][x][prev_y]);
806 auto& r_force_grids = mpArrays->rGetModifiableForceGrids();
812 for (
unsigned dim = 0; dim < r_force_grids.shape()[0]; dim++)
815 const std::vector<double> field = mpRandomField->SampleRandomField();
818 const double adjustment = std::accumulate(field.begin(), field.end(), 0.0) / field.size();
820 for (
unsigned x = 0, idx = 0; x < r_force_grids.shape()[1]; x += mNoiseSkip)
822 for (
unsigned y = 0; y < r_force_grids.shape()[2]; y += mNoiseSkip, idx++)
825 const double val = force_scale_factor * (field[idx] - adjustment);
828 for (
unsigned i = 0; i < mNoiseSkip; ++i)
830 for (
unsigned j = 0; j < mNoiseSkip; ++j)
832 r_force_grids[dim][x+i][y+j] += val;