-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Floquet periodic boundary conditions #314
base: main
Are you sure you want to change the base?
Changes from all commits
86d6055
31a4a9b
6420322
707b32b
82d1d61
1de268d
95502e2
6021e8f
0301a71
63096b3
974da2a
3f6bef0
975a274
57e6d3a
c45ef21
ff552d2
5a4f614
447df80
e2d6e46
49b29e4
e8b0d95
3b2700c
951d70f
1557829
7dfcccc
e687ab9
967c6e6
12ab759
e713a6f
b5ef9bd
9382889
6a27333
bebcab6
1101ef9
0dd577d
00e4b8c
cc6aeca
0a862ec
f2288a5
9fbe941
b865e9c
18eab19
a01573c
76e325b
650dce3
2e2ae18
102386d
7a1d90e
14c8ce3
7259f7c
4ea97e0
d72f40d
320b333
ed3532e
f0e0c2e
d7e972a
494a51a
d31c929
5ae3cf3
a4ea4f1
c14cb5b
d21f1e7
81a8fec
a029c60
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -432,10 +432,14 @@ iterative solver choices, and the default choice depends on the iterative solver | |
- `"Default"` | ||
|
||
`"DivFreeTol" [1.0e-12]` : Relative tolerance for divergence-free cleaning used in the | ||
eigenmode simulation type. | ||
eigenmode simulation type. Ignored if non-zero Floquet wave vector is specified in | ||
[`config["Boundaries"]["Periodic"]["FloquetWaveVector"]`](boundaries.md##boundaries%5B%%22Periodic%22%5D%22FloquetWaveVector%22%5D) | ||
or [`config["Boundaries"]["FloquetWaveVector"]`](boundaries.md##boundaries%5B%%22FloquetWaveVector%22%5D). | ||
Comment on lines
+435
to
+437
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is also necessary for London equations, so you'll need to reconcile this. |
||
|
||
`"DivFreeMaxIts" [1000]` : Maximum number of iterations for divergence-free cleaning use in | ||
the eigenmode simulation type. | ||
the eigenmode simulation type. Ignored if non-zero Floquet wave vector is specified in | ||
[`config["Boundaries"]["Periodic"]["FloquetWaveVector"]`](boundaries.md##boundaries%5B%%22Periodic%22%5D%22FloquetWaveVector%22%5D) | ||
or [`config["Boundaries"]["FloquetWaveVector"]`](boundaries.md##boundaries%5B%%22FloquetWaveVector%22%5D). | ||
|
||
`"EstimatorTol" [1.0e-6]` : Relative tolerance for flux projection used in the | ||
error estimate calculation. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -254,15 +254,18 @@ such modes to higher frequencies. The relevant modes are tabulated as | |
|
||
For this problem, we use curved tetrahedral elements from the mesh file | ||
[`mesh/cavity_tet.msh`](https://github.com/awslabs/palace/blob/main/examples/cylinder/mesh/cavity_tet.msh), | ||
and the configuration file | ||
[`waveguide.json`](https://github.com/awslabs/palace/blob/main/examples/cylinder/waveguide.json). | ||
and the configuration files | ||
[`waveguide.json`](https://github.com/awslabs/palace/blob/main/examples/cylinder/waveguide.json) and | ||
[`floquet.json`](https://github.com/awslabs/palace/blob/main/examples/cylinder/floquet.json). | ||
|
||
The main difference between this configuration file and those used in the cavity example is | ||
The main difference between these configuration files and those used in the cavity example is | ||
in the `"Boundaries"` object: `waveguide.json` specifies a perfect electric conductor | ||
(`"PEC"`) boundary condition for the exterior surface and a periodic boundary condition | ||
(`"Periodic"`) on the cross-sections of the cylinder (in the $z-$ direction). The periodic | ||
attribute pairs are defined by `"DonorAttributes"` and `"ReceiverAttributes"`, and the | ||
distance between them is given by the `"Translation"` vector in mesh units. | ||
distance between them is given by the `"Translation"` vector in mesh units. In `floquet.json`, | ||
an additional `"FloquetWaveVector"` specifies the phase delay between the donor and receiver | ||
boundaries in the X/Y/Z directions. | ||
Comment on lines
+266
to
+268
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the relative simplicity of this example, it would be great if we can derive the expected modes analytically. I suspect it'll be possible to map them to an equivalent set of modes on a full length cylinder without phase shift, at which point analytic results can be compared against like for the waveguide case. |
||
|
||
The file `postpro/waveguide/eig.csv` contains information about the computed eigenfrequencies and | ||
associated quality factors: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -74,7 +74,7 @@ Periodic boundary conditions on an existing mesh can be specified using the | |
["Periodic"](../config/boundaries.md#boundaries%5B%22Periodic%22%5D) boundary keyword. This | ||
boundary condition enforces that the solution on the specified boundaries be exactly equal, | ||
and requires that the surface meshes on the donor and receiver boundaries be identical up to | ||
translation. Periodicity in *Palace* is also supported through meshes generated | ||
translation or rotation. Periodicity in *Palace* is also supported through meshes generated | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How have you tested the rotational case? Might make for a cool example, thinking plausibly the waveguide cylinder could be split into wedges maybe and analyzed. |
||
incorporating periodicity as part of the meshing process. | ||
|
||
## Lumped and wave port excitation | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
{ | ||
"Problem": | ||
{ | ||
"Type": "Eigenmode", | ||
"Verbose": 2, | ||
"Output": "postpro/floquet" | ||
}, | ||
"Model": | ||
{ | ||
"Mesh": "mesh/cylinder_tet.msh", | ||
"L0": 1.0e-2, // cm | ||
}, | ||
"Domains": | ||
{ | ||
"Materials": | ||
[ | ||
{ | ||
"Attributes": [1], | ||
"Permeability": 1.0, | ||
"Permittivity": 2.08, | ||
"LossTan": 0.0004 | ||
} | ||
], | ||
"Postprocessing": | ||
{ | ||
"Energy": | ||
[ | ||
{ | ||
"Index": 1, | ||
"Attributes": [1] | ||
} | ||
] | ||
} | ||
}, | ||
"Boundaries": | ||
{ | ||
"Periodic": | ||
[ | ||
{ | ||
"DonorAttributes": [2], | ||
"ReceiverAttributes": [3], | ||
"FloquetWaveVector": [0.0, 0.0, 0.2] | ||
} | ||
], | ||
"PEC": | ||
{ | ||
"Attributes": [4] | ||
} | ||
}, | ||
"Solver": | ||
{ | ||
"Order": 4, | ||
"Device": "CPU", | ||
"Eigenmode": | ||
{ | ||
"N": 15, | ||
"Tol": 1.0e-8, | ||
"Target": 2.0, // TE f111 ~ 2.9 GHz | ||
"Save": 15 | ||
}, | ||
"Linear": | ||
{ | ||
"Type": "Default", | ||
"KSPType": "GMRES", | ||
"Tol": 1.0e-8, | ||
"MaxIts": 100 | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
#include "fem/errorindicator.hpp" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing the correction on the adaptive branch. |
||
#include "fem/mesh.hpp" | ||
#include "linalg/errorestimator.hpp" | ||
#include "linalg/floquetcorrection.hpp" | ||
#include "linalg/ksp.hpp" | ||
#include "linalg/operator.hpp" | ||
#include "linalg/vector.hpp" | ||
|
@@ -117,17 +118,17 @@ ErrorIndicator DrivenSolver::SweepUniform(SpaceOperator &space_op, PostOperator | |
auto C = space_op.GetDampingMatrix<ComplexOperator>(Operator::DIAG_ZERO); | ||
auto M = space_op.GetMassMatrix<ComplexOperator>(Operator::DIAG_ZERO); | ||
auto A2 = space_op.GetExtraSystemMatrix<ComplexOperator>(omega0, Operator::DIAG_ZERO); | ||
auto FP = space_op.GetFloquetMatrix<ComplexOperator>(Operator::DIAG_ZERO); | ||
const auto &Curl = space_op.GetCurlMatrix(); | ||
|
||
// Set up the linear solver and set operators for the first frequency step. The | ||
// preconditioner for the complex linear system is constructed from a real approximation | ||
// to the complex system matrix. | ||
auto A = space_op.GetSystemMatrix(std::complex<double>(1.0, 0.0), 1i * omega0, | ||
std::complex<double>(-omega0 * omega0, 0.0), K.get(), | ||
C.get(), M.get(), A2.get()); | ||
C.get(), M.get(), A2.get(), FP.get()); | ||
auto P = space_op.GetPreconditionerMatrix<ComplexOperator>(1.0, omega0, -omega0 * omega0, | ||
omega0); | ||
|
||
ComplexKspSolver ksp(iodata, space_op.GetNDSpaces(), &space_op.GetH1Spaces()); | ||
ksp.SetOperators(*A, *P); | ||
|
||
|
@@ -147,6 +148,15 @@ ErrorIndicator DrivenSolver::SweepUniform(SpaceOperator &space_op, PostOperator | |
iodata.solver.linear.estimator_mg); | ||
ErrorIndicator indicator; | ||
|
||
// If using Floquet BCs, a correction term (kp x E) needs to be added to the B field. | ||
std::unique_ptr<FloquetCorrSolver<ComplexVector>> floquet_corr; | ||
if (FP) | ||
{ | ||
floquet_corr = std::make_unique<FloquetCorrSolver<ComplexVector>>( | ||
space_op.GetMaterialOp(), space_op.GetPeriodicOp(), space_op.GetNDSpace(), | ||
space_op.GetRTSpace(), iodata.solver.linear.tol, iodata.solver.linear.max_it, 0); | ||
} | ||
|
||
// Main frequency sweep loop. | ||
int step = step0; | ||
double omega = omega0; | ||
|
@@ -164,7 +174,7 @@ ErrorIndicator DrivenSolver::SweepUniform(SpaceOperator &space_op, PostOperator | |
A2 = space_op.GetExtraSystemMatrix<ComplexOperator>(omega, Operator::DIAG_ZERO); | ||
A = space_op.GetSystemMatrix(std::complex<double>(1.0, 0.0), 1i * omega, | ||
std::complex<double>(-omega * omega, 0.0), K.get(), | ||
C.get(), M.get(), A2.get()); | ||
C.get(), M.get(), A2.get(), FP.get()); | ||
P = space_op.GetPreconditionerMatrix<ComplexOperator>(1.0, omega, -omega * omega, | ||
omega); | ||
ksp.SetOperators(*A, *P); | ||
|
@@ -179,6 +189,12 @@ ErrorIndicator DrivenSolver::SweepUniform(SpaceOperator &space_op, PostOperator | |
Curl.Mult(E.Real(), B.Real()); | ||
Curl.Mult(E.Imag(), B.Imag()); | ||
B *= -1.0 / (1i * omega); | ||
if (FP) | ||
{ | ||
// Calculate B field correction for Floquet BCs. | ||
// B = -1/(iω) ∇ x E - 1/ω kp x E | ||
floquet_corr->AddMult(E, B, -1.0 / omega); | ||
} | ||
post_op.SetEGridFunction(E); | ||
post_op.SetBGridFunction(B); | ||
post_op.UpdatePorts(space_op.GetLumpedPortOp(), space_op.GetWavePortOp(), omega); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
#include "linalg/arpack.hpp" | ||
#include "linalg/divfree.hpp" | ||
#include "linalg/errorestimator.hpp" | ||
#include "linalg/floquetcorrection.hpp" | ||
#include "linalg/ksp.hpp" | ||
#include "linalg/operator.hpp" | ||
#include "linalg/slepc.hpp" | ||
|
@@ -36,6 +37,10 @@ EigenSolver::Solve(const std::vector<std::unique_ptr<Mesh>> &mesh) const | |
auto K = space_op.GetStiffnessMatrix<ComplexOperator>(Operator::DIAG_ONE); | ||
auto C = space_op.GetDampingMatrix<ComplexOperator>(Operator::DIAG_ZERO); | ||
auto M = space_op.GetMassMatrix<ComplexOperator>(Operator::DIAG_ZERO); | ||
auto FP = space_op.GetFloquetMatrix<ComplexOperator>(Operator::DIAG_ZERO); | ||
auto A2 = space_op.GetExtraSystemMatrix<ComplexOperator>(1.0, Operator::DIAG_ZERO); | ||
A2 = nullptr; | ||
Comment on lines
+40
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Delete. We don't support the extra system matrix just yet with eigen solves. |
||
|
||
const auto &Curl = space_op.GetCurlMatrix(); | ||
SaveMetadata(space_op.GetNDSpaces()); | ||
|
||
|
@@ -124,11 +129,25 @@ EigenSolver::Solve(const std::vector<std::unique_ptr<Mesh>> &mesh) const | |
: EigenvalueSolver::ScaleType::NONE; | ||
if (C) | ||
{ | ||
eigen->SetOperators(*K, *C, *M, scale); | ||
if (FP) | ||
{ | ||
eigen->SetOperators(*K, *C, *M, *FP, scale); | ||
} | ||
else | ||
{ | ||
eigen->SetOperators(*K, *C, *M, scale); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Delete after |
||
} | ||
} | ||
else | ||
{ | ||
eigen->SetOperators(*K, *M, scale); | ||
if (FP) | ||
{ | ||
eigen->SetOperators(*K, *M, *FP, scale); | ||
} | ||
else | ||
{ | ||
eigen->SetOperators(*K, *M, scale); | ||
} | ||
} | ||
eigen->SetNumModes(iodata.solver.eigenmode.n, iodata.solver.eigenmode.max_size); | ||
eigen->SetTol(iodata.solver.eigenmode.tol); | ||
|
@@ -154,7 +173,7 @@ EigenSolver::Solve(const std::vector<std::unique_ptr<Mesh>> &mesh) const | |
// Construct a divergence-free projector so the eigenvalue solve is performed in the space | ||
// orthogonal to the zero eigenvalues of the stiffness matrix. | ||
std::unique_ptr<DivFreeSolver<ComplexVector>> divfree; | ||
if (iodata.solver.linear.divfree_max_it > 0) | ||
if (iodata.solver.linear.divfree_max_it > 0 && !FP) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given this will be disabled for both the London equatio and now Floquet, we should have a better mechanism for knowing if |
||
{ | ||
Mpi::Print(" Configuring divergence-free projection\n"); | ||
constexpr int divfree_verbose = 0; | ||
|
@@ -165,6 +184,15 @@ EigenSolver::Solve(const std::vector<std::unique_ptr<Mesh>> &mesh) const | |
eigen->SetDivFreeProjector(*divfree); | ||
} | ||
|
||
// If using Floquet BCs, a correction term (kp x E) needs to be added to the B field. | ||
std::unique_ptr<FloquetCorrSolver<ComplexVector>> floquet_corr; | ||
if (FP) | ||
{ | ||
floquet_corr = std::make_unique<FloquetCorrSolver<ComplexVector>>( | ||
space_op.GetMaterialOp(), space_op.GetPeriodicOp(), space_op.GetNDSpace(), | ||
space_op.GetRTSpace(), iodata.solver.linear.tol, iodata.solver.linear.max_it, 0); | ||
} | ||
Comment on lines
+188
to
+194
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than checking for the existence of FP, use |
||
|
||
// Set up the initial space for the eigenvalue solve. Satisfies boundary conditions and is | ||
// projected appropriately. | ||
if (iodata.solver.eigenmode.init_v0) | ||
|
@@ -242,7 +270,7 @@ EigenSolver::Solve(const std::vector<std::unique_ptr<Mesh>> &mesh) const | |
// to the complex system matrix. | ||
auto A = space_op.GetSystemMatrix(std::complex<double>(1.0, 0.0), 1i * target, | ||
std::complex<double>(-target * target, 0.0), K.get(), | ||
C.get(), M.get()); | ||
C.get(), M.get(), A2.get(), FP.get()); | ||
auto P = space_op.GetPreconditionerMatrix<ComplexOperator>(1.0, target, -target * target, | ||
target); | ||
auto ksp = std::make_unique<ComplexKspSolver>(iodata, space_op.GetNDSpaces(), | ||
|
@@ -306,6 +334,12 @@ EigenSolver::Solve(const std::vector<std::unique_ptr<Mesh>> &mesh) const | |
Curl.Mult(E.Real(), B.Real()); | ||
Curl.Mult(E.Imag(), B.Imag()); | ||
B *= -1.0 / (1i * omega); | ||
if (FP) | ||
{ | ||
// Calculate B field correction for Floquet BCs. | ||
// B = -1/(iω) ∇ x E - 1/ω kp x E. | ||
floquet_corr->AddMult(E, B, -1.0 / omega); | ||
} | ||
post_op.SetEGridFunction(E); | ||
post_op.SetBGridFunction(B); | ||
post_op.UpdatePorts(space_op.GetLumpedPortOp(), omega.real()); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: neither
"Translation"
nor"AffineTransformation"