Compare commits

...
Author SHA1 Message Date
psocratis 39150a110c Merge branch 'master' into DST-maxwell-solver-dev 2021-06-16 10:30:33 -07:00
psocratis 1bd031a948 ParDST first commit in new (clean) branch 2020-12-21 11:36:56 -08:00
17 changed files with 6494 additions and 0 deletions
@@ -0,0 +1,960 @@
#include "DofMapsDST.hpp"
double testcoeff(const Vector & x)
{
return sin(3*M_PI*(x.Sum()));
}
int get_rank(int tdof, std::vector<int> & tdof_offsets)
{
int size = tdof_offsets.size();
if (size == 1) { return 0; }
std::vector<int>::iterator up;
up=std::upper_bound(tdof_offsets.begin(), tdof_offsets.end(),tdof); //
return std::distance(tdof_offsets.begin(),up)-1;
}
void ComputeTdofOffsets(const MPI_Comm & comm, const ParFiniteElementSpace * pfes,
std::vector<int> & tdof_offsets)
{
int num_procs;
MPI_Comm_size(comm, &num_procs);
tdof_offsets.resize(num_procs);
int mytoffset = pfes->GetMyTDofOffset();
MPI_Allgather(&mytoffset,1,MPI_INT,&tdof_offsets[0],1,MPI_INT,comm);
}
void GetSubdomainijk(int ip, const Array<int> nxyz, Array<int> & ijk)
{
ijk.SetSize(3);
ijk[2] = ip/(nxyz[0]*nxyz[1]);
ijk[1] = (ip-ijk[2]*nxyz[0]*nxyz[1])/nxyz[0];
ijk[0] = (ip-ijk[2]*nxyz[0]*nxyz[1])%nxyz[0];
}
void GetDirectionijk(int id, Array<int> & ijk)
{
ijk.SetSize(3);
int n = 3;
ijk[2] = id/(n*n) - 1;
ijk[1] = (id-(ijk[2]+1)*n*n)/n - 1;
ijk[0] = (id-(ijk[2]+1)*n*n)%n - 1;
}
int GetSubdomainId(const Array<int> nxyz, Array<int> & ijk)
{
int dim=ijk.Size();
int k = (dim==2)? 0 : ijk[2];
return k*nxyz[1]*nxyz[0] + ijk[1]*nxyz[0] + ijk[0];
}
int GetDirectionId(const Array<int> & ijk)
{
int n = 3;
int dim = ijk.Size();
int k = (dim == 2) ? -1 : ijk[2];
return (k+1)*n*n + (ijk[1]+1)*n + ijk[0]+1;
}
void DofMaps::Init()
{
comm = pfes->GetComm();
MPI_Comm_size(comm, &num_procs);
MPI_Comm_rank(comm, &myid);
dim = pfes->GetParMesh()->Dimension();
ComputeTdofOffsets(comm, pfes, tdof_offsets);
myelemoffset = part->myelem_offset;
mytoffset = pfes->GetMyTDofOffset();
subdomain_rank = part->subdomain_rank;
nrsubdomains = part->nrsubdomains;
nxyz.SetSize(3);
for (int i = 0; i<3; i++) { nxyz[i] = part->nxyz[i]; }
//compute sign factors for tdofs
int lsize = pfes->GetVSize();
int tsize = pfes->GetTrueVSize();
tdof_sign.SetSize(tsize);
for (int i = 0; i<lsize; i++)
{
int j = pfes->GetGlobalTDofNumber(i);
if (j<mytoffset || j>=mytoffset+tsize) continue;
tdof_sign[j-mytoffset] = pfes->GetDofSign(i);
}
}
DofMaps::DofMaps(ParFiniteElementSpace *pfes_, ParMeshPartition * part_, bool CompFlag_)
: pfes(pfes_), part(part_), CompFlag(CompFlag_)
{
Init();
Setup();
}
void DofMaps::Setup()
{
// Setup the local FiniteElementSpaces
const FiniteElementCollection * fec = pfes->FEColl();
fes.SetSize(nrsubdomains);
for (int i = 0; i<nrsubdomains; i++)
{
fes[i] = nullptr; // initialize with null on all procs
if (myid == subdomain_rank[i])
{
fes[i] = new FiniteElementSpace(part->subdomain_mesh[i],fec);
}
}
// cout << "Computing Overlap Tdofs" << endl;
SubdomainToSubdomainMapsSetup();
// TestSubdomainToSubdomainMaps();
SubdomainToGlobalMapsSetup();
// TestSubdomainToGlobalMaps();
}
void DofMaps::SubdomainToSubdomainMapsSetup()
{
ComputeOvlpElems();
ComputeOvlpTdofs();
}
void DofMaps::AddElementToOvlpLists(int l, int iel,
const Array<bool> & neg, const Array<bool> & pos)
{
int kbeg = (dim == 2) ? 0 : -1;
int kend = (dim == 2) ? 0 : 1;
Array<int> dijk(3);
for (int k = kbeg; k<=kend; k++)
{
if (dim == 3)
{
if (k == -1 && !neg[2]) continue;
if (k == 1 && !pos[2]) continue;
}
for (int j = -1; j<=1; j++)
{
if (j== -1 && !neg[1]) continue;
if (j== 1 && !pos[1]) continue;
for (int i = -1; i<=1; i++)
{
// cases to skip
if (i==-1 && !neg[0]) continue;
if (i== 1 && !pos[0]) continue;
if (i==0 && j==0 && k == 0) continue;
dijk[0] = i; dijk[1] = j; dijk[2] = (dim==2)?-1 : k;
int DirId = GetDirectionId(dijk);
OvlpElems[l][DirId].Append(iel);
}
}
}
}
void DofMaps::ComputeOvlpElems()
{
// first compute the element in the overlaps
OvlpElems.resize(nrsubdomains);
int nlayers = 2*part->OvlpNlayers;
// loop through subdomains
for (int l = 0; l<nrsubdomains; l++)
{
if (myid == subdomain_rank[l])
{
Array<int> ijk;
GetSubdomainijk(l,nxyz,ijk);
Mesh * mesh = part->subdomain_mesh[l];
OvlpElems[l].resize(pow(3,dim));
Vector pmin, pmax;
mesh->GetBoundingBox(pmin,pmax);
double h = part->MeshSize;
// loop through the elements in the mesh and assign them to the
// appropriate lists of overlaps
for (int iel=0; iel< mesh->GetNE(); iel++)
{
// Get element center
Vector center(dim);
int geom = mesh->GetElementBaseGeometry(iel);
ElementTransformation * tr = mesh->GetElementTransformation(iel);
tr->Transform(Geometries.GetCenter(geom),center);
Array<bool> pos(dim); pos = false;
Array<bool> neg(dim); neg = false;
// loop through dimensions
for (int d=0;d<dim; d++)
{
if (ijk[d]>0 && center[d] < pmin[d]+h*nlayers)
{
neg[d] = true;
}
if (ijk[d]<nxyz[d]-1 && center[d] > pmax[d]-h*nlayers)
{
pos[d] = true;
}
}
// Add the element to the appropriate lists
AddElementToOvlpLists(l,iel,neg,pos);
}
}
}
}
void DofMaps::ComputeOvlpTdofs()
{
OvlpTDofs.resize(nrsubdomains);
int nrneighbors = pow(3,dim); // including its self
// loop through subdomains
for (int l = 0; l<nrsubdomains; l++)
{
if (myid != subdomain_rank[l]) continue;
int ntdofs = fes[l]->GetTrueVSize();
Array<int> tdof_marker(ntdofs);
OvlpTDofs[l].resize(nrneighbors);
// loop through neighboring directions/neighbors
for (int d=0; d<nrneighbors; d++)
{
tdof_marker = 0;
Array<int> tdoflist;
// Get the direction
Array<int> dijk;
GetDirectionijk(l,dijk);
int nel = OvlpElems[l][d].Size();
Array<int>Elems = OvlpElems[l][d];
for (int iel = 0; iel<nel; ++iel)
{
int jel = Elems[iel];
Array<int> ElemDofs;
fes[l]->GetElementDofs(jel,ElemDofs);
int ndof = ElemDofs.Size();
for (int i = 0; i<ndof; ++i)
{
int dof_ = ElemDofs[i];
int dof = (dof_ >= 0) ? dof_ : abs(dof_) - 1;
if (!tdof_marker[dof])
{
tdoflist.Append(dof); // dofs of ip0 in ovlp
tdof_marker[dof] = 1;
}
}
}
OvlpTDofs[l][d] = tdoflist;
if (CompFlag)
{
for (int i=0; i<tdoflist.Size(); i++)
{
tdoflist[i] += fes[l]->GetTrueVSize();
}
OvlpTDofs[l][d].Append(tdoflist);
}
}
}
}
void DofMaps::PrintOvlpTdofs()
{
int nrneighbors = pow(3,dim); // including its self
if (myid == 0)
{
for (int i = 0; i<nrsubdomains; i++)
{
if (myid != subdomain_rank[i]) continue;
Array<int> ijk;
GetSubdomainijk(i,nxyz,ijk);
cout << "subdomain = " ; ijk.Print();
cout << "myid = " << myid << endl;
cout << "ip = " << i << endl;
for (int d = 0; d<nrneighbors; d++)
{
Array<int> dijk;
GetDirectionijk(d,dijk);
cout << "direction = " ; dijk.Print();
if (OvlpTDofs[i][d].Size())
{
cout << "OvlpTdofs = " ;
OvlpTDofs[i][d].Print(cout,OvlpTDofs[i][d].Size() );
}
}
}
}
}
void DofMaps::TransferToNeighbors(const Array<int> & SubdomainIds, const Array<Vector *> & x,
std::vector<std::vector<Vector * >> & OvlpSol)
{
// 2D for now....
MFEM_VERIFY(SubdomainIds.Size() == x.Size(), "TransferToNeighbors: Size inconsistency");
int nrsendIds = SubdomainIds.Size();
int nrneighbors = pow(3,dim);
MPI_Request *recv_requests = new MPI_Request[nrsendIds*nrneighbors];
MPI_Request *send_requests = new MPI_Request[nrsendIds*nrneighbors];
MPI_Status *recv_statuses = new MPI_Status[nrsendIds*nrneighbors];
MPI_Status *send_statuses = new MPI_Status[nrsendIds*nrneighbors];
Array<Vector * > send_buffer(nrsendIds*nrneighbors);
Array<Vector * > recv_buffer(nrsendIds*nrneighbors);
int send_counter = 0;
int recv_counter = 0;
for (int is = 0; is<nrsendIds; is++)
{
int i0 = SubdomainIds[is];
Array<int> ijk;
GetSubdomainijk(i0,nxyz,ijk);
for (int d=0;d<nrneighbors; d++)
{
Array<int>directions;
GetDirectionijk(d,directions);
if (dim == 2 && directions[0] == 0 && directions[1] == 0) continue;
if (dim == 3 && directions[0] == 0
&& directions[1] == 0
&& directions[2] == 0) continue;
int i = ijk[0] + directions[0];
if (i<0 || i>=nxyz[0]) continue;
int j = ijk[1] + directions[1];
if (j<0 || j>=nxyz[1]) continue;
int k = (dim ==3 ) ? ijk[2] + directions[2] : 0;
if (k<0 || k>=nxyz[2]) continue;
Array<int>ijk1(3);
ijk1[0] = i;
ijk1[1] = j;
ijk1[2] = k;
int i1 = GetSubdomainId(nxyz,ijk1);
if (myid == subdomain_rank[i0])
{
Array<int> tdofs0 = OvlpTDofs[i0][d]; // map of dofs in the overlap
send_buffer[send_counter] = new Vector(tdofs0.Size());
x[is]->GetSubVector(tdofs0,*send_buffer[send_counter]);
// Destination rank
int dest = subdomain_rank[i1];
int tag = i0 * nrneighbors + d;
int count = tdofs0.Size();
MPI_Isend(send_buffer[send_counter]->GetData(),count,MPI_DOUBLE,dest,
tag,comm,&send_requests[send_counter]);
send_counter++;
}
if (myid == subdomain_rank[i1])
{
Array<int> direction1(3); direction1 = -1;
for (int dd=0;dd<dim;dd++)
{
direction1[dd] = -directions[dd];
}
int d1 = GetDirectionId(direction1);
int count = OvlpTDofs[i1][d1].Size();
recv_buffer[recv_counter] = new Vector(count);
int src = subdomain_rank[i0];
int tag = i0 * nrneighbors + d;
MPI_Irecv(recv_buffer[recv_counter]->GetData(), count,MPI_DOUBLE,src,
tag,comm, &recv_requests[recv_counter]);
recv_counter++;
}
}
}
MPI_Waitall(send_counter, send_requests, send_statuses);
MPI_Waitall(recv_counter, recv_requests, recv_statuses);
delete [] send_statuses;
delete [] send_requests;
delete [] recv_statuses;
delete [] recv_requests;
for (int i = 0; i<send_counter; i++)
{
delete send_buffer[i];
}
send_buffer.DeleteAll();
// Extract the transfered solutions
recv_counter = 0;
for (int is = 0; is<nrsendIds; is++)
{
int i0 = SubdomainIds[is];
Array<int> ijk;
GetSubdomainijk(i0,nxyz,ijk);
for (int d=0;d<nrneighbors; d++)
{
Array<int>directions;
GetDirectionijk(d,directions);
if (dim == 2 && directions[0] == 0 && directions[1] == 0) continue;
if (dim == 3 && directions[0] == 0
&& directions[1] == 0
&& directions[2] == 0) continue;
int i = ijk[0] + directions[0];
if (i<0 || i>=nxyz[0]) continue;
int j = ijk[1] + directions[1];
if (j<0 || j>=nxyz[1]) continue;
int k = (dim ==3 ) ? ijk[2] + directions[2] : 0;
if (k<0 || k>=nxyz[2]) continue;
Array<int>ijk1(3);
ijk1[0] = i;
ijk1[1] = j;
ijk1[2] = k;
int i1 = GetSubdomainId(nxyz,ijk1);
if (myid == subdomain_rank[i1])
{
Array<int> direction1(3); direction1 = -1;
for (int d=0;d<dim;d++)
{
direction1[d] = -directions[d];
}
int d1 = GetDirectionId(direction1);
Array<int> tdofs1 = OvlpTDofs[i1][d1];
if (!OvlpSol[i1][d1])
{
OvlpSol[i1][d1] = new Vector(2*fes[i1]->GetTrueVSize());
}
*OvlpSol[i1][d1] = 0.0;
OvlpSol[i1][d1]->SetSubVector(tdofs1,*recv_buffer[recv_counter]);
recv_counter++;
}
}
}
for (int i = 0; i<recv_counter; i++)
{
delete recv_buffer[i];
}
recv_buffer.DeleteAll();
}
void DofMaps::TestSubdomainToSubdomainMaps()
{
// testing inter-subdomain communication
FunctionCoefficient c1(testcoeff);
int nrsub = nrsubdomains;
Array<int> subdomain_ids(nrsub);
Array<Vector*> x(nrsub);
for (int i = 0; i<nrsub; i++)
{
x[i] = nullptr;
subdomain_ids[i] = i;
if (fes[i])
{
ComplexGridFunction gf(fes[i]);
gf = 0.0;
gf.ProjectCoefficient(c1,c1);
x[i] = new Vector(2*fes[i]->GetTrueVSize());
*x[i] = gf;
}
}
std::vector<std::vector<Vector * >> OvlpSol;
OvlpSol.resize(nrsubdomains);
int nrneighbors = pow(3,dim);
for (int ip = 0; ip<nrsubdomains; ip++)
{
if (myid == subdomain_rank[ip])
{
OvlpSol[ip].resize(nrneighbors);
}
}
TransferToNeighbors(subdomain_ids,x,OvlpSol);
string keys = "keys amrRljc\n";
for (int i0 = 0 ; i0< nrsubdomains; i0++)
{
if (fes[i0])
{
ComplexGridFunction gf0(fes[i0]);
for (int d = 0; d<nrneighbors; d++)
{
if(OvlpSol[i0][d])
{
Array<int>dijk;
GetDirectionijk(d,dijk);
Array<int>ijk;
GetSubdomainijk(i0,nxyz,ijk);
ostringstream oss;
oss << "myid: " << myid
<< ", subdomain: (" << ijk[0] << "," << ijk[1] <<")"
<< ", direction: (" << dijk[0] << "," << dijk[1] <<")";
gf0 = 0.0;
gf0.real().SetVector(*OvlpSol[i0][d],0);
gf0.imag().SetVector(*OvlpSol[i0][d],fes[i0]->GetTrueVSize());
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << *(part->subdomain_mesh[i0]) << gf0.real()
<< keys
<< "window_title '" << oss.str() << "'" << flush;
}
}
}
}
for (int i = 0; i<nrsub; i++)
{
delete x[i];
}
}
void DofMaps::SubdomainToGlobalMapsSetup()
{
// workspace for MPI_AlltoAll
send_count.SetSize(num_procs); send_count = 0;
send_displ.SetSize(num_procs); send_displ = 0;
recv_count.SetSize(num_procs); recv_count = 0;
recv_displ.SetSize(num_procs); recv_displ = 0;
// 1. Communicate to the subdomain rank the list of tdofs
// a. Compute send count
for (int ip = 0; ip<nrsubdomains; ++ip)
{
// avoid any communication if on subdomain rank
int nel = part->local_element_map[ip].Size();
for (int iel = 0; iel<nel; iel++)
{
int elem_idx = part->local_element_map[ip][iel] - myelemoffset;
// int ndofs = local_tdofs[ip].Size();
int ndofs = pfes->GetFE(elem_idx)->GetDof();
send_count[subdomain_rank[ip]] += 2 + ndofs;
}
}
// b. Compute receive count
MPI_Alltoall(send_count,1,MPI_INT,recv_count,1,MPI_INT,comm);
for (int k=0; k<num_procs-1; k++)
{
send_displ[k+1] = send_displ[k] + send_count[k];
recv_displ[k+1] = recv_displ[k] + recv_count[k];
}
sbuff_size = send_count.Sum();
rbuff_size = recv_count.Sum();
// c. Allocate and fill the send buffer
Array<int> sendbuf(sbuff_size); sendbuf = 0;
Array<int> soffs(num_procs); soffs = 0;
for (int ip = 0; ip<nrsubdomains; ++ip)
{
int nel = part->local_element_map[ip].Size();
for (int iel = 0; iel<nel; iel++)
{
int elem_idx = part->local_element_map[ip][iel] - myelemoffset;
Array<int>ElemDofs;
pfes->GetElementDofs(elem_idx,ElemDofs);
int ndofs = ElemDofs.Size();
int j = send_displ[subdomain_rank[ip]] + soffs[subdomain_rank[ip]];
sendbuf[j] = ip;
sendbuf[j+1] = ndofs;
for (int k = 0; k < ndofs ; ++k)
{
int edof_ = ElemDofs[k];
int edof = (edof_ >= 0) ? edof_ : abs(edof_) - 1;
sendbuf[j+2+k] = pfes->GetGlobalTDofNumber(edof);
}
soffs[subdomain_rank[ip]] += 2 + ndofs;
}
}
// d. Communication
Array<int> recvbuf(rbuff_size);
MPI_Alltoallv(sendbuf, send_count, send_displ, MPI_INT, recvbuf,
recv_count, recv_displ, MPI_INT, comm);
// 3. Extract from recv_buffer
std::vector<Array<int>> global_tdofs(nrsubdomains);
int k=0;
while (k<rbuff_size)
{
int ip = recvbuf[k++];
int ndofs = recvbuf[k++];
for (int i = 0; i < ndofs; ++i)
{
global_tdofs[ip].Append(recvbuf[i+k]);
}
k += ndofs;
}
SubdomainGTrueDofs.resize(nrsubdomains);
// 4. Construct SubdomainTdof to Global mesh tdof maps
for (int ip=0; ip<nrsubdomains; ++ip)
{
if (myid != subdomain_rank[ip]) continue;
int nrdof = fes[ip]->GetTrueVSize();
SubdomainGTrueDofs[ip].SetSize(nrdof);
int nel = part->element_map[ip].Size();
int k = 0;
for (int iel = 0; iel<nel; ++iel)
{
Array<int> elem_dofs;
fes[ip]->GetElementDofs(iel,elem_dofs);
int ndof = elem_dofs.Size();
for (int i = 0; i<ndof; ++i)
{
int edof_ = elem_dofs[i];
int edof = (edof_ >= 0) ? edof_ : abs(edof_) - 1;
// rearranging dofs from serial fespace to pfes ordering
SubdomainGTrueDofs[ip][edof] = global_tdofs[ip][k++];
}
}
}
// 5. Communicate SubdomainGTrueDofs to participating ranks
send_count = 0; send_displ = 0;
recv_count = 0; recv_displ = 0;
for (int ip = 0; ip < nrsubdomains; ++ip)
{
if (myid != subdomain_rank[ip]) continue;
int ndofs = SubdomainGTrueDofs[ip].Size();
for (int i = 0; i<ndofs; ++i)
{
int tdof = SubdomainGTrueDofs[ip][i];
int rank = get_rank(tdof,tdof_offsets);
if (rank == subdomain_rank[ip]) continue; // <--------------
send_count[rank] += 2; // 1 for the dof and 1 for the ip that goes to
}
}
// communicate so that recv_count is constructed
MPI_Alltoall(send_count,1,MPI_INT,recv_count,1,MPI_INT,comm);
//
for (int k=0; k<num_procs-1; k++)
{
send_displ[k+1] = send_displ[k] + send_count[k];
recv_displ[k+1] = recv_displ[k] + recv_count[k];
}
sbuff_size = send_count.Sum();
rbuff_size = recv_count.Sum();
sendbuf.SetSize(sbuff_size);
sendbuf = 0; soffs = 0;
for (int ip = 0; ip < nrsubdomains; ip++)
{
if (myid != subdomain_rank[ip]) continue;
int ndofs = SubdomainGTrueDofs[ip].Size();
// loop through dofs
for (int i = 0; i<ndofs; ++i)
{
int tdof = SubdomainGTrueDofs[ip][i];
int irank = get_rank(tdof,tdof_offsets);
if (irank == subdomain_rank[ip]) continue; // <--------------
int j = send_displ[irank] + soffs[irank];
sendbuf[j] = ip;
sendbuf[j+1] = SubdomainGTrueDofs[ip][i];
soffs[irank] += 2 ;
}
}
recvbuf.SetSize(rbuff_size);
MPI_Alltoallv(sendbuf, send_count, send_displ, MPI_INT, recvbuf,
recv_count, recv_displ, MPI_INT, comm);
// List of tdofs owned by the processor for subdomains not owned
SubdomainLTrueDofs.resize(nrsubdomains);
for (int k=0; k<rbuff_size/2; k++)
{
int ip = recvbuf[2*k];
int tdof = recvbuf[2*k+1];
SubdomainLTrueDofs[ip].Append(tdof);
}
}
// Restriction of global residual to subdomain residuals
void DofMaps::GlobalToSubdomains(const Vector & y, Array<Vector*> & x)
{
send_count = 0; send_displ = 0;
recv_count = 0; recv_displ = 0;
// Compute send_counts
int m = (CompFlag) ? 2 : 1 ;
for (int ip = 0; ip < nrsubdomains; ip++)
{
if (myid == subdomain_rank[ip]) continue; // <---------------
int ndofs = SubdomainLTrueDofs[ip].Size();
send_count[subdomain_rank[ip]] += m * ndofs;
}
// communicate so that recv_count is constructed
MPI_Alltoall(send_count,1,MPI_INT,recv_count,1,MPI_INT,comm);
for (int k=0; k<num_procs-1; k++)
{
send_displ[k+1] = send_displ[k] + send_count[k];
recv_displ[k+1] = recv_displ[k] + recv_count[k];
}
sbuff_size = send_count.Sum();
rbuff_size = recv_count.Sum();
Array<double> sendbuf(sbuff_size); sendbuf = 0;
Array<int> soffs(num_procs); soffs = 0;
for (int ip = 0; ip < nrsubdomains; ip++)
{
if (myid == subdomain_rank[ip]) continue; // <---------------
int ndofs = SubdomainLTrueDofs[ip].Size();
for (int i = 0; i<ndofs; i++)
{
int tdof = SubdomainLTrueDofs[ip][i];
int j = send_displ[subdomain_rank[ip]] + soffs[subdomain_rank[ip]];
soffs[subdomain_rank[ip]] +=m;
int k = tdof - mytoffset;
// sendbuf[j] = y[k];
sendbuf[j] = tdof_sign[k]*y[k];
if (CompFlag)
{ // if complex valued
int tsize = pfes->GetTrueVSize();
// sendbuf[j+1] = y[k+tsize];
sendbuf[j+1] = tdof_sign[k]*y[k+tsize];
}
}
}
// communication
Array<double> recvbuf(rbuff_size);
MPI_Alltoallv(sendbuf, send_count, send_displ, MPI_DOUBLE, recvbuf,
recv_count, recv_displ, MPI_DOUBLE, comm);
Array<int> roffs(num_procs);
roffs = 0;
// Now each process will construct the res vector
x.SetSize(nrsubdomains);
for (int ip = 0; ip < nrsubdomains; ip++)
{
if (myid != subdomain_rank[ip]) continue;
int ndof = SubdomainGTrueDofs[ip].Size();
if (!x[ip]) x[ip] = new Vector(m*ndof);
*x[ip] = 0.0;
// extract the data from receiv buffer
for (int i=0; i<ndof; i++)
{
// pick up the tdof and find its rank
int tdof = SubdomainGTrueDofs[ip][i];
int tdof_rank = get_rank(tdof,tdof_offsets);
if (tdof_rank != subdomain_rank[ip]) // <---------------
{
int k = recv_displ[tdof_rank] + roffs[tdof_rank];
roffs[tdof_rank] += m;
(*x[ip])[i] = recvbuf[k];
if (CompFlag)
{
(*x[ip])[i+ndof] = recvbuf[k+1];
}
}
else
{
int k = tdof - mytoffset;
// (*x[ip])[i] = y[k];
(*x[ip])[i] = tdof_sign[k]*y[k];
if (CompFlag)
{
int gtsize = pfes->GetTrueVSize();
(*x[ip])[i+ndof] = tdof_sign[k]*y[k+gtsize];
}
}
}
}
}
// Prolongation of subdomain solutions to the global solution
void DofMaps::SubdomainsToGlobal(const Array<Vector*> & x, Vector & y)
{
send_count = 0; send_displ = 0;
recv_count = 0; recv_displ = 0;
// Compute send_counts
int m = (CompFlag) ? 2 : 1 ;
for (int ip = 0; ip < nrsubdomains; ip++)
{
if (myid != subdomain_rank[ip]) continue;
int ndofs = SubdomainGTrueDofs[ip].Size();
for (int i=0; i<ndofs; i++)
{
// pick up the tdof and find its rank
int tdof = SubdomainGTrueDofs[ip][i];
int tdof_rank = get_rank(tdof,tdof_offsets);
if (tdof_rank == subdomain_rank[ip]) continue;
send_count[tdof_rank] +=m;
}
}
MPI_Alltoall(send_count,1,MPI_INT,recv_count,1,MPI_INT,comm);
for (int k=0; k<num_procs-1; k++)
{
send_displ[k+1] = send_displ[k] + send_count[k];
recv_displ[k+1] = recv_displ[k] + recv_count[k];
}
sbuff_size = send_count.Sum();
rbuff_size = recv_count.Sum();
Array<double> sendbuf(sbuff_size); sendbuf = 0;
Array<int> soffs(num_procs); soffs = 0;
for (int ip = 0; ip < nrsubdomains; ip++)
{
if (myid != subdomain_rank[ip]) continue;
int ndofs = SubdomainGTrueDofs[ip].Size();
// loop through dofs
for (int i=0; i<ndofs; i++)
{
// pick up the dof and find its tdof_rank
int tdof = SubdomainGTrueDofs[ip][i];
int tdof_rank = get_rank(tdof,tdof_offsets);
// offset
if (tdof_rank == subdomain_rank[ip]) continue;
int k = send_displ[tdof_rank] + soffs[tdof_rank];
soffs[tdof_rank] +=m;
sendbuf[k] = (*x[ip])[i];
if (CompFlag)
{
sendbuf[k+1] = (*x[ip])[i+ndofs];
}
}
}
Array<double> recvbuf(rbuff_size);
Array<int> roffs(num_procs); roffs = 0;
MPI_Alltoallv(sendbuf, send_count, send_displ, MPI_DOUBLE, recvbuf,
recv_count, recv_displ, MPI_DOUBLE, comm);
for (int ip = 0; ip < nrsubdomains; ip++)
{
if (myid == subdomain_rank[ip])
{
int ndofs = SubdomainGTrueDofs[ip].Size();
for (int i = 0; i<ndofs; i++)
{
int tdof = SubdomainGTrueDofs[ip][i];
int k = tdof - mytoffset;
if (k<0 || k>=pfes->GetTrueVSize()) continue;
y[k] += tdof_sign[k] * (*x[ip])[i];
if (CompFlag)
{
int gtsize = pfes->GetTrueVSize();
y[k+gtsize] += tdof_sign[k]*(*x[ip])[i+ndofs];
}
}
}
else
{
int ndofs = SubdomainLTrueDofs[ip].Size();
for (int i = 0; i<ndofs; i++)
{
int tdof = SubdomainLTrueDofs[ip][i];
int k = tdof - mytoffset;
int j = recv_displ[subdomain_rank[ip]] + roffs[subdomain_rank[ip]];
roffs[subdomain_rank[ip]] +=m;
y[k] += tdof_sign[k] * recvbuf[j];
if (CompFlag)
{
int tsize = pfes->GetTrueVSize();
y[k+tsize] += tdof_sign[k]*recvbuf[j+1];
}
}
}
}
}
void DofMaps::TestSubdomainToGlobalMaps()
{
cout << "Testing Subdomain To Global Maps" << endl;
FunctionCoefficient c1(testcoeff);
Array<Vector*> x(nrsubdomains);
Vector y(pfes->GetTrueVSize()); y = 0.0;
for (int i = 0 ; i<nrsubdomains; i++)
{
if (myid != subdomain_rank[i]) continue;
x[i] = new Vector(fes[i]->GetTrueVSize());
GridFunction gf(fes[i]);
gf = 0.0;
if (i==3) gf.ProjectCoefficient(c1);
*x[i] = gf;
}
SubdomainsToGlobal(x,y);
// cout << "1: myid = " << myid << ", y = "; y.Print();
string keys = (dim==2) ? "keys amrRljc\n": "keys m\n";
ParGridFunction pgf(pfes);
const Operator &P = *pfes->GetProlongationMatrix();
P.Mult(y, pgf);
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pfes->GetParMesh() << pgf
<< keys << flush;
ParGridFunction pgf1(pfes);
pgf1.ProjectCoefficient(c1);
Vector y1(pfes->GetTrueVSize());
const SparseMatrix * R = pfes->GetRestrictionMatrix();
R->Mult(pgf1,y1);
// P.MultTranspose(pgf1,y1);
Array<Vector*> x1;
GlobalToSubdomains(y1,x1);
// for (int i = 0 ; i<nrsubdomains; i++)
// {
// if (myid != subdomain_rank[i]) continue;
// ostringstream mesh_name;
// mesh_name << "output/mesh." << setfill('0') << setw(6) << i;
// ofstream mesh_ofs(mesh_name.str().c_str());
// mesh_ofs.precision(8);
// fes[i]->GetMesh()->Print(mesh_ofs);
// GridFunction gf(fes[i]);
// gf = x1[i];
// ostringstream gf_name;
// gf_name << "output/gf." << setfill('0') << setw(6) << i;
// ofstream gf_ofs(gf_name.str().c_str());
// gf_ofs.precision(8);
// gf.Save(gf_ofs);
// }
int nrsub = nrsubdomains;
for (int i = 0 ; i<nrsub; i++)
{
if (myid == subdomain_rank[i])
{
socketstream sol_sock1(vishost, visport);
sol_sock1.precision(8);
sol_sock1 << "parallel " << nrsub << " " << i << "\n";
GridFunction gf(fes[i]);
GridFunction gf1(fes[i]);
gf1.ProjectCoefficient(c1);
gf = *x1[i];
gf1-=gf;
cout << "ip, Diff norm = " <<i<<", " << gf1.Norml2() << endl;
sol_sock1 << "solution\n" << *fes[i]->GetMesh() << gf
<< keys << flush;
}
MPI_Barrier(MPI_COMM_WORLD);
}
socketstream gf_sock(vishost, visport);
gf_sock.precision(8);
gf_sock << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pfes->GetParMesh() << pgf1
<< keys << flush;
}
DofMaps::~DofMaps()
{
for (int i = 0; i<nrsubdomains; i++)
{
delete fes[i];
}
}
@@ -0,0 +1,109 @@
#pragma once
#include "../common/Utilities.hpp"
#include "../common/PML.hpp"
using namespace std;
using namespace mfem;
double testcoeff(const Vector & x);
int get_rank(int tdof, std::vector<int> & tdof_offsets);
void ComputeTdofOffsets(const MPI_Comm & comm, const ParFiniteElementSpace * pfes,
std::vector<int> & tdof_offsets);
void GetSubdomainijk(int ip, const Array<int> nxyz, Array<int> & ijk);
void GetDirectionijk(int id, Array<int> & ijk);
int GetSubdomainId(const Array<int> nxyz, Array<int> & ijk);
int GetDirectionId(const Array<int> & ijk);
// class handling two types of dof maps
// 1. Subdomain truedofs ---> Global truedofs
// 2. Subdomain truedofs ---> Neighbor truedofs
class DofMaps
{
private:
// The FE space of the problem (H1/Hcurl)
ParFiniteElementSpace *pfes = nullptr;
// The given partition of the parmesh
ParMeshPartition *part = nullptr;
// partition in x-y-z
Array<int> nxyz;
// MPI parameters
MPI_Comm comm = MPI_COMM_WORLD;
int num_procs, myid;
// true dof offset and element offset of the processor
vector<int> tdof_offsets;
int mytoffset;
int myelemoffset;
int dim;
// Total number of subdomains
int nrsubdomains;
// Array specifying the subdomain rank
Array<int> subdomain_rank;
// Complex flag
bool CompFlag;
// sign factors
Array<int> tdof_sign;
// Initializing mpi and helper parameters
void Init();
// 1. Setting up the subdomains FE spaces
// 2. Setting up the subdomains-to-subdomains maps
// 3. Setting up the subdomain-to-global maps
void Setup();
// -----------------------------------------------
// Subdomain to Subdomain maps
// -----------------------------------------------
std::vector<std::vector<Array<int>>> OvlpElems;
void AddElementToOvlpLists(int l, int iel,
const Array<bool> & neg,
const Array<bool> & pos);
std::vector<std::vector<Array<int>>> OvlpTDofs;
void SubdomainToSubdomainMapsSetup();
void ComputeOvlpElems();
void ComputeOvlpTdofs();
void PrintOvlpTdofs();
// -----------------------------------------------
// Subdomain to Global maps
// -----------------------------------------------
std::vector<Array<int>> SubdomainGTrueDofs; // Subdomain Tdofs to Global Tdofs
std::vector<Array<int>> SubdomainLTrueDofs; // Subdomain Tdofs to Local (on rank) Tdofs
Array<int> send_count, send_displ;
Array<int> recv_count, recv_displ;
int sbuff_size = 0;
int rbuff_size = 0;
void SubdomainToGlobalMapsSetup();
// Testing
void TestSubdomainToGlobalMaps();
void TestSubdomainToSubdomainMaps();
public:
// constructor
// FiniteElementSpaces of the subdomains
Array<FiniteElementSpace *> fes;
DofMaps(ParFiniteElementSpace *fespace_, ParMeshPartition * part_, bool CompFlag_ = false);
~DofMaps();
// Transfering from subdomains SubdomainIds to all their neighbors
void TransferToNeighbors(const Array<int> & SubdomainIds, const Array<Vector *> & x,
std::vector<std::vector<Vector * >> & OvlpSol);
// Prolongation of subdomain solutions to the global solution
void SubdomainsToGlobal(const Array<Vector*> & x, Vector & y);
// Restriction of global residual to subdomain residuals
// bool comp: true for complex valued problems
void GlobalToSubdomains(const Vector & y, Array<Vector*> & x);
};
@@ -0,0 +1,849 @@
//Parallel Diagonal Source Transfer Preconditioner
#include "ParDST.hpp"
ParDST::ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
double omega_, Coefficient * Q_, int nrlayers_ , int nx_, int ny_, int nz_)
: Solver(2*bf_->ParFESpace()->GetTrueVSize(), 2*bf_->ParFESpace()->GetTrueVSize()),
bf(bf_), Pmllength(Pmllength_), omega(omega_),
Q(Q_), nrlayers(nrlayers_)
{
nx = nx_; ny = ny_; nz = nz_;
Init();
}
ParDST::ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
double omega_, VectorCoefficient * VQ_, int nrlayers_ , int nx_, int ny_, int nz_)
: Solver(2*bf_->ParFESpace()->GetTrueVSize(), 2*bf_->ParFESpace()->GetTrueVSize()),
bf(bf_), Pmllength(Pmllength_), omega(omega_),
VQ(VQ_), nrlayers(nrlayers_)
{
nx = nx_; ny = ny_; nz = nz_;
Init();
}
ParDST::ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
double omega_, MatrixCoefficient * MQ_, int nrlayers_ , int nx_, int ny_, int nz_)
: Solver(2*bf_->ParFESpace()->GetTrueVSize(), 2*bf_->ParFESpace()->GetTrueVSize()),
bf(bf_), Pmllength(Pmllength_), omega(omega_),
MQ(MQ_), nrlayers(nrlayers_)
{
nx = nx_; ny = ny_; nz = nz_;
Init();
}
void ParDST::Init()
{
pfes = bf->ParFESpace();
fec = pfes->FEColl();
comm = pfes->GetComm();
MPI_Comm_size(comm, &num_procs);
MPI_Comm_rank(comm, &myid);
//1. Indentify problem ... Helmholtz or Maxwell
prob_kind = fec->GetContType();
if (myid == 0)
{
cout << " 1. Indentify problem to be solved ... " << endl;
if (prob_kind == 0) cout << " Helmholtz" << endl;
if (prob_kind == 1) cout << " Maxwell" << endl;
}
//2. Create the parallel mesh partition
pmesh = pfes->GetParMesh();
dim = pmesh->Dimension();
if (myid == 0)
{
cout << "\n 2. Generating ParMesh partitioning ... " << endl;
}
ovlpnrlayers = nrlayers+1;
part = new ParMeshPartition(pmesh,nx,ny,nz,ovlpnrlayers);
nxyz.SetSize(3);
nxyz[0] = nx = part->nxyz[0];
nxyz[1] = ny = part->nxyz[1];
nxyz[2] = nz = part->nxyz[2];
nrsubdomains = part->nrsubdomains;
SubdomainRank = part->subdomain_rank;
for (int ip = 0; ip<nrsubdomains; ip++)
{
if (myid == SubdomainRank[ip])
{
RankSubdomains.Append(ip);
}
}
cout << " myid: " << myid
<< ", nrsubdomains: " << RankSubdomains.Size() << endl;
MPI_Barrier(comm);
if (myid == 0)
{
cout << " Done ! " << endl;
}
//3. Setup info for sweeps
if (myid == 0)
{
cout << "\n 3. Computing sweeps info ..." << endl;
}
sweeps = new Sweep(dim);
if (myid == 0)
{
cout << " Done ! " << endl;
}
//4. Create LocalToGlobal maps
// (local GridFunctions/Vector to Global ParGridFunction/Vector)
if (myid == 0)
{
cout << "\n 4. Computing true dofs maps ..." << endl;
}
// if (myid == SubdomainRank[0])
// {
// cout << "myid = " << myid << endl;
// char vishost[] = "localhost";
// int visport = 19916;
// socketstream mesh_sock1(vishost, visport);
// mesh_sock1.precision(8);
// mesh_sock1 << "mesh\n"
// << *part->subdomain_mesh[0] << "window_title 'Subdomain'" << flush;
// part->subdomain_mesh[0]->Print();
// }
bool comp = true;
dmaps = new DofMaps(pfes,part, comp);
if (myid == 0)
{
cout << " Done ! " << endl;
}
// 4. Setting up the local problems
if (myid == 0)
{
cout << "\n 5. Setting up the subdomain problems ..." << endl;
}
SetupSubdomainProblems();
if (myid == 0)
{
cout << " Done ! " << endl;
}
if (myid == 0)
{
cout << "\n 6. Mark subdomain overlap truedofs ..." << endl;
}
MarkSubdomainOverlapDofs(comp);
if (myid == 0)
{
cout << " Done ! " << endl;
}
}
void ParDST::Mult(const Vector &r, Vector &z) const
{
// Initialize transfered residuals to 0.0;
for (int ip=0; ip<nrsubdomains; ip++)
{
if (myid != SubdomainRank[ip]) continue;
for (int i=0;i<sweeps->nsweeps; i++)
{
*f_transf[ip][i] = 0.0;
}
}
// restrict given residual to subdomains
dmaps->GlobalToSubdomains(r,f_orig);
for (int ip=0; ip<nrsubdomains; ip++)
{
if (myid != SubdomainRank[ip]) continue;
Array<int> ijk(3);
GetSubdomainijk(ip,nxyz,ijk);
Array2D<int> direct(dim,2); direct = 0;
for (int d=0;d<dim; d++)
{
if (ijk[d] > 0) direct[d][0] = 1;
if (ijk[d] < part->nxyz[d]-1) direct[d][1] = 1;
}
GetChiRes(*f_orig[ip],ip,direct);
}
z = 0.0;
int nsteps;
switch(dim)
{
case 1: nsteps = nx; break;
case 2: nsteps = nx+ny-1; break;
default: nsteps = nx+ny+nz-2; break;
}
int nsweeps = sweeps->nsweeps;
// 1. Loop through sweeps
if (dim == 3 && nz == 1) { nsweeps = 4; } // x-y partition only;
for (int l=0; l<nsweeps; l++)
{
// 2. loop through diagonals/steps of each sweep
for (int s = 0; s<nsteps; s++)
{
Array2D<int> subdomains;
GetStepSubdomains(l,s,subdomains);
int nsubdomains = subdomains.NumRows();
// 3. Loop through the subdomains on the diagonal
Array<int> subdomain_ids;
for (int sb=0; sb < nsubdomains; sb++)
{
Array<int> ijk(dim); ijk = 0;
for (int d=0; d<dim; d++) ijk[d] = subdomains[sb][d];
int ip = GetSubdomainId(nxyz,ijk);
subdomain_ids.Append(ip);
if (myid != SubdomainRank[ip]) continue;
int n = dmaps->fes[ip]->GetTrueVSize();
Vector res_local(2*n); res_local = 0.0;
if (l==0) { res_local += *f_orig[ip]; }
res_local += *f_transf[ip][l];
if (res_local.Norml2() < 1e-12)
{
*subdomain_sol[ip] = 0.0;
continue;
}
PmlMatInv[ip]->Mult(res_local, *subdomain_sol[ip]);
}
// 4. Transfer solutions to neighbors so that the subdomain
// residuals are updated
TransferSources(l,subdomain_ids);
}
// 5. Update the global solution
dmaps->SubdomainsToGlobal(subdomain_sol,z);
}
}
void ParDST::SetupSubdomainProblems()
{
sqf.SetSize(nrsubdomains);
Optr.SetSize(nrsubdomains);
PmlMat.SetSize(nrsubdomains);
PmlMatInv.SetSize(nrsubdomains);
f_orig.SetSize(nrsubdomains);
f_transf.SetSize(nrsubdomains);
subdomain_sol.SetSize(nrsubdomains);
for (int ip=0; ip<nrsubdomains; ip++)
{
sqf[ip] = nullptr;
f_orig[ip] = nullptr;
subdomain_sol[ip] = nullptr;
PmlMat[ip] = nullptr;
PmlMatInv[ip] = nullptr;
Optr[ip] = nullptr;
if (myid != SubdomainRank[ip]) continue;
subdomain_sol[ip] = new Vector(2*dmaps->fes[ip]->GetTrueVSize());
if (prob_kind == 0)
{
SetHelmholtzPmlSystemMatrix(ip);
}
else if (prob_kind == 1)
{
SetMaxwellPmlSystemMatrix(ip);
}
PmlMat[ip] = Optr[ip]->As<ComplexSparseMatrix>();
PmlMatInv[ip] = new ComplexUMFPackSolver;
PmlMatInv[ip]->Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
PmlMatInv[ip]->SetOperator(*PmlMat[ip]);
// HYPRE_Int rowstarts[2]; rowstarts[0] = 0;
// rowstarts[1] = dmaps->fes[ip]->GetTrueVSize();
// HypreParMatrix * HypreMat_r =
// new HypreParMatrix(MPI_COMM_SELF,rowstarts[1],rowstarts,
// &(PmlMat[ip]->real()));
// HypreParMatrix * HypreMat_i =
// new HypreParMatrix(MPI_COMM_SELF,rowstarts[1],rowstarts,
// &(PmlMat[ip]->imag()));
// ComplexHypreParMatrix * HypreMat =
// new ComplexHypreParMatrix(HypreMat_r,HypreMat_i,true,true);
// PmlMatInv[ip] = new ComplexMUMPSSolver;
// PmlMatInv[ip]->SetOperator(*HypreMat);
// delete HypreMat;
int ndofs = dmaps->fes[ip]->GetTrueVSize();
f_transf[ip].SetSize(sweeps->nsweeps);
for (int i=0;i<sweeps->nsweeps; i++)
{
f_transf[ip][i] = new Vector(2*ndofs);
}
}
}
void ParDST::SetHelmholtzPmlSystemMatrix(int ip)
{
MFEM_VERIFY(part->subdomain_mesh[ip], "Null mesh pointer");
Mesh * mesh = part->subdomain_mesh[ip];
double h = part->MeshSize;
Array2D<double> length(dim,2);
length = h*(nrlayers);
Array<int> ijk;
GetSubdomainijk(ip,nxyz,ijk);
int i = ijk[0];
int j = ijk[1];
int k = ijk[2];
if (i == 0 ) length[0][0] = Pmllength[0][0];
if (i == nx-1 ) length[0][1] = Pmllength[0][1];
if (dim > 1)
{
if (j == 0 ) length[1][0] = Pmllength[1][0];
if (j == ny-1 ) length[1][1] = Pmllength[1][1];
}
if (dim == 3)
{
if (k == 0 ) length[2][0] = Pmllength[2][0];
if (k == nz-1 ) length[2][1] = Pmllength[2][1];
}
CartesianPML pml(mesh, length);
pml.SetOmega(omega);
Array <int> ess_tdof_list;
if (mesh->bdr_attributes.Size())
{
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
dmaps->fes[ip]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
ConstantCoefficient one(1.0);
ConstantCoefficient sigma(-pow(omega, 2));
PmlMatrixCoefficient c1_re(dim,pml_detJ_JT_J_inv_Re,&pml);
PmlMatrixCoefficient c1_im(dim,pml_detJ_JT_J_inv_Im,&pml);
PmlCoefficient detJ_re(pml_detJ_Re,&pml);
PmlCoefficient detJ_im(pml_detJ_Im,&pml);
ProductCoefficient c2_re0(sigma, detJ_re);
ProductCoefficient c2_im0(sigma, detJ_im);
ProductCoefficient c2_re(c2_re0, *Q);
ProductCoefficient c2_im(c2_im0, *Q);
sqf[ip] = new SesquilinearForm (dmaps->fes[ip],bf->GetConvention());
sqf[ip]->AddDomainIntegrator(new DiffusionIntegrator(c1_re),
new DiffusionIntegrator(c1_im));
sqf[ip]->AddDomainIntegrator(new MassIntegrator(c2_re),
new MassIntegrator(c2_im));
sqf[ip]->Assemble(0);
Optr[ip] = new OperatorPtr;
sqf[ip]->FormSystemMatrix(ess_tdof_list,*Optr[ip]);
}
void ParDST::SetMaxwellPmlSystemMatrix(int ip)
{
MFEM_VERIFY(part->subdomain_mesh[ip], "Null mesh pointer");
Mesh * mesh = part->subdomain_mesh[ip];
double h = part->MeshSize;
Array2D<double> length(dim,2);
length = h*(nrlayers);
Array<int> ijk;
GetSubdomainijk(ip,nxyz,ijk);
int i = ijk[0];
int j = ijk[1];
int k = ijk[2];
if (i == 0 ) length[0][0] = Pmllength[0][0];
if (i == nx-1 ) length[0][1] = Pmllength[0][1];
if (dim > 1)
{
if (j == 0 ) length[1][0] = Pmllength[1][0];
if (j == ny-1 ) length[1][1] = Pmllength[1][1];
}
if (dim == 3)
{
if (k == 0 ) length[2][0] = Pmllength[2][0];
if (k == nz-1 ) length[2][1] = Pmllength[2][1];
}
CartesianPML pml(mesh, length);
pml.SetOmega(omega);
Array <int> ess_tdof_list;
if (mesh->bdr_attributes.Size())
{
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
dmaps->fes[ip]->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
ConstantCoefficient omeg(-pow(omega, 2));
int cdim = (dim == 2) ? 1 : dim;
PmlMatrixCoefficient pml_c1_Re(cdim,detJ_inv_JT_J_Re, &pml);
PmlMatrixCoefficient pml_c1_Im(cdim,detJ_inv_JT_J_Im, &pml);
PmlMatrixCoefficient pml_c2_Re(dim, detJ_JT_J_inv_Re,&pml);
PmlMatrixCoefficient pml_c2_Im(dim, detJ_JT_J_inv_Im,&pml);
ScalarMatrixProductCoefficient c2_Re0(omeg,pml_c2_Re);
ScalarMatrixProductCoefficient c2_Im0(omeg,pml_c2_Im);
MatrixCoefficient * c2_Re=nullptr;
MatrixCoefficient * c2_Im=nullptr;
if (Q)
{
c2_Re = new ScalarMatrixProductCoefficient(*Q,c2_Re0);
c2_Im = new ScalarMatrixProductCoefficient(*Q,c2_Im0);
}
else if (VQ)
{
MFEM_ABORT("Vector Coeffiecient not supported ");
}
else if (MQ)
{
c2_Re = new MatrixMatrixProductCoefficient(c2_Re0,*MQ);
c2_Im = new MatrixMatrixProductCoefficient(c2_Im0,*MQ);
}
sqf[ip] = new SesquilinearForm(dmaps->fes[ip],bf->GetConvention());
sqf[ip]->AddDomainIntegrator(new CurlCurlIntegrator(pml_c1_Re),
new CurlCurlIntegrator(pml_c1_Im));
sqf[ip]->AddDomainIntegrator(new VectorFEMassIntegrator(*c2_Re),
new VectorFEMassIntegrator(*c2_Im));
sqf[ip]->Assemble(0);
Optr[ip] = new OperatorPtr;
sqf[ip]->FormSystemMatrix(ess_tdof_list,*Optr[ip]);
delete c2_Re;
delete c2_Im;
}
void ParDST::MarkSubdomainOverlapDofs(const bool comp)
{
// First mark the elements
// cout<< "Compute Overlap Elements (in each possible direction) " << endl;
// Lists of elements
// x,y,z = +/- 1 ovlp
NovlpElems.resize(nrsubdomains);
for (int ip = 0; ip<nrsubdomains; ip++)
{
if (myid != SubdomainRank[ip]) continue;
Array<int> ijk;
GetSubdomainijk(ip,nxyz,ijk);
Mesh * mesh = dmaps->fes[ip]->GetMesh();
NovlpElems[ip].resize(2*dim);
Vector pmin, pmax;
mesh->GetBoundingBox(pmin,pmax);
double h = part->MeshSize;
// Loop through elements
for (int iel=0; iel<mesh->GetNE(); iel++)
{
// Get element center
Vector center(dim);
int geom = mesh->GetElementBaseGeometry(iel);
ElementTransformation * tr = mesh->GetElementTransformation(iel);
tr->Transform(Geometries.GetCenter(geom),center);
// Assign elements to the appropriate lists
for (int d=0;d<dim; d++)
{
if (ijk[d]>0)
{
if (center[d] >= pmin[d]+h*ovlpnrlayers)
{
NovlpElems[ip][d].Append(iel);
}
}
else
{
NovlpElems[ip][d].Append(iel);
}
if (ijk[d]<nxyz[d]-1)
{
if (center[d] <= pmax[d]-h*ovlpnrlayers)
{
NovlpElems[ip][dim+d].Append(iel);
}
}
else
{
NovlpElems[ip][dim+d].Append(iel);
}
}
}
}
// mark dofs
NovlpDofs.resize(nrsubdomains);
int mm = (comp) ? 2 : 1; // complex or real valued
for (int ip = 0; ip<nrsubdomains; ip++)
{
if (myid != SubdomainRank[ip]) continue;
FiniteElementSpace * fes = dmaps->fes[ip];
// Loop through the marked elements
NovlpDofs[ip].resize(2*dim);
int n = fes->GetTrueVSize();
Array<int> marker(n);
for (int d=0;d<2*dim; d++)
{
marker = 0;
int m = 0;
int melems = NovlpElems[ip][d].Size();
for (int iel=0; iel<melems; iel++)
{
Array<int> ElemDofs;
int el = NovlpElems[ip][d][iel];
fes->GetElementDofs(el,ElemDofs);
int ndof = ElemDofs.Size();
for (int i = 0; i<ndof; ++i)
{
int eldof = ElemDofs[i];
int tdof = (eldof >= 0) ? eldof : abs(eldof) - 1;
if (marker[tdof] == 1) continue;
marker[tdof] = 1;
m++;
}
}
int k = mm*(n-m);
NovlpDofs[ip][d].SetSize(k);
int l = 0;
for (int i = 0; i<n; i++)
{
if (marker[i]==0)
{
NovlpDofs[ip][d][l] = i; // real dofs
if (comp)
{
NovlpDofs[ip][d][l+k/2] = i+fes->GetTrueVSize();
}
l++;
}
}
}
}
}
void ParDST::GetChiRes(Vector & res, int ip, Array2D<int> direct) const
{
for (int d=0; d<dim; d++)
{
// negative direction
if (direct[d][0]==1) res.SetSubVector(NovlpDofs[ip][d],0.0);
// possitive direction
if (direct[d][1]==1) res.SetSubVector(NovlpDofs[ip][d+dim],0.0);
}
}
void ParDST::PlotLocal(Vector & sol, socketstream & sol_sock, int ip) const
{
FiniteElementSpace * fes = dmaps->fes[ip];
Mesh * mesh = fes->GetMesh();
GridFunction gf(fes);
double * data = sol.GetData();
gf.SetData(data);
string keys;
keys = "keys mrRljc\n";
sol_sock << "solution\n" << *mesh << gf << keys << flush;
}
void ParDST::GetStepSubdomains(const int sweep, const int step, Array2D<int> & subdomains) const
{
Array<int> aux;
switch(dim)
{
case 2:
for (int i=nx-1;i>=0; i--)
{
int j;
switch (sweep)
{
case 0: j = step-i; break;
case 1: j = step-nx+i+1; break;
case 2: j = nx+i-step-1; break;
default: j = nx+ny-i-step-2; break;
}
if (j<0 || j>=ny) continue;
aux.Append(i); aux.Append(j);
}
break;
default:
for (int i=nx-1;i>=0; i--)
{
for (int j=ny-1;j>=0; j--)
{
int k;
switch (sweep)
{
case 0: k = step-i-j; break;
case 1: k = step-nx+i+1-j; break;
case 2: k = step-ny+j+1-i; break;
case 3: k = step-nx-ny+i+j+2; break;
case 4: k = i+j+nz-1-step; break;
case 5: k = nx+nz-i+j-step-2; break;
case 6: k = ny+nz+i-j-step-2; break;
default: k = nx+ny+nz-i-j-step-3; break;
}
if (k<0 || k>=nz) continue;
aux.Append(i); aux.Append(j); aux.Append(k);
}
}
break;
}
int nrows = aux.Size()/dim;
int ncols = dim;
subdomains.SetSize(nrows,ncols);
for (int r=0;r<nrows; r++)
{
for (int c=0; c<ncols; c++)
{
int k = r*ncols + c;
subdomains[r][c] = aux[k];
}
}
}
void ParDST::TransferSources(int sweep, const Array<int> & subdomain_ids) const
{
OvlpSol.resize(nrsubdomains);
int nrneighbors = pow(3,dim);
for (int ip = 0; ip<nrsubdomains; ip++)
{
if (myid == SubdomainRank[ip])
{
OvlpSol[ip].resize(nrneighbors);
}
}
int m = subdomain_ids.Size();
Array<Vector *> x(m);
for (int i = 0; i<m; i++)
{
x[i] = nullptr;
int ip = subdomain_ids[i];
if (myid != SubdomainRank[ip]) continue;
x[i] = new Vector(subdomain_sol[ip]->GetData(),subdomain_sol[ip]->Size());
}
dmaps->TransferToNeighbors(subdomain_ids,x,OvlpSol);
for (int i = 0; i<m; i++)
{
delete x[i]; x[i] = nullptr;
}
// Update residuals
// Find all neighbors of patch ip0
for (int is = 0; is<m; is++)
{
int ip0 = subdomain_ids[is];
Array<int> ijk;
Array<int> ijk1(3);
GetSubdomainijk(ip0,nxyz,ijk);
Array<int> directions(3);
for (int i=-1; i<2; i++)
{
int i1 = ijk[0] + i;
if (i1 <0 || i1>=nx) continue;
directions[0] = i;
ijk1[0] = i1;
for (int j=-1; j<2; j++)
{
int j1 = ijk[1] + j;
if (j1 <0 || j1>=ny) continue;
directions[1] = j;
ijk1[1] = j1;
int kbeg = (dim == 2) ? 0 : -1;
int kend = (dim == 2) ? 1 : 2;
for (int k=kbeg; k<kend; k++)
{
int k1 = ijk[2] + k;
if (k1 <0 || k1>=nz) continue;
directions[2] = (dim == 3) ? k : -1 ;
if (i==0 && j==0 && k==0) continue;
int l = GetSweepToTransfer(sweep,directions);
if (l == -1) continue;
ijk1[2] = k1;
int ip1 = GetSubdomainId(nxyz,ijk1);
if (myid != SubdomainRank[ip1]) continue;
Array<int>directions1(3); directions1 = -1;
for (int i = 0; i<dim; i++) directions1[i] = -directions[i];
int dir = GetDirectionId(directions1);
int n = dmaps->fes[ip1]->GetTrueVSize();
Vector res(2*n);
PmlMat[ip1]->Mult(*OvlpSol[ip1][dir],res);
Array2D<int> direct(dim,2); direct = 0;
for (int d = 0; d<dim; d++)
{
if (directions[d]==1) direct[d][0] = 1;
if (directions[d]==-1) direct[d][1] = 1;
}
GetChiRes(res,ip1,direct);
*f_transf[ip1][l] -= res;
}
}
}
}
for (int ip = 0; ip<nrsubdomains; ip++)
{
if (myid == SubdomainRank[ip])
{
for (int i = 0; i<nrneighbors; i++)
{
if (OvlpSol[ip][i])
{
delete OvlpSol[ip][i];
}
}
OvlpSol[ip].clear();
}
}
}
int ParDST::GetSweepToTransfer(const int s, Array<int> directions) const
{
int l1=-1;
int nsweeps = sweeps->nsweeps;
Array<int> sweep0;
sweeps->GetSweep(s,sweep0);
switch (dim)
{
case 2:
for (int l=s; l<nsweeps; l++)
{
// Rule 1: the transfer source direction has to be similar with
// the sweep direction
Array<int> sweep1;
sweeps->GetSweep(l,sweep1);
int ddot = 0;
for (int d=0; d<dim; d++) ddot+= sweep1[d] * directions[d];
if (ddot <= 0) continue;
// Rule 2: The horizontal or vertical transfer source cannot be used
// Case of horizontal or vertical transfer source
// (it can't be both 0 cause it's skipped)
if (directions[0]==0 || directions[1] == 0)
{
if (sweep0[0] == -sweep1[0] && sweep0[1] == -sweep1[1]) continue;
}
l1 = l;
break;
}
break;
default:
for (int l=s; l<nsweeps; l++)
{
// Rule 1: (similar directions) the transfer source direction has to be similar with
// the sweep direction
Array<int> sweep1;
sweeps->GetSweep(l,sweep1);
int ddot = 0;
bool similar = true;
for (int d=0; d<dim; d++)
{
if (sweep1[d] * directions[d] < 0) similar = false;
ddot+= sweep1[d] * directions[d];
}
if (!similar || ddot<=0) continue; // not similar
// Rule 2: (oposite directions) the transfer source direction has to be similar with
// the sweep direction
//
// check any of the projections onto the planes
// (xy, xz, yz)
if ( (directions[0]==0 && directions[1] != 0) ||
(directions[0]!=0 && directions[1] == 0) ||
(directions[0]==0 && directions[2] != 0) ||
(directions[0]!=0 && directions[2] == 0) ||
(directions[2]==0 && directions[1] != 0) ||
(directions[2]!=0 && directions[1] == 0) )
{
if (sweep0[0] == -sweep1[0] &&
sweep0[1] == -sweep1[1] &&
sweep0[2] == -sweep1[2]) continue;
}
l1 = l;
break;
}
break;
}
return l1;
}
void ParDST::CorrectOrientation(int ip,Vector &x) const
{
FiniteElementSpace * fespace = dmaps->fes[ip];
Mesh * mesh = fespace->GetMesh();
int nrelems = mesh->GetNE();
// GridFunction test;
// test.SetFromTrueDofs(x)
Array<int> signs(fespace->GetTrueVSize()); signs = 0;
for (int iel=0; iel<nrelems; iel++)
{
Array<int> ElemDofs;
fespace->GetElementDofs(iel,ElemDofs);
int ndofs = ElemDofs.Size();
ElemDofs.Print();
for (int i = 0; i< ndofs; i++)
{
int pdof_ = ElemDofs[i];
if (pdof_ < 0)
{
signs[abs(pdof_)-1] += 1.0 ;
}
else
{
signs[pdof_] -= 1.0 ;
}
}
}
cout << "signs = " ; signs.Print();
for (int i = 0; i<fespace->GetTrueVSize(); i++)
{
if (signs[i]<0)
{
x(i) *= -1.0;
x(i+fespace->GetTrueVSize()) *= -1.0;
}
}
}
ParDST::~ParDST()
{
for (int ip=0; ip<nrsubdomains; ip++)
{
delete Optr[ip];
delete subdomain_sol[ip];
delete PmlMatInv[ip];
delete sqf[ip];
if (myid != SubdomainRank[ip]) continue;
for (int i=0;i<sweeps->nsweeps; i++)
{
delete f_transf[ip][i];
}
delete f_orig[ip];
}
f_orig.DeleteAll();
delete dmaps;
delete sweeps;
delete part;
}
@@ -0,0 +1,70 @@
#pragma once
#include "../common/Utilities.hpp"
#include "../common/PML.hpp"
#include "DofMapsDST.hpp"
using namespace std;
using namespace mfem;
class ParDST : public Solver//
{
private:
MPI_Comm comm = MPI_COMM_WORLD;
int num_procs, myid;
// Constructor inputs
int prob_kind;
ParSesquilinearForm *bf=nullptr;
ParFiniteElementSpace * pfes = nullptr;
ParMesh * pmesh = nullptr;
ParMeshPartition * part = nullptr;
Array<int> SubdomainRank;
Array<int> RankSubdomains;
const FiniteElementCollection * fec = nullptr;
Array2D<double> Pmllength;
int dim = 2;
double omega = 0.5;
Coefficient * Q=nullptr;
VectorCoefficient * VQ=nullptr;
MatrixCoefficient * MQ=nullptr;
int nrlayers;
int ovlpnrlayers;
int nrsubdomains = 0;
int nx,ny,nz;
Array<int> nxyz;
Sweep * sweeps = nullptr;
DofMaps * dmaps = nullptr;
Array< SesquilinearForm * > sqf;
Array< OperatorPtr * > Optr;
Array<ComplexSparseMatrix *> PmlMat;
Array<ComplexUMFPackSolver *> PmlMatInv;
// Array<ComplexMUMPSSolver *> PmlMatInv;
mutable Array<Vector *> f_orig;
mutable Array<Array<Vector * >> f_transf;
mutable Array<Vector * > subdomain_sol;
mutable std::vector<std::vector<Vector * >> OvlpSol;
void SetupSubdomainProblems();
std::vector<std::vector<Array<int>>> NovlpElems;
std::vector<std::vector<Array<int>>> NovlpDofs;
void MarkSubdomainOverlapDofs(const bool comp = false);
void SetHelmholtzPmlSystemMatrix(int ip);
void SetMaxwellPmlSystemMatrix(int ip);
void GetChiRes(Vector & res, int ip, Array2D<int> direct) const;
void PlotLocal(Vector & sol, socketstream & sol_sock, int ip) const;
void GetStepSubdomains(const int sweep, const int step, Array2D<int> & subdomains) const;
void TransferSources(int sweep, const Array<int> & subdomain_ids) const;
int GetSweepToTransfer(const int s, Array<int> directions) const;
void CorrectOrientation(int ip, Vector & x) const;
void Init();
public:
ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
double omega_, Coefficient * Q_, int nrlayers_, int nx_=2, int ny_=2, int nz_=2);
ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
double omega_, VectorCoefficient * VQ_, int nrlayers_, int nx_=2, int ny_=2, int nz_=2);
ParDST(ParSesquilinearForm * bf_, Array2D<double> & Pmllength_,
double omega_, MatrixCoefficient * MQ_, int nrlayers_, int nx_=2, int ny_=2, int nz_=2);
virtual void SetOperator(const Operator &op) {}
virtual void Mult(const Vector &r, Vector &z) const;
virtual ~ParDST();
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,151 @@
#pragma once
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
struct UniqueIndexGenerator
{
int counter = 0;
std::unordered_map<int,int> idx;
int Get(int i)
{
std::unordered_map<int,int>::iterator f = idx.find(i);
if (f == idx.end())
{
idx[i] = counter;
return counter++;
}
else
{
return (*f).second;
}
}
void Reset()
{
counter = 0;
idx.clear();
}
};
double GetUniformMeshElementSize(Mesh * mesh);
Mesh * ExtendMesh(Mesh * mesh, const Array<int> & directions);
class CartesianMeshPartition
{
private:
Mesh *mesh=nullptr;
public:
int nrpatch;
int nxyz[3];
double MeshSize;
std::vector<Array<int>> element_map;
Array3D<int>subdomains;
// constructor
CartesianMeshPartition(Mesh * mesh_,int & nx, int & ny, int & nz);
~CartesianMeshPartition() {};
};
class OverlappingCartesianMeshPartition
{
private:
Mesh *mesh=nullptr;
public:
int nrpatch;
double MeshSize;
int nxyz[3];
std::vector<Array<int>> element_map;
Array3D<int> subdomains;
// constructor
OverlappingCartesianMeshPartition(Mesh * mesh_,int & nx, int & ny, int & nz);
OverlappingCartesianMeshPartition(Mesh * mesh_,int & nx, int & ny, int & nz, int ovlp_nlayers);
~OverlappingCartesianMeshPartition() {};
};
class STPOverlappingCartesianMeshPartition // Special layered partition for STP
{
private:
Mesh *mesh=nullptr;
public:
int nrpatch;
int nx, ny, nz;
std::vector<Array<int>> element_map;
// constructor
STPOverlappingCartesianMeshPartition(Mesh * mesh_);
~STPOverlappingCartesianMeshPartition() {};
};
class MeshPartition
{
private:
Mesh *mesh=nullptr;
void AddElementToMesh(Mesh * mesh,mfem::Element::Type elem_type,int * ind);
void GetNumVertices(int type, mfem::Element::Type & elem_type, int & nrvert);
void PrintElementMap();
public:
int nrpatch;
double MeshSize;
std::vector<Array<int>> element_map;
Array3D<int> subdomains;
Array<Mesh *> patch_mesh;
int partition_kind;
int nxyz[3];
// constructor
MeshPartition(Mesh * mesh_, int part, int mx=1, int my=1, int mz=1, int ovl_nlayers=0);
~MeshPartition();
};
void SaveMeshPartition(Array<Mesh * > meshes,
string mfilename="output/mesh.",
string sfilename="output/sol.");
#ifdef MFEM_USE_MPI
class CartesianParMeshPartition
{
private:
ParMesh *pmesh=nullptr;
public:
int nrsubdomains;
int nxyz[3];
double MeshSize;
std::vector<Array<int>> local_element_map;
Array<int> subdomain_rank;
Array3D<int>subdomains;
// constructor
CartesianParMeshPartition(ParMesh * pmesh_,int & nx, int & ny, int & nz,
int ovlp_nlayers);
~CartesianParMeshPartition() {};
};
class ParMeshPartition
{
private:
MPI_Comm comm;
ParMesh *pmesh=nullptr;
void AddElementToMesh(Mesh * mesh,mfem::Element::Type elem_type,int * ind);
void GetNumVertices(int type, mfem::Element::Type & elem_type, int & nrvert);
void PrintElementMap();
public:
int nrsubdomains;
int OvlpNlayers;
int myelem_offset = 0;
double MeshSize;
std::vector<Array<int>> element_map;
std::vector<Array<int>> local_element_map;
Array3D<int> subdomains;
Array<Mesh *> subdomain_mesh;
Array<int> subdomain_rank;
int partition_kind;
int nxyz[3];
// constructor
ParMeshPartition(ParMesh * pmesh_, int mx=1, int my=1, int mz=1, int ovl_nlayers=0);
void SaveMeshPartition();
~ParMeshPartition();
};
#endif
+570
View File
@@ -0,0 +1,570 @@
#include "PML.hpp"
CartesianPML::CartesianPML(Mesh *mesh_, Array2D<double> length_)
: mesh(mesh_), length(length_)
{
dim = mesh->Dimension();
SetBoundaries();
}
void CartesianPML::SetBoundaries()
{
comp_dom_bdr.SetSize(dim, 2);
dom_bdr.SetSize(dim, 2);
// initialize
for (int i = 0; i < dim; i++)
{
dom_bdr(i, 0) = infinity();
dom_bdr(i, 1) = -infinity();
}
for (int i = 0; i < mesh->GetNBE(); i++)
{
Array<int> bdr_vertices;
mesh->GetBdrElementVertices(i, bdr_vertices);
for (int j = 0; j < bdr_vertices.Size(); j++)
{
for (int k = 0; k < dim; k++)
{
dom_bdr(k, 0) = min(dom_bdr(k, 0), mesh->GetVertex(bdr_vertices[j])[k]);
dom_bdr(k, 1) = max(dom_bdr(k, 1), mesh->GetVertex(bdr_vertices[j])[k]);
}
}
}
#ifdef MFEM_USE_MPI
ParMesh * pmesh = dynamic_cast<ParMesh *>(mesh);
if (pmesh)
{
for (int d=0; d<dim; d++)
{
MPI_Allreduce(MPI_IN_PLACE,&dom_bdr(d,0),1,MPI_DOUBLE,MPI_MIN,pmesh->GetComm());
MPI_Allreduce(MPI_IN_PLACE,&dom_bdr(d,1),1,MPI_DOUBLE,MPI_MAX,pmesh->GetComm());
}
}
#endif
for (int i = 0; i < dim; i++)
{
comp_dom_bdr(i, 0) = dom_bdr(i, 0) + length(i, 0);
comp_dom_bdr(i, 1) = dom_bdr(i, 1) - length(i, 1);
}
}
void CartesianPML::SetAttributes(Mesh *mesh_)
{
int nrelem = mesh_->GetNE();
elems.SetSize(nrelem);
for (int i = 0; i < nrelem; ++i)
{
elems[i] = 1;
bool in_pml = false;
Element *el = mesh_->GetElement(i);
Array<int> vertices;
// Initialize Attribute
el->SetAttribute(1);
el->GetVertices(vertices);
int nrvert = vertices.Size();
// Check if any vertex is in the pml
for (int iv = 0; iv < nrvert; ++iv)
{
int vert_idx = vertices[iv];
double *coords = mesh_->GetVertex(vert_idx);
for (int comp = 0; comp < dim; ++comp)
{
if (coords[comp] > comp_dom_bdr(comp, 1) ||
coords[comp] < comp_dom_bdr(comp, 0))
{
in_pml = true;
break;
}
}
}
if (in_pml)
{
elems[i] = 0;
el->SetAttribute(2);
}
}
mesh_->SetAttributes();
}
void CartesianPML::StretchFunction(const Vector &x,
vector<complex<double>> &dxs, double omega)
{
complex<double> zi = complex<double>(0., 1.);
double n = 2.0;
double c = 10.0;
// double c = log(omega);
double coeff;
// Stretch in each direction independently
for (int i = 0; i < dim; ++i)
{
dxs[i] = 1.0;
if (x(i) >= comp_dom_bdr(i, 1))
{
coeff = n * c / omega / pow(length(i, 1), n);
dxs[i] = 1.0 + zi * coeff * abs(pow(x(i) - comp_dom_bdr(i, 1), n - 1.0));
}
if (x(i) <= comp_dom_bdr(i, 0))
{
coeff = n * c / omega / pow(length(i, 0), n);
dxs[i] = 1.0 + zi * coeff * abs(pow(x(i) - comp_dom_bdr(i, 0), n - 1.0));
}
}
}
ToroidPML::ToroidPML(Mesh *mesh_)
: mesh(mesh_)
{
dim = mesh->Dimension();
zlim.SetSize(2);
rlim.SetSize(2);
alim.SetSize(2);
zpml_thickness.SetSize(2);
rpml_thickness.SetSize(2);
apml_thickness.SetSize(2);
SetBoundaries();
}
void ToroidPML::SetBoundaries()
{
mesh->EnsureNodes();
int nrnodes = mesh->GetNodalFESpace()->GetTrueVSize()/dim;
double zmin = infinity();
double zmax = -infinity();
double rmin = infinity();
double rmax = -infinity();
double amin = infinity(); // in degrees
double amax = -infinity(); // in degrees
for (int i = 0; i<nrnodes; i++)
{
Vector coord(dim);
mesh->GetNode(i,coord);
for (int d = 0; d<dim; d++)
{
if (abs(coord[d])<1e-13) coord[d] = 0.0;
}
// Find r and a for this point
double x = coord[0];
double y = coord[1];
double z = 0.0;
if (dim == 3) z = coord[2];
double a = GetAngle(x,y);
double r = sqrt(x*x + y*y);
zmin = min(zmin,z);
zmax = max(zmax,z);
rmin = min(rmin,r);
rmax = max(rmax,r);
amin = min(amin,a);
amax = max(amax,a);
}
zlim[0] = zmin;
zlim[1] = zmax;
rlim[0] = rmin;
rlim[1] = rmax;
alim[0] = amin;
alim[1] = amax;
#ifdef MFEM_USE_MPI
ParMesh * pmesh = dynamic_cast<ParMesh *>(mesh);
if (pmesh)
{
MPI_Allreduce(MPI_IN_PLACE,&zlim[0],1,MPI_DOUBLE,MPI_MIN,pmesh->GetComm());
MPI_Allreduce(MPI_IN_PLACE,&zlim[1],1,MPI_DOUBLE,MPI_MAX,pmesh->GetComm());
MPI_Allreduce(MPI_IN_PLACE,&rlim[0],1,MPI_DOUBLE,MPI_MIN,pmesh->GetComm());
MPI_Allreduce(MPI_IN_PLACE,&rlim[1],1,MPI_DOUBLE,MPI_MAX,pmesh->GetComm());
MPI_Allreduce(MPI_IN_PLACE,&alim[0],1,MPI_DOUBLE,MPI_MIN,pmesh->GetComm());
MPI_Allreduce(MPI_IN_PLACE,&alim[1],1,MPI_DOUBLE,MPI_MAX,pmesh->GetComm());
}
#endif
}
void ToroidPML::SetAttributes(Mesh *mesh_)
{
int nrelem = mesh_->GetNE();
elems.SetSize(nrelem);
// Loop through the elements and identify which of them are in the PML
for (int i = 0; i < nrelem; ++i)
{
// initialize with 1
elems[i] = 1;
Element *el = mesh_->GetElement(i);
// Initialize attribute
el->SetAttribute(1);
Array<int> vertices;
el->GetVertices(vertices);
int nrvert = vertices.Size();
// Check if any vertex is in the pml
bool in_pml = false;
for (int iv = 0; iv < nrvert; ++iv)
{
int vert_idx = vertices[iv];
double *coords = mesh_->GetVertex(vert_idx);
double x = coords[0];
double y = coords[1];
double a = GetAngle(x,y);
double r = sqrt(x*x + y*y);
if (astretch)
{
if ( (a <= alim[0]+apml_thickness[0]) ||
(a >= alim[1]-apml_thickness[1]) )
{
in_pml = true;
break;
}
}
if (rstretch)
{
if ( (r <= rlim[0]+rpml_thickness[0]) ||
(r >= rlim[1]-rpml_thickness[1]) )
{
in_pml = true;
break;
}
}
}
if (in_pml)
{
elems[i] = 0;
el->SetAttribute(2);
}
// Vector center;
// mesh_->GetElementCenter(i,center);
// double x = center[0];
// double y = center[1];
// double a = GetAngle(x,y);
// double r = sqrt(x*x + y*y);
// // check upper and lower bound
// if (astretch)
// {
// if ( (a <= alim[0]+apml_thickness[0]) ||
// (a >= alim[1]-apml_thickness[1]) )
// {
// elems[i] = 0;
// el->SetAttribute(2);
// }
// }
// if (rstretch)
// {
// if ( (r <= rlim[0]+rpml_thickness[0]) ||
// (r >= rlim[1]-rpml_thickness[1]) )
// {
// elems[i] = 0;
// el->SetAttribute(2);
// }
// }
}
mesh_->SetAttributes();
}
double ToroidPML::GetAngle(const double x, const double y)
{
// Find r and a for this point
double arad;
if (x == 0.0)
{
arad = (y > 0.0)? M_PI/2.0 : 3.0 * M_PI/2.0;
}
else
{
arad = atan(y/x);
int k = 0;
if (x<0)
{
k = 1;
}
else if (y<0)
{
k = 2;
}
arad += k*M_PI;
}
return arad * 180.0/M_PI;
}
// void ToroidPML::StretchFunction(const Vector &X,
// vector<complex<double>> &dxs, double omega)
void ToroidPML::StretchFunction(const Vector &X, ComplexDenseMatrix & J, double omega)
{
complex<double> zi = complex<double>(0., 1.);
double n = 2.0;
double c = 10.0;
// double c = log(omega);
// Stretch in the azimuthal direction
double x = X[0];
double y = X[1];
if (abs(x) < 1e-12) x = 0.0;
if (abs(y) < 1e-12) y = 0.0;
double a = GetAngle(x,y);
double r = sqrt(x*x + y*y);
// dxs[0] = 1.0;
// dxs[1] = 1.0;
J = 0.0;
J(0,0) = 1.0;
J(1,1) = 1.0;
if (dim == 3) J(2,2) = 1.0;
if (astretch)
{
double th = a * M_PI/180.0;
double thl, thL, thH;
bool in_pml = false;
// negative direction
if (a <= alim[0]+apml_thickness[0])
{
in_pml = true;
thL = alim[1] * M_PI/180.0;
thH = apml_thickness[1] * M_PI/180.0;
thl = thL + thH;
}
// positive direction
if (a >= alim[1]-apml_thickness[1])
{
in_pml = true;
thL = alim[1] * M_PI/180.0;
thH = apml_thickness[1] * M_PI/180.0;
thl = thL - thH;
}
// double c1 = min(20.0*M_PI/180.0,thH);
if (in_pml)
{
double c1 = thH;
double coeff = n * c / omega / pow(c1,n);
double f_th = pow(th - thl,n-1);
double th_x = - y / (r * r);
double th_y = x / (r * r);
J(0,0) = 1.0 + zi * coeff * abs(f_th * th_x);
J(0,1) = zi * f_th * th_y;
J(1,0) = zi * f_th * th_x;
J(1,1) = 1.0 + zi * coeff * abs(f_th * th_y);
}
}
// Stretch in the radial direction
if (rstretch)
{ // negative
double rl, rL, rH;
bool in_pml = false;
if (r <= rlim[0]+rpml_thickness[0])
{
in_pml = true;
rL = rlim[0];
rH = rpml_thickness[0];
rl = rL + rH;
}
// positive direction
if (r >= rlim[1]-rpml_thickness[1])
{
in_pml = true;
rL = rlim[1];
rH = rpml_thickness[1];
rl = rL - rH;
}
if (in_pml)
{
double coeff = n * c / omega / pow (rH,n);
double f_r = pow(r-rl,n-1.0);
double r_x = x / r;
double r_y = y / r;
J(0,0) = 1.0 + zi * coeff * abs(f_r*r_x);
J(0,1) = zi * f_r * r_y;
J(1,0) = zi * f_r * r_x;
J(1,1) = 1.0 + zi * coeff * abs(f_r*r_y);
}
}
}
double pml_detJ_Re(const Vector & x, CartesianPML * pml)
{
int dim = pml->dim;
double omega = pml->omega;
std::vector<std::complex<double>> dxs(dim);
complex<double> det(1.0,0.0);
pml->StretchFunction(x, dxs, omega);
for (int i=0; i<dim; ++i) det *= dxs[i];
return det.real();
}
double pml_detJ_Im(const Vector & x, CartesianPML * pml)
{
int dim = pml->dim;
double omega = pml->omega;
std::vector<std::complex<double>> dxs(dim);
complex<double> det(1.0,0.0);
pml->StretchFunction(x, dxs, omega);
for (int i=0; i<dim; ++i) det *= dxs[i];
return det.imag();
}
void pml_detJ_JT_J_inv_Re(const Vector & x, CartesianPML * pml , DenseMatrix & M)
{
int dim = pml->dim;
double omega = pml->omega;
std::vector<std::complex<double>> dxs(dim);
complex<double> det(1.0,0.0);
pml->StretchFunction(x, dxs, omega);
for (int i = 0; i<dim; ++i)
{
det *= dxs[i];
}
M=0.0;
for (int i = 0; i<dim; ++i)
{
M(i,i) = (det / pow(dxs[i],2)).real();
}
}
void pml_detJ_JT_J_inv_Im(const Vector & x, CartesianPML * pml , DenseMatrix & M)
{
int dim = pml->dim;
double omega = pml->omega;
std::vector<std::complex<double>> dxs(dim);
complex<double> det = 1.0;
pml->StretchFunction(x, dxs, omega);
for (int i = 0; i<dim; ++i)
{
det *= dxs[i];
}
M=0.0;
for (int i = 0; i<dim; ++i)
{
M(i,i) = (det / pow(dxs[i],2)).imag();
}
}
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M)
{
int dim = pml->dim;
double omega = pml->omega;
vector<complex<double>> dxs(dim);
complex<double> det(1.0, 0.0);
pml->StretchFunction(x, dxs, omega);
for (int i = 0; i < dim; ++i)
{
det *= dxs[i];
}
M = 0.0;
for (int i = 0; i < dim; ++i)
{
M(i, i) = (det / pow(dxs[i], 2)).real();
}
}
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M)
{
int dim = pml->dim;
double omega = pml->omega;
vector<complex<double>> dxs(dim);
complex<double> det = 1.0;
pml->StretchFunction(x, dxs, omega);
for (int i = 0; i < dim; ++i)
{
det *= dxs[i];
}
M = 0.0;
for (int i = 0; i < dim; ++i)
{
M(i, i) = (det / pow(dxs[i], 2)).imag();
}
}
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M)
{
int dim = pml->dim;
double omega = pml->omega;
vector<complex<double>> dxs(dim);
complex<double> det = 1.0;
pml->StretchFunction(x, dxs, omega);
for (int i = 0; i < dim; ++i)
{
det *= dxs[i];
}
M = 0.0;
for (int i = 0; i < dim; ++i)
{
M(i, i) = abs(det / pow(dxs[i], 2));
}
}
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M)
{
int dim = pml->dim;
double omega = pml->omega;
vector<complex<double>> dxs(dim);
complex<double> det(1.0, 0.0);
pml->StretchFunction(x, dxs, omega);
for (int i = 0; i < dim; ++i)
{
det *= dxs[i];
}
// in the 2D case the coefficient is scalar 1/det(J)
if (dim == 2)
{
M = (1.0 / det).real();
}
else
{
M = 0.0;
for (int i = 0; i < dim; ++i)
{
M(i, i) = (pow(dxs[i], 2) / det).real();
}
}
}
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M)
{
int dim = pml->dim;
double omega = pml->omega;
vector<complex<double>> dxs(dim);
complex<double> det = 1.0;
pml->StretchFunction(x, dxs, omega);
for (int i = 0; i < dim; ++i)
{
det *= dxs[i];
}
if (dim == 2)
{
M = (1.0 / det).imag();
}
else
{
M = 0.0;
for (int i = 0; i < dim; ++i)
{
M(i, i) = (pow(dxs[i], 2) / det).imag();
}
}
}
+179
View File
@@ -0,0 +1,179 @@
#pragma once
#include "mfem.hpp"
#include "complex_linalg.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Class for setting up a simple Cartesian PML region
class CartesianPML
{
private:
Mesh *mesh;
// Length of the PML Region in each direction
Array2D<double> length;
// Computational Domain Boundary
Array2D<double> comp_dom_bdr;
// Domain Boundary
Array2D<double> dom_bdr;
// Integer Array identifying elements in the pml
// 0: in the pml, 1: not in the pml
Array<int> elems;
// Compute Domain and Computational Domain Boundaries
void SetBoundaries();
public:
// Constructor
CartesianPML(Mesh *mesh_,Array2D<double> length_);
int dim;
double omega;
// Return Computational Domain Boundary
Array2D<double> GetCompDomainBdr() {return comp_dom_bdr;}
// Return Domain Boundary
Array2D<double> GetDomainBdr() {return dom_bdr;}
// Return Marker list for elements
Array<int> * GetMarkedPMLElements() {return &elems;}
// Mark element in the PML region
void SetAttributes(Mesh *mesh_);
void SetOmega(double omega_) {omega = omega_;}
// PML complex stretching function
void StretchFunction(const Vector &x, vector<complex<double>> &dxs, double omega);
};
class ToroidPML
{
private:
Mesh *mesh;
Vector zlim, zpml_thickness; // range in axial direction
Vector rlim, rpml_thickness; // range in radial direction
Vector alim, apml_thickness; // range in azimuthal direction
// Integer Array identifying elements in the pml
// 0: in the pml, 1: not in the pml
Array<int> elems;
double GetAngle(const double x, const double y);
// Compute Domain and Computational Domain Boundaries
void SetBoundaries();
bool zstretch = false;
bool rstretch = false;
bool astretch = false;
public:
// Constructor
ToroidPML(Mesh *mesh_);
int dim;
double omega;
// Return Computational Domain Boundary
// Return Domain Boundary
void GetDomainBdrs(Vector & zlim_, Vector & rlim_, Vector & alim_)
{
zlim_.SetSize(2); zlim_ = zlim;
rlim_.SetSize(2); rlim_ = rlim;
alim_.SetSize(2); alim_ = alim;
}
void SetPmlWidth(const Vector & zpml, const Vector & rpml, const Vector & apml)
{
MFEM_VERIFY(zpml.Size() == 2 , "Check zpml size");
MFEM_VERIFY(rpml.Size() == 2 , "Check rpml size");
MFEM_VERIFY(apml.Size() == 2 , "Check apml size");
zpml_thickness = zpml;
rpml_thickness = rpml;
apml_thickness = apml;
}
void SetPmlAxes(const bool zstretch_,
const bool rstretch_,
const bool astretch_ )
{
zstretch = zstretch_;
rstretch = rstretch_;
astretch = astretch_;
}
// // Return Marker list for elements
Array<int> * GetMarkedPMLElements() {return &elems;}
// Mark element in the PML region
void SetAttributes(Mesh *mesh_);
void SetOmega(double omega_) {omega = omega_;}
// PML complex stretching function
// void StretchFunction(const Vector &X, vector<complex<double>> &dxs, double omega);
void StretchFunction(const Vector &X, ComplexDenseMatrix & J, double omega);
};
class PmlCoefficient : public Coefficient
{
private:
CartesianPML * pml = nullptr;
double (*Function)(const Vector &, CartesianPML * );
public:
PmlCoefficient(double (*F)(const Vector &, CartesianPML *), CartesianPML * pml_)
: pml(pml_), Function(F)
{}
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip)
{
double x[3];
Vector transip(x, 3);
T.Transform(ip, transip);
return ((*Function)(transip, pml));
}
};
// This includes scalar coefficients
class PmlMatrixCoefficient : public MatrixCoefficient
{
private:
CartesianPML * pml = nullptr;
void (*Function)(const Vector &, CartesianPML * , DenseMatrix &);
public:
PmlMatrixCoefficient(int dim, void(*F)(const Vector &, CartesianPML *,
DenseMatrix &),
CartesianPML * pml_)
: MatrixCoefficient(dim), pml(pml_), Function(F)
{}
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip)
{
double x[3];
Vector transip(x, 3);
T.Transform(ip, transip);
K.SetSize(height, width);
(*Function)(transip, pml, K);
}
};
// Helmholtz pml Functions
double pml_detJ_Re(const Vector & x, CartesianPML * pml);
double pml_detJ_Im(const Vector & x, CartesianPML * pml);
void pml_detJ_JT_J_inv_Re(const Vector & x, CartesianPML * pml , DenseMatrix & M);
void pml_detJ_JT_J_inv_Im(const Vector & x, CartesianPML * pml , DenseMatrix & M);
// Maxwell Pml functions
void detJ_JT_J_inv_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M);
void detJ_JT_J_inv_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M);
void detJ_JT_J_inv_abs(const Vector &x, CartesianPML * pml, DenseMatrix &M);
void detJ_inv_JT_J_Re(const Vector &x, CartesianPML * pml, DenseMatrix &M);
void detJ_inv_JT_J_Im(const Vector &x, CartesianPML * pml, DenseMatrix &M);
@@ -0,0 +1,619 @@
#include "Utilities.hpp"
Sweep::Sweep(int dim_) : dim(dim_)
{
nsweeps = pow(2,dim);
sweeps.resize(nsweeps);
for (int is = 0; is<nsweeps; is++)
{
sweeps[is].SetSize(dim);
}
switch(dim)
{
case 1:
sweeps[0][0] = 1;
sweeps[1][0] = -1;
break;
case 2:
sweeps[0][0] = 1; sweeps[0][1] = 1;
sweeps[1][0] = -1; sweeps[1][1] = 1;
sweeps[2][0] = 1; sweeps[2][1] = -1;
sweeps[3][0] = -1; sweeps[3][1] = -1;
break;
default:
sweeps[0][0] = 1; sweeps[0][1] = 1; sweeps[0][2] = 1;
sweeps[1][0] = -1; sweeps[1][1] = 1; sweeps[1][2] = 1;
sweeps[2][0] = 1; sweeps[2][1] = -1; sweeps[2][2] = 1;
sweeps[3][0] = -1; sweeps[3][1] = -1; sweeps[3][2] = 1;
sweeps[4][0] = 1; sweeps[4][1] = 1; sweeps[4][2] = -1;
sweeps[5][0] = -1; sweeps[5][1] = 1; sweeps[5][2] = -1;
sweeps[6][0] = 1; sweeps[6][1] = -1; sweeps[6][2] = -1;
sweeps[7][0] = -1; sweeps[7][1] = -1; sweeps[7][2] = -1;
break;
}
}
Sweep::~Sweep()
{
for (int i = 0; i<nsweeps; i++)
{
sweeps[i].DeleteAll();
}
}
double CutOffFncn(const Vector &x, const Vector & pmin, const Vector & pmax, const Array2D<double> & h_)
{
int dim = pmin.Size();
Vector h0(dim);
Vector h1(dim);
for (int i=0; i<dim; i++)
{
h0(i) = h_[i][0];
h1(i) = h_[i][1];
}
Vector x0(dim);
Vector x1(dim);
x0 = pmin; x0+=h0;
x1 = pmax; x1-=h1;
double f = 1.0;
for (int i = 0; i<dim; i++)
{
double val = 1.0;
if( x(i) >= pmax(i) || x(i) <= pmin(i))
{
val = 0.0;
}
else if (x(i) < pmax(i) && x(i) >= x1(i))
{
if(h1(i) != 0.0)
// val = (x(i)-pmax(i))/(x1(i)-pmax(i));
val = pow((x(i)-pmax(i))/(x1(i)-pmax(i)),1.0);
}
else if (x(i) > pmin(i) && x(i) <= x0(i))
{
if (h0(i) != 0.0)
// val = (x(i)-pmin(i))/(x0(i)-pmin(i));
val = pow((x(i)-pmin(i))/(x0(i)-pmin(i)),1.0);
}
if (h0(i) == 0 && x(i) <= x1(i))
{
val = 1.0;
}
if (h1(i) == 0 && x(i) >= x0(i))
{
val = 1.0;
}
f *= val;
}
return f;
}
double ChiFncn(const Vector &x, const Vector & pmin, const Vector & pmax, const Array2D<double> & h_)
{
int dim = pmin.Size();
Vector h0(dim);
Vector h1(dim);
for (int i=0; i<dim; i++)
{
h0(i) = h_[i][0];
h1(i) = h_[i][1];
}
Vector x0(dim);
Vector x1(dim);
x0 = pmin; x0+=h0;
x1 = pmax; x1-=h1;
double f = 1.0;
for (int i = 0; i<dim; i++)
{
double val = 1.0;
if( x(i) >= pmax(i) || x(i) <= pmin(i))
{
val = 0.0;
}
else if (x(i) < pmax(i) && x(i) >= x1(i))
{
if(h1(i) != 0.0)
val = (x(i)-pmax(i))/(x1(i)-pmax(i));
// This function has to be changed to smth more reasonable
// val = pow((x(i)-pmax(i))/(x1(i)-pmax(i)),100.0);
}
else if (x(i) > pmin(i) && x(i) <= x0(i))
{
if (h0(i) != 0.0)
val = (x(i)-pmin(i))/(x0(i)-pmin(i));
// val = pow((x(i)-pmin(i))/(x0(i)-pmin(i)),100.0);
}
if (h0(i) == 0 && x(i) <= x1(i))
{
val = 1.0;
}
if (h1(i) == 0 && x(i) >= x0(i))
{
val = 1.0;
}
f *= val;
}
return f;
}
DofMap::DofMap(FiniteElementSpace * fes , MeshPartition * partition)
{
const FiniteElementCollection * fec = fes->FEColl();
nrpatch = partition->nrpatch;
fespaces.SetSize(nrpatch);
Dof2GlobalDof.resize(nrpatch);
for (int ip=0; ip<nrpatch; ++ip)
{
// create finite element spaces for each patch
fespaces[ip] = new FiniteElementSpace(partition->patch_mesh[ip],fec);
// construct the patch tdof to global tdof map
int nrdof = fespaces[ip]->GetTrueVSize();
Dof2GlobalDof[ip].SetSize(2*nrdof);
// loop through the elements in the patch
for (int iel = 0; iel<partition->element_map[ip].Size(); ++iel)
{
// index in the global mesh
int iel_idx = partition->element_map[ip][iel];
// get the dofs of this element
Array<int> ElemDofs;
Array<int> GlobalElemDofs;
fespaces[ip]->GetElementDofs(iel,ElemDofs);
fes->GetElementDofs(iel_idx,GlobalElemDofs);
// the sizes have to match
MFEM_VERIFY(ElemDofs.Size() == GlobalElemDofs.Size(),
"Size inconsistency");
// loop through the dofs and take into account the signs;
int ndof = ElemDofs.Size();
for (int i = 0; i<ndof; ++i)
{
int pdof_ = ElemDofs[i];
int gdof_ = GlobalElemDofs[i];
int pdof = (pdof_ >= 0) ? pdof_ : abs(pdof_) - 1;
int gdof = (gdof_ >= 0) ? gdof_ : abs(gdof_) - 1;
Dof2GlobalDof[ip][pdof] = gdof;
Dof2GlobalDof[ip][pdof+nrdof] = gdof+fes->GetTrueVSize();
}
}
}
}
DofMap::DofMap(FiniteElementSpace * fes , MeshPartition * partition, int nrlayers)
{
nx = partition->nxyz[0];
ny = partition->nxyz[1];
nz = partition->nxyz[2];
int partition_kind = partition->partition_kind;
// Mesh * mesh = fespace->GetMesh();
const FiniteElementCollection * fec = fes->FEColl();
nrpatch = partition->nrpatch;
fespaces.SetSize(nrpatch);
PmlMeshes.SetSize(nrpatch);
// Extend patch meshes to include pml
for (int ip = 0; ip<nrpatch; ip++)
{
int k = ip/(nx*ny);
int j = (ip-k*nx*ny)/nx;
int i = (ip-k*nx*ny)%nx;
Array<int> directions;
if (i > 0)
{
for (int i=0; i<nrlayers; i++)
{
directions.Append(-1);
}
}
if (j > 0)
{
for (int i=0; i<nrlayers; i++)
{
directions.Append(-2);
}
}
if (k > 0)
{
for (int i=0; i<nrlayers; i++)
{
directions.Append(-3);
}
}
if (i < nx-1)
{
for (int i=0; i<nrlayers; i++)
{
if (partition_kind == 3 || partition_kind == 2) directions.Append(1);
}
}
if (j < ny-1)
{
for (int i=0; i<nrlayers; i++)
{
if (partition_kind == 3 || partition_kind == 2) directions.Append(2);
}
}
if (k < nz-1)
{
for (int i=0; i<nrlayers; i++)
{
if (partition_kind == 3 || partition_kind == 2) directions.Append(1);
}
}
PmlMeshes[ip] = ExtendMesh(partition->patch_mesh[ip],directions);
}
// Save PML_meshes
string meshpath;
string solpath;
if (partition_kind == 3 || partition_kind == 2)
{
meshpath = "output/mesh_ovlp_pml.";
solpath = "output/sol_ovlp_pml.";
}
else if (partition_kind == 4)
{
meshpath = "output/mesh_novlp_pml.";
solpath = "output/sol_novlp_pml.";
}
else
{
MFEM_ABORT("This partition kind not supported yet");
}
// SaveMeshPartition(PmlMeshes, meshpath, solpath);
PmlFespaces.SetSize(nrpatch);
Dof2GlobalDof.resize(nrpatch);
Dof2PmlDof.resize(nrpatch);
for (int ip=0; ip<nrpatch; ++ip)
{
// create finite element spaces for each patch
fespaces[ip] = new FiniteElementSpace(partition->patch_mesh[ip],fec);
PmlFespaces[ip] = new FiniteElementSpace(PmlMeshes[ip],fec);
// construct the patch tdof to global tdof map
int nrdof = fespaces[ip]->GetTrueVSize();
Dof2GlobalDof[ip].SetSize(2*nrdof);
Dof2PmlDof[ip].SetSize(2*nrdof);
// build dof maps between patch and extended patch
// loop through the patch elements and constract the dof map
// The same elements in the extended mesh have the same ordering (but not the dofs)
// loop through the elements in the patch
for (int iel = 0; iel<partition->element_map[ip].Size(); ++iel)
{
// index in the global mesh
int iel_idx = partition->element_map[ip][iel];
// get the dofs of this element
Array<int> ElemDofs;
Array<int> PmlElemDofs;
Array<int> GlobalElemDofs;
fespaces[ip]->GetElementDofs(iel,ElemDofs);
PmlFespaces[ip]->GetElementDofs(iel,PmlElemDofs);
fes->GetElementDofs(iel_idx,GlobalElemDofs);
// the sizes have to match
MFEM_VERIFY(ElemDofs.Size() == GlobalElemDofs.Size(),
"Size inconsistency");
MFEM_VERIFY(ElemDofs.Size() == PmlElemDofs.Size(),
"Size inconsistency");
// loop through the dofs and take into account the signs;
int ndof = ElemDofs.Size();
for (int i = 0; i<ndof; ++i)
{
int pdof_ = ElemDofs[i];
int gdof_ = GlobalElemDofs[i];
int pmldof_ = PmlElemDofs[i];
int pdof = (pdof_ >= 0) ? pdof_ : abs(pdof_) - 1;
int gdof = (gdof_ >= 0) ? gdof_ : abs(gdof_) - 1;
int pmldof = (pmldof_ >= 0) ? pmldof_ : abs(pmldof_) - 1;
Dof2GlobalDof[ip][pdof] = gdof;
Dof2GlobalDof[ip][pdof+nrdof] = gdof+fes->GetTrueVSize();
Dof2PmlDof[ip][pdof] = pmldof;
Dof2PmlDof[ip][pdof+nrdof] = pmldof+PmlFespaces[ip]->GetTrueVSize();
}
}
}
}
LocalDofMap::LocalDofMap(const FiniteElementCollection * fec_, MeshPartition * part1_,
MeshPartition * part2_):fec(fec_), part1(part1_), part2(part2_)
{
// Each overlapping patch has 2 non-overlapping subdomains
// Thre are n non-overlapping and and n-1 overlapping subdomains
int nrpatch = part2->nrpatch;
MFEM_VERIFY(part1->nrpatch-1 == part2->nrpatch, "Check number of subdomains");
cout << "Constructing local dof maps" << endl;
map1.resize(nrpatch);
map2.resize(nrpatch);
for (int ip=0; ip<nrpatch; ip++)
{
// Get the 3 meshes involved
Mesh * mesh = part2->patch_mesh[ip];
Mesh * mesh1 = part1->patch_mesh[ip];
Mesh * mesh2 = part1->patch_mesh[ip+1];
// Define the fespaces
FiniteElementSpace fespace(mesh, fec);
FiniteElementSpace fespace1(mesh1, fec);
FiniteElementSpace fespace2(mesh2, fec);
int ndof1 = fespace1.GetTrueVSize();
int ndof2 = fespace2.GetTrueVSize();
map1[ip].SetSize(2*ndof1); // times 2 because it's complex
map2[ip].SetSize(2*ndof2); // times 2 because it's complex
// loop through the elements in the patches
// map 1 is constructed by the first half of elements
// map 2 is constructed by the second half of elements
for (int iel = 0; iel<part1->element_map[ip].Size(); ++iel)
{
// index in the overlapping mesh
int iel_idx = iel;
Array<int> ElemDofs;
Array<int> GlobalElemDofs;
fespace1.GetElementDofs(iel,ElemDofs);
fespace.GetElementDofs(iel_idx,GlobalElemDofs);
// the sizes have to match
MFEM_VERIFY(ElemDofs.Size() == GlobalElemDofs.Size(),
"Size inconsistency");
// loop through the dofs and take into account the signs;
int ndof = ElemDofs.Size();
for (int i = 0; i<ndof; ++i)
{
int pdof_ = ElemDofs[i];
int gdof_ = GlobalElemDofs[i];
int pdof = (pdof_ >= 0) ? pdof_ : abs(pdof_) - 1;
int gdof = (gdof_ >= 0) ? gdof_ : abs(gdof_) - 1;
map1[ip][pdof] = gdof;
map1[ip][pdof+ndof1] = gdof+fespace.GetTrueVSize();
}
}
for (int iel = 0; iel<part1->element_map[ip+1].Size(); ++iel)
{
// index in the overlapping mesh
int k = part1->element_map[ip].Size();
int iel_idx = iel+k;
Array<int> ElemDofs;
Array<int> GlobalElemDofs;
fespace2.GetElementDofs(iel,ElemDofs);
fespace.GetElementDofs(iel_idx,GlobalElemDofs);
// the sizes have to match
MFEM_VERIFY(ElemDofs.Size() == GlobalElemDofs.Size(),
"Size inconsistency");
// loop through the dofs and take into account the signs;
int ndof = ElemDofs.Size();
for (int i = 0; i<ndof; ++i)
{
int pdof_ = ElemDofs[i];
int gdof_ = GlobalElemDofs[i];
int pdof = (pdof_ >= 0) ? pdof_ : abs(pdof_) - 1;
int gdof = (gdof_ >= 0) ? gdof_ : abs(gdof_) - 1;
map2[ip][pdof] = gdof;
map2[ip][pdof+ndof2] = gdof+fespace.GetTrueVSize();
}
}
}
};
NeighborDofMaps::NeighborDofMaps(MeshPartition * part_, FiniteElementSpace * fes_,
DofMap * dmap_,
int ovlp_layers_) : part(part_), fes(fes_),
dmap(dmap_),
ovlp_layers(ovlp_layers_)
{
nrsubdomains = part->nrpatch;
nxyz.SetSize(3);
mesh = fes->GetMesh();
dim = mesh->Dimension();
for (int d=0; d<3; d++) nxyz[d] = part->nxyz[d];
MarkOvlpElements();
ComputeNeighborDofMaps();
}
void NeighborDofMaps::MarkOvlpElements()
{
// Lists of elements
// x,y,z = +/- 1 ovlp
OvlpElems.resize(nrsubdomains);
for (int ip = 0; ip<nrsubdomains; ip++)
{
int i0,j0,k0;
Getijk(ip,i0,j0,k0);
int ijk[dim]; ijk[0] = i0; ijk[1]=j0;
if (dim==3) ijk[2] = k0;
FiniteElementSpace * sub_fes = dmap->fespaces[ip];
Mesh * sub_mesh = sub_fes->GetMesh();
// OvlpElems[ip].resize(2*dim);
OvlpElems[ip].resize(pow(3,dim));
Vector pmin, pmax;
sub_mesh->GetBoundingBox(pmin,pmax);
double h = part->MeshSize;
// Loop through elements
for (int iel=0; iel<sub_mesh->GetNE(); iel++)
{
// Get element center
Vector center(dim);
int geom = sub_mesh->GetElementBaseGeometry(iel);
ElementTransformation * tr = sub_mesh->GetElementTransformation(iel);
tr->Transform(Geometries.GetCenter(geom),center);
// loop through dimensions
Array<bool> pos(dim); pos = 0;
Array<bool> neg(dim); neg = 0;
for (int d=0;d<dim; d++)
{
if (ijk[d]>0 && center[d] < pmin[d]+2.0*h*ovlp_layers)
{
neg[d] = true;
}
if (ijk[d]<nxyz[d]-1 && center[d] > pmax[d]-2.0*h*ovlp_layers)
{
pos[d] = true;
}
}
SetElementToOverlap(ip,iel,neg,pos);
}
}
}
void NeighborDofMaps::ComputeNeighborDofMaps()
{
OvlpDofMaps.resize(nrsubdomains);
// Array<UniqueIndexGen * > Gen(nrsubdomains);
// // construct unique number generator for the elements of a patch
// for (int ip = 0; ip<nrsubdomains; ip++)
// {
// Gen[ip] = new UniqueIndexGen;
// // register the elements
// int nel = part->element_map[ip].Size();
// for (int iel=0; iel<nel; iel++)
// {
// int iel_idx = part->element_map[ip][iel];
// Gen[ip]->Set(iel_idx);
// }
// }
// construct dof maps
int nrneighbors = pow(3,dim); // including its self
for (int ip0 = 0; ip0<nrsubdomains; ip0++)
{
OvlpDofMaps[ip0].resize(nrneighbors);
FiniteElementSpace * fes0 = dmap->fespaces[ip0];
int tdofs0 = fes0->GetTrueVSize();
Array<int> marker0(tdofs0); marker0 = 0;
int i0, j0, k0;
Array<int> ijk(dim);
Getijk(ip0, i0,j0,k0);
int kbeg = (dim == 2) ? 0 : -1;
int kend = (dim == 2) ? 1 : 2;
for (int k=kbeg; k<kend; k++)
{
int k1 = k0 + k;
if (k1 <0 || k1>=nxyz[2]) continue;
int kk = (dim == 2) ? -1 : k;
for (int j=-1; j<2; j++)
{
int j1 = j0 + j;
if (j1 <0 || j1>=nxyz[1]) continue;
for (int i=-1; i<2; i++)
{
int i1 = i0 + i;
if (i1 <0 || i1>=nxyz[0]) continue;
Array<int> ip0list; marker0 = 0;
int directionId = GetDirectionId(i,j,kk);
Array<int> Elems = OvlpElems[ip0][directionId];
int nel = Elems.Size();
for (int iel = 0; iel<nel; ++iel)
{
int iel0 = Elems[iel];
Array<int> ElemDofs0;
fes0->GetElementDofs(iel0,ElemDofs0);
int ndof = ElemDofs0.Size();
// since the elements are added to the subdomain meshes
// in the same ordered fashion (as they come from the
// original mesh) then the ordering of elements in each
// subdomain is the same. Hence the dof ovlp lists
// can be computed for each subdomain independendly
for (int l = 0; l<ndof; ++l)
{
int dof0_ = ElemDofs0[l];
int dof0 = (dof0_ >= 0) ? dof0_ : abs(dof0_) - 1;
if (!marker0[dof0])
{
ip0list.Append(dof0); // dofs of ip0 in ovlp
marker0[dof0] = 1;
}
}
}
OvlpDofMaps[ip0][directionId].Append(ip0list);
int tsize = fes0->GetTrueVSize();
// Imaginary part
for (int l=0;l<ip0list.Size(); l++) { ip0list[l] += tsize; }
OvlpDofMaps[ip0][directionId].Append(ip0list);
}
}
}
}
}
void NeighborDofMaps::GetNeighborDofMap(const int ip,
const Array<int> & directions,
Array<int> & dofmap)
{
int k = (dim == 2) ? -1 : directions[2];
int directionid = GetDirectionId(directions[0],directions[1],k);
dofmap = OvlpDofMaps[ip][directionid];
}
void NeighborDofMaps::SetElementToOverlap(int ip, int iel,
const Array<bool> & neg,
const Array<bool> & pos)
{
int kbeg = (dim == 2) ? 0 : -1;
int kend = (dim == 2) ? 0 : 1;
for (int k = kbeg; k<=kend; k++)
{
if (dim == 3)
{
if (k == -1 && !neg[2]) continue;
if (k == 1 && !pos[2]) continue;
}
for (int j = -1; j<=1; j++)
{
if (j== -1 && !neg[1]) continue;
if (j== 1 && !pos[1]) continue;
for (int i = -1; i<=1; i++)
{
// cases to skip
if (i==-1 && !neg[0]) continue;
if (i== 1 && !pos[0]) continue;
if (i==0 && j==0 && k == 0) continue;
int kk = (dim==2)?-1 : k;
int DirId = GetDirectionId(i,j,kk);
OvlpElems[ip][DirId].Append(iel);
}
}
}
}
@@ -0,0 +1,179 @@
#pragma once
#include "MeshPartition.hpp"
#include "complex_linalg.hpp"
struct UniqueIndexGen
{
int counter = 0;
std::unordered_map<int,int> idx;
void Set(int i)
{
std::unordered_map<int,int>::iterator f = idx.find(i);
if (f == idx.end())
{
idx[i] = counter;
counter++;
}
}
int Get(int i)
{
std::unordered_map<int,int>::iterator f = idx.find(i);
if (f == idx.end())
{
return -1;
}
else
{
return (*f).second;
}
}
void Reset()
{
counter = 0;
idx.clear();
}
};
struct Sweep
{
private:
int dim;
std::vector<Array<int>> sweeps;
public:
int nsweeps;
Sweep(int dim_);
~Sweep();
void GetSweep(const int i, Array<int> & sweep)
{
MFEM_VERIFY(i<nsweeps, "Sweep number out of bounds");
sweep.SetSize(dim);
sweep = sweeps[i];
}
};
// Function coefficient that takes the bounding box of the mesh as an input
class CutOffFnCoefficient : public Coefficient
{
private:
double (*Function)(const Vector &, const Vector &, const Vector &, const Array2D<double> &);
Vector pmin, pmax;
Array2D<double> h; // specify the with of the cutoff function (h in each direction)
public:
CutOffFnCoefficient(double (*F)(const Vector &, const Vector &, const Vector &, const Array2D<double> &),
const Vector & pmin_, const Vector & pmax_, Array2D<double> & h_)
: Function(F), pmin(pmin_), pmax(pmax_), h(h_)
{}
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip)
{
double x[3];
Vector transip(x, 3);
T.Transform(ip, transip);
return ((*Function)(transip, pmin, pmax, h));
}
};
double CutOffFncn(const Vector &x, const Vector & pmin,
const Vector & pmax, const Array2D<double> & h_);
double ChiFncn(const Vector &x, const Vector & pmin,
const Vector & pmax, const Array2D<double> & h_);
class DofMap // Constructs dof maps for a given partition
{
public:
int nrpatch, nx, ny, nz;
vector<Array<int>> Dof2GlobalDof;
vector<Array<int>> Dof2PmlDof;
Array<Mesh *> PmlMeshes;
Array<FiniteElementSpace *> fespaces;
Array<FiniteElementSpace *> PmlFespaces;
// constructor
// Non PML constructor dof map
DofMap(FiniteElementSpace * fes, MeshPartition * partition);
// PML
DofMap(FiniteElementSpace * fes , MeshPartition * partition, int nrlayers);
~DofMap(){};
};
class LocalDofMap // Constructs dof mapbetween two partitions
{
const FiniteElementCollection *fec=nullptr;
MeshPartition * part1=nullptr;
MeshPartition * part2=nullptr;
public:
int nrpatch, nx, ny, nz;
vector<Array<int>> map1;
vector<Array<int>> map2;
// constructor
LocalDofMap(const FiniteElementCollection * fec_, MeshPartition * part1_,
MeshPartition * part2_);
~LocalDofMap();
};
struct NeighborDofMaps
{
private:
int dim;
MeshPartition * part = nullptr;
FiniteElementSpace * fes = nullptr;
Mesh * mesh = nullptr;
std::vector<std::vector<Array<int>>> OvlpElems;
std::vector<std::vector<Array<int>>> OvlpDofMaps;
DofMap * dmap = nullptr;
int nrsubdomains = 0;
int ovlp_layers = 0;
Array<int> nxyz;
void SetElementToOverlap(int ip, int iel,
const Array<bool> & neg,
const Array<bool> & pos);
void MarkOvlpElements();
void ComputeNeighborDofMaps();
void Getijk(int ip, int & i, int & j, int & k) const
{
k = ip/(nxyz[0]*nxyz[1]);
j = (ip-k*nxyz[0]*nxyz[1])/nxyz[0];
i = (ip-k*nxyz[0]*nxyz[1])%nxyz[0];
}
int GetPatchId(const Array<int> & ijk) const
{
int d=ijk.Size();
int z = (d==2)? 0 : ijk[2];
return part->subdomains(ijk[0],ijk[1],z);
}
int GetDirectionId(int i, int j, int k=-1)
{
int n = 3;
return (k+1)*n*n + (j+1)*n + i+1;
}
void GetDirections(const int id, int & i, int & j, int & k)
{
int n = 3;
k = id/(n*n) - 1;
j = (id-(k+1)*n*n)/n - 1;
i = (id-(k+1)*n*n)%n - 1;
}
public:
NeighborDofMaps(MeshPartition * part_,
FiniteElementSpace * fes_,
DofMap * dmap_,
int ovlp_layers_);
void GetNeighborDofMap(const int ip, const Array<int> & directions,
Array<int> & dofmap);
};
@@ -0,0 +1,358 @@
#include "../../../linalg/kernels.hpp"
#include "complex_linalg.hpp"
ComplexDenseMatrix::ComplexDenseMatrix(){}
ComplexDenseMatrix::ComplexDenseMatrix(int s)
{
MFEM_ASSERT(s >= 0, "invalid ComplexDenseMatrix size: " << s);
height = s;
width = s;
if (s > 0)
{
data = new complex<double>[s*s];
*this = 0.0; // init with zeroes
}
}
ComplexDenseMatrix::ComplexDenseMatrix(int m, int n)
{
MFEM_VERIFY(m >= 0 && n >= 0,
"invalid DenseMatrix size: " << m << " x " << n);
const int s = m*n;
height = m;
width = n;
if (s > 0)
{
data = new complex<double>[s];
*this = 0.0; // init with zeroes
}
}
void ComplexDenseMatrix::SetSize(int h, int w)
{
MFEM_VERIFY(h >= 0 && w >= 0,
"invalid ComplexDenseMatrix size: " << h << " x " << w);
if (Height() == h && Width() == w)
{
return;
}
height = h;
width = w;
const int hw = h*w;
delete data;
data = new complex<double>[hw];
*this = 0.0; // init with zeroes
}
ComplexDenseMatrix &ComplexDenseMatrix::operator=(double c)
{
const int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] = c;
}
return *this;
}
ComplexDenseMatrix &ComplexDenseMatrix::operator=(complex<double> c)
{
const int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] = c;
}
return *this;
}
std::complex<double> ComplexDenseMatrix::Det() const
{
MFEM_ASSERT(Height() == Width() && Height() > 0,
"The matrix must be square and "
<< "sized larger than zero to compute the determinant."
<< " Height() = " << Height()
<< ", Width() = " << Width());
switch (Height())
{
case 1:
return data[0];
case 2:
return data[0] * data[3] - data[1] * data[2];
case 3:
{
const complex<double> *d = data;
return
d[0] * (d[4] * d[8] - d[5] * d[7]) +
d[3] * (d[2] * d[7] - d[1] * d[8]) +
d[6] * (d[1] * d[5] - d[2] * d[4]);
}
default:
{
MFEM_ABORT("dim>3 not supported yet");
return 0;
}
}
}
DenseMatrix * ComplexDenseMatrix::real() const
{
DenseMatrix * Ar = new DenseMatrix(height,width);
double * data = Ar->Data();
complex<double> * zdata = this->data;
for (int s = 0; s<height*width; s++)
{
data[s] = zdata[s].real();
}
return Ar;
}
DenseMatrix * ComplexDenseMatrix::imag() const
{
DenseMatrix * Ai = new DenseMatrix(height,width);
double * data = Ai->Data();
complex<double> * zdata = this->data;
for (int s = 0; s<height*width; s++)
{
data[s] = zdata[s].imag();
}
return Ai;
}
void ComplexDenseMatrix::GetReal(DenseMatrix & Ar)
{
MFEM_ASSERT(Ar.Height() == height && Ar.Width() == width, "Incompatible dimensions");
double * data = Ar.Data();
complex<double> * zdata = this->data;
for (int s = 0; s<height*width; s++)
{
data[s] = zdata[s].real();
}
}
void ComplexDenseMatrix::GetImag(DenseMatrix & Ai)
{
double * data = Ai.Data();
complex<double> * zdata = this->data;
for (int s = 0; s<height*width; s++)
{
data[s] = zdata[s].imag();
}
}
ComplexDenseMatrix &ComplexDenseMatrix::operator=(const ComplexDenseMatrix &m)
{
SetSize(m.height, m.width);
const int hw = height * width;
for (int i = 0; i < hw; i++)
{
data[i] = m.data[i];
}
return *this;
}
ComplexDenseMatrix &ComplexDenseMatrix::operator+=(const complex<double> *m)
{
const int hw = Height()*Width();
for (int i = 0; i < hw; i++)
{
data[i] += m[i];
}
return *this;
}
ComplexDenseMatrix &ComplexDenseMatrix::operator+=(const ComplexDenseMatrix &m)
{
MFEM_ASSERT(Height() == m.Height() && Width() == m.Width(),
"incompatible matrix sizes.");
return *this += m.GetData();
}
ComplexDenseMatrix &ComplexDenseMatrix::operator-=(const ComplexDenseMatrix &m)
{
int s = Height()*Width();
complex<double> * mdata = m.GetData();
for (int i = 0; i < s; i++)
{
data[i] -= mdata[s];
}
return *this;
}
ComplexDenseMatrix &ComplexDenseMatrix::operator*=(complex<double> c)
{
int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] *= c;
}
return *this;
}
void ComplexDenseMatrix::Print(std::ostream &out, int width_) const
{
// save current output flags
ios::fmtflags old_flags = out.flags();
// output flags = scientific + show sign
out << setiosflags(ios::scientific | ios::showpos);
for (int i = 0; i < height; i++)
{
out << "[row " << i << "]\n";
for (int j = 0; j < width; j++)
{
out << (*this)(i,j);
if (j+1 == width || (j+1) % width_ == 0)
{
out << '\n';
}
else
{
out << ' ';
}
}
}
// reset output flags to original values
out.flags(old_flags);
}
void ComplexDenseMatrix::PrintMatlab(std::ostream &out) const
{
// save current output flags
// ios::fmtflags old_flags = out.flags();
// output flags = scientific + show sign
// out << setiosflags(ios::scientific | ios::showpos);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
out << (*this)(i,j);
out << ' ';
}
out << "\n";
}
// reset output flags to original values
// out.flags(old_flags);
}
ComplexDenseMatrixInverse::ComplexDenseMatrixInverse(const ComplexDenseMatrix & A) : ComplexDenseMatrix(A.Height())
{
MFEM_VERIFY(A.Height() == A.Width(), "The matrix is not square");
MFEM_VERIFY(A.Height() < 4, "dim > 3 is not supported yet");
std::complex<double> detA = A.Det();
MFEM_VERIFY(abs(A.Det())>1e-14, "The given matrix is singular");
std::complex<double> * d = this->Data();
std::complex<double> *dA = A.GetData();
switch (A.Height())
{
case 1:
d[0] = 1.0/dA[0];
break;
case 2:
d[0] = 1.0/detA * dA[3];
d[1] = -1.0/detA * dA[1];
d[2] = -1.0/detA * dA[2];
d[3] = 1.0/detA * dA[0];
break;
case 3:
d[0] = 1.0/detA*(dA[4]*dA[8] - dA[5]*dA[7]);
d[1] = -1.0/detA*(dA[1]*dA[8] - dA[2]*dA[7]);
d[2] = 1.0/detA*(dA[1]*dA[5] - dA[2]*dA[4]);
d[3] = -1.0/detA*(dA[3]*dA[8] - dA[5]*dA[6]);
d[4] = 1.0/detA*(dA[0]*dA[8] - dA[2]*dA[6]);
d[5] = -1.0/detA*(dA[0]*dA[5] - dA[2]*dA[3]);
d[6] = 1.0/detA*(dA[3]*dA[7] - dA[4]*dA[6]);
d[7] = -1.0/detA*(dA[0]*dA[7] - dA[1]*dA[6]);
d[8] = 1.0/detA*(dA[0]*dA[4] - dA[1]*dA[3]);
break;
default:
// Should be unreachable
break;
}
}
/// Matrix matrix multiplication. A = B * C.
void Mult(const ComplexDenseMatrix &b, const ComplexDenseMatrix &c, ComplexDenseMatrix &a)
{
MFEM_ASSERT(a.Height() == b.Height() && a.Width() == c.Width() &&
b.Width() == c.Height(), "incompatible dimensions");
const int ah = a.Height();
const int aw = a.Width();
const int bw = b.Width();
complex<double> *ad = a.Data();
const complex<double> *bd = b.Data();
const complex<double> *cd = c.Data();
kernels::Mult(ah,aw,bw,bd,cd,ad);
}
/// Multiply the transpose of a matrix A with a matrix B: At*B
void MultAtB(const ComplexDenseMatrix &A, const ComplexDenseMatrix &B, ComplexDenseMatrix &AtB)
{
MFEM_ASSERT(A.Width() == AtB.Height() && B.Width() == AtB.Width() &&
A.Height() == B.Height(), "incompatible dimensions");
const int ah = A.Height();
const int aw = A.Width();
const int bw = B.Width();
const complex<double> *ad = A.Data();
const complex<double> *bd = B.Data();
complex<double> *cd = AtB.Data();
for (int j = 0; j < bw; j++)
{
const complex<double> *ap = ad;
for (int i = 0; i < aw; i++)
{
complex<double> d = 0.0;
for (int k = 0; k < ah; k++)
{
d += ap[k] * bd[k];
}
*(cd++) = d;
ap += ah;
}
bd += ah;
}
}
/// Multiply the conjugate transpose of a matrix A with a matrix B: At*B
void MultAhB(const ComplexDenseMatrix &A, const ComplexDenseMatrix &B, ComplexDenseMatrix &AtB)
{
MFEM_ASSERT(A.Width() == AtB.Height() && B.Width() == AtB.Width() &&
A.Height() == B.Height(), "incompatible dimensions");
MFEM_ASSERT(A.Width() == AtB.Height() && B.Width() == AtB.Width() &&
A.Height() == B.Height(), "incompatible dimensions");
const int ah = A.Height();
const int aw = A.Width();
const int bw = B.Width();
const complex<double> *ad = A.Data();
const complex<double> *bd = B.Data();
complex<double> *cd = AtB.Data();
for (int j = 0; j < bw; j++)
{
const complex<double> *ap = ad;
for (int i = 0; i < aw; i++)
{
complex<double> d = 0.0;
for (int k = 0; k < ah; k++)
{
d += conj(ap[k]) * bd[k];
}
*(cd++) = d;
ap += ah;
}
bd += ah;
}
}
@@ -0,0 +1,98 @@
#pragma once
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
class ComplexDenseMatrix
{
private:
std::complex<double> * data = nullptr;
int height = 0;
int width = 0;
public:
ComplexDenseMatrix();
/// Creates square matrix of size s.
explicit ComplexDenseMatrix(int s);
/// Creates rectangular matrix of size m x n.
ComplexDenseMatrix(int m, int n);
/// Change the size of the DenseMatrix to s x s.
void SetSize(int s) { SetSize(s, s); }
/// Change the size of the DenseMatrix to h x w.
void SetSize(int h, int w);
/// Returns the matrix data array.
inline complex<double> *Data() const
{ return const_cast<complex<double>*>((const complex<double>*)data);}
/// Returns the matrix data array.
inline complex<double> *GetData() const { return Data(); }
/// Returns reference to a_{ij}.
inline complex<double> &operator()(int i, int j);
inline const complex<double> &operator()(int i, int j) const;
inline int Height() const { return height; }
inline int Width() const { return width; }
/// Sets the matrix elements equal to constant c
ComplexDenseMatrix &operator=(std::complex<double> c);
ComplexDenseMatrix &operator=(double c);
/// Sets the matrix size and elements equal to those of m
ComplexDenseMatrix &operator=(const ComplexDenseMatrix &m);
ComplexDenseMatrix &operator+=(const complex<double> *m);
ComplexDenseMatrix &operator+=(const ComplexDenseMatrix &m);
ComplexDenseMatrix &operator-=(const ComplexDenseMatrix &m);
ComplexDenseMatrix &operator*=(complex<double> c);
/// Calculates the determinant of the matrix
/// (for 2x2, 3x3)
std::complex<double> Det() const;
virtual void Print(std::ostream &out = mfem::out, int width_ = 4) const;
virtual void PrintMatlab(std::ostream &out = mfem::out) const;
DenseMatrix * real() const;
DenseMatrix * imag() const;
void GetReal(DenseMatrix & Ar);
void GetImag(DenseMatrix & Ai);
};
inline complex<double> &ComplexDenseMatrix::operator()(int i, int j)
{
MFEM_VERIFY(data && i >= 0 && i < height && j >= 0 && j < width, "");
// return data[i*width+j];
return data[j*height+i];
}
inline const complex<double> &ComplexDenseMatrix::operator()(int i, int j) const
{
MFEM_VERIFY(data && i >= 0 && i < height && j >= 0 && j < width, "");
// return data[i*width+j];
return data[j*height+i];
}
class ComplexDenseMatrixInverse : public ComplexDenseMatrix
{
private:
public:
ComplexDenseMatrixInverse(const ComplexDenseMatrix & );
};
/// Matrix matrix multiplication. A = B * C.
void Mult(const ComplexDenseMatrix &b, const ComplexDenseMatrix &c, ComplexDenseMatrix &a);
/// Multiply the transpose of a matrix A with a matrix B: At*B
void MultAtB(const ComplexDenseMatrix &A, const ComplexDenseMatrix &B, ComplexDenseMatrix &AtB);
/// Multiply the conjugate transpose of a matrix A with a matrix B: At*B
void MultAhB(const ComplexDenseMatrix &A, const ComplexDenseMatrix &B, ComplexDenseMatrix &AtB);
+404
View File
@@ -0,0 +1,404 @@
//
// Compile with: make helmholtzp
//
// Sample runs: mpirun -np 4 ./helmholtzp -nd 2 -nx 4 -ny 4 -sr 3 -pr 3 -k 16.0 -o 2
// mpirun -np 4 ./helmholtzp -nd 3 -nx 2 -ny 2 -nz 2 -sr 3 -pr 1 -k 2.0 -o 2
//
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "ParDST/ParDST.hpp"
using namespace std;
using namespace mfem;
// Exact solution and r.h.s., see below for implementation.
double f_exact_Re(const Vector &x);
double f_exact_Im(const Vector &x);
double wavespeed(const Vector &x);
double funccoeff_re(const Vector & x);
double funccoeff_im(const Vector & x);
int dim;
double omega;
int sol = 1;
double length = 1.0;
double pml_length = 0.25;
Array2D<double>comp_bdr;
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
// finite element order of approximation
int order = 1;
bool visualization = 1;
// number of wavelengths
double k = 0.5;
// number of serial refinements
int ser_ref_levels = 1;
// number of parallel refinements
int par_ref_levels = 2;
// dimension
int nd = 2;
int nx=2;
int ny=2;
int nz=2;
bool herm_conv = true;
// optional command line inputs
OptionsParser args(argc, argv);
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&nd, "-nd", "--dim","Problem space dimension");
args.AddOption(&nx, "-nx", "--nx","Number of subdomains in x direction");
args.AddOption(&ny, "-ny", "--ny","Number of subdomains in y direction");
args.AddOption(&nz, "-nz", "--nz","Number of subdomains in z direction");
args.AddOption(&sol, "-sol", "--exact",
"Exact solution flag - 0:polynomial, 1: plane wave, -1: unknown exact");
args.AddOption(&k, "-k", "--wavelengths",
"Number of wavelengths.");
args.AddOption(&pml_length, "-pml_length", "--pml_length",
"Length of the PML region in each direction");
args.AddOption(&length, "-length", "--length",
"length of the domain in each direction.");
args.AddOption(&ser_ref_levels, "-sr", "--ser_ref_levels",
"Number of Serial Refinements.");
args.AddOption(&par_ref_levels, "-pr", "--par_ref_levels",
"Number of Parallel Refinements.");
args.AddOption(&herm_conv, "-herm", "--hermitian", "-no-herm",
"--no-hermitian", "Use convention for Hermitian operators.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
// check if the inputs are correct
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// Angular frequency
omega = 2.0 * M_PI * k;
// 3. Read the mesh from the given mesh file.
Mesh *mesh;
if (nd == 2)
{
mesh = new Mesh(1, 1, Element::QUADRILATERAL, true, length, length, false);
}
else
{
mesh = new Mesh(1, 1, 1, Element::HEXAHEDRON, true, length, length, length,false);
}
// 3. Executing uniform h-refinement
dim = mesh->Dimension();
for (int i = 0; i < ser_ref_levels; i++ )
{
mesh->UniformRefinement();
}
// 4. Define a parallel mesh by a partitioning of the serial mesh.
// ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
int nprocs;
int nprocsx;
int nprocsy;
int nprocsz;
if (dim == 2)
{
nprocs = sqrt(num_procs);
// nprocsx = nprocs;
// nprocsy = nprocs;
nprocsx = 1;
nprocsy = num_procs;
nprocsz = 1;
}
else
{
nprocs = cbrt(num_procs);
// nprocsx = nprocs;
// nprocsy = nprocs;
// nprocsz = nprocs;
nprocsx = 1;
if (nz != 1)
{
nprocsy = sqrt(num_procs);
nprocsz = nprocsy;
}
else
{
nprocsy = num_procs;
nprocsz = 1;
}
}
// MFEM_VERIFY(nprocs*nprocs == num_procs, "Check MPI partitioning");
// int nxyz[3] = {num_procs,1,1};
// int nxyz[3] = {nprocs,nprocs,1};
// int nxyz[3] = {1,num_procs,1};
int nxyz[3] = {nprocsx,nprocsy,nprocsz};
// int nxyz[3] = {num_procs,1,1};
int * part = mesh->CartesianPartitioning(nxyz);
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD,*mesh,part);
// ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD,*mesh);
delete [] part;
delete mesh;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
double hl = GetUniformMeshElementSize(pmesh);
int nrlayers = 3;
Array2D<double> lengths(dim,2);
lengths = hl*nrlayers;
// lengths[0][1] = 0.0;
// lengths[1][1] = 0.0;
// lengths[1][0] = 0.0;
// lengths[0][0] = 0.0;
CartesianPML pml(pmesh,lengths);
pml.SetOmega(omega);
comp_bdr.SetSize(dim,2);
comp_bdr = pml.GetCompDomainBdr();
// 6. Define a finite element space on the mesh.
FiniteElementCollection *fec = new H1_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 6. Set up the linear form (Real and Imaginary part)
FunctionCoefficient f_Re(f_exact_Re);
FunctionCoefficient f_Im(f_exact_Im);
// 8. Setup Complex Operator convention
ComplexOperator::Convention conv =
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
// ParLinearForm *b_Re(new ParLinearForm);
ParComplexLinearForm b(fespace, conv);
b.AddDomainIntegrator(new DomainLFIntegrator(f_Re),
new DomainLFIntegrator(f_Im));
b.real().Vector::operator=(0.0);
b.imag().Vector::operator=(0.0);
b.Assemble();
// 7. Set up the bilinear form (Real and Imaginary part)
ConstantCoefficient one(1.0);
ConstantCoefficient sigma(-pow(omega, 2));
FunctionCoefficient ws(wavespeed);
PmlMatrixCoefficient c1_re(dim,pml_detJ_JT_J_inv_Re,&pml);
PmlMatrixCoefficient c1_im(dim,pml_detJ_JT_J_inv_Im,&pml);
PmlCoefficient detJ_re(pml_detJ_Re,&pml);
PmlCoefficient detJ_im(pml_detJ_Im,&pml);
ProductCoefficient c2_re0(sigma, detJ_re);
ProductCoefficient c2_im0(sigma, detJ_im);
ProductCoefficient c2_re(c2_re0, ws);
ProductCoefficient c2_im(c2_im0, ws);
ParSesquilinearForm a(fespace,conv);
a.AddDomainIntegrator(new DiffusionIntegrator(c1_re),
new DiffusionIntegrator(c1_im));
a.AddDomainIntegrator(new MassIntegrator(c2_re),
new MassIntegrator(c2_im));
a.Assemble();
a.Finalize();
Array<int> ess_tdof_list;
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
// Solution grid function
ParComplexGridFunction p_gf(fespace); p_gf = 0.0;
OperatorHandle Ah;
Vector X, B;
a.FormLinearSystem(ess_tdof_list, p_gf, b, Ah, X, B);
{
StopWatch chrono;
chrono.Clear();
chrono.Start();
ParDST S(&a,lengths,omega, &ws,nrlayers,nx,ny,nz);
chrono.Stop();
double t1 = chrono.RealTime();
chrono.Clear();
chrono.Start();
// X = 0.0;
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetPreconditioner(S);
gmres.SetOperator(*Ah);
gmres.SetRelTol(1e-6);
gmres.SetMaxIter(20);
gmres.SetPrintLevel(1);
gmres.Mult(B, X);
chrono.Stop();
double t2 = chrono.RealTime();
MPI_Barrier(MPI_COMM_WORLD);
cout << " myid: " << myid
<< ", setup time: " << t1
<< ", solution time: " << t2 << endl;
a.RecoverFEMSolution(X,B,p_gf);
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
string keys;
if (dim ==2 )
{
keys = "keys mrRljc\n";
}
else
{
keys = "keys mc\n";
}
socketstream sol_sock_re(vishost, visport);
sol_sock_re.precision(8);
sol_sock_re << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh << p_gf.real() << keys
<< "window_title 'Numerical Pressure: Real Part' " << flush;
socketstream sol_sock_im(vishost, visport);
sol_sock_im.precision(8);
sol_sock_im << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh << p_gf.imag() << keys
<< "window_title 'Numerical Pressure: Imag Part' " << flush;
}
}
delete fespace;
delete fec;
delete pmesh;
MPI_Finalize();
return 0;
}
double f_exact_Re(const Vector &x)
{
int nrsources = (dim == 2) ? 4 : 8;
Vector x0(nrsources);
Vector y0(nrsources);
Vector z0(nrsources);
x0(0) = 0.25; y0(0) = 0.25; z0(0) = 0.25;
x0(1) = 0.75; y0(1) = 0.25; z0(1) = 0.25;
x0(2) = 0.25; y0(2) = 0.75; z0(2) = 0.25;
x0(3) = 0.75; y0(3) = 0.75; z0(3) = 0.25;
if (dim == 3)
{
x0(4) = 0.25; y0(4) = 0.25; z0(4) = 0.75;
x0(5) = 0.75; y0(5) = 0.25; z0(5) = 0.75;
x0(6) = 0.25; y0(6) = 0.75; z0(6) = 0.75;
x0(7) = 0.75; y0(7) = 0.75; z0(7) = 0.75;
}
double n = 4.0*omega/M_PI;
double coeff = 16.0*omega*omega/M_PI/M_PI/M_PI;
double f_re = 0.0;
// for (int i = 0; i<1; i++)
for (int i = 0; i<nrsources; i++)
{
double beta = pow(x0(i)-x(0),2) + pow(y0(i)-x(1),2);
if (dim == 3) { beta += pow(z0(i)-x(2),2); }
double alpha = -pow(n,2) * beta;
f_re += coeff*exp(alpha);
}
bool in_pml = false;
for (int i = 0; i<dim; i++)
{
if (x(i)<=comp_bdr(i,0) || x(i)>=comp_bdr(i,1))
{
in_pml = true;
break;
}
}
if (in_pml) f_re = 0.0;
return f_re;
}
double f_exact_Im(const Vector &x)
{
double f_im;
f_im = 0.0;
return f_im;
}
double wavespeed(const Vector &x)
{
double ws;
ws = 1.0;
return ws;
}
double funccoeff_re(const Vector & x)
{
return sin(3*M_PI*(x.Sum()));
}
double funccoeff_im(const Vector & x)
{
return cos(10*M_PI*(x.Sum()));
}
+67
View File
@@ -0,0 +1,67 @@
# Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at the
# Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights reserved.
# See file COPYRIGHT for details.
#
# This file is part of the MFEM library. For more information and source code
# availability see http://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
SRC = $(if $(MFEM_DIR:../../..=),$(MFEM_DIR)/examples/maxwell-solver-dev/,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES =
PAR_EXAMPLES = helmholtzp maxwellp
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean
.PRECIOUS: %.o
COMMON_O= common/PML.o common/MeshPartition.o \
common/Utilities.o common/complex_linalg.o\
ParDST/ParDST.o ParDST/DofMapsDST.o
# Remove built-in rules
%: %.cpp
%.o: %.cpp
all: $(EXAMPLES)
# Rules for building the EXAMPLES
%: $(SRC)%.cpp $(COMMON_O) $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(COMMON_O) $(MFEM_LIBS)
# Rules for compiling miniapp dependencies
$(COMMON_O) $($(EXAMPLES)): \
%.o: $(SRC)%.cpp $(SRC)%.hpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $(<) -o $(@)
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -f DST/*.o
rm -f ParDST/*.o
rm -f common/*.o
rm -f DST2D/*.o
rm -rf *.dSYM *.TVD.*breakpoints
rm output/*
+535
View File
@@ -0,0 +1,535 @@
//
// Compile with: make maxwellp
//
// Sample runs: mpirun -np 4 ./maxwellp -nd 2 -nx 4 -ny 4 -sr 3 -pr 3 -k 16.0 -o 2
// mpirun -np 4 ./maxwellp -nd 3 -nx 2 -ny 2 -nz 2 -sr 3 -pr 1 -k 2.0 -o 2
//
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#include "ParDST/ParDST.hpp"
using namespace std;
using namespace mfem;
void source_re(const Vector &x, Vector & f);
void source_im(const Vector &x, Vector & f);
void exact_re(const Vector & x, Vector & E);
void exact_im(const Vector & x, Vector & E);
void maxwell_solution(const Vector & x, double E[], double curl2E[]);
double wavespeed(const Vector &x);
void Mwavespeed(const Vector & x, DenseMatrix & M);
void ess_data_func(const Vector & x, Vector & E);
double mu = 1.0;
double epsilon = 1.0;
double omega;
int dim;
double length = 1.0;
Array2D<double> comp_bdr;
Array2D<double> domain_bdr;
bool exact_known = false;
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
int order = 1;
// number of serial refinements
int ser_ref_levels = 1;
// number of parallel refinements
int par_ref_levels = 2;
// number of wavelengths
double k = 5.0; //
bool herm_conv = true;
bool visualization = 1;
int nd=2;
int nx=2;
int ny=2;
int nz=2;
OptionsParser args(argc, argv);
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&nd, "-nd", "--dim","Problem space dimension");
args.AddOption(&nx, "-nx", "--nx","Number of subdomains in x direction");
args.AddOption(&ny, "-ny", "--ny","Number of subdomains in y direction");
args.AddOption(&nz, "-nz", "--nz","Number of subdomains in z direction");
args.AddOption(&ser_ref_levels, "-sr", "--ser_ref_levels",
"Number of Serial Refinements.");
args.AddOption(&par_ref_levels, "-pr", "--par_ref_levels",
"Number of Parallel Refinements.");
args.AddOption(&mu, "-mu", "--permeability",
"Permeability of free space (or 1/(spring constant)).");
args.AddOption(&epsilon, "-eps", "--permittivity",
"Permittivity of free space (or mass constant).");
args.AddOption(&k, "-k", "--wavelengths",
"Number of wavelengths.");
args.AddOption(&herm_conv, "-herm", "--hermitian", "-no-herm",
"--no-hermitian", "Use convention for Hermitian operators.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
// check if the inputs are correct
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// Angular frequency
omega = 2.0 * M_PI * k;
Mesh *mesh;
int nel = 1;
if (nd == 2)
{
mesh = new Mesh(nel, nel, Element::QUADRILATERAL, true, length, length, false);
}
else
{
mesh = new Mesh(nel, nel, nel, Element::HEXAHEDRON, true, length, length, length,false);
}
dim = mesh->Dimension();
// 4. Refine the mesh to increase the resolution.
for (int l = 0; l < ser_ref_levels; l++)
{
mesh->UniformRefinement();
}
// 4. Define a parallel mesh by a partitioning of the serial mesh.
// ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
int nprocs;
int nprocsx;
int nprocsy;
int nprocsz;
if (dim == 2)
{
nprocs = sqrt(num_procs);
nprocsx = nprocs;
nprocsy = nprocs;
nprocsz = 1;
}
else
{
nprocs = cbrt(num_procs);
nprocsx = nprocs;
nprocsy = nprocs;
nprocsz = nprocs;
}
int nxyz[3] = {nprocsx,nprocsy,nprocsz};
int * part = mesh->CartesianPartitioning(nxyz);
// ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD,*mesh,part);
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD,*mesh);
delete [] part;
delete mesh;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh->UniformRefinement();
}
double hl = GetUniformMeshElementSize(pmesh);
int nrlayers = 3;
Array2D<double> lengths(dim,2);
lengths = hl*nrlayers;
// lengths[0][1] = 0.0;
// lengths[1][1] = 0.0;
// lengths[1][0] = 0.0;
// lengths[0][0] = 0.0;
if (exact_known) lengths = 0.0;
// CartesianPML pml(mesh,lengths);
CartesianPML pml(pmesh,lengths);
pml.SetOmega(omega);
comp_bdr.SetSize(dim,2);
comp_bdr = pml.GetCompDomainBdr();
// 6. Define a finite element space on the mesh. Here we use the Nedelec
// finite elements of the specified order.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 7. Determine the list of true essential boundary dofs. In this example,
// the boundary conditions are defined based on the specific mesh and the
// problem type.
Array<int> ess_tdof_list;
if (pmesh->bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
ess_bdr = 1;
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 8. Setup Complex Operator convention
ComplexOperator::Convention conv =
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
// 9. Set up the linear form b(.) which corresponds to the right-hand side of
// the FEM linear system.
VectorFunctionCoefficient f_re(dim, source_re);
VectorFunctionCoefficient f_im(dim, source_re);
ParComplexLinearForm b(fespace, conv);
b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(f_re),
new VectorFEDomainLFIntegrator(f_im));
b.Vector::operator=(0.0);
b.Assemble();
// 10. Define the solution vector x as a complex finite element grid function
// corresponding to fespace.
ParComplexGridFunction x(fespace);
x = 0.0;
// VectorFunctionCoefficient done(dim,ess_data_func);
// x.ProjectCoefficient(done,done);
VectorFunctionCoefficient E_re(dim,exact_re);
VectorFunctionCoefficient E_im(dim,exact_re);
if (exact_known)
{
x.ProjectCoefficient(E_re,E_re);
}
// 11. Set up the sesquilinear form a(.,.)
//
// 1/mu (1/det(J) J^T J Curl E, Curl F)
// - omega^2 * epsilon (det(J) * (J^T J)^-1 * E, F)
//
FunctionCoefficient ws(wavespeed);
// MatrixFunctionCoefficient Mws(dim,Mwavespeed);
// DenseMatrix M(dim); M = 0.0;
// M(0,0) = -pow(omega, 2);
// M(1,1) = -pow(omega, 2);
// M(2,2) = -pow(omega, 2);
// MatrixConstantCoefficient Momeg(M);
MatrixFunctionCoefficient eps_func(dim,Mwavespeed);
ConstantCoefficient omeg(-pow(omega, 2));
int cdim = (dim == 2) ? 1 : dim;
PmlMatrixCoefficient pml_c1_Re(cdim,detJ_inv_JT_J_Re, &pml);
PmlMatrixCoefficient pml_c1_Im(cdim,detJ_inv_JT_J_Im, &pml);
PmlMatrixCoefficient pml_c2_Re(dim, detJ_JT_J_inv_Re,&pml);
PmlMatrixCoefficient pml_c2_Im(dim, detJ_JT_J_inv_Im,&pml);
ScalarMatrixProductCoefficient c2_Re0(omeg,pml_c2_Re);
ScalarMatrixProductCoefficient c2_Im0(omeg,pml_c2_Im);
// ScalarMatrixProductCoefficient c2_Re(ws,c2_Re0);
// ScalarMatrixProductCoefficient c2_Im(ws,c2_Im0);
MatrixMatrixProductCoefficient c2_Re(c2_Re0,eps_func);
MatrixMatrixProductCoefficient c2_Im(c2_Im0,eps_func);
// MatrixMatrixProductCoefficient c2_Re0(Momeg,pml_c2_Re);
// MatrixMatrixProductCoefficient c2_Im0(Momeg,pml_c2_Im);
// MatrixMatrixProductCoefficient c2_Re(Mws,c2_Re0);
// MatrixMatrixProductCoefficient c2_Im(Mws,c2_Im0);
ParSesquilinearForm a(fespace, conv);
a.AddDomainIntegrator(new CurlCurlIntegrator(pml_c1_Re),
new CurlCurlIntegrator(pml_c1_Im));
a.AddDomainIntegrator(new VectorFEMassIntegrator(c2_Re),
new VectorFEMassIntegrator(c2_Im));
a.Assemble(0);
OperatorHandle Ah;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, x, b, Ah, X, B);
ComplexSparseMatrix * Ac = Ah.As<ComplexSparseMatrix>();
StopWatch chrono;
chrono.Clear();
chrono.Start();
ParDST * S = new ParDST(&a,lengths, omega, &ws, nrlayers, nx, ny, nz);
chrono.Stop();
double t1 = chrono.RealTime();
chrono.Clear();
chrono.Start();
// X = 0.0;
GMRESSolver gmres(MPI_COMM_WORLD);
// gmres.iterative_mode = true;
gmres.SetPreconditioner(*S);
gmres.SetOperator(*Ac);
gmres.SetRelTol(1e-8);
gmres.SetMaxIter(100);
gmres.SetPrintLevel(1);
gmres.Mult(B, X);
delete S;
chrono.Stop();
double t2 = chrono.RealTime();
MPI_Barrier(MPI_COMM_WORLD);
cout << " myid: " << myid
<< ", setup time: " << t1
<< ", solution time: " << t2 << endl;
a.RecoverFEMSolution(X, b, x);
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
string keys;
if (dim ==2 )
{
keys = "keys mrRljc\n";
}
else
{
keys = "keys mc\n";
}
socketstream sol_sock_re(vishost, visport);
sol_sock_re.precision(8);
sol_sock_re << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh << x.real() << keys
<< "window_title 'E: Real Part' " << flush;
socketstream sol_sock_im(vishost, visport);
sol_sock_im.precision(8);
sol_sock_im << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh << x.imag() << keys
<< "window_title 'E: Imag Part' " << flush;
{
ParGridFunction x_t(fespace);
x_t = x.real();
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh << x_t << keys << "autoscale off\n"
<< "window_title 'Harmonic Solution (t = 0.0 T)'"
<< "pause\n" << flush;
if (myid == 0)
{
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
}
int num_frames = 32;
int i = 0;
while (sol_sock)
{
double t = (double)(i % num_frames) / num_frames;
ostringstream oss;
oss << "Harmonic Solution (t = " << t << " T)";
add(cos(2.0*M_PI*t), x.real(), sin(2.0*M_PI*t), x.imag(), x_t);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock << "solution\n" << *pmesh << x_t
<< "window_title '" << oss.str() << "'" << flush;
i++;
}
}
}
// 18. Free the used memory.
delete fespace;
delete fec;
delete pmesh;
MPI_Finalize();
return 0;
}
void source_re(const Vector &x, Vector &f)
{
f = 0.0;
if (exact_known)
{
double E[3], curl2E[3];
maxwell_solution(x, E, curl2E);
// curl ( curl E) +/- omega^2 E = f
double coeff = -omega * omega;
f(0) = curl2E[0] + coeff * E[0];
f(1) = curl2E[1] + coeff * E[1];
if (dim == 2)
{
if (x.Size() == 3) {f(2)=0.0;}
}
else
{
f(2) = curl2E[2] + coeff * E[2];
}
}
else
{
int nrsources = (dim == 2) ? 4 : 8;
Vector x0(nrsources);
Vector y0(nrsources);
Vector z0(nrsources);
x0(0) = 0.25; y0(0) = 0.25; z0(0) = 0.25;
x0(1) = 0.75; y0(1) = 0.25; z0(1) = 0.25;
x0(2) = 0.25; y0(2) = 0.75; z0(2) = 0.25;
x0(3) = 0.75; y0(3) = 0.75; z0(3) = 0.25;
if (dim == 3)
{
x0(4) = 0.25; y0(4) = 0.25; z0(4) = 0.75;
x0(5) = 0.75; y0(5) = 0.25; z0(5) = 0.75;
x0(6) = 0.25; y0(6) = 0.75; z0(6) = 0.75;
x0(7) = 0.75; y0(7) = 0.75; z0(7) = 0.75;
}
double n = 4.0*omega/M_PI;
double coeff = 16.0*omega*omega/M_PI/M_PI/M_PI;
for (int i = 0; i<nrsources; i++)
{
double beta = pow(x0(i)-x(0),2) + pow(y0(i)-x(1),2);
if (dim == 3) { beta += pow(z0(i)-x(2),2); }
double alpha = -pow(n,2) * beta;
f[0] += coeff*exp(alpha);
}
bool in_pml = false;
for (int i = 0; i<dim; i++)
{
if (x(i)<=comp_bdr(i,0) || x(i)>=comp_bdr(i,1))
{
in_pml = true;
break;
}
}
if (in_pml) f = 0.0;
}
}
void source_im(const Vector &x, Vector &f)
{
f = 0.0;
}
double wavespeed(const Vector &x)
{
double ws;
ws = 1.0;
return ws;
}
void Mwavespeed(const Vector & x, DenseMatrix & M)
{
M = 0.0;
M(0,0) = 1.0;
M(1,1) = 1.0;
// M(2,2) = 4.0*x(0)-1.0;
if (dim == 3) M(2,2) = 1.0;
}
void exact_re(const Vector & x, Vector & E)
{
double curl2E[3];
maxwell_solution(x, E, curl2E);
}
void exact_im(const Vector & x, Vector & E)
{
// double curl2E[3];
// maxwell_solution(x, E, curl2E);
E = 0.0;
}
void maxwell_solution(const Vector & x, double E[], double curl2E[])
{
// point source
if (dim == 2)
{
// shift to avoid singularity
double x0 = x(0) + 0.1;
double x1 = x(1) + 0.1;
//
double r = sqrt(x0 * x0 + x1 * x1);
E[0] = cos(omega * r);
E[1] = 0.0;
double r_x = x0 / r;
double r_y = x1 / r;
double r_xy = -(r_x / r) * r_y;
double r_yx = r_xy;
double r_yy = (1.0 / r) * (1.0 - r_y * r_y);
curl2E[0] = omega * ((r_yy ) * sin(omega * r) + (omega * r_y * r_y) * cos(omega * r));
curl2E[1] = -omega * (r_yx * sin(omega * r) + omega * r_y * r_x * cos(omega * r));
curl2E[2] = 0.0;
}
else
{
// shift to avoid singularity
double x0 = x(0) + 0.1;
double x1 = x(1) + 0.1;
double x2 = x(2) + 0.1;
//
double r = sqrt(x0 * x0 + x1 * x1 + x2 * x2);
E[0] = cos(omega * r);
E[1] = 0.0;
E[2] = 0.0;
double r_x = x0 / r;
double r_y = x1 / r;
double r_z = x2 / r;
double r_xy = -(r_x / r) * r_y;
double r_xz = -(r_x / r) * r_z;
double r_yx = r_xy;
double r_yy = (1.0 / r) * (1.0 - r_y * r_y);
double r_zx = r_xz;
double r_zz = (1.0 / r) * (1.0 - r_z * r_z);
curl2E[0] = omega * ((r_yy + r_zz) * sin(omega * r) +
(omega * r_y * r_y + omega * r_z * r_z) * cos(omega * r));
curl2E[1] = -omega * (r_yx * sin(omega * r) + omega * r_y * r_x * cos(omega * r));
curl2E[2] = -omega * (r_zx * sin(omega * r) + omega * r_z * r_x * cos(omega * r));
}
}
void ess_data_func(const Vector & x, Vector & E)
{
E = 0.0;
if (x(1)==0.0) E[0] = sin(x(0)+x(1));
bool in_pml = false;
for (int i = 0; i<dim; i++)
{
if (x(i)<comp_bdr(i,0) || x(i)>comp_bdr(i,1))
{
in_pml = true;
break;
}
}
if (in_pml) E = 0.0;
}
+21
View File
@@ -629,6 +629,27 @@ void MatrixVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T,
ma.Mult(vb, V);
}
MatrixMatrixProductCoefficient::MatrixMatrixProductCoefficient(MatrixCoefficient &A,
MatrixCoefficient &B)
: MatrixCoefficient(A.GetHeight(), A.GetWidth()),
a(&A), b(&B),
ma(A.GetHeight(), A.GetWidth()),
mb(B.GetHeight(), B.GetWidth())
{
MFEM_ASSERT(A.GetWidth() == B.GetHeight(),
"MatrixMatrixProductCoefficient: "
"Arguments must have the same dimensions.");
}
void MatrixMatrixProductCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip)
{
a->Eval(ma, T, ip);
b->Eval(mb, T, ip);
Mult(ma, mb, M);
}
void IdentityMatrixCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip)
{
+36
View File
@@ -1470,6 +1470,42 @@ public:
const IntegrationPoint &ip);
};
/** @brief Matrix coefficient defined as a product of two
matrix coefficients */
class MatrixMatrixProductCoefficient : public MatrixCoefficient
{
private:
MatrixCoefficient * a;
MatrixCoefficient * b;
mutable DenseMatrix ma;
mutable DenseMatrix mb;
public:
/// Constructor with two coefficients. Result is A*B.
MatrixMatrixProductCoefficient(MatrixCoefficient &A, MatrixCoefficient &B);
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
/// Return the matrix coefficient
MatrixCoefficient * GetACoef() const { return a; }
/// Reset the vector coefficient
void SetBCoef(MatrixCoefficient &B) { b = &B; }
/// Return the vector coefficient
MatrixCoefficient * GetBCoef() const { return b; }
/// Evaluate the vector coefficient at @a ip.
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
/// Convenient alias for the MatrixVectorProductCoefficient
typedef MatrixMatrixProductCoefficient MatMatCoefficient;
/// Matrix coefficient defined as the linear combination of two matrices
class MatrixSumCoefficient : public MatrixCoefficient
{