MFEM  v4.6.0
Finite element discretization library
tmop_tools.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "tmop_tools.hpp"
13 #include "nonlinearform.hpp"
14 #include "pnonlinearform.hpp"
15 #include "../general/osockstream.hpp"
16 
17 namespace mfem
18 {
19 
20 using namespace mfem;
21 
22 void AdvectorCG::SetInitialField(const Vector &init_nodes,
23  const Vector &init_field)
24 {
25  nodes0 = init_nodes;
26  field0 = init_field;
27 }
28 
30  Vector &new_field,
31  int new_nodes_ordering)
32 {
34 #ifdef MFEM_USE_MPI
35  if (pfes) { space = pfes; }
36 #endif
37  int fes_ordering = space->GetOrdering(),
38  ncomp = space->GetVDim();
39 
40  // TODO: Implement for AMR meshes.
41  const int pnt_cnt = field0.Size() / ncomp;
42 
43  new_field = field0;
44  Vector new_field_temp;
45  for (int i = 0; i < ncomp; i++)
46  {
47  if (fes_ordering == Ordering::byNODES)
48  {
49  new_field_temp.MakeRef(new_field, i*pnt_cnt, pnt_cnt);
50  }
51  else
52  {
53  new_field_temp.SetSize(pnt_cnt);
54  for (int j = 0; j < pnt_cnt; j++)
55  {
56  new_field_temp(j) = new_field(i + j*ncomp);
57  }
58  }
59  ComputeAtNewPositionScalar(new_nodes, new_field_temp);
60  if (fes_ordering == Ordering::byVDIM)
61  {
62  for (int j = 0; j < pnt_cnt; j++)
63  {
64  new_field(i + j*ncomp) = new_field_temp(j);
65  }
66  }
67  }
68 
69  field0 = new_field;
70  nodes0 = new_nodes;
71 }
72 
73 void AdvectorCG::ComputeAtNewPositionScalar(const Vector &new_nodes,
74  Vector &new_field)
75 {
76  Mesh *m = mesh;
77 #ifdef MFEM_USE_MPI
78  if (pmesh) { m = pmesh; }
79 #endif
80 
81  MFEM_VERIFY(m != NULL, "No mesh has been given to the AdaptivityEvaluator.");
82 
83  // This will be used to move the positions.
84  GridFunction *mesh_nodes = m->GetNodes();
85  *mesh_nodes = nodes0;
86  double minv = new_field.Min(), maxv = new_field.Max();
87 
88  // Velocity of the positions.
89  GridFunction u(mesh_nodes->FESpace());
90  subtract(new_nodes, nodes0, u);
91 
92  // Define a scalar FE space for the solution, and the advection operator.
93  TimeDependentOperator *oper = NULL;
94  FiniteElementSpace *fess = NULL;
95 #ifdef MFEM_USE_MPI
96  ParFiniteElementSpace *pfess = NULL;
97 #endif
98  if (fes)
99  {
100  fess = new FiniteElementSpace(fes->GetMesh(), fes->FEColl(), 1);
101  oper = new SerialAdvectorCGOper(nodes0, u, *fess, al);
102  }
103 #ifdef MFEM_USE_MPI
104  else if (pfes)
105  {
106  pfess = new ParFiniteElementSpace(pfes->GetParMesh(), pfes->FEColl(), 1);
107  oper = new ParAdvectorCGOper(nodes0, u, *pfess, al, opt_mt);
108  }
109 #endif
110  MFEM_VERIFY(oper != NULL,
111  "No FE space has been given to the AdaptivityEvaluator.");
112  ode_solver.Init(*oper);
113 
114  // Compute some time step [mesh_size / speed].
115  double h_min = std::numeric_limits<double>::infinity();
116  for (int i = 0; i < m->GetNE(); i++)
117  {
118  h_min = std::min(h_min, m->GetElementSize(i));
119  }
120  double v_max = 0.0;
121  const int s = new_field.Size();
122 
123  u.HostReadWrite();
124  for (int i = 0; i < s; i++)
125  {
126  double vel = 0.;
127  for (int j = 0; j < m->Dimension(); j++)
128  {
129  vel += u(i+j*s)*u(i+j*s);
130  }
131  v_max = std::max(v_max, vel);
132  }
133 
134 #ifdef MFEM_USE_MPI
135  if (pfes)
136  {
137  double v_loc = v_max, h_loc = h_min;
138  MPI_Allreduce(&v_loc, &v_max, 1, MPI_DOUBLE, MPI_MAX, pfes->GetComm());
139  MPI_Allreduce(&h_loc, &h_min, 1, MPI_DOUBLE, MPI_MIN, pfes->GetComm());
140  }
141 #endif
142 
143  if (v_max == 0.0) // No need to change the field.
144  {
145  delete oper;
146  delete fess;
147 #ifdef MFEM_USE_MPI
148  delete pfess;
149 #endif
150  return;
151  }
152 
153  v_max = std::sqrt(v_max);
154  double dt = dt_scale * h_min / v_max;
155 
156  double t = 0.0;
157  bool last_step = false;
158  while (!last_step)
159  {
160  if (t + dt >= 1.0)
161  {
162  dt = 1.0 - t;
163  last_step = true;
164  }
165  ode_solver.Step(new_field, t, dt);
166  }
167 
168  double glob_minv = minv,
169  glob_maxv = maxv;
170 #ifdef MFEM_USE_MPI
171  if (pfes)
172  {
173  MPI_Allreduce(&minv, &glob_minv, 1, MPI_DOUBLE, MPI_MIN, pfes->GetComm());
174  MPI_Allreduce(&maxv, &glob_maxv, 1, MPI_DOUBLE, MPI_MAX, pfes->GetComm());
175  }
176 #endif
177 
178  // Trim the overshoots and undershoots.
179  new_field.HostReadWrite();
180  for (int i = 0; i < s; i++)
181  {
182  if (new_field(i) < glob_minv) { new_field(i) = glob_minv; }
183  if (new_field(i) > glob_maxv) { new_field(i) = glob_maxv; }
184  }
185 
186  delete oper;
187  delete fess;
188 #ifdef MFEM_USE_MPI
189  delete pfess;
190 #endif
191 }
192 
194  GridFunction &vel,
195  FiniteElementSpace &fes,
196  AssemblyLevel al)
197  : TimeDependentOperator(fes.GetVSize()),
198  x0(x_start), x_now(*fes.GetMesh()->GetNodes()),
199  u(vel), u_coeff(&u), M(&fes), K(&fes), al(al)
200 {
202  K.AddDomainIntegrator(Kinteg);
204  K.Assemble(0);
205  K.Finalize(0);
206 
207  MassIntegrator *Minteg = new MassIntegrator;
208  M.AddDomainIntegrator(Minteg);
210  M.Assemble(0);
211  M.Finalize(0);
212 }
213 
214 void SerialAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
215 {
216  // Move the mesh.
217  const double t = GetTime();
218  add(x0, t, u, x_now);
219  K.FESpace()->GetMesh()->NodesUpdated();
220 
221  // Assemble on the new mesh.
222  K.BilinearForm::operator=(0.0);
223  K.Assemble();
224  Vector rhs(K.Size());
225  K.Mult(ind, rhs);
226  M.BilinearForm::operator=(0.0);
227  M.Assemble();
228 
229  di_dt = 0.0;
230  CGSolver lin_solver;
231  Solver *prec = nullptr;
232  Array<int> ess_tdof_list;
233  if (al == AssemblyLevel::PARTIAL)
234  {
235  prec = new OperatorJacobiSmoother(M, ess_tdof_list);
236  lin_solver.SetOperator(M);
237  }
238  else
239  {
240  prec = new DSmoother(M.SpMat());
241  lin_solver.SetOperator(M.SpMat());
242  }
243  lin_solver.SetPreconditioner(*prec);
244  lin_solver.SetRelTol(1e-12); lin_solver.SetAbsTol(0.0);
245  lin_solver.SetMaxIter(100);
246  lin_solver.SetPrintLevel(0);
247  lin_solver.Mult(rhs, di_dt);
248 
249  delete prec;
250 }
251 
252 #ifdef MFEM_USE_MPI
254  GridFunction &vel,
255  ParFiniteElementSpace &pfes,
256  AssemblyLevel al,
257  MemoryType mt)
258  : TimeDependentOperator(pfes.GetVSize()),
259  x0(x_start), x_now(*pfes.GetMesh()->GetNodes()),
260  u(vel), u_coeff(&u), M(&pfes), K(&pfes), al(al)
261 {
263  if (al == AssemblyLevel::PARTIAL)
264  {
265  Kinteg->SetPAMemoryType(mt);
266  }
267  K.AddDomainIntegrator(Kinteg);
269  K.Assemble(0);
270  K.Finalize(0);
271 
272  MassIntegrator *Minteg = new MassIntegrator;
273  if (al == AssemblyLevel::PARTIAL)
274  {
275  Minteg->SetPAMemoryType(mt);
276  }
277  M.AddDomainIntegrator(Minteg);
279  M.Assemble(0);
280  M.Finalize(0);
281 }
282 
283 void ParAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
284 {
285  // Move the mesh.
286  const double t = GetTime();
287  add(x0, t, u, x_now);
289 
290  // Assemble on the new mesh.
291  K.BilinearForm::operator=(0.0);
292  K.Assemble();
294  K.Mult(ind, rhs);
295  M.BilinearForm::operator=(0.0);
296  M.Assemble();
297 
298  HypreParVector *RHS = rhs.ParallelAssemble();
300  X = 0.0;
301 
302  OperatorHandle Mop;
303  Solver *prec = nullptr;
304  Array<int> ess_tdof_list;
305  if (al == AssemblyLevel::PARTIAL)
306  {
307  M.FormSystemMatrix(ess_tdof_list, Mop);
308  prec = new OperatorJacobiSmoother(M, ess_tdof_list);
309  }
310  else
311  {
312  Mop.Reset(M.ParallelAssemble());
313  prec = new HypreSmoother;
314  static_cast<HypreSmoother*>(prec)->SetType(HypreSmoother::Jacobi, 1);
315  }
316 
317  CGSolver lin_solver(M.ParFESpace()->GetParMesh()->GetComm());
318  lin_solver.SetPreconditioner(*prec);
319  lin_solver.SetOperator(*Mop);
320  lin_solver.SetRelTol(1e-8);
321  lin_solver.SetAbsTol(0.0);
322  lin_solver.SetMaxIter(100);
323  lin_solver.SetPrintLevel(0);
324  lin_solver.Mult(*RHS, X);
325  K.ParFESpace()->GetProlongationMatrix()->Mult(X, di_dt);
326 
327  delete RHS;
328  delete prec;
329 }
330 #endif
331 
332 #ifdef MFEM_USE_GSLIB
334  const Vector &init_field)
335 {
336  nodes0 = init_nodes;
337  Mesh *m = mesh;
338 #ifdef MFEM_USE_MPI
339  if (pmesh) { m = pmesh; }
340 #endif
341  m->SetNodes(nodes0);
342 
343  const double rel_bbox_el = 0.1;
344  const double newton_tol = 1.0e-12;
345  const int npts_at_once = 256;
346 
347  if (finder)
348  {
349  finder->FreeData();
350  delete finder;
351  }
352 
354 #ifdef MFEM_USE_MPI
355  if (pfes)
356  {
357  f = pfes;
358  finder = new FindPointsGSLIB(pfes->GetComm());
359  }
360  else { finder = new FindPointsGSLIB(); }
361 #else
362  finder = new FindPointsGSLIB();
363 #endif
364  finder->Setup(*m, rel_bbox_el, newton_tol, npts_at_once);
365 
366  field0_gf.SetSpace(f);
367  field0_gf = init_field;
368 }
369 
371  Vector &new_field,
372  int new_nodes_ordering)
373 {
374  finder->Interpolate(new_nodes, field0_gf, new_field, new_nodes_ordering);
375 }
376 
377 #endif
378 
380  const Vector &b) const
381 {
382  const FiniteElementSpace *fes = NULL;
383  double energy_in = 0.0;
384 #ifdef MFEM_USE_MPI
385  const ParNonlinearForm *p_nlf = dynamic_cast<const ParNonlinearForm *>(oper);
386  MFEM_VERIFY(!(parallel && p_nlf == NULL), "Invalid Operator subclass.");
387  if (parallel)
388  {
389  fes = p_nlf->FESpace();
390  energy_in = p_nlf->GetEnergy(x);
391  }
392 #endif
393  const bool serial = !parallel;
394  const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
395  MFEM_VERIFY(!(serial && nlf == NULL), "Invalid Operator subclass.");
396  if (serial)
397  {
398  fes = nlf->FESpace();
399  energy_in = nlf->GetEnergy(x);
400  }
401 
402  // Get the local prolongation of the solution vector.
403  Vector x_out_loc(fes->GetVSize(),
405  if (serial)
406  {
407  const SparseMatrix *cP = fes->GetConformingProlongation();
408  if (!cP) { x_out_loc = x; }
409  else { cP->Mult(x, x_out_loc); }
410  }
411 #ifdef MFEM_USE_MPI
412  else
413  {
414  fes->GetProlongationMatrix()->Mult(x, x_out_loc);
415  }
416 #endif
417 
418  double scale = 1.0;
419  double avg_surf_fit_err, max_surf_fit_err = 0.0;
420  if (surf_fit_max_threshold > 0.0)
421  {
422  GetSurfaceFittingError(x_out_loc, avg_surf_fit_err, max_surf_fit_err);
423  if (max_surf_fit_err < surf_fit_max_threshold)
424  {
426  {
427  mfem::out << "TMOPNewtonSolver converged "
428  "based on the surface fitting error.\n";
429  }
430  scale = 0.0;
431  return scale;
432  }
433  }
435  {
437  {
438  mfem::out << "TMOPNewtonSolver converged "
439  "based on max number of times surface fitting weight can"
440  "be increased. \n";
441  }
442  scale = 0.0;
443  return scale;
444  }
445 
446  // Check if the starting mesh (given by x) is inverted. Note that x hasn't
447  // been modified by the Newton update yet.
448  const double min_detT_in = ComputeMinDet(x_out_loc, *fes);
449  const bool untangling = (min_detT_in <= 0.0) ? true : false;
450  const double untangle_factor = 1.5;
451  if (untangling)
452  {
453  // Needed for the line search below. The untangling metrics see this
454  // reference to detect deteriorations.
455  MFEM_VERIFY(min_det_ptr != NULL, " Initial mesh was valid, but"
456  " intermediate mesh is invalid. Contact TMOP Developers.");
457  MFEM_VERIFY(min_detJ_threshold == 0.0,
458  "This setup is not supported. Contact TMOP Developers.");
459  *min_det_ptr = untangle_factor * min_detT_in;
460  }
461 
462  const bool have_b = (b.Size() == Height());
463 
464  Vector x_out(x.Size());
465  bool x_out_ok = false;
466  double energy_out = 0.0, min_detT_out;
467  const double norm_in = Norm(r);
468 
469  const double detJ_factor = (solver_type == 1) ? 0.25 : 0.5;
471  // TODO:
472  // - Customized line search for worst-quality optimization.
473  // - What is the Newton exit criterion for worst-quality optimization?
474 
475  // Perform the line search.
476  for (int i = 0; i < 12; i++)
477  {
478  // Update the mesh and get the L-vector in x_out_loc.
479  add(x, -scale, c, x_out);
480  if (serial)
481  {
482  const SparseMatrix *cP = fes->GetConformingProlongation();
483  if (!cP) { x_out_loc = x_out; }
484  else { cP->Mult(x_out, x_out_loc); }
485  }
486 #ifdef MFEM_USE_MPI
487  else { fes->GetProlongationMatrix()->Mult(x_out, x_out_loc); }
488 #endif
489 
490  // Check the changes in detJ.
491  min_detT_out = ComputeMinDet(x_out_loc, *fes);
492  if (untangling == false && min_detT_out <= min_detJ_threshold)
493  {
494  // No untangling, and detJ got negative (or small) -- no good.
496  {
497  mfem::out << "Scale = " << scale << " Neg det(J) found.\n";
498  }
499  scale *= detJ_factor; continue;
500  }
501  if (untangling == true && min_detT_out < *min_det_ptr)
502  {
503  // Untangling, and detJ got even more negative -- no good.
505  {
506  mfem::out << "Scale = " << scale << " Neg det(J) decreased.\n";
507  }
508  scale *= detJ_factor; continue;
509  }
510 
511  // Skip the energy and residual checks when we're untangling. The
512  // untangling metrics change their denominators, which can affect the
513  // energy and residual, so their increase/decrease is not relevant.
514  if (untangling) { x_out_ok = true; break; }
515 
516  // Check the changes in total energy.
517  ProcessNewState(x_out);
518 
519  double avg_fit_err, max_fit_err = 0.0;
520  if (surf_fit_max_threshold > 0.0)
521  {
522  GetSurfaceFittingError(x_out_loc, avg_fit_err, max_fit_err);
523  }
524  if (surf_fit_max_threshold > 0.0 && max_fit_err >= 1.2*max_surf_fit_err)
525  {
527  {
528  mfem::out << "Scale = " << scale << " Surf fit err increased.\n";
529  }
530  scale *= 0.5; continue;
531  }
532 
533  if (serial)
534  {
535  energy_out = nlf->GetGridFunctionEnergy(x_out_loc);
536  }
537 #ifdef MFEM_USE_MPI
538  else
539  {
540  energy_out = p_nlf->GetParGridFunctionEnergy(x_out_loc);
541  }
542 #endif
543  if (energy_out > energy_in + 0.2*fabs(energy_in) ||
544  std::isnan(energy_out) != 0)
545  {
547  {
548  mfem::out << "Scale = " << scale << " Increasing energy: "
549  << energy_in << " --> " << energy_out << '\n';
550  }
551  scale *= 0.5; continue;
552  }
553 
554  // Check the changes in the Newton residual.
555  oper->Mult(x_out, r);
556  if (have_b) { r -= b; }
557  double norm_out = Norm(r);
558 
559  if (norm_out > 1.2*norm_in)
560  {
562  {
563  mfem::out << "Scale = " << scale << " Norm increased: "
564  << norm_in << " --> " << norm_out << '\n';
565  }
566  scale *= 0.5; continue;
567  }
568  else { x_out_ok = true; break; }
569  } // end line search
570 
571  if (untangling)
572  {
573  // Update the global min detJ. Untangling metrics see this min_det_ptr.
574  if (min_detT_out > 0.0)
575  {
576  *min_det_ptr = 0.0;
579  { mfem::out << "The mesh has been untangled at the used points!\n"; }
580  }
581  else { *min_det_ptr = untangle_factor * min_detT_out; }
582  }
583 
586  {
587  if (untangling)
588  {
589  mfem::out << "Min det(T) change: "
590  << min_detT_in << " -> " << min_detT_out
591  << " with " << scale << " scaling.\n";
592  }
593  else
594  {
595  mfem::out << "Energy decrease: "
596  << energy_in << " --> " << energy_out << " or "
597  << (energy_in - energy_out) / energy_in * 100.0
598  << "% with " << scale << " scaling.\n";
599  }
600  }
601 
602  if (x_out_ok == false) { scale = 0.0; }
603 
604  if (surf_fit_scale_factor > 0.0) { update_surf_fit_coeff = true; }
606 
607  return scale;
608 }
609 
611 {
612  const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
613  const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
614  TMOP_Integrator *ti = NULL;
615  TMOPComboIntegrator *co = NULL;
616  for (int i = 0; i < integs.Size(); i++)
617  {
618  ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
619  if (ti)
620  {
621  ti->UpdateSurfaceFittingWeight(factor);
622  }
623  co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
624  if (co)
625  {
627  for (int j = 0; j < ati.Size(); j++)
628  {
629  ati[j]->UpdateSurfaceFittingWeight(factor);
630  }
631  }
632  }
633 }
634 
636 {
637  const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
638  const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
639  TMOP_Integrator *ti = NULL;
640  TMOPComboIntegrator *co = NULL;
641  weights.SetSize(0);
642  double weight;
643 
644  for (int i = 0; i < integs.Size(); i++)
645  {
646  ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
647  if (ti)
648  {
649  weight = ti->GetSurfaceFittingWeight();
650  weights.Append(weight);
651  }
652  co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
653  if (co)
654  {
656  for (int j = 0; j < ati.Size(); j++)
657  {
658  weight = ati[j]->GetSurfaceFittingWeight();
659  weights.Append(weight);
660  }
661  }
662  }
663 }
664 
666  double &err_avg,
667  double &err_max) const
668 {
669  const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
670  const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
671  TMOP_Integrator *ti = NULL;
672  TMOPComboIntegrator *co = NULL;
673 
674  err_avg = 0.0;
675  err_max = 0.0;
676  double err_avg_loc, err_max_loc;
677  for (int i = 0; i < integs.Size(); i++)
678  {
679  ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
680  if (ti)
681  {
682  if (ti->IsSurfaceFittingEnabled())
683  {
684  ti->GetSurfaceFittingErrors(x_loc, err_avg_loc, err_max_loc);
685  err_avg = std::fmax(err_avg_loc, err_avg);
686  err_max = std::fmax(err_max_loc, err_max);
687  }
688  }
689  co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
690  if (co)
691  {
693  for (int j = 0; j < ati.Size(); j++)
694  {
695  if (ati[j]->IsSurfaceFittingEnabled())
696  {
697  ati[j]->GetSurfaceFittingErrors(x_loc, err_avg_loc, err_max_loc);
698  err_avg = std::fmax(err_avg_loc, err_avg);
699  err_max = std::fmax(err_max_loc, err_max);
700  }
701  }
702  }
703  }
704 }
705 
707 {
708  const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
709  const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
710 
711  // Reset the update flags of all TargetConstructors. This is done to avoid
712  // repeated updates of shared TargetConstructors.
713  TMOP_Integrator *ti = NULL;
714  TMOPComboIntegrator *co = NULL;
715  DiscreteAdaptTC *dtc = NULL;
716  for (int i = 0; i < integs.Size(); i++)
717  {
718  ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
719  if (ti)
720  {
721  dtc = ti->GetDiscreteAdaptTC();
722  if (dtc) { dtc->ResetUpdateFlags(); }
723  }
724  co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
725  if (co)
726  {
728  for (int j = 0; j < ati.Size(); j++)
729  {
730  dtc = ati[j]->GetDiscreteAdaptTC();
731  if (dtc) { dtc->ResetUpdateFlags(); }
732  }
733  }
734  }
735 
736  Vector x_loc;
737  const FiniteElementSpace *x_fes = nullptr;
738  if (parallel)
739  {
740 #ifdef MFEM_USE_MPI
741  const ParNonlinearForm *pnlf =
742  dynamic_cast<const ParNonlinearForm *>(oper);
743 
744  x_fes = pnlf->ParFESpace();
745  x_loc.SetSize(x_fes->GetVSize());
746  x_fes->GetProlongationMatrix()->Mult(x, x_loc);
747 #endif
748  }
749  else
750  {
751  x_fes = nlf->FESpace();
752  const Operator *P = nlf->GetProlongation();
753  if (P)
754  {
755  x_loc.SetSize(P->Height());
756  P->Mult(x,x_loc);
757  }
758  else { x_loc = x; }
759  }
760 
761  for (int i = 0; i < integs.Size(); i++)
762  {
763  ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
764  if (ti)
765  {
766  ti->UpdateAfterMeshPositionChange(x_loc, *x_fes);
768  {
769  ti->ComputeUntangleMetricQuantiles(x_loc, *x_fes);
770  }
771  }
772  co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
773  if (co)
774  {
776  for (int j = 0; j < ati.Size(); j++)
777  {
778  ati[j]->UpdateAfterMeshPositionChange(x_loc, *x_fes);
780  {
781  ati[j]->ComputeUntangleMetricQuantiles(x_loc, *x_fes);
782  }
783  }
784  }
785  }
786 
787  // Constant coefficient associated with the surface fitting terms if
788  // adaptive surface fitting is enabled. The idea is to increase the
789  // coefficient if the surface fitting error does not sufficiently
790  // decrease between subsequent TMOPNewtonSolver iterations.
792  {
793  // Get surface fitting errors.
795  // Get array with surface fitting weights.
796  Array<double> weights;
797  GetSurfaceFittingWeight(weights);
798 
800  {
801  mfem::out << "Avg/Max surface fitting error: " <<
802  surf_fit_err_avg << " " <<
803  surf_fit_err_max << "\n";
804  mfem::out << "Min/Max surface fitting weight: " <<
805  weights.Min() << " " << weights.Max() << "\n";
806  }
807 
808  double change_surf_fit_err = surf_fit_err_avg_prvs-surf_fit_err_avg;
809  double rel_change_surf_fit_err = change_surf_fit_err/surf_fit_err_avg_prvs;
810  // Increase the surface fitting coefficient if the surface fitting error
811  // does not decrease sufficiently.
812  if (rel_change_surf_fit_err < surf_fit_rel_change_threshold)
813  {
815  adapt_inc_count += 1;
816  }
817  else
818  {
819  adapt_inc_count = 0;
820  }
822  update_surf_fit_coeff = false;
823  }
824 }
825 
827  const FiniteElementSpace &fes) const
828 {
829  double min_detJ = infinity();
830  const int NE = fes.GetNE(), dim = fes.GetMesh()->Dimension();
831  Array<int> xdofs;
832  DenseMatrix Jpr(dim);
833  const bool mixed_mesh = fes.GetMesh()->GetNumGeometries(dim) > 1;
834  if (dim == 1 || mixed_mesh || UsesTensorBasis(fes) == false)
835  {
836  for (int i = 0; i < NE; i++)
837  {
838  const int dof = fes.GetFE(i)->GetDof();
839  DenseMatrix dshape(dof, dim), pos(dof, dim);
840  Vector posV(pos.Data(), dof * dim);
841 
842  fes.GetElementVDofs(i, xdofs);
843  x_loc.GetSubVector(xdofs, posV);
844 
845  const IntegrationRule &irule = GetIntegrationRule(*fes.GetFE(i));
846  const int nsp = irule.GetNPoints();
847  for (int j = 0; j < nsp; j++)
848  {
849  fes.GetFE(i)->CalcDShape(irule.IntPoint(j), dshape);
850  MultAtB(pos, dshape, Jpr);
851  min_detJ = std::min(min_detJ, Jpr.Det());
852  }
853  }
854  }
855  else
856  {
857  min_detJ = dim == 2 ? MinDetJpr_2D(&fes, x_loc) :
858  dim == 3 ? MinDetJpr_3D(&fes, x_loc) : 0.0;
859  }
860  double min_detT_all = min_detJ;
861 #ifdef MFEM_USE_MPI
862  if (parallel)
863  {
864  auto p_nlf = dynamic_cast<const ParNonlinearForm *>(oper);
865  MPI_Allreduce(&min_detJ, &min_detT_all, 1, MPI_DOUBLE, MPI_MIN,
866  p_nlf->ParFESpace()->GetComm());
867  }
868 #endif
869  const DenseMatrix &Wideal =
871  min_detT_all /= Wideal.Det();
872 
873  return min_detT_all;
874 }
875 
876 #ifdef MFEM_USE_MPI
877 // Metric values are visualized by creating an L2 finite element functions and
878 // computing the metric values at the nodes.
880  const TargetConstructor &tc, ParMesh &pmesh,
881  char *title, int position)
882 {
884  ParFiniteElementSpace fes(&pmesh, &fec, 1);
885  ParGridFunction metric(&fes);
886  InterpolateTMOP_QualityMetric(qm, tc, pmesh, metric);
887  socketstream sock;
888  if (pmesh.GetMyRank() == 0)
889  {
890  sock.open("localhost", 19916);
891  sock << "solution\n";
892  }
893  pmesh.PrintAsOne(sock);
894  metric.SaveAsOne(sock);
895  if (pmesh.GetMyRank() == 0)
896  {
897  sock << "window_title '"<< title << "'\n"
898  << "window_geometry "
899  << position << " " << 0 << " " << 600 << " " << 600 << "\n"
900  << "keys jRmclA\n";
901  }
902 }
903 #endif
904 
905 // Metric values are visualized by creating an L2 finite element functions and
906 // computing the metric values at the nodes.
908  const TargetConstructor &tc, Mesh &mesh,
909  char *title, int position)
910 {
912  FiniteElementSpace fes(&mesh, &fec, 1);
913  GridFunction metric(&fes);
914  InterpolateTMOP_QualityMetric(qm, tc, mesh, metric);
915  osockstream sock(19916, "localhost");
916  sock << "solution\n";
917  mesh.Print(sock);
918  metric.Save(sock);
919  sock.send();
920  sock << "window_title '"<< title << "'\n"
921  << "window_geometry "
922  << position << " " << 0 << " " << 600 << " " << 600 << "\n"
923  << "keys jRmclA\n";
924 }
925 
926 }
VectorGridFunctionCoefficient u_coeff
Definition: tmop_tools.hpp:91
void vis_tmop_metric_p(int order, TMOP_QualityMetric &qm, const TargetConstructor &tc, ParMesh &pmesh, char *title, int position)
Definition: tmop_tools.cpp:879
void ComputeUntangleMetricQuantiles(const Vector &x, const FiniteElementSpace &fes)
Definition: tmop.cpp:4645
virtual void Interpolate(const GridFunction &field_in, Vector &field_out)
Definition: gslib.cpp:820
T Min() const
Find the minimal element in the array, using the comparison operator < for class T.
Definition: array.cpp:85
virtual void Mult(const Vector &x, Vector &y) const
Matrix vector multiplication.
Definition: sparsemat.cpp:733
Conjugate gradient method.
Definition: solvers.hpp:493
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition: intrules.hpp:253
ParFiniteElementSpace * ParFESpace() const
Return the parallel FE space associated with the ParBilinearForm.
virtual void GetSurfaceFittingError(const Vector &x_loc, double &err_avg, double &err_max) const
Definition: tmop_tools.cpp:665
AssemblyLevel
Enumeration defining the assembly level for bilinear and nonlinear form classes derived from Operator...
Class for an integration rule - an Array of IntegrationPoint.
Definition: intrules.hpp:96
virtual const Operator * GetProlongationMatrix() const
The returned Operator is owned by the FiniteElementSpace.
Definition: pfespace.cpp:1152
Class for grid function - Vector with associated FE space.
Definition: gridfunc.hpp:30
const Operator * oper
Definition: solvers.hpp:120
Data type for scaled Jacobi-type smoother of sparse matrix.
virtual void ComputeAtNewPosition(const Vector &new_nodes, Vector &new_field, int new_nodes_ordering=Ordering::byNODES)
Definition: tmop_tools.cpp:370
double GetSurfaceFittingWeight()
Get the surface fitting weight.
Definition: tmop.cpp:4203
void Assemble(int skip_zeros=1)
Assembles the form i.e. sums over all domain/bdr integrators.
virtual void Mult(const Vector &ind, Vector &di_dt) const
Perform the action of the operator: y = k = f(x, t), where k solves the algebraic equation F(x...
Definition: tmop_tools.cpp:214
const SparseMatrix & SpMat() const
Returns a const reference to the sparse matrix: .
void GetSurfaceFittingWeight(Array< double > &weights) const
Get the surface fitting weight for all the TMOP integrators.
Definition: tmop_tools.cpp:635
FiniteElementSpace * FESpace()
Return the FE space associated with the BilinearForm.
int Dimension() const
Dimension of the reference space used within the elements.
Definition: mesh.hpp:1020
double ComputeMinDet(const Vector &x_loc, const FiniteElementSpace &fes) const
Definition: tmop_tools.cpp:826
void SaveAsOne(const char *fname, int precision=16) const
Definition: pgridfunc.cpp:935
void SetSize(int s)
Resize the vector to size s.
Definition: vector.hpp:517
void InterpolateTMOP_QualityMetric(TMOP_QualityMetric &metric, const TargetConstructor &tc, const Mesh &mesh, GridFunction &metric_gf)
Interpolates the metric&#39;s values at the nodes of metric_gf.
Definition: tmop.cpp:4867
Base abstract class for first order time dependent operators.
Definition: operator.hpp:316
bool summary
A summary of the solver process will be reported after the last iteration to mfem::out.
Definition: solvers.hpp:91
const Array< TMOP_Integrator * > & GetTMOPIntegrators() const
Definition: tmop.hpp:2256
bool first_and_last
Information about the first and last iteration will be printed to mfem::out.
Definition: solvers.hpp:94
Pointer to an Operator of a specified type.
Definition: handle.hpp:33
DiscreteAdaptTC * GetDiscreteAdaptTC() const
Definition: tmop.hpp:2204
virtual void Mult(const Vector &b, Vector &x) const
Operator application: y=A(x).
Definition: solvers.cpp:718
double Det() const
Definition: densemat.cpp:487
int Size() const
Returns the size of the vector.
Definition: vector.hpp:197
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition: array.cpp:68
ParFiniteElementSpace * pfes
Definition: tmop.hpp:1285
Data type dense matrix using column-major storage.
Definition: densemat.hpp:23
double MinDetJpr_2D(const FiniteElementSpace *, const Vector &) const
Definition: tmop_pa_jp2.cpp:75
double * Data() const
Returns the matrix data array.
Definition: densemat.hpp:111
Parallel non-linear operator on the true dofs.
void Init(TimeDependentOperator &f_) override
Associate a TimeDependentOperator with the ODE solver.
Definition: ode.cpp:100
bool IsSurfaceFittingEnabled()
Definition: tmop.hpp:2143
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
void SetAssemblyLevel(AssemblyLevel assembly_level)
Set the desired assembly level.
Abstract parallel finite element space.
Definition: pfespace.hpp:28
void vis_tmop_metric_s(int order, TMOP_QualityMetric &qm, const TargetConstructor &tc, Mesh &mesh, char *title, int position)
Definition: tmop_tools.cpp:907
void GetSubVector(const Array< int > &dofs, Vector &elemvect) const
Extract entries listed in dofs to the output Vector elemvect.
Definition: vector.cpp:579
void UpdateSurfaceFittingWeight(double factor)
Update the surface fitting weight as surf_fit_coeff *= factor;.
Definition: tmop.cpp:4191
bool UsesTensorBasis(const FiniteElementSpace &fes)
Return true if the mesh contains only one topology and the elements are tensor elements.
Definition: fespace.hpp:1306
virtual void Mult(const Vector &x, Vector &y) const
Matrix vector multiplication: .
void UpdateAfterMeshPositionChange(const Vector &x_new, const FiniteElementSpace &x_fes)
Definition: tmop.cpp:4345
Geometry Geometries
Definition: fe.cpp:49
double surf_fit_rel_change_threshold
Definition: tmop_tools.hpp:145
void add(const Vector &v1, const Vector &v2, Vector &v)
Definition: vector.cpp:317
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i&#39;th element in t...
Definition: fespace.cpp:2841
int GetNumGeometries(int dim) const
Return the number of geometries of the given dimension present in the mesh.
Definition: mesh.cpp:6274
HypreParMatrix * ParallelAssemble()
Returns the matrix assembled on the true dofs, i.e. P^t A P.
virtual void ComputeAtNewPosition(const Vector &new_nodes, Vector &new_field, int new_nodes_ordering=Ordering::byNODES)
Definition: tmop_tools.cpp:29
double t
Current time.
Definition: operator.hpp:340
std::function< double(const Vector &)> f(double mass_coeff)
Definition: lor_mms.hpp:30
double Norm(const Vector &x) const
Definition: solvers.hpp:172
Geometry::Type GetGeomType() const
Returns the Geometry::Type of the reference element.
Definition: fe_base.hpp:320
Abstract class for local mesh quality metrics in the target-matrix optimization paradigm (TMOP) by P...
Definition: tmop.hpp:23
IntegrationPoint & IntPoint(int i)
Returns a reference to the i-th integration point.
Definition: intrules.hpp:256
virtual void SetPrintLevel(int print_lvl)
Legacy method to set the level of verbosity of the solver output.
Definition: solvers.cpp:71
double GetElementSize(ElementTransformation *T, int type=0)
Definition: mesh.cpp:83
Data type sparse matrix.
Definition: sparsemat.hpp:50
int Append(const T &el)
Append element &#39;el&#39; to array, resize if necessary.
Definition: array.hpp:759
virtual void FreeData()
Definition: gslib.cpp:272
const FiniteElementCollection * FEColl() const
Definition: fespace.hpp:723
Array< NonlinearFormIntegrator * > * GetDNFI()
Access all integrators added with AddDomainIntegrator().
Jacobi smoothing for a given bilinear form (no matrix necessary).
Definition: solvers.hpp:302
double b
Definition: lissajous.cpp:42
const AssemblyLevel al
Definition: tmop_tools.hpp:93
static MemoryType GetDeviceMemoryType()
Get the current Device MemoryType. This is the MemoryType used by most MFEM classes when allocating m...
Definition: device.hpp:273
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const
Definition: tmop_tools.cpp:379
ParMesh * GetParMesh() const
Definition: pfespace.hpp:273
void SetMaxIter(int max_it)
Definition: solvers.hpp:201
void Assemble(int skip_zeros=1)
Assemble the local matrix.
void Setup(Mesh &m, const double bb_t=0.1, const double newt_tol=1.0e-12, const int npt_max=256)
Definition: gslib.cpp:107
Performs a single remap advection step in serial.
Definition: tmop_tools.hpp:85
const SparseMatrix * GetConformingProlongation() const
The returned SparseMatrix is owned by the FiniteElementSpace.
Definition: fespace.cpp:1268
virtual double GetEnergy(const Vector &x) const
Compute the energy corresponding to the state x.
virtual void Mult(const Vector &ind, Vector &di_dt) const
Perform the action of the operator: y = k = f(x, t), where k solves the algebraic equation F(x...
Definition: tmop_tools.cpp:283
Parallel smoothers in hypre.
Definition: hypre.hpp:971
double GetGridFunctionEnergy(const Vector &x) const
Compute the energy corresponding to the state x.
FiniteElementSpace * FESpace()
Definition: gridfunc.hpp:691
int GetNE() const
Returns number of elements in the mesh.
Definition: fespace.hpp:736
void SetPAMemoryType(MemoryType mt)
Definition: nonlininteg.hpp:72
MPI_Comm GetComm() const
Definition: pmesh.hpp:351
double GetParGridFunctionEnergy(const Vector &x) const
Compute the energy corresponding to the state x.
FiniteElementSpace * FESpace()
DofTransformation * GetElementVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom for the i&#39;th element. The returned indices are offsets into an ...
Definition: fespace.cpp:281
int GetMyRank() const
Definition: pmesh.hpp:353
Wrapper for hypre&#39;s parallel vector class.
Definition: hypre.hpp:161
Mesh * GetMesh() const
Returns the mesh.
Definition: fespace.hpp:555
void SetAbsTol(double atol)
Definition: solvers.hpp:200
virtual void ProcessNewState(const Vector &x) const
Definition: tmop_tools.cpp:706
PrintLevel print_options
Output behavior for the iterative solver.
Definition: solvers.hpp:137
void SetRelTol(double rtol)
Definition: solvers.hpp:199
ComplexDenseMatrix * MultAtB(const ComplexDenseMatrix &A, const ComplexDenseMatrix &B)
Multiply the complex conjugate transpose of a matrix A with a matrix B. A^H*B.
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition: fespace.hpp:219
ParFiniteElementSpace * ParFESpace() const
const AssemblyLevel al
Definition: tmop_tools.hpp:115
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition: globals.hpp:66
void subtract(const Vector &x, const Vector &y, Vector &z)
Definition: vector.cpp:473
MemoryType
Memory types supported by MFEM.
Definition: mem_manager.hpp:31
double Min() const
Returns the minimal element of the vector.
Definition: vector.cpp:1215
Performs a single remap advection step in parallel.
Definition: tmop_tools.hpp:107
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:687
string space
double MinDetJpr_3D(const FiniteElementSpace *, const Vector &) const
Definition: tmop_pa_jp3.cpp:77
void PrintAsOne(std::ostream &out=mfem::out) const
Definition: pmesh.cpp:4969
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition: fe_base.hpp:323
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition: operator.hpp:66
double Max() const
Returns the maximal element of the vector.
Definition: vector.cpp:925
void ResetUpdateFlags()
Used in combination with the Update methods to avoid extra computations.
Definition: tmop.hpp:1610
const IntegrationRule & GetIntegrationRule(const FiniteElement &el) const
Definition: tmop_tools.hpp:164
virtual double GetTime() const
Read the currently set time.
Definition: operator.hpp:357
int GetNE() const
Returns number of elements.
Definition: mesh.hpp:1086
virtual void FormSystemMatrix(const Array< int > &ess_tdof_list, OperatorHandle &A)
Form the linear system matrix A, see FormLinearSystem() for details.
virtual const Operator * GetProlongationMatrix() const
The returned Operator is owned by the FiniteElementSpace.
Definition: fespace.hpp:593
virtual void Finalize(int skip_zeros=1)
Finalizes the matrix initialization.
Mesh * GetMesh(int type)
Definition: ex29.cpp:218
int dim
Definition: ex24.cpp:53
virtual void SetInitialField(const Vector &init_nodes, const Vector &init_field)
Definition: tmop_tools.cpp:22
VectorGridFunctionCoefficient u_coeff
Definition: tmop_tools.hpp:113
void AddDomainIntegrator(BilinearFormIntegrator *bfi)
Adds new Domain Integrator. Assumes ownership of bfi.
void GetSurfaceFittingErrors(const Vector &pos, double &err_avg, double &err_max)
Definition: tmop.cpp:3047
SerialAdvectorCGOper(const Vector &x_start, GridFunction &vel, FiniteElementSpace &fes, AssemblyLevel al=AssemblyLevel::LEGACY)
Definition: tmop_tools.cpp:193
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Definition: fespace.hpp:709
double infinity()
Define a shortcut for std::numeric_limits<double>::infinity()
Definition: vector.hpp:44
const DenseMatrix & GetGeomToPerfGeomJac(int GeomType) const
Definition: geom.hpp:98
virtual void SetSpace(FiniteElementSpace *f)
Associate a new FiniteElementSpace with the GridFunction.
Definition: gridfunc.cpp:195
int open(const char hostname[], int port)
Open the socket stream on &#39;port&#39; at &#39;hostname&#39;.
int Size() const
Return the logical size of the array.
Definition: array.hpp:141
RefCoord t[3]
MPI_Comm GetComm() const
Definition: pfespace.hpp:269
virtual void SetOperator(const Operator &op)
Also calls SetOperator for the preconditioner if there is one.
Definition: solvers.hpp:507
ParAdvectorCGOper(const Vector &x_start, GridFunction &vel, ParFiniteElementSpace &pfes, AssemblyLevel al=AssemblyLevel::LEGACY, MemoryType mt=MemoryType::DEFAULT)
Definition: tmop_tools.cpp:253
FindPointsGSLIB can robustly evaluate a GridFunction on an arbitrary collection of points...
Definition: gslib.hpp:52
Vector data type.
Definition: vector.hpp:58
virtual void Print(std::ostream &os=mfem::out) const
Definition: mesh.hpp:2011
void MakeRef(Vector &base, int offset, int size)
Reset the Vector to be a reference to a sub-vector of base.
Definition: vector.hpp:581
virtual void SetPreconditioner(Solver &pr)
This should be called before SetOperator.
Definition: solvers.cpp:173
void Step(Vector &x, double &t, double &dt) override
Perform a time step from time t [in] to time t [out] based on the requested step size dt [in]...
Definition: ode.cpp:109
void GetNodes(Vector &node_coord) const
Definition: mesh.cpp:8302
virtual const Operator * GetProlongation() const
Get the finite element space prolongation matrix.
RefCoord s[3]
int Size() const
Get the size of the BilinearForm as a square matrix.
bool iterations
Detailed information about each iteration will be reported to mfem::out.
Definition: solvers.hpp:88
double GetEnergy(const ParGridFunction &x) const
Compute the energy of a ParGridFunction.
double u(const Vector &xvec)
Definition: lor_mms.hpp:22
void SetNodes(const Vector &node_coord)
Updates the vertex/node locations. Invokes NodesUpdated().
Definition: mesh.cpp:8314
Base class for solvers.
Definition: operator.hpp:682
Class for parallel grid function.
Definition: pgridfunc.hpp:32
Base class representing target-matrix construction algorithms for mesh optimization via the target-ma...
Definition: tmop.hpp:1329
virtual void Save(std::ostream &out) const
Save the GridFunction to an output stream.
Definition: gridfunc.cpp:3696
Abstract operator.
Definition: operator.hpp:24
virtual void CalcDShape(const IntegrationPoint &ip, DenseMatrix &dshape) const =0
Evaluate the gradients of all shape functions of a scalar finite element in reference space at the gi...
Class for parallel meshes.
Definition: pmesh.hpp:32
virtual double * HostReadWrite()
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
Definition: vector.hpp:473
void UpdateSurfaceFittingWeight(double factor) const
Update surface fitting weight as surf_fit_weight *= factor.
Definition: tmop_tools.cpp:610
void Reset(OpType *A, bool own_A=true)
Reset the OperatorHandle to the given OpType pointer, A.
Definition: handle.hpp:145
virtual void SetInitialField(const Vector &init_nodes, const Vector &init_field)
Definition: tmop_tools.cpp:333
FiniteElementSpace * fes
Definition: tmop.hpp:1280
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition: fe_coll.hpp:327
void NodesUpdated()
This function should be called after the mesh node coordinates have been updated externally, e.g. by modifying the internal nodal GridFunction returned by GetNodes().
Definition: mesh.hpp:1835
alpha (q . grad u, v)
A TMOP integrator class based on any given TMOP_QualityMetric and TargetConstructor.
Definition: tmop.hpp:1733