Intrepid2
Intrepid2_HCURL_QUAD_In_FEMDef.hpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Intrepid2 Package
4 //
5 // Copyright 2007 NTESS and the Intrepid2 contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
16 #ifndef __INTREPID2_HCURL_QUAD_IN_FEM_DEF_HPP__
17 #define __INTREPID2_HCURL_QUAD_IN_FEM_DEF_HPP__
18 
19 namespace Intrepid2 {
20 
21  // -------------------------------------------------------------------------------------
22  namespace Impl {
23 
24  template<EOperator OpType>
25  template<typename OutputViewType,
26  typename InputViewType,
27  typename WorkViewType,
28  typename VinvViewType>
29  KOKKOS_INLINE_FUNCTION
30  void
31  Basis_HCURL_QUAD_In_FEM::Serial<OpType>::
32  getValues( OutputViewType output,
33  const InputViewType input,
34  WorkViewType work,
35  const VinvViewType vinvLine,
36  const VinvViewType vinvBubble) {
37  const ordinal_type cardLine = vinvLine.extent(0);
38  const ordinal_type cardBubble = vinvBubble.extent(0);
39 
40  const ordinal_type npts = input.extent(0);
41 
42  typedef Kokkos::pair<ordinal_type,ordinal_type> range_type;
43  const auto input_x = Kokkos::subview(input, Kokkos::ALL(), range_type(0,1));
44  const auto input_y = Kokkos::subview(input, Kokkos::ALL(), range_type(1,2));
45 
46  const int dim_s = get_dimension_scalar(input);
47  auto ptr0 = work.data();
48  auto ptr1 = work.data()+cardLine*npts*dim_s;
49  auto ptr2 = work.data()+2*cardLine*npts*dim_s;
50 
51  typedef typename Kokkos::DynRankView<typename InputViewType::value_type, typename WorkViewType::memory_space> ViewType;
52  auto vcprop = Kokkos::common_view_alloc_prop(input);
53 
54  switch (OpType) {
55  case OPERATOR_VALUE: {
56  ViewType workLine(Kokkos::view_wrap(ptr0, vcprop), cardLine, npts);
57  ViewType outputLine(Kokkos::view_wrap(ptr1, vcprop), cardLine, npts);
58  ViewType outputBubble(Kokkos::view_wrap(ptr2, vcprop), cardBubble, npts);
59 
60  // tensor product
61  ordinal_type idx = 0;
62  {
63  Impl::Basis_HGRAD_LINE_Cn_FEM::Serial<OPERATOR_VALUE>::
64  getValues(outputBubble, input_x, workLine, vinvBubble);
65 
66  Impl::Basis_HGRAD_LINE_Cn_FEM::Serial<OPERATOR_VALUE>::
67  getValues(outputLine, input_y, workLine, vinvLine);
68 
69  // x component (lineBasis(y) bubbleBasis(x))
70  const auto output_x = outputBubble;
71  const auto output_y = outputLine;
72 
73  for (ordinal_type j=0;j<cardLine;++j) // y
74  for (ordinal_type i=0;i<cardBubble;++i,++idx) // x
75  for (ordinal_type k=0;k<npts;++k) {
76  output.access(idx,k,0) = output_x.access(i,k)*output_y.access(j,k);
77  output.access(idx,k,1) = 0.0;
78  }
79  }
80 
81  {
82  Impl::Basis_HGRAD_LINE_Cn_FEM::Serial<OPERATOR_VALUE>::
83  getValues(outputBubble, input_y, workLine, vinvBubble);
84 
85  Impl::Basis_HGRAD_LINE_Cn_FEM::Serial<OPERATOR_VALUE>::
86  getValues(outputLine, input_x, workLine, vinvLine);
87 
88  // y component (bubbleBasis(y) lineBasis(x))
89  const auto output_x = outputLine;
90  const auto output_y = outputBubble;
91  for (ordinal_type j=0;j<cardBubble;++j) // y
92  for (ordinal_type i=0;i<cardLine;++i,++idx) // x
93  for (ordinal_type k=0;k<npts;++k) {
94  output.access(idx,k,0) = 0.0;
95  output.access(idx,k,1) = output_x.access(i,k)*output_y.access(j,k);
96  }
97  }
98 
99  break;
100  }
101  case OPERATOR_CURL: {
102  ordinal_type idx = 0;
103  { // x - component
104  ViewType workLine(Kokkos::view_wrap(ptr0, vcprop), cardLine, npts);
105  // x bubble value
106  ViewType output_x(Kokkos::view_wrap(ptr2, vcprop), cardBubble, npts);
107  // y line grad
108  ViewType output_y(Kokkos::view_wrap(ptr1, vcprop), cardLine, npts,1);
109 
110  Impl::Basis_HGRAD_LINE_Cn_FEM::Serial<OPERATOR_VALUE>::
111  getValues(output_x, input_x, workLine, vinvBubble);
112 
113  Impl::Basis_HGRAD_LINE_Cn_FEM::Serial<OPERATOR_Dn>::
114  getValues(output_y, input_y, workLine, vinvLine, 1);
115 
116  // tensor product (extra dimension of ouput x and y are ignored)
117  for (ordinal_type j=0;j<cardLine;++j) // y
118  for (ordinal_type i=0;i<cardBubble;++i,++idx) // x
119  for (ordinal_type k=0;k<npts;++k)
120  output.access(idx,k) = -output_x.access(i,k)*output_y.access(j,k,0);
121  }
122  { // y - component
123  ViewType workLine(Kokkos::view_wrap(ptr0, vcprop), cardLine, npts);
124  // x line grad
125  ViewType output_x(Kokkos::view_wrap(ptr1, vcprop), cardLine, npts,1);
126  // y bubble value
127  ViewType output_y(Kokkos::view_wrap(ptr2, vcprop), cardBubble, npts);
128 
129  Impl::Basis_HGRAD_LINE_Cn_FEM::Serial<OPERATOR_VALUE>::
130  getValues(output_y, input_y, workLine, vinvBubble);
131 
132  Impl::Basis_HGRAD_LINE_Cn_FEM::Serial<OPERATOR_Dn>::
133  getValues(output_x, input_x, workLine, vinvLine, 1);
134 
135  // tensor product (extra dimension of ouput x and y are ignored)
136  for (ordinal_type j=0;j<cardBubble;++j) // y
137  for (ordinal_type i=0;i<cardLine;++i,++idx) // x
138  for (ordinal_type k=0;k<npts;++k)
139  output.access(idx,k) = output_x.access(i,k,0)*output_y.access(j,k);
140  }
141  break;
142  }
143  default: {
144  INTREPID2_TEST_FOR_ABORT( true,
145  ">>> ERROR: (Intrepid2::Basis_HCURL_QUAD_In_FEM::Serial::getValues) operator is not supported" );
146  }
147  }
148  }
149 
150  template<typename DT, ordinal_type numPtsPerEval,
151  typename outputValueValueType, class ...outputValueProperties,
152  typename inputPointValueType, class ...inputPointProperties,
153  typename vinvValueType, class ...vinvProperties>
154  void
155  Basis_HCURL_QUAD_In_FEM::
156  getValues( Kokkos::DynRankView<outputValueValueType,outputValueProperties...> outputValues,
157  const Kokkos::DynRankView<inputPointValueType, inputPointProperties...> inputPoints,
158  const Kokkos::DynRankView<vinvValueType, vinvProperties...> vinvLine,
159  const Kokkos::DynRankView<vinvValueType, vinvProperties...> vinvBubble,
160  const EOperator operatorType ) {
161  typedef Kokkos::DynRankView<outputValueValueType,outputValueProperties...> outputValueViewType;
162  typedef Kokkos::DynRankView<inputPointValueType, inputPointProperties...> inputPointViewType;
163  typedef Kokkos::DynRankView<vinvValueType, vinvProperties...> vinvViewType;
164  typedef typename ExecSpace<typename inputPointViewType::execution_space,typename DT::execution_space>::ExecSpaceType ExecSpaceType;
165 
166  // loopSize corresponds to cardinality
167  const auto loopSizeTmp1 = (inputPoints.extent(0)/numPtsPerEval);
168  const auto loopSizeTmp2 = (inputPoints.extent(0)%numPtsPerEval != 0);
169  const auto loopSize = loopSizeTmp1 + loopSizeTmp2;
170  Kokkos::RangePolicy<ExecSpaceType,Kokkos::Schedule<Kokkos::Static> > policy(0, loopSize);
171 
172  typedef typename inputPointViewType::value_type inputPointType;
173 
174  const ordinal_type cardinality = outputValues.extent(0);
175  //get basis order based on basis cardinality.
176  ordinal_type order = 0;
177  ordinal_type cardBubble; // = std::sqrt(cardinality/2);
178  ordinal_type cardLine; // = cardBubble+1;
179  do {
180  cardBubble = Intrepid2::getPnCardinality<1>(order);
181  cardLine = Intrepid2::getPnCardinality<1>(++order);
182  } while((2*cardBubble*cardLine != cardinality) && (order != Parameters::MaxOrder));
183 
184  auto vcprop = Kokkos::common_view_alloc_prop(inputPoints);
185  typedef typename Kokkos::DynRankView< inputPointType, typename inputPointViewType::memory_space> workViewType;
186 
187  switch (operatorType) {
188  case OPERATOR_VALUE: {
189  auto workSize = Serial<OPERATOR_VALUE>::getWorkSizePerPoint(order);
190  workViewType work(Kokkos::view_alloc("Basis_HCURL_QUAD_In_FEM::getValues::work", vcprop), workSize, inputPoints.extent(0));
191  typedef Functor<outputValueViewType,inputPointViewType,vinvViewType, workViewType,
192  OPERATOR_VALUE,numPtsPerEval> FunctorType;
193  Kokkos::parallel_for( policy, FunctorType(outputValues, inputPoints, vinvLine, vinvBubble, work) );
194  break;
195  }
196  case OPERATOR_CURL: {
197  auto workSize = Serial<OPERATOR_CURL>::getWorkSizePerPoint(order);
198  workViewType work(Kokkos::view_alloc("Basis_HCURL_QUAD_In_FEM::getValues::work", vcprop), workSize, inputPoints.extent(0));
199  typedef Functor<outputValueViewType,inputPointViewType,vinvViewType, workViewType,
200  OPERATOR_CURL,numPtsPerEval> FunctorType;
201  Kokkos::parallel_for( policy, FunctorType(outputValues, inputPoints, vinvLine, vinvBubble, work) );
202  break;
203  }
204  default: {
205  INTREPID2_TEST_FOR_EXCEPTION( true , std::invalid_argument,
206  ">>> ERROR (Basis_HCURL_QUAD_In_FEM): Operator type not implemented" );
207  }
208  }
209  }
210  }
211 
212  // -------------------------------------------------------------------------------------
213  template<typename DT, typename OT, typename PT>
215  Basis_HCURL_QUAD_In_FEM( const ordinal_type order,
216  const EPointType pointType ) {
217 
218  INTREPID2_TEST_FOR_EXCEPTION( !(pointType == POINTTYPE_EQUISPACED ||
219  pointType == POINTTYPE_WARPBLEND), std::invalid_argument,
220  ">>> ERROR (Basis_HCURL_QUAD_In_FEM): pointType must be either equispaced or warpblend.");
221 
222  // this should be in host
223  Basis_HGRAD_LINE_Cn_FEM<DT,OT,PT> lineBasis( order, pointType );
224  Basis_HVOL_LINE_Cn_FEM<DT,OT,PT> bubbleBasis( order - 1, POINTTYPE_GAUSS );
225 
226  const ordinal_type
227  cardLine = lineBasis.getCardinality(),
228  cardBubble = bubbleBasis.getCardinality();
229 
230  this->vinvLine_ = Kokkos::DynRankView<typename ScalarViewType::value_type,DT>("Hcurl::Quad::In::vinvLine", cardLine, cardLine);
231  this->vinvBubble_ = Kokkos::DynRankView<typename ScalarViewType::value_type,DT>("Hcurl::Quad::In::vinvBubble", cardBubble, cardBubble);
232 
233  lineBasis.getVandermondeInverse(this->vinvLine_);
234  bubbleBasis.getVandermondeInverse(this->vinvBubble_);
235 
236  const ordinal_type spaceDim = 2;
237  this->basisCardinality_ = 2*cardLine*cardBubble;
238  this->basisDegree_ = order;
239  this->basisCellTopologyKey_ = shards::Quadrilateral<4>::key;
240  this->basisType_ = BASIS_FEM_LAGRANGIAN;
241  this->basisCoordinates_ = COORDINATES_CARTESIAN;
242  this->functionSpace_ = FUNCTION_SPACE_HCURL;
243  pointType_ = pointType;
244 
245  // initialize tags
246  {
247  // Basis-dependent initializations
248  const ordinal_type tagSize = 4; // size of DoF tag, i.e., number of fields in the tag
249  const ordinal_type posScDim = 0; // position in the tag, counting from 0, of the subcell dim
250  const ordinal_type posScOrd = 1; // position in the tag, counting from 0, of the subcell ordinal
251  const ordinal_type posDfOrd = 2; // position in the tag, counting from 0, of DoF ordinal relative to the subcell
252 
253  // Note: the only reason why equispaced can't support higher order than Parameters::MaxOrder appears to be the fact that the tags below get stored into a fixed-length array.
254  // TODO: relax the maximum order requirement by setting up tags in a different container, perhaps directly into an OrdinalTypeArray1DHost (tagView, below). (As of this writing (1/25/22), looks like other nodal bases do this in a similar way -- those should be fixed at the same time; maybe search for Parameters::MaxOrder.)
255  INTREPID2_TEST_FOR_EXCEPTION( order > Parameters::MaxOrder, std::invalid_argument, "polynomial order exceeds the max supported by this class");
256 
257  // An array with local DoF tags assigned to the basis functions, in the order of their local enumeration
258  constexpr ordinal_type maxCardLine = Parameters::MaxOrder + 1;
259  constexpr ordinal_type maxCardBubble = Parameters::MaxOrder;
260  ordinal_type tags[2*maxCardLine*maxCardBubble][4];
261 
262  const ordinal_type edge_x[2] = {0,2};
263  const ordinal_type edge_y[2] = {3,1};
264  {
265  ordinal_type idx = 0;
266 
270 
271  // since there are x/y components in the interior
272  // dof sum should be computed before the information
273  // is assigned to tags
274  const ordinal_type
275  intr_ndofs_per_direction = (cardLine-2)*cardBubble,
276  intr_ndofs = 2*intr_ndofs_per_direction;
277 
278  // x component (lineBasis(y) bubbleBasis(x))
279  for (ordinal_type j=0;j<cardLine;++j) { // y
280  const auto tag_y = lineBasis.getDofTag(j);
281  for (ordinal_type i=0;i<cardBubble;++i,++idx) { // x
282  const auto tag_x = bubbleBasis.getDofTag(i);
283 
284  if (tag_x(0) == 1 && tag_y(0) == 0) {
285  // edge: x edge, y vert
286  tags[idx][0] = 1; // edge dof
287  tags[idx][1] = edge_x[tag_y(1)];
288  tags[idx][2] = tag_x(2); // local dof id
289  tags[idx][3] = tag_x(3); // total number of dofs in this vertex
290  } else {
291  // interior
292  tags[idx][0] = 2; // interior dof
293  tags[idx][1] = 0;
294  tags[idx][2] = tag_x(2) + tag_x(3)*tag_y(2); // local dof id
295  tags[idx][3] = intr_ndofs; // total number of dofs in this vertex
296  }
297  }
298  }
299 
300  // y component (bubbleBasis(y) lineBasis(x))
301  for (ordinal_type j=0;j<cardBubble;++j) { // y
302  const auto tag_y = bubbleBasis.getDofTag(j);
303  for (ordinal_type i=0;i<cardLine;++i,++idx) { // x
304  const auto tag_x = lineBasis.getDofTag(i);
305 
306  if (tag_x(0) == 0 && tag_y(0) == 1) {
307  // edge: x vert, y edge
308  tags[idx][0] = 1; // edge dof
309  tags[idx][1] = edge_y[tag_x(1)];
310  tags[idx][2] = tag_y(2); // local dof id
311  tags[idx][3] = tag_y(3); // total number of dofs in this vertex
312  } else {
313  // interior
314  tags[idx][0] = 2; // interior dof
315  tags[idx][1] = 0;
316  tags[idx][2] = intr_ndofs_per_direction + tag_x(2) + tag_x(3)*tag_y(2); // local dof id
317  tags[idx][3] = intr_ndofs; // total number of dofs in this vertex
318  }
319  }
320  }
321  INTREPID2_TEST_FOR_EXCEPTION( idx != this->basisCardinality_ , std::runtime_error,
322  ">>> ERROR (Basis_HCURL_QUAD_In_FEM): " \
323  "counted tag index is not same as cardinality." );
324  }
325 
326  OrdinalTypeArray1DHost tagView(&tags[0][0], this->basisCardinality_*4);
327 
328  // Basis-independent function sets tag and enum data in tagToOrdinal_ and ordinalToTag_ arrays:
329  // tags are constructed on host
330  this->setOrdinalTagData(this->tagToOrdinal_,
331  this->ordinalToTag_,
332  tagView,
333  this->basisCardinality_,
334  tagSize,
335  posScDim,
336  posScOrd,
337  posDfOrd);
338  }
339 
340  // dofCoords on host and create its mirror view to device
341  Kokkos::DynRankView<typename ScalarViewType::value_type,typename DT::execution_space::array_layout,Kokkos::HostSpace>
342  dofCoordsHost("dofCoordsHost", this->basisCardinality_, spaceDim);
343 
344  // dofCoeffs on host and create its mirror view to device
345  Kokkos::DynRankView<typename ScalarViewType::value_type,typename DT::execution_space::array_layout,Kokkos::HostSpace>
346  dofCoeffsHost("dofCoeffsHost", this->basisCardinality_, spaceDim);
347 
348  Kokkos::DynRankView<typename ScalarViewType::value_type,DT>
349  dofCoordsLine("dofCoordsLine", cardLine, 1),
350  dofCoordsBubble("dofCoordsBubble", cardBubble, 1);
351 
352  lineBasis.getDofCoords(dofCoordsLine);
353  auto dofCoordsLineHost = Kokkos::create_mirror_view(dofCoordsLine);
354  Kokkos::deep_copy(dofCoordsLineHost, dofCoordsLine);
355 
356  bubbleBasis.getDofCoords(dofCoordsBubble);
357  auto dofCoordsBubbleHost = Kokkos::create_mirror_view(dofCoordsBubble);
358  Kokkos::deep_copy(dofCoordsBubbleHost, dofCoordsBubble);
359 
360  {
361  ordinal_type idx = 0;
362 
363  // x component (lineBasis(y) bubbleBasis(x))
364  for (ordinal_type j=0;j<cardLine;++j) { // y
365  for (ordinal_type i=0;i<cardBubble;++i,++idx) { // x
366  dofCoordsHost(idx,0) = dofCoordsBubbleHost(i,0);
367  dofCoordsHost(idx,1) = dofCoordsLineHost(j,0);
368  dofCoeffsHost(idx,0) = 1.0;
369  }
370  }
371 
372  // y component (bubbleBasis(y) lineBasis(x))
373  for (ordinal_type j=0;j<cardBubble;++j) { // y
374  for (ordinal_type i=0;i<cardLine;++i,++idx) { // x
375  dofCoordsHost(idx,0) = dofCoordsLineHost(i,0);
376  dofCoordsHost(idx,1) = dofCoordsBubbleHost(j,0);
377  dofCoeffsHost(idx,1) = 1.0;
378  }
379  }
380  }
381 
382  this->dofCoords_ = Kokkos::create_mirror_view(typename DT::memory_space(), dofCoordsHost);
383  Kokkos::deep_copy(this->dofCoords_, dofCoordsHost);
384 
385  this->dofCoeffs_ = Kokkos::create_mirror_view(typename DT::memory_space(), dofCoeffsHost);
386  Kokkos::deep_copy(this->dofCoeffs_, dofCoeffsHost);
387  }
388 
389  template<typename DT, typename OT, typename PT>
390  void
392  ordinal_type& perTeamSpaceSize,
393  ordinal_type& perThreadSpaceSize,
394  const PointViewType inputPoints,
395  const EOperator operatorType) const {
396  perTeamSpaceSize = 0;
397  perThreadSpaceSize = (2*this->vinvLine_.extent(0)+this->vinvBubble_.extent(0))*get_dimension_scalar(inputPoints)*sizeof(typename BasisBase::scalarType);
398  }
399 
400  template<typename DT, typename OT, typename PT>
401  KOKKOS_INLINE_FUNCTION
402  void
403  Basis_HCURL_QUAD_In_FEM<DT,OT,PT>::getValues(
404  OutputViewType outputValues,
405  const PointViewType inputPoints,
406  const EOperator operatorType,
407  const typename Kokkos::TeamPolicy<typename DT::execution_space>::member_type& team_member,
408  const typename DT::execution_space::scratch_memory_space & scratchStorage,
409  const ordinal_type subcellDim,
410  const ordinal_type subcellOrdinal) const {
411 
412  INTREPID2_TEST_FOR_ABORT( !((subcellDim == -1) && (subcellOrdinal == -1)),
413  ">>> ERROR: (Intrepid2::Basis_HCURL_QUAD_In_FEM::getValues), The capability of selecting subsets of basis functions has not been implemented yet.");
414 
415  const int numPoints = inputPoints.extent(0);
416  using ScalarType = typename ScalarTraits<typename PointViewType::value_type>::scalar_type;
417  using WorkViewType = Kokkos::DynRankView< ScalarType,typename DT::execution_space::scratch_memory_space,Kokkos::MemoryTraits<Kokkos::Unmanaged> >;
418  ordinal_type sizePerPoint = (2*this->vinvLine_.extent(0)+this->vinvBubble_.extent(0))*get_dimension_scalar(inputPoints);
419  WorkViewType workView(scratchStorage, sizePerPoint*team_member.team_size());
420  using range_type = Kokkos::pair<ordinal_type,ordinal_type>;
421 
422  switch(operatorType) {
423  case OPERATOR_VALUE:
424  Kokkos::parallel_for (Kokkos::TeamThreadRange (team_member, numPoints), [=] (ordinal_type& pt) {
425  auto output = Kokkos::subview( outputValues, Kokkos::ALL(), range_type (pt,pt+1), Kokkos::ALL() );
426  const auto input = Kokkos::subview( inputPoints, range_type(pt, pt+1), Kokkos::ALL() );
427  WorkViewType work(workView.data() + sizePerPoint*team_member.team_rank(), sizePerPoint);
428  Impl::Basis_HCURL_QUAD_In_FEM::Serial<OPERATOR_VALUE>::getValues( output, input, work, this->vinvLine_, this->vinvBubble_ );
429  });
430  break;
431  case OPERATOR_CURL:
432  Kokkos::parallel_for (Kokkos::TeamThreadRange (team_member, numPoints), [=] (ordinal_type& pt) {
433  auto output = Kokkos::subview( outputValues, Kokkos::ALL(), range_type(pt,pt+1), Kokkos::ALL() );
434  const auto input = Kokkos::subview( inputPoints, range_type(pt,pt+1), Kokkos::ALL() );
435  WorkViewType work(workView.data() + sizePerPoint*team_member.team_rank(), sizePerPoint);
436  Impl::Basis_HCURL_QUAD_In_FEM::Serial<OPERATOR_CURL>::getValues( output, input, work, this->vinvLine_, this->vinvBubble_ );
437  });
438  break;
439  default: {
440  INTREPID2_TEST_FOR_ABORT( true,
441  ">>> ERROR (Basis_HCURL_QUAD_In_FEM): getValues not implemented for this operator");
442  }
443  }
444  }
445 
446 } // namespace Intrepid2
447 
448 #endif
const OrdinalTypeArrayStride1DHost getDofTag(const ordinal_type dofOrd) const
DoF ordinal to DoF tag lookup.
virtual void getDofCoords(ScalarViewType dofCoords) const override
Returns spatial locations (coordinates) of degrees of freedom on the reference cell.
ordinal_type getCardinality() const
Returns cardinality of the basis.
Implementation of the default H(curl)-compatible FEM basis on Quadrilateral cell. ...
Basis_HCURL_QUAD_In_FEM(const ordinal_type order, const EPointType pointType=POINTTYPE_EQUISPACED)
Constructor.
virtual void getDofCoords(ScalarViewType dofCoords) const override
Returns spatial locations (coordinates) of degrees of freedom on the reference cell.
Implementation of the locally H(grad)-compatible FEM basis of variable order on the [-1...
Implementation of the locally HVOL-compatible FEM basis of variable order on the [-1,1] reference line cell, using Lagrange polynomials.
static constexpr ordinal_type MaxOrder
The maximum reconstruction order.