DataTransferKit - Multiphysics Solution Transfer Services  2.0
DTK_ProjectionPrimitives.cpp
Go to the documentation of this file.
1 //---------------------------------------------------------------------------//
2 /*
3  Copyright (c) 2012, Stuart R. Slattery
4  All rights reserved.
5 
6  Redistribution and use in source and binary forms, with or without
7  modification, are permitted provided that the following conditions are
8  met:
9 
10  *: Redistributions of source code must retain the above copyright
11  notice, this list of conditions and the following disclaimer.
12 
13  *: Redistributions in binary form must reproduce the above copyright
14  notice, this list of conditions and the following disclaimer in the
15  documentation and/or other materials provided with the distribution.
16 
17  *: Neither the name of the University of Wisconsin - Madison nor the
18  names of its contributors may be used to endorse or promote products
19  derived from this software without specific prior written permission.
20 
21  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 //---------------------------------------------------------------------------//
39 //---------------------------------------------------------------------------//
40 
41 #include <algorithm>
42 #include <cmath>
43 #include <limits>
44 
45 #include "DTK_DBC.hpp"
47 #include "DTK_NewtonSolver.hpp"
50 
51 #include <Teuchos_RCP.hpp>
52 #include <Teuchos_ScalarTraits.hpp>
53 #include <Teuchos_as.hpp>
54 
55 #include <Shards_BasicTopologies.hpp>
56 
57 #include <Intrepid_RealSpaceTools.hpp>
58 
59 namespace DataTransferKit
60 {
61 
62 //---------------------------------------------------------------------------//
63 // PUBLIC INTERFACE
64 //---------------------------------------------------------------------------//
69  const shards::CellTopology &cell_topo,
70  Intrepid::FieldContainer<double> &cell_center )
71 {
72  DTK_REQUIRE( 2 == cell_center.rank() );
73  DTK_REQUIRE( Teuchos::as<unsigned>( cell_center.dimension( 1 ) ) ==
74  cell_topo.getDimension() );
75 
76  int num_cells = cell_center.dimension( 0 );
77 
78  switch ( cell_topo.getKey() )
79  {
80  case shards::Line<2>::key:
81  case shards::Line<3>::key:
82  for ( int n = 0; n < num_cells; ++n )
83  {
84  cell_center( n, 0 ) = Teuchos::ScalarTraits<double>::zero();
85  }
86  break;
87 
88  case shards::Triangle<3>::key:
89  case shards::Triangle<4>::key:
90  case shards::Triangle<6>::key:
91  for ( int n = 0; n < num_cells; ++n )
92  {
93 
94  cell_center( n, 0 ) = 1.0 / 3.0;
95  cell_center( n, 1 ) = 1.0 / 3.0;
96  }
97  break;
98 
99  case shards::Quadrilateral<4>::key:
100  case shards::Quadrilateral<8>::key:
101  case shards::Quadrilateral<9>::key:
102  for ( int n = 0; n < num_cells; ++n )
103  {
104  cell_center( n, 0 ) = Teuchos::ScalarTraits<double>::zero();
105  cell_center( n, 1 ) = Teuchos::ScalarTraits<double>::zero();
106  }
107  break;
108 
109  case shards::Tetrahedron<4>::key:
110  case shards::Tetrahedron<10>::key:
111  case shards::Tetrahedron<11>::key:
112  for ( int n = 0; n < num_cells; ++n )
113  {
114  cell_center( n, 0 ) = 1.0 / 6.0;
115  cell_center( n, 1 ) = 1.0 / 6.0;
116  cell_center( n, 2 ) = 1.0 / 6.0;
117  }
118  break;
119 
120  case shards::Hexahedron<8>::key:
121  case shards::Hexahedron<20>::key:
122  case shards::Hexahedron<27>::key:
123  for ( int n = 0; n < num_cells; ++n )
124  {
125  cell_center( n, 0 ) = Teuchos::ScalarTraits<double>::zero();
126  cell_center( n, 1 ) = Teuchos::ScalarTraits<double>::zero();
127  cell_center( n, 2 ) = Teuchos::ScalarTraits<double>::zero();
128  }
129  break;
130 
131  case shards::Wedge<6>::key:
132  case shards::Wedge<15>::key:
133  case shards::Wedge<18>::key:
134  for ( int n = 0; n < num_cells; ++n )
135  {
136  cell_center( n, 0 ) = 1.0 / 3.0;
137  cell_center( n, 1 ) = 1.0 / 3.0;
138  cell_center( n, 2 ) = Teuchos::ScalarTraits<double>::zero();
139  }
140  break;
141 
142  case shards::Pyramid<5>::key:
143  case shards::Pyramid<13>::key:
144  case shards::Pyramid<14>::key:
145  for ( int n = 0; n < num_cells; ++n )
146  {
147  cell_center( n, 0 ) = Teuchos::ScalarTraits<double>::zero();
148  cell_center( n, 1 ) = Teuchos::ScalarTraits<double>::zero();
149  cell_center( n, 2 ) = 1.0 / 4.0;
150  }
151  break;
152 
153  default:
154  bool cell_topo_supported = false;
155  DTK_INSIST( cell_topo_supported );
156  break;
157  }
158 }
159 
160 //---------------------------------------------------------------------------//
177  const Teuchos::ParameterList &parameters,
178  const Intrepid::FieldContainer<double> &point,
179  const Intrepid::FieldContainer<double> &face_nodes,
180  const Intrepid::FieldContainer<double> &face_node_normals,
181  const shards::CellTopology &face_topology )
182 {
183  DTK_REQUIRE( 1 == point.rank() );
184  DTK_REQUIRE( 2 == face_nodes.rank() );
185  DTK_REQUIRE( 2 == face_node_normals.rank() );
186  DTK_REQUIRE( point.dimension( 0 ) == face_nodes.dimension( 1 ) );
187  DTK_REQUIRE( point.dimension( 0 ) == face_node_normals.dimension( 1 ) );
188  DTK_REQUIRE( face_nodes.dimension( 0 ) ==
189  face_node_normals.dimension( 0 ) );
190  DTK_REQUIRE( Teuchos::as<unsigned>( face_nodes.dimension( 0 ) ) ==
191  face_topology.getNodeCount() );
192 
193  // Get the spatial dimension
194  int space_dim = point.dimension( 0 );
195  DTK_CHECK( 3 == space_dim );
196 
197  // Get the geometric tolerance.
198  double geometric_tolerance =
199  parameters.get<double>( "Geometric Tolerance" );
200 
201  // Project the point onto each bilinear surface formed by the face vertex
202  // normals and the face edges. If the point is on the correct face of each
203  // surface then it is within the volume of influence of the
204  // face. Counter-clockwise ordering of the nodes on the face about the
205  // outward facing normal is required.
206  Intrepid::FieldContainer<double> face_edge_nodes( 2, space_dim );
207  Intrepid::FieldContainer<double> face_edge_node_normals( 2, space_dim );
208  double distance_to_surface = 0.0;
209  int face_num_edges = face_nodes.dimension( 0 );
210 
211  // First edges.
212  for ( int e = 0; e < face_num_edges - 1; ++e )
213  {
214  for ( int i = 0; i < space_dim; ++i )
215  {
216  face_edge_nodes( 0, i ) = face_nodes( e, i );
217  face_edge_nodes( 1, i ) = face_nodes( e + 1, i );
218  face_edge_node_normals( 0, i ) = face_node_normals( e, i );
219  face_edge_node_normals( 1, i ) = face_node_normals( e + 1, i );
220  }
221  distance_to_surface = distanceToFaceBilinearSurface(
222  parameters, point, face_edge_nodes, face_edge_node_normals );
223  if ( distance_to_surface > geometric_tolerance )
224  {
225  return false;
226  }
227  }
228 
229  // Last edge.
230  for ( int i = 0; i < space_dim; ++i )
231  {
232  face_edge_nodes( 0, i ) = face_nodes( face_num_edges - 1, i );
233  face_edge_nodes( 1, i ) = face_nodes( 0, i );
234  face_edge_node_normals( 0, i ) =
235  face_node_normals( face_num_edges - 1, i );
236  face_edge_node_normals( 1, i ) = face_node_normals( 0, i );
237  }
238  distance_to_surface = distanceToFaceBilinearSurface(
239  parameters, point, face_edge_nodes, face_edge_node_normals );
240  if ( distance_to_surface > geometric_tolerance )
241  {
242  return false;
243  }
244 
245  // If we got here then the point is in the volume of influence of the
246  // face as it was to the left of all bilinear surfaces formed by the
247  // edges and their node normals.
248  return true;
249 }
250 
251 //---------------------------------------------------------------------------//
280  const Teuchos::ParameterList &parameters,
281  const Intrepid::FieldContainer<double> &point,
282  const Intrepid::FieldContainer<double> &face_nodes,
283  const Intrepid::FieldContainer<double> &face_node_normals,
284  const shards::CellTopology &face_topology,
285  Intrepid::FieldContainer<double> &parametric_point,
286  Intrepid::FieldContainer<double> &physical_point, int &face_edge_id,
287  int &face_node_id )
288 {
289  DTK_REQUIRE( 1 == point.rank() );
290  DTK_REQUIRE( 2 == face_nodes.rank() );
291  DTK_REQUIRE( 2 == face_node_normals.rank() );
292  DTK_REQUIRE( 1 == physical_point.rank() );
293  DTK_REQUIRE( point.dimension( 0 ) == face_nodes.dimension( 1 ) );
294  DTK_REQUIRE( point.dimension( 0 ) == face_node_normals.dimension( 1 ) );
295  DTK_REQUIRE( point.dimension( 0 ) == physical_point.dimension( 0 ) );
296  DTK_REQUIRE( face_nodes.dimension( 0 ) ==
297  face_node_normals.dimension( 0 ) );
298  DTK_REQUIRE( Teuchos::as<unsigned>( face_nodes.dimension( 0 ) ) ==
299  face_topology.getNodeCount() );
300  DTK_REQUIRE( 2 == parametric_point.rank() );
301  DTK_REQUIRE( 1 == parametric_point.dimension( 0 ) );
302  DTK_REQUIRE( point.dimension( 0 ) == parametric_point.dimension( 1 ) );
303 
304  // Get dimensions.
305  int space_dim = point.dimension( 0 );
306  int topo_dim = face_topology.getDimension();
307 
308  // Get the geometric tolerance.
309  double geometric_tolerance =
310  parameters.get<double>( "Geometric Tolerance" );
311 
312  // Get the newton tolerance.
313  double newton_tolerance = parameters.get<double>( "Newton Tolerance" );
314 
315  // Get the max newton iterations.
316  double max_newton_iters = parameters.get<int>( "Max Newton Iterations" );
317 
318  // Get the basis functions for the face cell topology.
319  Teuchos::RCP<Intrepid::Basis<double, Intrepid::FieldContainer<double>>>
320  face_basis = IntrepidBasisFactory::create( face_topology );
321 
322  // Initializeunknown vector.
323  parametric_point.initialize( 0.0 );
324 
325  // Set the initial solution guess to the center of the cell for the
326  // parametric coordinates and 0 for distance.
327  int num_points = 1;
328  Intrepid::FieldContainer<double> face_center( num_points, topo_dim );
329  referenceCellCenter( face_topology, face_center );
330  for ( int n = 0; n < topo_dim; ++n )
331  {
332  parametric_point( 0, n ) = face_center( 0, n );
333  }
334  parametric_point( 0, topo_dim ) = Teuchos::ScalarTraits<double>::zero();
335 
336  // Build the nonlinear problem data.
337  ProjectPointToFaceNonlinearProblem nonlinear_problem(
338  face_basis, point, face_nodes, face_node_normals );
339 
340  // Solve the nonlinear problem.
342  parametric_point, nonlinear_problem, newton_tolerance,
343  max_newton_iters );
344 
345  // Apply tolerancing. If the point projected near a face edge or
346  // node within the tolerance, move it to that point.
347  if ( std::abs( parametric_point( 0, 0 ) ) < geometric_tolerance )
348  {
349  parametric_point( 0, 0 ) = 0.0;
350  }
351  else if ( std::abs( 1.0 - parametric_point( 0, 0 ) ) < geometric_tolerance )
352  {
353  parametric_point( 0, 0 ) = 1.0;
354  }
355  else if ( std::abs( 1.0 + parametric_point( 0, 0 ) ) < geometric_tolerance )
356  {
357  parametric_point( 0, 0 ) = -1.0;
358  }
359  if ( std::abs( parametric_point( 0, 1 ) ) < geometric_tolerance )
360  {
361  parametric_point( 0, 1 ) = 0.0;
362  }
363  else if ( std::abs( 1.0 - parametric_point( 0, 1 ) ) < geometric_tolerance )
364  {
365  parametric_point( 0, 1 ) = 1.0;
366  }
367  else if ( std::abs( 1.0 + parametric_point( 0, 1 ) ) < geometric_tolerance )
368  {
369  parametric_point( 0, 1 ) = -1.0;
370  }
371  // This case is for the diagonal of the triangle reference geometry.
372  if ( std::abs( 1.0 - parametric_point( 0, 0 ) - parametric_point( 0, 1 ) ) <
373  geometric_tolerance )
374  {
375  parametric_point( 0, 0 ) = 1.0 - parametric_point( 0, 1 );
376  }
377 
378  // Compute the physical coordinates from the projection in the reference
379  // space.
380  nonlinear_problem.updateState( parametric_point );
381  for ( int d = 0; d < space_dim; ++d )
382  {
383  physical_point( d ) = 0.0;
384  for ( int n = 0; n < nonlinear_problem.d_cardinality; ++n )
385  {
386  physical_point( d ) +=
387  nonlinear_problem.d_basis_evals( n, 0 ) * face_nodes( n, d );
388  }
389  }
390 
391  // Determine if the point projected onto any of the face nodes or edges.
392  face_edge_id = -1;
393  face_node_id = -1;
394 
395  // Triangle case.
396  if ( ( shards::Triangle<3>::key == face_topology.getKey() ) ||
397  ( shards::Triangle<4>::key == face_topology.getKey() ) ||
398  ( shards::Triangle<6>::key == face_topology.getKey() ) )
399  {
400  if ( ( 0.0 == parametric_point( 0, 0 ) ) &&
401  ( 0.0 == parametric_point( 0, 1 ) ) )
402  {
403  face_node_id = 0;
404  }
405  else if ( ( 1.0 == parametric_point( 0, 0 ) ) &&
406  ( 0.0 == parametric_point( 0, 1 ) ) )
407  {
408  face_node_id = 1;
409  }
410  else if ( ( 0.0 == parametric_point( 0, 0 ) ) &&
411  ( 1.0 == parametric_point( 0, 1 ) ) )
412  {
413  face_node_id = 2;
414  }
415  else if ( 0.0 == parametric_point( 0, 1 ) )
416  {
417  face_edge_id = 0;
418  }
419  else if ( 1.0 == parametric_point( 0, 0 ) + parametric_point( 0, 1 ) )
420  {
421  face_edge_id = 1;
422  }
423  else if ( 0.0 == parametric_point( 0, 0 ) )
424  {
425  face_edge_id = 2;
426  }
427  }
428  // Quadrilateral case.
429  else if ( ( shards::Quadrilateral<4>::key == face_topology.getKey() ) ||
430  ( shards::Quadrilateral<8>::key == face_topology.getKey() ) ||
431  ( shards::Quadrilateral<9>::key == face_topology.getKey() ) )
432  {
433  if ( ( -1.0 == parametric_point( 0, 0 ) ) &&
434  ( -1.0 == parametric_point( 0, 1 ) ) )
435  {
436  face_node_id = 0;
437  }
438  else if ( ( 1.0 == parametric_point( 0, 0 ) ) &&
439  ( -1.0 == parametric_point( 0, 1 ) ) )
440  {
441  face_node_id = 1;
442  }
443  else if ( ( 1.0 == parametric_point( 0, 0 ) ) &&
444  ( 1.0 == parametric_point( 0, 1 ) ) )
445  {
446  face_node_id = 2;
447  }
448  else if ( ( -1.0 == parametric_point( 0, 0 ) ) &&
449  ( 1.0 == parametric_point( 0, 1 ) ) )
450  {
451  face_node_id = 3;
452  }
453  else if ( -1.0 == parametric_point( 0, 1 ) )
454  {
455  face_edge_id = 0;
456  }
457  else if ( 1.0 == parametric_point( 0, 0 ) )
458  {
459  face_edge_id = 1;
460  }
461  else if ( 1.0 == parametric_point( 0, 1 ) )
462  {
463  face_edge_id = 2;
464  }
465  else if ( -1.0 == parametric_point( 0, 0 ) )
466  {
467  face_edge_id = 3;
468  }
469  }
470  // Unsupported case.
471  else
472  {
473  DTK_INSIST(
474  ( shards::Triangle<3>::key == face_topology.getKey() ) ||
475  ( shards::Triangle<4>::key == face_topology.getKey() ) ||
476  ( shards::Triangle<6>::key == face_topology.getKey() ) ||
477  ( shards::Quadrilateral<4>::key == face_topology.getKey() ) ||
478  ( shards::Quadrilateral<8>::key == face_topology.getKey() ) ||
479  ( shards::Quadrilateral<9>::key == face_topology.getKey() ) );
480  }
481 }
482 
483 //---------------------------------------------------------------------------//
484 /*
485  * \brief Project a feature point to a feature edge. Return false if
486  * the point did not project onto the edge.
487  *
488  * \param parameters Projection parameters.
489  *
490  * \param point Point coordinates (Dim)
491  *
492  * \param edge_nodes Edge node coordinates (Node,Dim)
493  *
494  * \param edge_node_normals Edge node normal vectors (Node,Dim)
495  *
496  * \param projected_point Projected point in physical coordinates (Dim)
497  *
498  * \param edge_node_id The local id of the edge node onto which the
499  * feature node projected. Will be -1 if the feature node did not
500  * project onto any of the edge nodes.
501  *
502  * \return Return true of the feature node projected onto the feature edge.
503  */
504 bool ProjectionPrimitives::projectPointFeatureToEdgeFeature(
505  const Teuchos::ParameterList &parameters,
506  const Intrepid::FieldContainer<double> &point,
507  const Intrepid::FieldContainer<double> &point_normal,
508  const Intrepid::FieldContainer<double> &edge_nodes,
509  const Intrepid::FieldContainer<double> &edge_node_normals,
510  Intrepid::FieldContainer<double> &projected_point, int &edge_node_id )
511 {
512  DTK_REQUIRE( 1 == point.rank() );
513  DTK_REQUIRE( 1 == point_normal.rank() );
514  DTK_REQUIRE( 2 == edge_nodes.rank() );
515  DTK_REQUIRE( 2 == edge_node_normals.rank() );
516  DTK_REQUIRE( 1 == projected_point.rank() );
517  DTK_REQUIRE( point.dimension( 0 ) == edge_nodes.dimension( 1 ) );
518  DTK_REQUIRE( point.dimension( 0 ) == edge_node_normals.dimension( 1 ) );
519  DTK_REQUIRE( point.dimension( 0 ) == projected_point.dimension( 0 ) );
520  DTK_REQUIRE( edge_nodes.dimension( 0 ) ==
521  edge_node_normals.dimension( 0 ) );
522  DTK_REQUIRE( edge_nodes.dimension( 0 ) == 2 );
523 
524  // Get dimensions.
525  int space_dim = point.dimension( 0 );
526 
527  // Get the geometric tolerance.
528  double geometric_tolerance =
529  parameters.get<double>( "Geometric Tolerance" );
530 
531  // Get the normal tolerance.
532  double normal_tolerance = parameters.get<double>( "Normal Tolerance" );
533 
534  // Get the newton tolerance.
535  double newton_tolerance = parameters.get<double>( "Newton Tolerance" );
536 
537  // Get the max newton iterations.
538  double max_newton_iters = parameters.get<int>( "Max Newton Iterations" );
539 
540  // Check to make sure that the projection direction is opposite the node
541  // normal directions.
542  double dot_1 = 0.0;
543  double dot_2 = 0.0;
544  for ( int d = 0; d < space_dim; ++d )
545  {
546  dot_1 += point_normal( d ) * edge_node_normals( 0, d );
547  dot_2 += point_normal( d ) * edge_node_normals( 1, d );
548  }
549  if ( dot_1 > -0.5 * normal_tolerance || dot_2 > -0.5 * normal_tolerance )
550  {
551  return false;
552  }
553 
554  // Build the binormals.
555  Intrepid::FieldContainer<double> gvec( space_dim );
556  for ( int d = 0; d < space_dim; ++d )
557  {
558  gvec( d ) = edge_nodes( 1, d ) - edge_nodes( 0, d );
559  }
560  Intrepid::FieldContainer<double> normal( space_dim );
561  Intrepid::FieldContainer<double> l( space_dim );
562  Intrepid::FieldContainer<double> edge_node_binormals(
563  edge_node_normals.dimension( 0 ), edge_node_normals.dimension( 1 ) );
564  for ( int n = 0; n < 2; ++n )
565  {
566  for ( int d = 0; d < space_dim; ++d )
567  {
568  normal( d ) = edge_node_normals( n, d );
569  }
570  Intrepid::RealSpaceTools<double>::vecprod( l, normal, gvec );
571  for ( int d = 0; d < space_dim; ++d )
572  {
573  edge_node_binormals( n, d ) = l( d );
574  }
575  }
576 
577  // Allocate unknown vector.
578  int num_points = 1;
579  Intrepid::FieldContainer<double> u( num_points, space_dim );
580 
581  // Set the initial solution guess to 0.5.
582  for ( int n = 0; n < space_dim; ++n )
583  {
584  u( 0, n ) = 0.5;
585  }
586 
587  // Build the nonlinear problem data.
589  point, edge_nodes, edge_node_normals, edge_node_binormals );
590 
591  // Solve the nonlinear problem.
593  u, nonlinear_problem, newton_tolerance, max_newton_iters );
594 
595  // Apply tolerancing.
596  if ( std::abs( u( 0, 0 ) ) < geometric_tolerance )
597  {
598  u( 0, 0 ) = 0.0;
599  }
600  else if ( std::abs( 1.0 - u( 0, 0 ) ) < geometric_tolerance )
601  {
602  u( 0, 0 ) = 1.0;
603  }
604 
605  // See if the solution was outside the parameteric bounds of the edge.
606  if ( 0.0 > u( 0, 0 ) || 1.0 < u( 0, 0 ) )
607  {
608  return false;
609  }
610 
611  // Compute the physical coordinates from the projection in the reference
612  // space.
613  for ( int d = 0; d < space_dim; ++d )
614  {
615  projected_point( d ) = edge_nodes( 0, d ) + u( 0, 0 ) * gvec( d );
616  }
617 
618  // Check to see if the point projected onto one of the vertices of the
619  // edge.
620  edge_node_id = -1;
621  if ( u( 0, 0 ) == 0.0 )
622  {
623  edge_node_id = 0;
624  }
625  else if ( u( 0, 0 ) == 1.0 )
626  {
627  edge_node_id = 1;
628  }
629 
630  // There was an interesection so return true.
631  return true;
632 }
633 
634 //---------------------------------------------------------------------------//
664  const Teuchos::ParameterList &parameters,
665  const Intrepid::FieldContainer<double> &edge_1,
666  const Intrepid::FieldContainer<double> &edge_2,
667  const Intrepid::FieldContainer<double> &edge_2_node_normals,
668  Intrepid::FieldContainer<double> &edge_1_intersection,
669  Intrepid::FieldContainer<double> &edge_2_intersection, int &edge_1_node_id,
670  int &edge_2_node_id )
671 {
672  DTK_REQUIRE( 2 == edge_1.rank() );
673  DTK_REQUIRE( 2 == edge_2.rank() );
674  DTK_REQUIRE( 2 == edge_2_node_normals.rank() );
675  DTK_REQUIRE( 1 == edge_1_intersection.rank() );
676  DTK_REQUIRE( 1 == edge_2_intersection.rank() );
677  DTK_REQUIRE( 2 == edge_1.dimension( 0 ) );
678  DTK_REQUIRE( 2 == edge_2.dimension( 0 ) );
679  DTK_REQUIRE( 2 == edge_2_node_normals.dimension( 0 ) );
680  DTK_REQUIRE( edge_1.dimension( 1 ) == edge_2.dimension( 1 ) );
681  DTK_REQUIRE( edge_1.dimension( 1 ) == edge_2_node_normals.dimension( 1 ) );
682  DTK_REQUIRE( edge_1.dimension( 1 ) == edge_1_intersection.dimension( 0 ) );
683  DTK_REQUIRE( edge_1.dimension( 1 ) == edge_2_intersection.dimension( 0 ) );
684 
685  // The condition number tolerance.
686  double cond_tol = 1.0 / std::sqrt( std::numeric_limits<double>::epsilon() );
687 
688  // Get the spatial dimension.
689  int space_dim = edge_1.dimension( 1 );
690 
691  // Get the geometric tolerance.
692  double geometric_tolerance =
693  parameters.get<double>( "Geometric Tolerance" );
694 
695  // Get the merge tolerance.
696  double merge_tolerance = parameters.get<double>( "Merge Tolerance" );
697 
698  // Build intermediate vectors.
699  Intrepid::FieldContainer<double> bvec( space_dim );
700  Intrepid::FieldContainer<double> gvec( space_dim );
701  Intrepid::FieldContainer<double> dvec( space_dim );
702  Intrepid::FieldContainer<double> d0( space_dim );
703  Intrepid::FieldContainer<double> g0minusb0( space_dim );
704  for ( int i = 0; i < space_dim; ++i )
705  {
706  bvec( i ) = edge_1( 1, i ) - edge_1( 0, i );
707  gvec( i ) = edge_2( 1, i ) - edge_2( 0, i );
708  dvec( i ) = edge_2_node_normals( 1, i ) - edge_2_node_normals( 0, i );
709  d0( i ) = edge_2_node_normals( 0, i );
710  g0minusb0( i ) = edge_2( 0, i ) - edge_1( 0, i );
711  }
712 
713  // Intersection parametric coordinates.
714  double alpha = 0.0;
715  double beta = 0.0;
716 
717  // Compute the parameterized realization of the intersection on edge
718  // 2. Start by building the quadratic equation coefficients.
719  Intrepid::FieldContainer<double> work_vec( space_dim );
720  Intrepid::RealSpaceTools<double>::vecprod( work_vec, bvec, dvec );
721  double a = Intrepid::RealSpaceTools<double>::dot( work_vec, gvec );
722  double b = Intrepid::RealSpaceTools<double>::dot( work_vec, g0minusb0 );
723  Intrepid::RealSpaceTools<double>::vecprod( work_vec, bvec, d0 );
724  b += Intrepid::RealSpaceTools<double>::dot( work_vec, gvec );
725  double c = Intrepid::RealSpaceTools<double>::dot( work_vec, g0minusb0 );
726 
727  // Solve the quadratic equation if a != 0 to get the parameterized
728  // realization of the intersection on the edge 2.
729  if ( std::abs( a ) > geometric_tolerance )
730  {
731  double ax2 = 2 * a;
732  double sqrt_arg = b * b - 4 * a * c;
733 
734  if ( std::abs( sqrt_arg ) < geometric_tolerance )
735  {
736  sqrt_arg = 0.0;
737  }
738 
739  // If we get a negative argument for the square root then beta
740  // will be in non-physical (complex) coordinates.
741  if ( sqrt_arg < 0.0 )
742  {
743  return false;
744  }
745 
746  // Compute beta.
747  double sqrt_val = std::sqrt( sqrt_arg );
748  double beta_1 = ( -b + sqrt_val ) / ( ax2 );
749  double beta_2 = ( -b - sqrt_val ) / ( ax2 );
750 
751  // Resolve inconsistencies by perturbing small beta onto vertices.
752  // These small values will result from the inexact primitive
753  // solutions.
754  if ( std::abs( beta_1 ) < merge_tolerance )
755  {
756  beta_1 = 0.0;
757  }
758  else if ( std::abs( 1.0 - beta_1 ) < merge_tolerance )
759  {
760  beta_1 = 1.0;
761  }
762  if ( std::abs( beta_2 ) < merge_tolerance )
763  {
764  beta_2 = 0.0;
765  }
766  else if ( std::abs( 1.0 - beta_2 ) < merge_tolerance )
767  {
768  beta_2 = 1.0;
769  }
770 
771  // If there are two valid solutions then return no intersection as
772  // these edges are nearly parallel.
773  if ( ( beta_1 >= 0.0 && beta_1 <= 1.0 ) &&
774  ( beta_2 >= 0.0 && beta_2 <= 1.0 ) )
775  {
776  return false;
777  }
778 
779  // Choose the solution that is in the accepted range.
780  if ( beta_1 >= 0.0 && beta_1 <= 1.0 )
781  {
782  beta = beta_1;
783  }
784  else if ( beta_2 >= 0.0 && beta_2 <= 1.0 )
785  {
786  beta = beta_2;
787  }
788  else
789  {
790  // There is no intersection if neither solution to the quadratic
791  // equation is between 0 and 1.
792  return false;
793  }
794  }
795  // If a = 0 and b != 0 then the equation is linear.
796  else if ( std::abs( b ) > geometric_tolerance )
797  {
798  beta = -c / b;
799 
800  // Resolve inconsistencies by perturbing small beta onto vertices.
801  // These small values will result from the inexact primitive
802  // solutions.
803  if ( std::abs( beta ) < merge_tolerance )
804  {
805  beta = 0.0;
806  }
807  else if ( std::abs( 1.0 - beta ) < merge_tolerance )
808  {
809  beta = 1.0;
810  }
811  }
812  // If a = 0 and b = 0 then beta is undefined.
813  else
814  {
815  return false;
816  }
817 
818  // Check the condition number of beta.
819  Intrepid::FieldContainer<double> nvec( space_dim );
820  double n_length = 0.0;
821  double g_length = 0.0;
822  for ( int i = 0; i < space_dim; ++i )
823  {
824  nvec( i ) = g0minusb0( i ) + beta * gvec( i );
825  n_length += nvec( i ) * nvec( i );
826  g_length += gvec( i ) * gvec( i );
827  }
828  Intrepid::FieldContainer<double> ncrossg( space_dim );
829  Intrepid::RealSpaceTools<double>::vecprod( ncrossg, nvec, gvec );
830  double ncrossg_length = 0.0;
831  for ( int i = 0; i < space_dim; ++i )
832  {
833  ncrossg_length += ncrossg( i ) * ncrossg( i );
834  }
835 
836  // Only check the condition number if it is defined.
837  if ( ncrossg_length > geometric_tolerance &&
838  n_length > geometric_tolerance )
839  {
840  double beta_cond = std::sqrt( n_length ) * std::sqrt( g_length ) /
841  std::sqrt( ncrossg_length );
842  if ( beta_cond > cond_tol )
843  {
844  return false;
845  }
846  }
847 
848  // Compute the parameterized realization of the intersection on the edge
849  // 1.
850  Intrepid::FieldContainer<double> l( space_dim );
851  Intrepid::RealSpaceTools<double>::scale( dvec, beta );
852  Intrepid::RealSpaceTools<double>::add( work_vec, d0, dvec );
853  Intrepid::RealSpaceTools<double>::vecprod( l, gvec, work_vec );
854  double dot1 = Intrepid::RealSpaceTools<double>::dot( l, g0minusb0 );
855  double dot2 = Intrepid::RealSpaceTools<double>::dot( l, bvec );
856 
857  if ( std::abs( dot2 ) < geometric_tolerance )
858  {
859  return false;
860  }
861  alpha = dot1 / dot2;
862 
863  // Resolve inconsistencies by pertubing small alpha onto vertices. These
864  // small values will result from the inexact primitive solutions.
865  if ( std::abs( alpha ) < merge_tolerance )
866  {
867  alpha = 0.0;
868  }
869  else if ( std::abs( 1.0 - alpha ) < merge_tolerance )
870  {
871  alpha = 1.0;
872  }
873 
874  // Check the condition number of alpha.
875  Intrepid::FieldContainer<double> lvec( space_dim );
876  double l_length = 0.0;
877  double b_length = 0.0;
878  for ( int i = 0; i < space_dim; ++i )
879  {
880  lvec( i ) = alpha * bvec( i ) - g0minusb0( i );
881  l_length += lvec( i ) * lvec( i );
882  b_length += bvec( i ) * bvec( i );
883  }
884  Intrepid::FieldContainer<double> lcrossb( space_dim );
885  Intrepid::RealSpaceTools<double>::vecprod( lcrossb, lvec, bvec );
886  double lcrossb_length = 0.0;
887  for ( int i = 0; i < space_dim; ++i )
888  {
889  lcrossb_length += lcrossb( i ) * lcrossb( i );
890  }
891 
892  // Only check the condition number if it is defined.
893  if ( lcrossb_length > geometric_tolerance &&
894  l_length > geometric_tolerance )
895  {
896  double alpha_cond = std::sqrt( l_length ) * std::sqrt( b_length ) /
897  std::sqrt( lcrossb_length );
898  if ( alpha_cond > cond_tol )
899  {
900  return false;
901  }
902  }
903 
904  // Check for an intersection on the edge 1.
905  if ( alpha < 0.0 || alpha > 1.0 )
906  {
907  return false;
908  }
909 
910  // Check for an intersection on the edge 2.
911  if ( beta < 0.0 || beta > 1.0 )
912  {
913  return false;
914  }
915 
916  // Build the physical intersection location on the edge 2.
917  for ( int i = 0; i < space_dim; ++i )
918  {
919  edge_2_intersection( i ) = edge_2( 0, i ) + beta * gvec( i );
920  }
921 
922  // Build the physical intersection location on the edge 1.
923  for ( int i = 0; i < space_dim; ++i )
924  {
925  edge_1_intersection( i ) = edge_1( 0, i ) + alpha * bvec( i );
926  }
927 
928  // Check the error in the solution.
929  double d_length = 0.0;
930  Intrepid::FieldContainer<double> v_vec( space_dim );
931  for ( int i = 0; i < space_dim; ++i )
932  {
933  d_length += work_vec( i ) * work_vec( i );
934  v_vec( i ) = edge_1_intersection( i ) - edge_2_intersection( i );
935  }
936  d_length *= -d_length;
937  d_length += 1.0;
938  for ( int i = 0; i < space_dim; ++i )
939  {
940  v_vec( i ) *= d_length;
941  }
942  double v_length = 0.0;
943  for ( int i = 0; i < space_dim; ++i )
944  {
945  v_length += v_vec( i ) * v_vec( i );
946  }
947  double error = std::sqrt( v_length ) /
948  std::min( std::sqrt( b_length ), std::sqrt( g_length ) );
949  if ( error > cond_tol || b_length < geometric_tolerance ||
950  g_length < geometric_tolerance )
951  {
952  return false;
953  }
954 
955  // Determine if any of the edge 1 nodes were intersected.
956  if ( 0.0 == alpha )
957  {
958  edge_1_node_id = 0;
959  }
960  else if ( 1.0 == alpha )
961  {
962  edge_1_node_id = 1;
963  }
964  else
965  {
966  edge_1_node_id = -1;
967  }
968 
969  // Determine if any of the edge 2 nodes were intersected.
970  if ( 0.0 == beta )
971  {
972  edge_2_node_id = 0;
973  }
974  else if ( 1.0 == beta )
975  {
976  edge_2_node_id = 1;
977  }
978  else
979  {
980  edge_2_node_id = -1;
981  }
982 
983  // Both alpha and beta are between 0 and 1 so there is an
984  // intersection.
985  return true;
986 }
987 
988 //---------------------------------------------------------------------------//
989 // PRIVATE IMPLEMENTATION
990 //---------------------------------------------------------------------------//
1004 typename Intrepid::FieldContainer<double>::scalar_type
1005 ProjectionPrimitives::distanceToFaceBilinearSurface(
1006  const Teuchos::ParameterList &parameters,
1007  const Intrepid::FieldContainer<double> &point,
1008  const Intrepid::FieldContainer<double> &face_edge_nodes,
1009  const Intrepid::FieldContainer<double> &face_edge_node_normals )
1010 {
1011  // Get the geometric tolerance.
1012  double geometric_tolerance =
1013  parameters.get<double>( "Geometric Tolerance" );
1014 
1015  // Get the newton tolerance.
1016  double newton_tolerance = parameters.get<double>( "Newton Tolerance" );
1017 
1018  // Get the max newton iterations.
1019  double max_newton_iters = parameters.get<int>( "Max Newton Iterations" );
1020 
1021  // Compute the scale factor. Choose half the length of the face for
1022  // simplicity.
1023  double xdist = face_edge_nodes( 1, 0 ) - face_edge_nodes( 0, 0 );
1024  double ydist = face_edge_nodes( 1, 1 ) - face_edge_nodes( 0, 1 );
1025  double zdist = face_edge_nodes( 1, 2 ) - face_edge_nodes( 0, 2 );
1026  double c = 0.5 * std::sqrt( xdist * xdist + ydist * ydist + zdist * zdist );
1027 
1028  // Get the distance to the bilinear surface.
1029  Intrepid::FieldContainer<double> u( 1, point.dimension( 0 ) );
1030  u( 0, 0 ) = 0.5;
1031  u( 0, 1 ) = 0.5;
1032  u( 0, 2 ) = Teuchos::ScalarTraits<double>::zero();
1034  point, face_edge_nodes, face_edge_node_normals, c );
1036  u, nonlinear_problem, newton_tolerance, max_newton_iters );
1037 
1038  // Check for degeneracy.
1039  nonlinear_problem.updateState( u );
1040  Intrepid::FieldContainer<double> v1( nonlinear_problem.d_space_dim );
1041  Intrepid::FieldContainer<double> v2( nonlinear_problem.d_space_dim );
1042  for ( int i = 0; i < nonlinear_problem.d_space_dim; ++i )
1043  {
1044  v1( i ) = nonlinear_problem.d_face_edge_nodes( 1, i ) -
1045  nonlinear_problem.d_face_edge_nodes( 0, i );
1046  v2( i ) = v1( i ) +
1047  nonlinear_problem.d_c * u( 0, 1 ) *
1048  ( nonlinear_problem.d_face_edge_node_normals( 1, i ) -
1049  nonlinear_problem.d_face_edge_node_normals( 0, i ) );
1050  }
1051 
1052  // Return a positive number if degenerate.
1053  if ( Intrepid::RealSpaceTools<double>::dot( v1, v2 ) < geometric_tolerance )
1054  {
1055  return 1.0;
1056  }
1057 
1058  // Return the solution if not degenerate.
1059  return u( 0, 2 );
1060 }
1061 
1062 //---------------------------------------------------------------------------//
1063 
1064 } // end namespace DataTransferKit
1065 
1066 //---------------------------------------------------------------------------//
1067 // end DTK_ProjectionPrimitives.cpp
1068 //---------------------------------------------------------------------------//
static void solve(MDArray &u, NonlinearProblem &problem, const double tolerance, const int max_iters)
Get the center of the reference cell of the given topology.
Nonlinear problem struct for ProjectBlueFeatureToGreenFeature. This problem projects a feature point ...
A stateless class for Newton&#39;s method.
static void referenceCellCenter(const shards::CellTopology &cell_topo, Intrepid::FieldContainer< double > &center)
Get the center of the reference cell of the given topology.
Nonlinear problem for projecting a point into the reference frame of a face.
void updateState(const Intrepid::FieldContainer< double > &u)
Update the state of the problem given the new solution vector.
Nonlinear problem struct for pointInFaceVolumeOfInfluence. This problem projects a point onto the sur...
Assertions and Design-by-Contract for error handling.
static void projectPointToFace(const Teuchos::ParameterList &parameters, const Intrepid::FieldContainer< double > &point, const Intrepid::FieldContainer< double > &face_nodes, const Intrepid::FieldContainer< double > &face_node_normals, const shards::CellTopology &face_topology, Intrepid::FieldContainer< double > &parametric_point, Intrepid::FieldContainer< double > &physical_point, int &face_edge_id, int &face_node_id)
Project a point onto a face and return the physical coordinates of the projected point on that face...
void updateState(const Intrepid::FieldContainer< double > &u)
Update the state of the problem given the new solution vector.
static bool edgeEdgeIntersection(const Teuchos::ParameterList &parameters, const Intrepid::FieldContainer< double > &edge_1, const Intrepid::FieldContainer< double > &edge_2, const Intrepid::FieldContainer< double > &edge_2_node_normals, Intrepid::FieldContainer< double > &edge_1_intersection, Intrepid::FieldContainer< double > &edge_2_intersection, int &edge_1_node_id, int &edge_2_node_id)
Intersect two edges in 3 dimensions and return their intersection point realized on both edges...
Factory for Intrepid basis functions.
Nonlinear problem definitions for projection primitives.
DTK_BasicEntitySet.cpp.
static bool pointInFaceVolumeOfInfluence(const Teuchos::ParameterList &parameters, const Intrepid::FieldContainer< double > &point, const Intrepid::FieldContainer< double > &face_nodes, const Intrepid::FieldContainer< double > &face_node_normals, const shards::CellTopology &face_topology)
Determine if a point is within the volume of influence of a face.
A stateless class of projection primitive operations.