Skip to content

Commit 40d0730

Browse files
authored
Extending preconditioners to handle a diagonal operator for E-field (BLAST-WarpX#5716)
- Extended the `CurlCurlMLMGPC` interface to work with non-scalar `beta` coefficient. It can now be a `MultiFab` array (same size/layout as E-Fields) with one component representing a diagonal matrix/operator on the E-field. - Implemented a `JacobiPC` preconditioner to solve the system `\sigma E = b` using the Jacobi iterations. For now, `\sigma` is a diagonal matrix (stored as a `MultiFab` array (same size/layout as E-Fields) with one component. The solution is set as `E = b / \sigma`, so the iterations are not yet implemented. The complete Jacobi method will be implemented once non-diagonal elements of `\sigma` are available. The preconditioners fetch a pointer to the matrix operator (`beta` or `\sigma`) from the implicit time integrator object.
1 parent a380961 commit 40d0730

File tree

8 files changed

+347
-15
lines changed

8 files changed

+347
-15
lines changed

Source/FieldSolver/ImplicitSolvers/ImplicitSolver.H

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ public:
6666
amrex::Real a_dt,
6767
int a_step ) = 0;
6868

69+
70+
/**
71+
* \brief Return pointer to MultiFab array for mass matrix
72+
*/
73+
[[nodiscard]] virtual const amrex::Vector<amrex::Array<amrex::MultiFab*,3>>* GetSigmaCoeff() const { return nullptr; }
74+
6975
//
7076
// the following routines are called by the linear and nonlinear solvers
7177
//

Source/FieldSolver/ImplicitSolvers/ThetaImplicitEM.H

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,21 @@ public:
8484
int a_nl_iter,
8585
bool a_from_jacobian ) override;
8686

87+
/**
88+
* \brief Return pointer to MultiFab array for mass matrix
89+
*/
90+
[[nodiscard]] const amrex::Vector<amrex::Array<amrex::MultiFab*,3>>* GetSigmaCoeff() const override
91+
{
92+
return &m_sigma_mfarrvec;
93+
}
94+
8795
private:
8896

97+
/**
98+
* \brief Array of multifab pointers to mass matrix
99+
*/
100+
amrex::Vector<amrex::Array<amrex::MultiFab*,3>> m_sigma_mfarrvec;
101+
89102
/**
90103
* \brief Solver vectors to be used in the nonlinear solver to solve for the
91104
* electric field E. The main logic for determining which variables should be

Source/FieldSolver/ImplicitSolvers/ThetaImplicitEM.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,26 @@ void ThetaImplicitEM::Define ( WarpX* const a_WarpX )
4949
// Parse nonlinear solver parameters
5050
parseNonlinearSolverParams( pp );
5151

52+
// Define sigmaPC mutlifabs
53+
using ablastr::fields::Direction;
54+
for (int lev = 0; lev < m_num_amr_levels; ++lev) {
55+
const auto& ba_Ex = m_WarpX->m_fields.get(FieldType::Efield_fp, Direction{0}, lev)->boxArray();
56+
const auto& ba_Ey = m_WarpX->m_fields.get(FieldType::Efield_fp, Direction{1}, lev)->boxArray();
57+
const auto& ba_Ez = m_WarpX->m_fields.get(FieldType::Efield_fp, Direction{2}, lev)->boxArray();
58+
const auto& dm = m_WarpX->m_fields.get(FieldType::Efield_fp, Direction{0}, lev)->DistributionMap();
59+
const amrex::IntVect ngb = m_WarpX->m_fields.get(FieldType::Efield_fp, Direction{0}, lev)->nGrowVect();
60+
m_WarpX->m_fields.alloc_init(FieldType::sigmaPC, Direction{0}, lev, ba_Ex, dm, 1, ngb, 0.0_rt);
61+
m_WarpX->m_fields.alloc_init(FieldType::sigmaPC, Direction{1}, lev, ba_Ey, dm, 1, ngb, 0.0_rt);
62+
m_WarpX->m_fields.alloc_init(FieldType::sigmaPC, Direction{2}, lev, ba_Ez, dm, 1, ngb, 0.0_rt);
63+
}
64+
65+
// Set the pointer to mass matrix MultiFab
66+
for (int lev = 0; lev < m_num_amr_levels; ++lev) {
67+
m_sigma_mfarrvec.push_back(m_WarpX->m_fields.get_alldirs(FieldType::sigmaPC, 0));
68+
// setting m_sigma to 1.0 right now for testing
69+
for (int dim = 0; dim < 3; dim++) { m_sigma_mfarrvec[lev][dim]->setVal(1.0); }
70+
}
71+
5272
// Define the nonlinear solver
5373
m_nlsolver->Define(m_E, this);
5474
m_is_defined = true;

Source/Fields.H

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ namespace warpx::fields
8383
Bfield_avg_cp,
8484
B_old, /**< Stores the value of B at the beginning of the timestep, for the implicit solver */
8585
ECTRhofield,
86-
Venl
86+
Venl,
87+
sigmaPC
8788
);
8889

8990
/** these are vector fields */
@@ -125,7 +126,8 @@ namespace warpx::fields
125126
FieldType::Bfield_avg_cp,
126127
FieldType::B_old,
127128
FieldType::ECTRhofield,
128-
FieldType::Venl
129+
FieldType::Venl,
130+
FieldType::sigmaPC
129131
};
130132

131133
/** Returns true if a FieldType represents a vector field */

Source/NonlinearSolvers/CurlCurlMLMGPC.H

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,12 @@ class CurlCurlMLMGPC : public Preconditioner<T,Ops>
7575
/**
7676
* \brief Define the preconditioner
7777
*/
78-
void Define (const T&, Ops*) override;
78+
void Define (const T&, Ops* const) override;
7979

8080
/**
8181
* \brief Update the preconditioner
8282
*/
83-
void Update (const T&) override;
83+
void Update (const T& a_U) override;
8484

8585
/**
8686
* \brief Apply (solve) the preconditioner given a RHS
@@ -118,6 +118,8 @@ class CurlCurlMLMGPC : public Preconditioner<T,Ops>
118118
int m_max_iter = 10;
119119
int m_max_coarsening_level = 30;
120120

121+
bool m_beta_scalar = true;
122+
121123
RT m_atol = 1.0e-16;
122124
RT m_rtol = 1.0e-4;
123125

@@ -129,6 +131,8 @@ class CurlCurlMLMGPC : public Preconditioner<T,Ops>
129131
amrex::Vector<amrex::DistributionMapping> m_dmap;
130132
amrex::IntVect m_gv;
131133

134+
const amrex::Vector<amrex::Array<amrex::MultiFab*,3>>* m_bcoefs = nullptr;
135+
132136
amrex::Array<amrex::LinOpBCType, AMREX_SPACEDIM> m_bc_lo;
133137
amrex::Array<amrex::LinOpBCType, AMREX_SPACEDIM> m_bc_hi;
134138

@@ -153,6 +157,7 @@ void CurlCurlMLMGPC<T,Ops>::printParameters() const
153157
auto pc_name = getEnumNameString(PreconditionerType::pc_curl_curl_mlmg);
154158
Print() << pc_name << " verbose: " << (m_verbose?"true":"false") << "\n";
155159
Print() << pc_name << " bottom verbose: " << (m_bottom_verbose?"true":"false") << "\n";
160+
Print() << pc_name << " beta coeff: " << (m_beta_scalar?"scalar (1.0)":"MultiFab") << "\n";
156161
Print() << pc_name << " max iter: " << m_max_iter << "\n";
157162
Print() << pc_name << " agglomeration: " << m_agglomeration << "\n";
158163
Print() << pc_name << " consolidation: " << m_consolidation << "\n";
@@ -203,18 +208,21 @@ void CurlCurlMLMGPC<T,Ops>::Define ( const T& a_U,
203208
// read preconditioner parameters
204209
readParameters();
205210

211+
// Get data vectors from a_U
212+
auto& u_mfarrvec = a_U.getArrayVec();
213+
206214
// create info object for curl-curl op
207215
m_info = std::make_unique<LPInfo>();
208216
m_info->setAgglomeration(m_agglomeration);
209217
m_info->setConsolidation(m_consolidation);
210218
m_info->setMaxCoarseningLevel(m_max_coarsening_level);
211219

212-
// Get data vectors from a_U
213-
auto& u_mfarrvec = a_U.getArrayVec();
214-
215220
// Set number of AMR levels and create geometry, grids, and
216221
// distribution mapping vectors.
217222
m_num_amr_levels = m_ops->numAMRLevels();
223+
if (m_num_amr_levels > 1) {
224+
WARPX_ABORT_WITH_MESSAGE("CurlCurlMLMGPC::Define(): m_num_amr_levels > 1");
225+
}
218226
m_geom.resize(m_num_amr_levels);
219227
m_grids.resize(m_num_amr_levels);
220228
m_dmap.resize(m_num_amr_levels);
@@ -248,32 +256,54 @@ void CurlCurlMLMGPC<T,Ops>::Define ( const T& a_U,
248256
m_gmres_solver->setVerbose(static_cast<int>(m_verbose));
249257
}
250258

259+
m_bcoefs = m_ops->GetSigmaCoeff();
260+
if (m_bcoefs != nullptr) { m_beta_scalar = false; }
261+
251262
m_is_defined = true;
252263
}
253264

254265
template <class T, class Ops>
255266
void CurlCurlMLMGPC<T,Ops>::Update (const T& a_U)
256267
{
268+
using namespace amrex;
269+
257270
WARPX_ALWAYS_ASSERT_WITH_MESSAGE(
258271
IsDefined(),
259272
"CurlCurlMLMGPC::Update() called on undefined object" );
260273

261274
// a_U is not needed for a linear operator
262275
amrex::ignore_unused(a_U);
263276

264-
// set the coefficients alpha and beta for curl-curl op
277+
// set the alpha for curl-curl op
265278
const RT thetaDt = m_ops->GetTheta()*this->m_dt;
266279
const RT alpha = (thetaDt*PhysConst::c) * (thetaDt*PhysConst::c);
267-
const RT beta = RT(1.0);
280+
m_curl_curl->setScalars(alpha, RT(1.0));
268281

269-
m_curl_curl->setScalars(alpha, beta);
282+
if (!m_beta_scalar) {
283+
for (int n = 0; n < m_num_amr_levels; n++) {
284+
#if defined(WARPX_DIM_1D_Z)
285+
// Missing dimensions is x,y in WarpX and y,z in AMReX
286+
m_curl_curl->setBeta({Array<MultiFab const*,3>{ (*m_bcoefs)[n][2],
287+
(*m_bcoefs)[n][1],
288+
(*m_bcoefs)[n][0]}});
289+
#elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ)
290+
// Missing dimension is y in WarpX and z in AMReX
291+
m_curl_curl->setBeta({Array<MultiFab const*,3>{ (*m_bcoefs)[n][0],
292+
(*m_bcoefs)[n][2],
293+
(*m_bcoefs)[n][1]}});
294+
#elif defined(WARPX_DIM_3D)
295+
m_curl_curl->setBeta({Array<MultiFab const*,3>{ (*m_bcoefs)[n][0],
296+
(*m_bcoefs)[n][1],
297+
(*m_bcoefs)[n][2]}});
298+
#endif
299+
}
300+
}
270301

271302
if (m_verbose) {
272303
amrex::Print() << "Updating " << amrex::getEnumNameString(PreconditionerType::pc_curl_curl_mlmg)
273304
<< ": theta*dt = " << thetaDt << ", "
274305
<< " coefficients: "
275-
<< "alpha = " << alpha << ", "
276-
<< "beta = " << beta << "\n";
306+
<< "alpha = " << alpha << "\n";
277307
}
278308
}
279309

0 commit comments

Comments
 (0)