Nektar++
ProcessVarOpti.cpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: ProcessJac.cpp
4 //
5 // For more information, please see: http://www.nektar.info/
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a
14 // copy of this software and associated documentation files (the "Software"),
15 // to deal in the Software without restriction, including without limitation
16 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 // and/or sell copies of the Software, and to permit persons to whom the
18 // Software is furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included
21 // in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 // DEALINGS IN THE SOFTWARE.
30 //
31 // Description: Calculate Jacobians of elements.
32 //
33 ////////////////////////////////////////////////////////////////////////////////
34 
35 #include <boost/core/ignore_unused.hpp>
36 
39 
40 #include "ElUtil.h"
41 #include "NodeOpti.h"
42 #include "ProcessVarOpti.h"
44 
45 #include <StdRegions/StdPrismExp.h>
46 #include <StdRegions/StdQuadExp.h>
47 #include <StdRegions/StdTetExp.h>
48 #include <StdRegions/StdTriExp.h>
49 
52 
53 // Including Timer.h includes Windows.h, which causes GetJob to be set as a
54 // macro for some reason.
55 #if _WIN32
56 #undef GetJob
57 #endif
58 
59 using namespace std;
60 using namespace Nektar::NekMeshUtils;
61 
62 namespace Nektar
63 {
64 namespace Utilities
65 {
66 
67 ModuleKey ProcessVarOpti::className =
69  ModuleKey(eProcessModule, "varopti"), ProcessVarOpti::create,
70  "Optimise mesh locations.");
71 
72 ProcessVarOpti::ProcessVarOpti(MeshSharedPtr m) : ProcessModule(m)
73 {
74  // clang-format off
75  m_config["linearelastic"] =
76  ConfigOption(true, "", "Optimise for linear elasticity");
77  m_config["winslow"] =
78  ConfigOption(true, "", "Optimise for winslow");
79  m_config["roca"] =
80  ConfigOption(true, "", "Optimise for roca method");
81  m_config["hyperelastic"] =
82  ConfigOption(true, "", "Optimise for hyper elasticity");
83  m_config["numthreads"] =
84  ConfigOption(false, "1", "Number of threads");
85  m_config["restol"] =
86  ConfigOption(false, "1e-6", "Tolerance criterion");
87  m_config["maxiter"] =
88  ConfigOption(false, "500", "Maximum number of iterations");
89  m_config["nq"] =
90  ConfigOption(false, "-1", "Order of mesh");
91  m_config["region"] =
92  ConfigOption(false, "0.0", "create regions based on target");
93  m_config["resfile"] =
94  ConfigOption(false, "", "writes residual values to file");
95  m_config["histfile"] =
96  ConfigOption(false, "", "histogram of scaled jac");
97  m_config["overint"] =
98  ConfigOption(false, "6", "over integration order");
99  m_config["analytics"] =
100  ConfigOption(false, "", "basic analytics module");
101  // clang-format on
102 }
103 
105 {
106 }
107 
109 {
110  if (m_mesh->m_verbose)
111  {
112  cout << "ProcessVarOpti: Optimising... " << endl;
113  }
114 
115  if (m_config["linearelastic"].beenSet)
116  {
117  m_opti = eLinEl;
118  }
119  else if (m_config["winslow"].beenSet)
120  {
121  m_opti = eWins;
122  }
123  else if (m_config["roca"].beenSet)
124  {
125  m_opti = eRoca;
126  }
127  else if (m_config["hyperelastic"].beenSet)
128  {
129  m_opti = eHypEl;
130  }
131  else
132  {
133  ASSERTL0(false, "not opti type set");
134  }
135 
136  const int maxIter = m_config["maxiter"].as<int>();
137  const NekDouble restol = m_config["restol"].as<NekDouble>();
138 
139  // m_mesh->m_nummode = m_config["nq"].as<int>();
140 
141  bool fd = false;
142 
143  if (m_config["nq"].beenSet)
144  {
145  m_mesh->m_nummode = m_config["nq"].as<int>();
146  fd = true;
147  }
148 
149  if (!fd)
150  {
151  for (auto &edge : m_mesh->m_edgeSet)
152  {
153  if (edge->m_edgeNodes.size() > 0)
154  {
155  m_mesh->m_nummode = edge->m_edgeNodes.size() + 2;
156  fd = true;
157  break;
158  }
159  }
160  }
161  ASSERTL0(fd, "failed to find order of mesh");
162 
163  // Safety feature: limit over-integration order for high-order triangles
164  // over order 5.
165  int intOrder = m_config["overint"].as<int>();
166  intOrder = m_mesh->m_nummode + intOrder <= 11 ?
167  intOrder : 11 - m_mesh->m_nummode;
168 
169  if (m_mesh->m_verbose)
170  {
171  cout << "Identified mesh order as: " << m_mesh->m_nummode - 1 << endl;
172  }
173 
174  if (m_mesh->m_expDim == 2 && m_mesh->m_spaceDim == 3)
175  {
176  ASSERTL0(false, "cannot deal with manifolds");
177  }
178 
179  m_res = std::shared_ptr<Residual>(new Residual);
180  m_res->val = 1.0;
181 
182 
183  m_mesh->MakeOrder(m_mesh->m_nummode - 1,
185 
186  if (m_config["analytics"].beenSet)
187  {
188  Analytics();
189  return;
190  }
191 
192  map<LibUtilities::ShapeType, DerivUtilSharedPtr> derivUtils =
193  BuildDerivUtil(intOrder);
194 
195  GetElementMap(intOrder, derivUtils);
196 
197  m_res->startInv = 0;
198  m_res->worstJac = numeric_limits<double>::max();
199  for (int i = 0; i < m_dataSet.size(); i++)
200  {
201  m_dataSet[i]->Evaluate();
202  m_dataSet[i]->InitialMinJac();
203  }
204 
205  vector<ElUtilSharedPtr> elLock;
206 
207  if (m_config["region"].beenSet)
208  {
209  elLock = GetLockedElements(m_config["region"].as<NekDouble>());
210  }
211 
212 
213  vector<vector<NodeSharedPtr> > freenodes = GetColouredNodes(elLock);
214  vector<vector<NodeOptiSharedPtr> > optiNodes;
215 
216  // turn the free nodes into optimisable objects with all required data
217  set<int> check;
218  for (int i = 0; i < freenodes.size(); i++)
219  {
220  vector<NodeOptiSharedPtr> ns;
221  for (int j = 0; j < freenodes[i].size(); j++)
222  {
223  auto it = m_nodeElMap.find(freenodes[i][j]->m_id);
224  ASSERTL0(it != m_nodeElMap.end(), "could not find");
225 
226  int optiKind = m_mesh->m_spaceDim;
227 
228  if (freenodes[i][j]->GetNumCadCurve() == 1)
229  {
230  optiKind += 10;
231  }
232  else if (freenodes[i][j]->GetNumCADSurf() == 1)
233  {
234  optiKind += 20;
235  }
236  else
237  {
238  optiKind += 10 * m_mesh->m_expDim;
239  }
240 
241  auto c = check.find(freenodes[i][j]->m_id);
242  ASSERTL0(c == check.end(), "duplicate node");
243  check.insert(freenodes[i][j]->m_id);
244 
245  ns.push_back(GetNodeOptiFactory().CreateInstance(
246  optiKind, freenodes[i][j], it->second, m_res, derivUtils,
247  m_opti));
248  }
249  optiNodes.push_back(ns);
250  }
251 
252  int nset = optiNodes.size();
253  int p = 0;
254  int mn = numeric_limits<int>::max();
255  int mx = 0;
256  for (int i = 0; i < nset; i++)
257  {
258  p += optiNodes[i].size();
259  mn = min(mn, int(optiNodes[i].size()));
260  mx = max(mx, int(optiNodes[i].size()));
261  }
262 
263  if (m_config["histfile"].beenSet)
264  {
265  ofstream histFile;
266  string name = m_config["histfile"].as<string>() + "_start.txt";
267  histFile.open(name.c_str());
268 
269  for (int i = 0; i < m_dataSet.size(); i++)
270  {
271  histFile << m_dataSet[i]->GetScaledJac() << endl;
272  }
273  histFile.close();
274  }
275 
276  if(m_mesh->m_verbose)
277  {
278  cout << scientific << endl;
279  cout << "N elements:\t\t" << m_mesh->m_element[m_mesh->m_expDim].size() - elLock.size() << endl
280  << "N elements invalid:\t" << m_res->startInv << endl
281  << "Worst jacobian:\t\t" << m_res->worstJac << endl
282  << "N free nodes:\t\t" << m_res->n << endl
283  << "N Dof:\t\t\t" << m_res->nDoF << endl
284  << "N color sets:\t\t" << nset << endl
285  << "Avg set colors:\t\t" << p/nset << endl
286  << "Min set:\t\t" << mn << endl
287  << "Max set:\t\t" << mx << endl
288  << "Residual tolerance:\t" << restol << endl;
289  }
290 
291  int nThreads = m_config["numthreads"].as<int>();
292 
293  int ctr = 0;
295  tms.SetThreadingType("ThreadManagerBoost");
298 
300  t.Start();
301 
302  ofstream resFile;
303  if (m_config["resfile"].beenSet)
304  {
305  resFile.open(m_config["resfile"].as<string>().c_str());
306  }
307 
308  for (int i = 0; i < optiNodes.size(); i++)
309  {
310  vector<Thread::ThreadJob *> jobs(optiNodes[i].size());
311  for (int j = 0; j < optiNodes[i].size(); j++)
312  {
313  optiNodes[i][j]->CalcMinJac();
314  }
315  }
316 
317  while (m_res->val > restol)
318  {
319  ctr++;
320  m_res->val = 0.0;
321  m_res->func = 0.0;
322  m_res->nReset[0] = 0;
323  m_res->nReset[1] = 0;
324  m_res->nReset[2] = 0;
325  m_res->alphaI = 0;
326  for (int i = 0; i < optiNodes.size(); i++)
327  {
328  vector<Thread::ThreadJob *> jobs(optiNodes[i].size());
329  for (int j = 0; j < optiNodes[i].size(); j++)
330  {
331  jobs[j] = optiNodes[i][j]->GetJob();
332  }
333 
334  tm->SetNumWorkers(0);
335  tm->QueueJobs(jobs);
336  tm->SetNumWorkers(nThreads);
337  tm->Wait();
338  }
339 
340  m_res->startInv = 0;
341  m_res->worstJac = numeric_limits<double>::max();
342 
343  vector<Thread::ThreadJob *> elJobs(m_dataSet.size());
344  for (int i = 0; i < m_dataSet.size(); i++)
345  {
346  elJobs[i] = m_dataSet[i]->GetJob();
347  }
348 
349  tm->SetNumWorkers(0);
350  tm->QueueJobs(elJobs);
351  tm->SetNumWorkers(nThreads);
352  tm->Wait();
353 
354  if (m_config["resfile"].beenSet)
355  {
356  resFile << m_res->val << " " << m_res->worstJac << " "
357  << m_res->func << endl;
358  }
359 
360  if(m_mesh->m_verbose)
361  {
362  cout << ctr
363  << "\tResidual: " << m_res->val
364  << "\tMin Jac: " << m_res->worstJac
365  << "\tInvalid: " << m_res->startInv
366  << "\tReset nodes: " << m_res->nReset[0] << "/" << m_res->nReset[1]
367  << "/" << m_res->nReset[2] << "\tFunctional: " << m_res->func
368  << endl;
369  }
370 
371  if(ctr >= maxIter)
372  {
373  break;
374  }
375  }
376 
377  if (m_config["histfile"].beenSet)
378  {
379  ofstream histFile;
380  string name = m_config["histfile"].as<string>() + "_end.txt";
381  histFile.open(name.c_str());
382 
383  for (int i = 0; i < m_dataSet.size(); i++)
384  {
385  histFile << m_dataSet[i]->GetScaledJac() << endl;
386  }
387  histFile.close();
388  }
389  if (m_config["resfile"].beenSet)
390  {
391  resFile.close();
392  }
393 
394  t.Stop();
395 
396  //RemoveLinearCurvature();
397 
398  if(m_mesh->m_verbose)
399  {
400  cout << "Time to compute: " << t.TimePerTest(1) << endl;
401  cout << "Invalid at end: " << m_res->startInv << endl;
402  cout << "Worst at end: " << m_res->worstJac << endl;
403  }
404 }
405 
407 {
408 public:
411  : NodalUtilTriangle(degree, r, s)
412  {
413  }
414 
416  {
417  }
418 
419 protected:
420  virtual NekVector<NekDouble> v_OrthoBasis(const int mode)
421  {
422  // Monomial basis.
423  std::pair<int, int> modes = m_ordering[mode];
424  NekVector<NekDouble> ret(m_numPoints);
425 
426  for (int i = 0; i < m_numPoints; ++i)
427  {
428  ret(i) =
429  pow(m_xi[0][i], modes.first) * pow(m_xi[1][i], modes.second);
430  }
431 
432  return ret;
433  }
434 
436  const int mode)
437  {
438  boost::ignore_unused(dir, mode);
439  NEKERROR(ErrorUtil::efatal, "OrthoBasisDeriv: not supported");
440  return NekVector<NekDouble>();
441  }
442 
443  virtual std::shared_ptr<NodalUtil> v_CreateUtil(
445  {
447  m_degree, xi[0], xi[1]);
448  }
449 };
450 
452 {
453  // Grab the first element from the list
454  ElementSharedPtr elmt = m_mesh->m_element[m_mesh->m_expDim][0];
455 
456  // Get curved nodes
457  vector<NodeSharedPtr> nodes;
458  elmt->GetCurvedNodes(nodes);
459 
460  // We're going to investigate only the first node (corner node)
461  NodeSharedPtr node = nodes[4];
462 
463  // Loop over overintegration orders
464  const int nPoints = 200;
465  const int overInt = 40;
466  const NekDouble originX = -1.0;
467  const NekDouble originY = -1.0;
468  const NekDouble length = 2.0;
469  const NekDouble dx = length / (nPoints - 1);
470 
471  cout << "# overint = " << overInt << endl;
472  cout << "# Columns: x, y, over-integration orders (0 -> " << overInt - 1
473  << "), "
474  << " min(scaledJac)" << endl;
475 
476  // Loop over square defined by (originX, originY), length
477  for (int k = 0; k < nPoints; ++k)
478  {
479  node->m_y = originY + k * dx;
480  for (int j = 0; j < nPoints; ++j)
481  {
482  node->m_x = originX + j * dx;
483  cout << node->m_x << " " << node->m_y << " ";
484 
485  NekDouble minJacNew;
486 
487  for (int i = 0; i < overInt; ++i)
488  {
489  // Clear any existing node to element mapping.
490  m_dataSet.clear();
491  m_nodeElMap.clear();
492 
493  // Build deriv utils and element map.
494  map<LibUtilities::ShapeType, DerivUtilSharedPtr> derivUtils =
495  BuildDerivUtil(i);
496 
497  // Reconstruct element map
498  GetElementMap(i, derivUtils);
499 
500  for (int j = 0; j < m_dataSet.size(); j++)
501  {
502  m_dataSet[j]->Evaluate();
503  m_dataSet[j]->InitialMinJac();
504  }
505 
506  // Create NodeOpti object.
507  NodeOptiSharedPtr nodeOpti =
509  m_mesh->m_spaceDim * 11, node,
510  m_nodeElMap.find(node->m_id)->second, m_res, derivUtils,
511  m_opti);
512 
513  minJacNew = 0.0;
514 
515  // Evaluate functional.
516  nodeOpti->CalcMinJac();
517  cout << nodeOpti->GetFunctional<2>(minJacNew) << " ";
518  // NekDouble eigen;
519  // nodeOpti->GetFunctional<2>(minJacNew);
520  // nodeOpti->MinEigen<2>(eigen);
521  // cout << eigen << " ";
522  }
523 
524  cout << minJacNew << endl;
525  }
526  }
527 }
528 }
529 }
virtual std::shared_ptr< NodalUtil > v_CreateUtil(Array< OneD, Array< OneD, NekDouble > > &xi)
Construct a NodalUtil object of the appropriate element type for a given set of points.
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
NodeOptiFactory & GetNodeOptiFactory()
Definition: NodeOpti.cpp:51
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mod...
Definition: ErrorUtil.hpp:209
std::shared_ptr< ThreadManager > ThreadManagerSharedPtr
Definition: Thread.h:72
Specialisation of the NodalUtil class to support nodal triangular elements.
Definition: NodalUtil.h:169
NekDouble TimePerTest(unsigned int n)
Returns amount of seconds per iteration in a test with n iterations.
Definition: Timer.cpp:57
virtual NekVector< NekDouble > v_OrthoBasis(const int mode)
Return the value of the modal functions for the triangular element at the nodal points m_xi for a giv...
std::vector< ElUtilSharedPtr > GetLockedElements(NekDouble thres)
void GetElementMap(int o, std::map< LibUtilities::ShapeType, DerivUtilSharedPtr > derMap)
std::map< LibUtilities::ShapeType, DerivUtilSharedPtr > BuildDerivUtil(int o)
std::vector< ElUtilSharedPtr > m_dataSet
STL namespace.
std::shared_ptr< NodeOpti > NodeOptiSharedPtr
Definition: NodeOpti.h:135
std::shared_ptr< Mesh > MeshSharedPtr
Shared pointer to a mesh.
Definition: Mesh.h:156
std::shared_ptr< Node > NodeSharedPtr
Definition: CADVert.h:49
std::vector< std::vector< NodeSharedPtr > > GetColouredNodes(std::vector< ElUtilSharedPtr > elLock)
NodalUtilTriMonomial(int degree, Array< OneD, NekDouble > r, Array< OneD, NekDouble > s)
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
Definition: NekFactory.hpp:144
std::pair< ModuleType, std::string > ModuleKey
std::shared_ptr< Element > ElementSharedPtr
Definition: Edge.h:49
Represents a command-line configuration option.
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
double NekDouble
std::map< std::string, ConfigOption > m_config
List of configuration values.
ThreadManagerSharedPtr CreateInstance(const ThreadManagerName t, unsigned int nThr)
Creates an instance of a ThreadManager (which one is determined by a previous call to SetThreadingTyp...
Definition: Thread.cpp:195
Abstract base class for processing modules.
virtual NekVector< NekDouble > v_OrthoBasisDeriv(const int dir, const int mode)
Return the value of the derivative of the modal functions for the triangular element at the nodal poi...
void SetThreadingType(const std::string &p_type)
Sets what ThreadManagers will be created in CreateInstance.
Definition: Thread.cpp:157
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:199
std::pair< ModuleType, std::string > ModuleKey
1D Gauss-Lobatto-Legendre quadrature points
Definition: PointsType.h:51
ModuleFactory & GetModuleFactory()