GEOS
ThermalCompositionalMultiphaseWellKernels.hpp
Go to the documentation of this file.
1 /*
2  * ------------------------------------------------------------------------------------------------------------
3  * SPDX-License-Identifier: LGPL-2.1-only
4  *
5  * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC
6  * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University
7  * Copyright (c) 2018-2020 TotalEnergies
8  * Copyright (c) 2019- GEOSX Contributors
9  * All rights reserved
10  *
11  * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
12  * ------------------------------------------------------------------------------------------------------------
13  */
14 
19 #ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_THERMALCOMPOSITIONALMULTIPHASEWELLKERNELS_HPP
20 #define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_THERMALCOMPOSITIONALMULTIPHASEWELLKERNELS_HPP
21 
24 
25 namespace geos
26 {
27 
28 namespace thermalCompositionalMultiphaseWellKernels
29 {
30 
31 using namespace constitutive;
32 
33 /******************************** TotalMassDensityKernel ****************************/
34 
41 template< integer NUM_COMP, integer NUM_PHASE >
43 {
44 public:
46  using Base::m_dCompFrac_dCompDens;
47  using Base::m_dPhaseMassDens;
48  using Base::m_dPhaseVolFrac;
49  using Base::m_dTotalMassDens;
50  using Base::m_phaseMassDens;
51  using Base::m_phaseVolFrac;
52  using Base::m_totalMassDens;
53  using Base::numComp;
54  using Base::numPhase;
55 
62  MultiFluidBase const & fluid )
63  : Base( subRegion, fluid )
64  {}
65 
72  GEOS_HOST_DEVICE inline void compute( localIndex const ei ) const
73  {
74  using Deriv = multifluid::DerivativeOffset;
75 
76  arraySlice1d< real64 const, compflow::USD_PHASE - 1 > phaseVolFrac = m_phaseVolFrac[ei];
77  arraySlice2d< real64 const, compflow::USD_PHASE_DC - 1 > dPhaseVolFrac = m_dPhaseVolFrac[ei];
78  arraySlice1d< real64 const, multifluid::USD_PHASE - 2 > phaseMassDens = m_phaseMassDens[ei][0];
79  arraySlice2d< real64 const, multifluid::USD_PHASE_DC - 2 > dPhaseMassDens = m_dPhaseMassDens[ei][0];
80 
81  real64 & dTotalMassDens_dT = m_dTotalMassDens[ei][Deriv::dT];
82 
83  // Call the base compute the compute the total mass density and derivatives
84  return Base::compute( ei, [&]( localIndex const ip )
85  {
86  dTotalMassDens_dT += dPhaseVolFrac[ip][Deriv::dT] * phaseMassDens[ip] + phaseVolFrac[ip] * dPhaseMassDens[ip][Deriv::dT];
87  } );
88  }
89 
90 protected:
91  // outputs
92  arrayView1d< real64 > m_dTotalMassDens_dTemp;
93 };
94 
99 {
100 public:
109  template< typename POLICY >
110  static void
111  createAndLaunch( integer const numComp,
112  integer const numPhase,
113  ObjectManagerBase & subRegion,
114  MultiFluidBase const & fluid )
115  {
116  if( numPhase == 2 )
117  {
118  isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComp, [&]( auto NC )
119  {
120  integer constexpr NUM_COMP = NC();
121  TotalMassDensityKernel< NUM_COMP, 2 > kernel( subRegion, fluid );
122  TotalMassDensityKernel< NUM_COMP, 2 >::template launch< POLICY >( subRegion.size(), kernel );
123  } );
124  }
125  else if( numPhase == 3 )
126  {
127  isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComp, [&]( auto NC )
128  {
129  integer constexpr NUM_COMP = NC();
130  TotalMassDensityKernel< NUM_COMP, 3 > kernel( subRegion, fluid );
131  TotalMassDensityKernel< NUM_COMP, 3 >::template launch< POLICY >( subRegion.size(), kernel );
132  } );
133  }
134  }
135 };
136 
137 /******************************** ResidualNormKernel ********************************/
138 
142 template< localIndex NUM_COMP >
144 {
145 public:
146 
148  static constexpr integer numComp = NUM_COMP;
149 
150 
152 
154  using Base::m_minNormalizer;
155  using Base::m_rankOffset;
156  using Base::m_localResidual;
157  using Base::m_dofNumber;
158 
159  ResidualNormKernel( globalIndex const rankOffset,
160  arrayView1d< real64 const > const & localResidual,
161  arrayView1d< globalIndex const > const & dofNumber,
162  arrayView1d< localIndex const > const & ghostRank,
163  integer const targetPhaseIndex,
164  WellElementSubRegion const & subRegion,
165  MultiFluidBase const & fluid,
166  WellControls const & wellControls,
167  real64 const timeAtEndOfStep,
168  real64 const dt,
169  real64 const minNormalizer )
170  : Base( rankOffset,
171  localResidual,
172  dofNumber,
173  ghostRank,
174  minNormalizer ),
175  m_numPhases( fluid.numFluidPhases()),
176  m_targetPhaseIndex( targetPhaseIndex ),
177  m_dt( dt ),
178  m_isLocallyOwned( subRegion.isLocallyOwned() ),
179  m_iwelemControl( subRegion.getTopWellElementIndex() ),
180  m_isProducer( wellControls.isProducer() ),
181  m_currentControl( wellControls.getControl() ),
182  m_targetBHP( wellControls.getTargetBHP( timeAtEndOfStep ) ),
183  m_targetTotalRate( wellControls.getTargetTotalRate( timeAtEndOfStep ) ),
184  m_targetPhaseRate( wellControls.getTargetPhaseRate( timeAtEndOfStep ) ),
185  m_targetMassRate( wellControls.getTargetMassRate( timeAtEndOfStep ) ),
186  m_volume( subRegion.getElementVolume() ),
187  m_phaseDens_n( fluid.phaseDensity_n() ),
188  m_totalDens_n( fluid.totalDensity_n() ),
189  m_phaseVolFraction_n( subRegion.getField< fields::well::phaseVolumeFraction_n >()),
190  m_phaseInternalEnergy_n( fluid.phaseInternalEnergy_n() )
191  {}
192 
193 
195  void computeMassEnergyNormalizers( localIndex const iwelem,
196  real64 & massNormalizer,
197  real64 & energyNormalizer ) const
198  {
199  massNormalizer = LvArray::math::max( m_minNormalizer, m_totalDens_n[iwelem][0] * m_volume[iwelem] );
200 
201  for( integer ip = 0; ip < m_numPhases; ++ip )
202  {
203  energyNormalizer += m_phaseInternalEnergy_n[iwelem][0][ip] * m_phaseDens_n[iwelem][0][ip] * m_phaseVolFraction_n[iwelem][ip] * m_volume[iwelem];
204  }
205  // warning: internal energy can be negative
206  energyNormalizer = LvArray::math::max( m_minNormalizer, LvArray::math::abs( energyNormalizer ) );
207  }
208 
210  virtual void computeLinf( localIndex const iwelem,
211  LinfStackVariables & stack ) const override
212  {
213  real64 normalizer = 0.0;
214  for( integer idof = 0; idof < WJ_ROFFSET::nEqn; ++idof )
215  {
216 
217  // Step 1: compute a normalizer for the control or pressure equation
218 
219  // for the control equation, we distinguish two cases
220  if( idof == WJ_ROFFSET::CONTROL )
221  {
222 
223  // for the top well element, normalize using the current control
224  if( m_isLocallyOwned && iwelem == m_iwelemControl )
225  {
226  if( m_currentControl == WellControls::Control::BHP )
227  {
228  // the residual entry is in pressure units
229  normalizer = m_targetBHP;
230  }
231  else if( m_currentControl == WellControls::Control::TOTALVOLRATE )
232  {
233  // the residual entry is in volume / time units
234  normalizer = LvArray::math::max( LvArray::math::abs( m_targetTotalRate ), m_minNormalizer );
235  }
236  else if( m_currentControl == WellControls::Control::PHASEVOLRATE )
237  {
238  // the residual entry is in volume / time units
239  normalizer = LvArray::math::max( LvArray::math::abs( m_targetPhaseRate ), m_minNormalizer );
240  }
241  else if( m_currentControl == WellControls::Control::MASSRATE )
242  {
243  // the residual entry is in volume / time units
244  normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer );
245  }
246  }
247  // for the pressure difference equation, always normalize by the BHP
248  else
249  {
250  normalizer = m_targetBHP;
251  }
252  }
253  // Step 2: compute a normalizer for the mass balance equations
254  else if( idof >= WJ_ROFFSET::MASSBAL && idof < WJ_ROFFSET::MASSBAL + numComp )
255  {
256  if( m_isProducer ) // only PHASEVOLRATE is supported for now
257  {
258  // the residual is in mass units
259  normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex];
260  }
261  else // Type::INJECTOR, only TOTALVOLRATE is supported for now
262  {
263  if( m_currentControl == WellControls::Control::MASSRATE )
264  {
265  normalizer = m_dt * LvArray::math::abs( m_targetMassRate );
266  }
267  else
268  {
269  // the residual is in mass units
270  normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ) * m_totalDens_n[iwelem][0];
271  }
272 
273  }
274 
275  // to make sure that everything still works well if the rate is zero, we add this check
276  normalizer = LvArray::math::max( normalizer, m_volume[iwelem] * m_totalDens_n[iwelem][0] );
277  }
278  // Step 3: compute a normalizer for the volume balance equations
279  else if( idof == WJ_ROFFSET::VOLBAL )
280  {
281  if( m_isProducer ) // only PHASEVOLRATE is supported for now
282  {
283  // the residual is in volume units
284  normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate );
285  }
286  else // Type::INJECTOR, only TOTALVOLRATE is supported for now
287  {
288  if( m_currentControl == WellControls::Control::MASSRATE )
289  {
290  normalizer = m_dt * LvArray::math::abs( m_targetMassRate/ m_totalDens_n[iwelem][0] );
291  }
292  else
293  {
294  normalizer = m_dt * LvArray::math::abs( m_targetTotalRate );
295  }
296 
297  }
298  // to make sure that everything still works well if the rate is zero, we add this check
299  normalizer = LvArray::math::max( normalizer, m_volume[iwelem] );
300  }
301  // step 3: energy residual
302  if( idof == WJ_ROFFSET::ENERGYBAL )
303  {
304  real64 massNormalizer = 0.0, energyNormalizer = 0.0;
305  computeMassEnergyNormalizers( iwelem, massNormalizer, energyNormalizer );
306  real64 const valEnergy = LvArray::math::abs( m_localResidual[stack.localRow + WJ_ROFFSET::ENERGYBAL] ) / energyNormalizer;
307  if( valEnergy > stack.localValue[1] )
308  {
309  stack.localValue[1] = valEnergy;
310  }
311 
312  }
313  else
314  {
315  normalizer = LvArray::math::max( m_minNormalizer, normalizer );
316  // Step 4: compute the contribution to the residual
317  real64 const val = LvArray::math::abs( m_localResidual[stack.localRow + idof] ) / normalizer;
318  if( val > stack.localValue[0] )
319  {
320  stack.localValue[0] = val;
321  }
322  }
323  }
324  }
325 
327  virtual void computeL2( localIndex const iwelem,
328  L2StackVariables & stack ) const override
329  {
330  GEOS_UNUSED_VAR( iwelem, stack );
331  GEOS_ERROR( "The L2 norm is not implemented for CompositionalMultiphaseWell" );
332  }
333 
334 
335 protected:
336 
339 
342 
344  real64 const m_dt;
345 
347  bool const m_isLocallyOwned;
348 
351 
353  bool const m_isProducer;
354 
357  real64 const m_targetBHP;
358  real64 const m_targetTotalRate;
359  real64 const m_targetPhaseRate;
360  real64 const m_targetMassRate;
361 
364 
368  arrayView2d< real64 const, compflow::USD_PHASE > const m_phaseVolFraction_n;
369  arrayView3d< real64 const, multifluid::USD_PHASE > const m_phaseInternalEnergy_n;
370 
371 };
372 
373 /*
374  *@class ResidualNormKernelFactory
375  */
377 {
378 public:
379 
396  template< typename POLICY >
397  static void
398  createAndLaunch( integer const numComp,
399  integer const targetPhaseIndex,
400  globalIndex const rankOffset,
401  string const & dofKey,
402  arrayView1d< real64 const > const & localResidual,
403  WellElementSubRegion const & subRegion,
404  MultiFluidBase const & fluid,
405  WellControls const & wellControls,
406  real64 const timeAtEndOfStep,
407  real64 const dt,
408  real64 const minNormalizer,
409  real64 (& residualNorm)[2] )
410  {
411  isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch ( numComp, [&]( auto NC )
412  {
413 
414  integer constexpr NUM_COMP = NC();
415  using kernelType = ResidualNormKernel< NUM_COMP >;
416  arrayView1d< globalIndex const > const dofNumber = subRegion.getReference< array1d< globalIndex > >( dofKey );
417  arrayView1d< integer const > const ghostRank = subRegion.ghostRank();
418 
419  kernelType kernel( rankOffset, localResidual, dofNumber, ghostRank,
420  targetPhaseIndex, subRegion, fluid, wellControls, timeAtEndOfStep, dt, minNormalizer );
421  kernelType::template launchLinf< POLICY >( subRegion.size(), kernel, residualNorm );
422  } );
423  }
424 
425 };
426 
427 /******************************** ElementBasedAssemblyKernel ********************************/
428 
435 template< localIndex NUM_COMP >
437 {
438 public:
440  using Base::m_dCompFrac_dCompDens;
441  using Base::m_dofNumber;
442  using Base::m_dPhaseCompFrac;
443  using Base::m_dPhaseDens;
444  using Base::m_dPhaseVolFrac;
445  using Base::m_dPoro_dPres;
446  using Base::m_elemGhostRank;
447  using Base::m_localMatrix;
448  using Base::m_localRhs;
449  using Base::m_numPhases;
450  using Base::m_phaseCompFrac;
451  using Base::m_phaseCompFrac_n;
452  using Base::m_phaseDens;
453  using Base::m_phaseDens_n;
454  using Base::m_phaseVolFrac;
455  using Base::m_phaseVolFrac_n;
456  using Base::m_porosity;
457  using Base::m_porosity_n;
458  using Base::m_rankOffset;
459  using Base::m_volume;
460  using Base::numComp;
461  using Base::numDof;
462  using Base::numEqn;
463 
464  using FLUID_PROP_COFFSET = multifluid::DerivativeOffsetC< NUM_COMP, 1 >;
465 
466 
478  integer const isProducer,
479  globalIndex const rankOffset,
480  string const dofKey,
481  WellElementSubRegion const & subRegion,
482  MultiFluidBase const & fluid,
483  CRSMatrixView< real64, globalIndex const > const & localMatrix,
484  arrayView1d< real64 > const & localRhs,
485  BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > const kernelFlags )
486  : Base( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ),
487  m_phaseInternalEnergy_n( fluid.phaseInternalEnergy_n()),
488  m_phaseInternalEnergy( fluid.phaseInternalEnergy()),
489  m_dPhaseInternalEnergy( fluid.dPhaseInternalEnergy())
490  {}
491 
492  struct StackVariables : public Base::StackVariables
493  {
494 public:
497  : Base::StackVariables()
498  {}
499  using Base::StackVariables::eqnRowIndices;
500  using Base::StackVariables::dofColIndices;
501  using Base::StackVariables::localJacobian;
502  using Base::StackVariables::localResidual;
503  using Base::StackVariables::localRow;
504  using Base::StackVariables::volume;
505 
506 
507 
508  };
515  void setup( localIndex const ei,
516  StackVariables & stack ) const
517  {
518  Base::setup( ei, stack );
519 
520 
521 
522  }
523 
532  StackVariables & stack ) const
533  {
534  using Deriv = multifluid::DerivativeOffset;
535 
536  Base::computeAccumulation( ei, stack, [&]( integer const ip
537  , real64 const & phaseAmount
538  , real64 const & phaseAmount_n
539  , real64 const (&dPhaseAmount)[FLUID_PROP_COFFSET::nDer] )
540  {
541  // We are in the loop over phases, ip provides the current phase index.
542  // We have to do two things:
543  // 1- Assemble the derivatives of the component mass balance equations with respect to temperature
544  // 2- Assemble the phase-dependent part of the accumulation term of the energy equation
545 
546  real64 dPhaseInternalEnergy_dC[numComp]{};
547 
548  // construct the slices
549  arraySlice2d< real64 const, compflow::USD_COMP_DC - 1 > dCompFrac_dCompDens = m_dCompFrac_dCompDens[ei];
550  arraySlice1d< real64 const, multifluid::USD_PHASE - 2 > phaseInternalEnergy_n = m_phaseInternalEnergy_n[ei][0];
551  arraySlice1d< real64 const, multifluid::USD_PHASE - 2 > phaseInternalEnergy = m_phaseInternalEnergy[ei][0];
552  arraySlice2d< real64 const, multifluid::USD_PHASE_DC - 2 > dPhaseInternalEnergy = m_dPhaseInternalEnergy[ei][0];
553 
554  // Step 1: assemble the phase-dependent part of the accumulation term of the energy equation
555 
556  real64 const phaseEnergy = phaseAmount * phaseInternalEnergy[ip];
557  real64 const phaseEnergy_n = phaseAmount_n * phaseInternalEnergy_n[ip];
558  real64 const dPhaseEnergy_dP = dPhaseAmount[FLUID_PROP_COFFSET::dP] * phaseInternalEnergy[ip]
559  + phaseAmount * dPhaseInternalEnergy[ip][Deriv::dP];
560  real64 const dPhaseEnergy_dT = dPhaseAmount[FLUID_PROP_COFFSET::dT] * phaseInternalEnergy[ip]
561  + phaseAmount * dPhaseInternalEnergy[ip][Deriv::dT];
562  // local accumulation
563  stack.localResidual[numEqn-1] += phaseEnergy - phaseEnergy_n;
564 
565  // derivatives w.r.t. pressure and temperature
566  stack.localJacobian[numEqn-1][0] += dPhaseEnergy_dP;
567  stack.localJacobian[numEqn-1][numDof-1] += dPhaseEnergy_dT;
568 
569  // derivatives w.r.t. component densities
570  applyChainRule( numComp, dCompFrac_dCompDens, dPhaseInternalEnergy[ip], dPhaseInternalEnergy_dC, Deriv::dC );
571  for( integer jc = 0; jc < numComp; ++jc )
572  {
573  stack.localJacobian[numEqn-1][jc + 1] += phaseInternalEnergy[ip] * dPhaseAmount[FLUID_PROP_COFFSET::dC+jc]
574  + dPhaseInternalEnergy_dC[jc] * phaseAmount;
575  }
576  } );
577 
578 
579  }
580 
589  StackVariables & stack ) const
590  {
591  Base::computeVolumeBalance( ei, stack );
592 
593  }
594 
596  void complete( localIndex const ei,
597  StackVariables & stack ) const
598  {
599  // Assemble the component mass balance equations and volume balance equations
600  // Energy balance equation updates to solver matrices included in Base class
601  Base::complete( ei, stack );
602 
603  }
604 
605 protected:
608 
613 
614 };
615 
620 {
621 public:
634  template< typename POLICY >
635  static void
636  createAndLaunch( localIndex const numComps,
637  localIndex const numPhases,
638  integer const isProducer,
639  globalIndex const rankOffset,
640  integer const useTotalMassEquation,
641  string const dofKey,
642  WellElementSubRegion const & subRegion,
643  MultiFluidBase const & fluid,
644  CRSMatrixView< real64, globalIndex const > const & localMatrix,
645  arrayView1d< real64 > const & localRhs )
646  {
647  isothermalCompositionalMultiphaseBaseKernels::
648  internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC )
649  {
650  localIndex constexpr NUM_COMP = NC();
651 
652 
653  BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags;
654  if( useTotalMassEquation )
655  kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation );
656 
658  kernel( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags );
660  launch< POLICY, ElementBasedAssemblyKernel< NUM_COMP > >( subRegion.size(), kernel );
661  } );
662  }
663 };
669 template< integer NC >
671 {
672 public:
673  static constexpr integer IS_THERMAL = 1;
675 
676  // Well jacobian column and row indicies
679 
680  using CP_Deriv = multifluid::DerivativeOffsetC< NC, IS_THERMAL >;
681 
683 
684 
685  using Base::m_isProducer;
686  using Base::m_dt;
687  using Base::m_localRhs;
688  using Base::m_localMatrix;
689  using Base::m_rankOffset;
690  using Base::maxNumElems;
691  using Base::maxStencilSize;
692  using Base::m_useTotalMassEquation;
693 
695  static constexpr integer numComp = NC;
696 
698  static constexpr integer numDof = WJ_COFFSET::nDer;
699 
701  static constexpr integer numEqn = WJ_ROFFSET::nEqn - 2;
702 
718  globalIndex const rankOffset,
719  string const wellDofKey,
720  WellControls const & wellControls,
721  WellElementSubRegion const & subRegion,
722  MultiFluidBase const & fluid,
723  CRSMatrixView< real64, globalIndex const > const & localMatrix,
724  arrayView1d< real64 > const & localRhs,
725  BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags )
726  : Base( dt
727  , rankOffset
728  , wellDofKey
729  , wellControls
730  , subRegion
731  , localMatrix
732  , localRhs
733  , kernelFlags ),
734  m_numPhases ( fluid.numFluidPhases()),
735  m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ),
736  m_phaseFraction( fluid.phaseFraction()),
737  m_dPhaseFraction( fluid.dPhaseFraction()),
738  m_phaseEnthalpy( fluid.phaseEnthalpy()),
739  m_dPhaseEnthalpy( fluid.dPhaseEnthalpy())
740  { }
741 
743  {
744 public:
745 
747  StackVariables( localIndex const size )
748  : Base::StackVariables( size )
749  {}
750 
757  };
758 
760  inline
761  void setup( localIndex const iwelem, StackVariables & stack ) const
762  {
763  Base::setup ( iwelem, stack );
764  stack.localEnergyFlux.resize( stack.numConnectedElems );
765  stack.localEnergyFluxJacobian.resize( stack.numConnectedElems, stack.stencilSize * numDof );
766  stack.localEnergyFluxJacobian_dQ.resize( stack.numConnectedElems, 1 );
767  for( integer i=0; i<stack.numConnectedElems; i++ )
768  {
769  stack.localEnergyFlux[i]=0.0;
770  stack.localEnergyFluxJacobian_dQ[i][0]=0.0;
771  for( integer j=0; j< stack.stencilSize * numDof; j++ )
772  stack.localEnergyFluxJacobian[i][j] = 0.0;
773  }
774  }
775 
777  inline
778  void complete( localIndex const iwelem, StackVariables & stack ) const
779  {
780  Base::complete ( iwelem, stack );
781 
782  using namespace compositionalMultiphaseUtilities;
783  if( stack.numConnectedElems ==1 )
784  {
785  // Setup Jacobian global row indicies for energy equation
786  globalIndex oneSidedEqnRowIndices = stack.offsetUp + WJ_ROFFSET::ENERGYBAL - m_rankOffset;
787 
788  if( oneSidedEqnRowIndices >= 0 && oneSidedEqnRowIndices < m_localMatrix.numRows() )
789  {
790 
791  if( !m_isProducer && m_globalWellElementIndex[iwelem] == 0 )
792  {
793  // For top segment energy balance eqn replaced with T(n+1) - T = 0
794  // No other energy balance derivatives
795  // Assumption is global index == 0 is top segment with fixed temp BC
796  for( integer i=0; i< CP_Deriv::nDer; i++ )
797  {
798  stack.localEnergyFluxJacobian[0][i] = 0.0;
799  }
800  stack.localEnergyFluxJacobian_dQ[0][0]=0;
801  stack.localEnergyFlux[0]=0;
802  }
803 
804 
805  // Setup Jacobian global col indicies ( Mapping from local jac order to well jac order)
806  globalIndex oneSidedDofColIndices_dRate = stack.offsetCurrent + WJ_COFFSET::dQ;
807  globalIndex oneSidedDofColIndices_dPresCompTempUp[CP_Deriv::nDer]{};
808 
809  int ioff=0;
810  oneSidedDofColIndices_dPresCompTempUp[ioff++] = stack.offsetUp + WJ_COFFSET::dP;
811  oneSidedDofColIndices_dPresCompTempUp[ioff++] = stack.offsetUp + WJ_COFFSET::dT;
812  for( integer jdof = 0; jdof < NC; ++jdof )
813  {
814  oneSidedDofColIndices_dPresCompTempUp[ioff++] = stack.offsetUp + WJ_COFFSET::dC+ jdof;
815  }
816 
817  m_localMatrix.template addToRow< parallelDeviceAtomic >( oneSidedEqnRowIndices,
818  &oneSidedDofColIndices_dRate,
819  stack.localEnergyFluxJacobian_dQ[0],
820  1 );
821  m_localMatrix.template addToRowBinarySearchUnsorted< parallelDeviceAtomic >( oneSidedEqnRowIndices,
822  oneSidedDofColIndices_dPresCompTempUp,
823  stack.localEnergyFluxJacobian[0],
824  CP_Deriv::nDer );
825  RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[oneSidedEqnRowIndices], stack.localEnergyFlux[0] );
826  }
827  }
828  else // if ( stack.numConnectedElems == 2 )
829  {
830  globalIndex row_current = stack.offsetCurrent + WJ_ROFFSET::ENERGYBAL - m_rankOffset;
831  globalIndex row_next = stack.offsetNext + WJ_ROFFSET::ENERGYBAL - m_rankOffset;
832 
833  if( !m_isProducer )
834  {
835  if( row_next >= 0 && row_next < m_localMatrix.numRows() )
836  {
837  if( m_globalWellElementIndex[stack.iwelemNext] == 0 )
838  {
839  for( integer i=0; i<CP_Deriv::nDer; i++ )
840  stack.localEnergyFluxJacobian[TAG::NEXT][i] = 0.0;
841  stack.localEnergyFluxJacobian_dQ[TAG::NEXT][0] =0;
842  stack.localEnergyFlux[TAG::NEXT] =0;
843  }
844 
845  }
846  }
847  // Setup Jacobian global row indicies
848  // equations for COMPONENT + ENERGY balances
849  globalIndex eqnRowIndices[2]{};
850 
851  // energy balance equations
852  eqnRowIndices[TAG::CURRENT ] = row_current;
853  eqnRowIndices[TAG::NEXT ] = row_next;
854 
855 
856  // Setup Jacobian global col indicies ( Mapping from local jac order to well jac order)
857  globalIndex dofColIndices[CP_Deriv::nDer]{};
858  globalIndex dofColIndices_dRate = stack.offsetCurrent + WJ_COFFSET::dQ;
859 
860  int ioff=0;
861  // Indice storage order reflects local jac col storage order CP::Deriv order P T DENS
862  // well jacobian order is P DENS Q T
863  dofColIndices[ioff++] = stack.offsetUp + WJ_COFFSET::dP;
864 
865  if constexpr ( IS_THERMAL )
866  {
867  dofColIndices[ioff++] = stack.offsetUp + WJ_COFFSET::dT;
868  }
869  for( integer jdof = 0; jdof < NC; ++jdof )
870  {
871  dofColIndices[ioff++] = stack.offsetUp + WJ_COFFSET::dC+ jdof;
872  }
873  // Note this updates diag and offdiag
874  for( integer i = 0; i < 2; ++i )
875  {
876  if( eqnRowIndices[i] >= 0 && eqnRowIndices[i] < m_localMatrix.numRows() )
877  {
878  m_localMatrix.template addToRow< parallelDeviceAtomic >( eqnRowIndices[i],
879  &dofColIndices_dRate,
880  stack.localEnergyFluxJacobian_dQ[i],
881  1 );
882  m_localMatrix.template addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices[i],
883  dofColIndices,
884  stack.localEnergyFluxJacobian[i],
885  CP_Deriv::nDer );
886  RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[eqnRowIndices[i]], stack.localEnergyFlux[i] );
887  }
888  }
889  }
890 
891  }
892 
902  inline
903  void computeFlux( localIndex const iwelem, StackVariables & stack ) const
904  {
905  Base::computeFlux ( iwelem, stack, [&] ( localIndex const & iwelemNext
906  , localIndex const & iwelemUp
907  , real64 const & currentConnRate
908  , real64 const (&dCompFrac_dCompDens)[NC][NC] )
909  {
910 
911  if( iwelemNext < 0 && !m_isProducer ) // exit connection, injector
912  {
913  real64 eflux=0;
914  real64 eflux_dq=0;
915  for( integer ip = 0; ip < m_numPhases; ++ip )
916  {
917  eflux += m_phaseEnthalpy[iwelemUp][0][ip]* m_phaseFraction[iwelemUp][0][ip];
918  eflux_dq += m_phaseEnthalpy[iwelemUp][0][ip] * m_phaseFraction[iwelemUp][0][ip];
919 
920  stack.localEnergyFluxJacobian[0] [CP_Deriv::dP] += m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dP]
921  + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dP]*m_phaseFraction[iwelemUp][0][ip];
922  stack.localEnergyFluxJacobian[0] [CP_Deriv::dT] += m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dT]
923  + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dT]*m_phaseFraction[iwelemUp][0][ip];
924 
925  real64 dProp1_dC[numComp]{};
926  applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseEnthalpy[iwelemUp][0][ip], dProp1_dC, CP_Deriv::dC );
927  real64 dProp2_dC[numComp]{};
928  applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseFraction[iwelemUp][0][ip], dProp2_dC, CP_Deriv::dC );
929  for( integer dof=0; dof < numComp; dof++ )
930  {
931  stack.localEnergyFluxJacobian[0] [CP_Deriv::dC+dof] += m_phaseEnthalpy[iwelemUp][0][ip]*dProp2_dC[dof]
932  + dProp1_dC[dof]*m_phaseFraction[iwelemUp][0][ip];
933  }
934  }
935  for( integer dof=0; dof < CP_Deriv::nDer; dof++ )
936  {
937  stack.localEnergyFluxJacobian[0] [dof] *= -m_dt*currentConnRate;
938  }
939  // Energy equation
940  stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate;
941  stack.localEnergyFluxJacobian_dQ[0][0] = -m_dt * eflux_dq;
942  }
943  else if( ( iwelemNext < 0 && m_isProducer ) || currentConnRate < 0 ) // exit connection, producer
944  {
945  real64 eflux=0;
946  real64 eflux_dq=0;
947  for( integer ip = 0; ip < m_numPhases; ++ip )
948  {
949  eflux += m_phaseEnthalpy[iwelemUp][0][ip]* m_phaseFraction[iwelemUp][0][ip];
950  eflux_dq += m_phaseEnthalpy[iwelemUp][0][ip] * m_phaseFraction[iwelemUp][0][ip];
951  stack.localEnergyFluxJacobian[0] [CP_Deriv::dP] += m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dP]
952  + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dP]*m_phaseFraction[iwelemUp][0][ip];
953  stack.localEnergyFluxJacobian[0] [CP_Deriv::dT] += m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dT]
954  + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dT]*m_phaseFraction[iwelemUp][0][ip];
955 
956  real64 dProp1_dC[numComp]{};
957  applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseEnthalpy[iwelemUp][0][ip], dProp1_dC, CP_Deriv::dC );
958  real64 dProp2_dC[numComp]{};
959  applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseFraction[iwelemUp][0][ip], dProp2_dC, CP_Deriv::dC );
960  for( integer dof=0; dof < numComp; dof++ )
961  {
962  stack.localEnergyFluxJacobian[0] [CP_Deriv::dC+dof] += m_phaseEnthalpy[iwelemUp][0][ip]*dProp2_dC[dof]
963  + dProp1_dC[dof]*m_phaseFraction[iwelemUp][0][ip];
964  }
965 
966  }
967 
968  for( integer dof=0; dof < CP_Deriv::nDer; dof++ )
969  {
970  stack.localEnergyFluxJacobian[0][dof] *= -m_dt*currentConnRate;
971  }
972  stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate;
973  stack.localEnergyFluxJacobian_dQ[0][0] = -m_dt*eflux_dq;
974  }
975  else
976  {
977  real64 eflux=0;
978  real64 eflux_dq=0;
979  for( integer ip = 0; ip < m_numPhases; ++ip )
980  {
981  eflux += m_phaseEnthalpy[iwelemUp][0][ip]* m_phaseFraction[iwelemUp][0][ip];
982  eflux_dq += m_phaseEnthalpy[iwelemUp][0][ip] * m_phaseFraction[iwelemUp][0][ip];
983 
984  real64 dprop_dp = m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dP]
985  + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dP]*m_phaseFraction[iwelemUp][0][ip];
986  real64 dprop_dt = m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dT]
987  + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dT]*m_phaseFraction[iwelemUp][0][ip];
988 
989  stack.localEnergyFluxJacobian[TAG::NEXT ] [CP_Deriv::dP] += dprop_dp;
990  stack.localEnergyFluxJacobian[TAG::NEXT] [CP_Deriv::dT] += dprop_dt;
991 
992  stack.localEnergyFluxJacobian[TAG::CURRENT ] [CP_Deriv::dP] += dprop_dp;
993  stack.localEnergyFluxJacobian[TAG::CURRENT] [CP_Deriv::dT] += dprop_dt;
994 
995  real64 dPE_dC[numComp]{};
996  applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseEnthalpy[iwelemUp][0][ip], dPE_dC, CP_Deriv::dC );
997  real64 dPF_dC[numComp]{};
998  applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseFraction[iwelemUp][0][ip], dPF_dC, CP_Deriv::dC );
999 
1000  for( integer dof=0; dof < numComp; dof++ )
1001  {
1002  stack.localEnergyFluxJacobian[TAG::NEXT ][CP_Deriv::dC+dof] += m_phaseEnthalpy[iwelemUp][0][ip]*dPF_dC[dof]
1003  +dPE_dC[dof]*m_phaseFraction[iwelemUp][0][ip];
1004  stack.localEnergyFluxJacobian[TAG::CURRENT ][CP_Deriv::dC+dof] += m_phaseEnthalpy[iwelemUp][0][ip]*dPF_dC[dof]
1005  +dPE_dC[dof]*m_phaseFraction[iwelemUp][0][ip];
1006  }
1007  }
1008  stack.localEnergyFlux[TAG::NEXT ] = m_dt * eflux * currentConnRate;
1009  stack.localEnergyFlux[TAG::CURRENT ] = -m_dt * eflux * currentConnRate;
1010  stack.localEnergyFluxJacobian_dQ [TAG::NEXT ][0] = m_dt * eflux_dq;
1011  stack.localEnergyFluxJacobian_dQ [TAG::CURRENT][0] = -m_dt * eflux_dq;
1012  for( integer dof=0; dof < CP_Deriv::nDer; dof++ )
1013  {
1014  stack.localEnergyFluxJacobian[TAG::NEXT ][dof] *= m_dt*currentConnRate;
1015  stack.localEnergyFluxJacobian[TAG::CURRENT ][dof] *= -m_dt*currentConnRate;
1016  }
1017  }
1018 
1019  } );
1020 
1021  }
1022 
1023 
1032  template< typename POLICY, typename KERNEL_TYPE >
1033  static void
1034  launch( localIndex const numElements,
1035  KERNEL_TYPE const & kernelComponent )
1036  {
1038  forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const ie )
1039  {
1040  typename KERNEL_TYPE::StackVariables stack( 1 );
1041 
1042  kernelComponent.setup( ie, stack );
1043  kernelComponent.computeFlux( ie, stack );
1044  kernelComponent.complete( ie, stack );
1045  } );
1046  }
1047 
1048 protected:
1051 
1054 
1058 
1062 
1063 
1064 };
1065 
1070 {
1071 public:
1072 
1086  template< typename POLICY >
1087  static void
1088  createAndLaunch( integer const numComps,
1089  real64 const dt,
1090  globalIndex const rankOffset,
1091  integer const useTotalMassEquation,
1092  string const dofKey,
1093  WellControls const & wellControls,
1094  WellElementSubRegion const & subRegion,
1095  MultiFluidBase const & fluid,
1096  CRSMatrixView< real64, globalIndex const > const & localMatrix,
1097  arrayView1d< real64 > const & localRhs )
1098  {
1099  isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC )
1100  {
1101  integer constexpr NUM_COMP = NC();
1102 
1103 
1104  BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags;
1105  if( useTotalMassEquation )
1106  kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::KernelFlags::TotalMassEquation );
1107 
1108 
1109  using kernelType = FaceBasedAssemblyKernel< NUM_COMP >;
1110 
1111 
1112  kernelType kernel( dt, rankOffset, dofKey, wellControls, subRegion, fluid, localMatrix, localRhs, kernelFlags );
1113  kernelType::template launch< POLICY >( subRegion.size(), kernel );
1114  } );
1115  }
1116 };
1117 
1118 } // end namespace thermalCompositionalMultiphaseWellKernels
1119 
1120 } // end namespace geos
1121 
1122 #endif // GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_THERMALCOMPOSITIONALMULTIPHASEWELLKERNELS_HPP
#define GEOS_HOST_DEVICE
Marks a host-device function.
Definition: GeosxMacros.hpp:49
#define GEOS_UNUSED_VAR(...)
Mark an unused variable and silence compiler warnings.
Definition: GeosxMacros.hpp:84
#define GEOS_ERROR(msg)
Raise a hard error and terminate the program.
Definition: Logger.hpp:157
#define GEOS_MARK_FUNCTION
Mark function with both Caliper and NVTX if enabled.
The ObjectManagerBase is the base object of all object managers in the mesh data hierachy.
array1d< integer > const & ghostRank()
Get the ghost information of each object.
This class describes the controls used to operate a well.
This class describes a collection of local well elements and perforations.
Define the interface for the assembly kernel in charge of accumulation and volume balance.
Define the interface for the assembly kernel in charge of flux terms.
Define the interface for the property kernel in charge of computing the total mass density.
GEOS_DECLTYPE_AUTO_RETURN getReference(LOOKUP_TYPE const &lookup) const
Look up a wrapper and get reference to wrapped object.
Definition: Group.hpp:1273
localIndex size() const
Get the "size" of the group, which determines the number of elements in resizable wrappers.
Definition: Group.hpp:1315
Define the base interface for the property update kernels.
Define the base interface for the residual calculations.
static void createAndLaunch(localIndex const numComps, localIndex const numPhases, integer const isProducer, globalIndex const rankOffset, integer const useTotalMassEquation, string const dofKey, WellElementSubRegion const &subRegion, MultiFluidBase const &fluid, CRSMatrixView< real64, globalIndex const > const &localMatrix, arrayView1d< real64 > const &localRhs)
Create a new kernel and launch.
Define the interface for the assembly kernel in charge of thermal accumulation and volume balance.
arrayView2d< real64 const > const m_dPoro_dTemp
View on derivative of porosity w.r.t temperature.
GEOS_HOST_DEVICE void computeVolumeBalance(localIndex const ei, StackVariables &stack) const
Compute the local volume balance contributions to the residual and Jacobian.
arrayView3d< real64 const, multifluid::USD_PHASE > m_phaseInternalEnergy_n
Views on phase internal energy.
GEOS_HOST_DEVICE void computeAccumulation(localIndex const ei, StackVariables &stack) const
Compute the local accumulation contributions to the residual and Jacobian.
ElementBasedAssemblyKernel(localIndex const numPhases, integer const isProducer, globalIndex const rankOffset, string const dofKey, WellElementSubRegion const &subRegion, MultiFluidBase const &fluid, CRSMatrixView< real64, globalIndex const > const &localMatrix, arrayView1d< real64 > const &localRhs, BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > const kernelFlags)
Constructor.
GEOS_HOST_DEVICE void setup(localIndex const ei, StackVariables &stack) const
Performs the setup phase for the kernel.
static void createAndLaunch(integer const numComps, real64 const dt, globalIndex const rankOffset, integer const useTotalMassEquation, string const dofKey, WellControls const &wellControls, WellElementSubRegion const &subRegion, MultiFluidBase const &fluid, CRSMatrixView< real64, globalIndex const > const &localMatrix, arrayView1d< real64 > const &localRhs)
Create a new kernel and launch.
Define the interface for the assembly kernel in charge of flux terms.
FaceBasedAssemblyKernel(real64 const dt, globalIndex const rankOffset, string const wellDofKey, WellControls const &wellControls, WellElementSubRegion const &subRegion, MultiFluidBase const &fluid, CRSMatrixView< real64, globalIndex const > const &localMatrix, arrayView1d< real64 > const &localRhs, BitFlags< isothermalCompositionalMultiphaseBaseKernels::KernelFlags > kernelFlags)
Constructor for the kernel interface.
arrayView3d< real64 const, multifluid::USD_PHASE > m_phaseEnthalpy
Views on phase enthalpy.
GEOS_HOST_DEVICE void computeFlux(localIndex const iwelem, StackVariables &stack) const
Compute the local flux contributions to the residual and Jacobian.
arrayView1d< globalIndex const > m_globalWellElementIndex
Global index of local element.
arrayView3d< real64 const, multifluid::USD_PHASE > const m_phaseFraction
Element phase fraction.
static void launch(localIndex const numElements, KERNEL_TYPE const &kernelComponent)
Performs the kernel launch.
static void createAndLaunch(integer const numComp, integer const targetPhaseIndex, globalIndex const rankOffset, string const &dofKey, arrayView1d< real64 const > const &localResidual, WellElementSubRegion const &subRegion, MultiFluidBase const &fluid, WellControls const &wellControls, real64 const timeAtEndOfStep, real64 const dt, real64 const minNormalizer, real64(&residualNorm)[2])
Create a new kernel and launch.
arrayView3d< real64 const, multifluid::USD_PHASE > const m_phaseDens_n
View on phase/total density at the previous converged time step.
localIndex const m_iwelemControl
Index of the element where the control is enforced.
virtual GEOS_HOST_DEVICE void computeLinf(localIndex const iwelem, LinfStackVariables &stack) const override
Compute the local values for the Linf norm.
virtual GEOS_HOST_DEVICE void computeL2(localIndex const iwelem, L2StackVariables &stack) const override
Compute the local values and normalizer for the L2 norm.
bool const m_isProducer
Flag indicating whether the well is a producer or an injector.
bool const m_isLocallyOwned
Flag indicating whether the well is locally owned or not.
static void createAndLaunch(integer const numComp, integer const numPhase, ObjectManagerBase &subRegion, MultiFluidBase const &fluid)
Create a new kernel and launch.
Define the interface for the property kernel in charge of computing the total mass density.
GEOS_HOST_DEVICE void compute(localIndex const ei) const
Compute the total mass density in an element.
TotalMassDensityKernel(ObjectManagerBase &subRegion, MultiFluidBase const &fluid)
Constructor.
ArrayView< T, 1 > arrayView1d
Alias for 1D array view.
Definition: DataTypes.hpp:180
StackArray< T, 2, MAXSIZE > stackArray2d
Alias for 2D stack array.
Definition: DataTypes.hpp:204
LvArray::CRSMatrixView< T, COL_INDEX, localIndex const, LvArray::ChaiBuffer > CRSMatrixView
Alias for CRS Matrix View.
Definition: DataTypes.hpp:310
GEOS_GLOBALINDEX_TYPE globalIndex
Global index type (for indexing objects across MPI partitions).
Definition: DataTypes.hpp:88
StackArray< T, 1, MAXSIZE > stackArray1d
Alias for 1D stack array.
Definition: DataTypes.hpp:188
ArraySlice< T, 2, USD > arraySlice2d
Alias for 2D array slice.
Definition: DataTypes.hpp:200
double real64
64-bit floating point type.
Definition: DataTypes.hpp:99
GEOS_LOCALINDEX_TYPE localIndex
Local index type (for indexing objects within an MPI partition).
Definition: DataTypes.hpp:85
ArraySlice< T, 1, USD > arraySlice1d
Alias for 1D array slice.
Definition: DataTypes.hpp:184
std::int32_t integer
Signed integer type.
Definition: DataTypes.hpp:82
ArrayView< T, 4, USD > arrayView4d
Alias for 4D array view.
Definition: DataTypes.hpp:228
ArrayView< T, 2, USD > arrayView2d
Alias for 2D array view.
Definition: DataTypes.hpp:196
Array< T, 1 > array1d
Alias for 1D array.
Definition: DataTypes.hpp:176
ArrayView< T, 3, USD > arrayView3d
Alias for 3D array view.
Definition: DataTypes.hpp:212
stackArray1d< real64, maxNumElems > localEnergyFlux
Storage for the face local residual vector (energy equation)
stackArray2d< real64, maxNumElems *maxStencilSize > localEnergyFluxJacobian_dQ
Storage for the face local Jacobian matrix dQ only.
stackArray2d< real64, maxNumElems *maxStencilSize *CP_Deriv::nDer > localEnergyFluxJacobian
Storage for the face local energy Jacobian matrix dC dP dT.