This commit is contained in:
Markus Schmidt 2026-01-06 14:05:44 +01:00
commit 4c92446409
74 changed files with 169041 additions and 0 deletions

View file

@ -0,0 +1,17 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/lib/x86_64-linux-gnu/openmpi/include"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}

2877
sheet7/E1234/Doxyfile Normal file

File diff suppressed because it is too large Load diff

23
sheet7/E1234/Makefile Normal file
View file

@ -0,0 +1,23 @@
#
# Compile with
# make 2>&1 | grep -v openmpi
# to avoid warnings caused by OpenMPI
# use GNU-Compiler tools
COMPILER=GCC_
# alternatively from the shell
# export COMPILER=GCC_
# or, alternatively from the shell
# make COMPILER=GCC_
MAIN = main
SOURCES = ${MAIN}.cpp greetings.cpp
OBJECTS = $(SOURCES:.cpp=.o)
PROGRAM = ${MAIN}.${COMPILER}
# uncomment the next to lines for debugging and detailed performance analysis
CXXFLAGS += -g
LINKFLAGS +=
include ../${COMPILER}default.mk

View file

@ -0,0 +1,85 @@
#include "greetings.h"
#include <cassert>
#include <cstring>
#include <iostream>
#include <mpi.h> // MPI
#include <string>
using namespace std;
// see http://www.open-mpi.org/doc/current
// for details on MPI functions
void greetings(MPI_Comm const &icomm)
{
int myrank, numprocs;
MPI_Comm_rank(icomm, &myrank); // my MPI-rank
MPI_Comm_size(icomm, &numprocs); // #MPI processes
char *name = new char [MPI_MAX_PROCESSOR_NAME],
*chbuf = new char [MPI_MAX_PROCESSOR_NAME];
int reslen, ierr;
MPI_Get_processor_name( name, &reslen);
if (0==myrank) {
cout << " " << myrank << " runs on " << name << endl;
for (int i = 1; i < numprocs; ++i) {
MPI_Status stat;
stat.MPI_ERROR = 0; // M U S T be initialized!!
ierr = MPI_Recv(chbuf, MPI_MAX_PROCESSOR_NAME, MPI_CHAR, i, MPI_ANY_TAG, icomm, &stat);
assert(0==ierr);
cout << " " << stat.MPI_SOURCE << " runs on " << chbuf;
int count;
MPI_Get_count(&stat, MPI_CHAR, &count); // size of received data
cout << " (length: " << count << " )" << endl;
// stat.Get_error() // Error code
}
}
else {
int dest = 0;
ierr = MPI_Send(name, strlen(name) + 1, MPI_CHAR, dest, myrank, icomm);
assert(0==ierr);
}
delete [] chbuf;
delete [] name;
return;
}
void greetings_cpp(MPI_Comm const &icomm)
{
int myrank, numprocs;
MPI_Comm_rank(icomm, &myrank); // my MPI-rank
MPI_Comm_size(icomm, &numprocs); // #MPI processes
string name(MPI_MAX_PROCESSOR_NAME,'#'), // C++
recvbuf(MPI_MAX_PROCESSOR_NAME,'#'); // C++: receive buffer, don't change size
int reslen, ierr;
MPI_Get_processor_name(name.data(), &reslen);
name.resize(reslen); // C++
if (0==myrank) {
cout << " " << myrank << " runs on " << name << endl;
for (int i = 1; i < numprocs; ++i) {
MPI_Status stat;
stat.MPI_ERROR = 0; // M U S T be initialized!!
ierr = MPI_Recv(recvbuf.data(), MPI_MAX_PROCESSOR_NAME, MPI_CHAR, i, MPI_ANY_TAG, icomm, &stat);
assert(0==ierr);
int count;
MPI_Get_count(&stat, MPI_CHAR, &count); // size of received data
string const chbuf(recvbuf,0,count); // C++
cout << " " << stat.MPI_SOURCE << " runs on " << chbuf;
cout << " (length: " << count << " )" << endl;
// stat.Get_error() // Error code
}
}
else {
int dest = 0;
ierr = MPI_Send(name.data(), name.size(), MPI_CHAR, dest, myrank, icomm);
assert(0==ierr);
}
return;
}

16
sheet7/E1234/greetings.h Normal file
View file

@ -0,0 +1,16 @@
// general header for all functions in directory
#ifndef GREETINGS_FILE
#define GREETINGS_FILE
#include <mpi.h>
/** Each process finds out its host, sends this information
to root process 0 which prints this information for each process.
@param[in] icomm the MPI process group that is used.
*/
void greetings(MPI_Comm const &icomm);
void greetings_cpp(MPI_Comm const &icomm);
#endif

33
sheet7/E1234/main.cpp Normal file
View file

@ -0,0 +1,33 @@
// MPI code in C++.
// See [Gropp/Lusk/Skjellum, "Using MPI", p.33/41 etc.]
// and /opt/mpich/include/mpi2c++/comm.h for details
#include "greetings.h"
#include <iostream> // MPI
#include <mpi.h>
using namespace std;
int main(int argc, char *argv[])
{
MPI_Comm icomm = MPI_COMM_WORLD;
MPI_Init(&argc, &argv);
int myrank, numprocs;
//numprocs = 1; // delete this line when uncommenting the next line
MPI_Comm_rank(icomm, &myrank); // my MPI-rank
MPI_Comm_size(icomm, &numprocs);
if (0==myrank) {
cout << "\n There are " << numprocs << " processes running.\n \n";
}
greetings(icomm);
greetings_cpp(icomm);
if (0==myrank) cout << endl;
MPI_Finalize();
return 0;
}

View file

@ -0,0 +1,17 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/lib/x86_64-linux-gnu/openmpi/include"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}

23
sheet7/E5678/Makefile Normal file
View file

@ -0,0 +1,23 @@
#
# Compile with
# make 2>&1 | grep -v openmpi
# to avoid warnings caused by OpenMPI
# use GNU-Compiler tools
COMPILER=GCC_
# alternatively from the shell
# export COMPILER=GCC_
# or, alternatively from the shell
# make COMPILER=GCC_
MAIN = main
SOURCES = ${MAIN}.cpp
OBJECTS = $(SOURCES:.cpp=.o)
PROGRAM = ${MAIN}.${COMPILER}
# uncomment the next to lines for debugging and detailed performance analysis
CXXFLAGS += -g
LINKFLAGS +=
include ../${COMPILER}default.mk

136
sheet7/E5678/main.cpp Normal file
View file

@ -0,0 +1,136 @@
#include <iostream> // MPI
#include <mpi.h>
#include <vector>
using namespace std;
void DebugVector(vector<double> const& xin, MPI_Comm const& icomm)
{
int myrank, numprocs;
MPI_Comm_rank(icomm, &myrank);
MPI_Comm_size(icomm, &numprocs);
int selectedProcess = 0;
while(selectedProcess >= 0 && selectedProcess < numprocs)
{
if(myrank == 0)
{
cout << "Enter process number: " << endl;
cin >> selectedProcess;
}
MPI_Bcast(&selectedProcess, 1, MPI_INT, 0, icomm);
MPI_Barrier(icomm);
if(myrank == selectedProcess )
{
for(double x : xin)
{
cout << x << " ";
}
cout << endl;
}
MPI_Barrier(icomm);
}
}
double par_scalar(const vector<double>& x, const vector<double>& y, MPI_Comm icomm) {
double local = 0.0;
for (unsigned int i = 0; i < x.size(); ++i) {
local += x[i] * y[i];
}
double sum = 0.0;
MPI_Allreduce(&local, &sum, 1, MPI_DOUBLE, MPI_SUM, icomm);
return sum;
}
void E7(const vector<double>& x, MPI_Comm comm,
double& min, double& max)
{
double local_min = x[0];
double local_max = x[0];
for (double x_i : x) {
if (x_i < local_min)
{
local_min = x_i;
}
if (x_i> local_max)
{
local_max = x_i;
}
}
MPI_Allreduce(&local_min, &min, 1, MPI_DOUBLE, MPI_MIN, comm);
MPI_Allreduce(&local_max, &max, 1, MPI_DOUBLE, MPI_MAX, comm);
}
int main(int argc, char *argv[])
{
MPI_Comm icomm = MPI_COMM_WORLD;
MPI_Init(&argc, &argv);
int myrank, numprocs;
//numprocs = 1; // delete this line when uncommenting the next line
MPI_Comm_rank(icomm, &myrank); // my MPI-rank
MPI_Comm_size(icomm, &numprocs);
if (0==myrank) {
cout << "\n There are " << numprocs << " processes running.\n \n";
}
if (0==myrank) cout << endl;
unsigned int n=20;
vector<double> x(n);
vector<double> y(n);
double x_i;
for(unsigned int i=0; i<n; i++)
{
x_i = myrank*100+(i%5)*10+i;
x[i] = x_i;
if(myrank == 0 && i == 0)
{
y[i] = 0.0;
}
else{
y[i] = 1.0/x_i;
}
}
DebugVector(x,icomm);
double result = par_scalar(x,y,icomm);
if(myrank == 0)
{
cout << "result scalar: " << result << endl;
}
double min, max;
E7(x,icomm,min,max);
if(myrank == 0)
{
cout << "min: " << min << ", max: "<< max << endl;
}
MPI_Alltoall(MPI_IN_PLACE, 0, MPI_DOUBLE, x.data(), 5, MPI_DOUBLE, icomm);
DebugVector(x,icomm);
MPI_Finalize();
return 0;
}

2877
sheet7/E910111213/Doxyfile Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,54 @@
#
# use GNU-Compiler tools
COMPILER=GCC_
# COMPILER=GCC_SEQ_
# alternatively from the shell
# export COMPILER=GCC_
# or, alternatively from the shell
# make COMPILER=GCC_
MAIN = main
SOURCES = ${MAIN}.cpp vdop.cpp geom.cpp par_geom.cpp
OBJECTS = $(SOURCES:.cpp=.o)
PROGRAM = ${MAIN}.${COMPILER}
# uncomment the next to lines for debugging and detailed performance analysis
CXXFLAGS += -g
# -DNDEBUG
# -pg slows down the code on my laptop when using CLANG_
LINKFLAGS += -g
#-pg
#CXXFLAGS += -Q --help=optimizers
#CXXFLAGS += -fopt-info
include ../${COMPILER}default.mk
#############################################################################
# additional specific cleaning in this directory
clean_all::
@rm -f uv.txt
#############################################################################
# special testing
# NPROCS = 4
#
TFILE = t.dat
# TTMP = t.tmp
#
graph: $(PROGRAM)
# @rm -f $(TFILE).*
# next two lines only sequentially
./$(PROGRAM)
@mv $(TFILE).000 $(TFILE)
# $(MPIRUN) $(MPIFLAGS) -np $(NPROCS) $(PROGRAM)
# @echo " "; echo "Manipulate data for graphics."; echo " "
# @cat $(TFILE).* > $(TTMP)
# @sort -b -k 2 $(TTMP) -o $(TTMP).1
# @sort -b -k 1 $(TTMP).1 -o $(TTMP).2
# @awk -f nl.awk $(TTMP).2 > $(TFILE)
# @rm -f $(TTMP).* $(TTMP) $(TFILE).*
#
-gnuplot jac.dem

View file

@ -0,0 +1,43 @@
function [ xc, ia, v ] = ascii_read_meshvector( fname )
%
% Loads the 2D triangular mesh (coordinates, vertex connectivity)
% together with values on its vertices from an ASCII file.
% Matlab indexing is stored (starts with 1).
%
% The input file format is compatible
% with Mesh_2d_3_matlab:Write_ascii_matlab(..) in jacobi_oo_stl/geom.h
%
%
% IN: fname - filename
% OUT: xc - coordinates
% ia - mesh connectivity
% v - solution vector
DELIMETER = ' ';
fprintf('Read file %s\n',fname)
% Read mesh constants
nn = dlmread(fname,DELIMETER,[0 0 0 3]); %% row_1, col_1, row_2, col_2 in C indexing!!!
nnode = nn(1);
ndim = nn(2);
nelem = nn(3);
nvert = nn(4);
% Read coordinates
row_start = 0+1;
row_end = 0+nnode;
xc = dlmread(fname,DELIMETER,[row_start 0 row_end ndim-1]);
% Read connectivity
row_start = row_end+1;
row_end = row_end+nelem;
ia = dlmread(fname,DELIMETER,[row_start 0 row_end nvert-1]);
% Read solution
row_start = row_end+1;
row_end = row_end+nnode;
v = dlmread(fname,DELIMETER,[row_start 0 row_end 0]);
end

View file

@ -0,0 +1,49 @@
function ascii_write_mesh( xc, ia, e, basename)
%
% Saves the 2D triangular mesh in the minimal way (only coordinates, vertex connectivity, minimal boundary edge info)
% in an ASCII file.
% Matlab indexing is stored (starts with 1).
%
% The output file format is compatible with Mesh_2d_3_matlab:Mesh_2d_3_matlab(std::string const &fname) in jacobi_oo_stl/geom.h
%
% IN:
% coordinates xc: [2][nnode]
% connectivity ia: [4][nelem] with t(4,:) are the subdomain numbers
% edges e: [7][nedges] boundary edges
% e([1,2],:) - start/end vertex of edge
% e([3,4],:) - start/end values
% e(5,:) - segment number
% e([6,7],:) - left/right subdomain
% basename: file name without extension
%
% Data have been generated via <https://de.mathworks.com/help/pde/ug/initmesh.html initmesh>.
%
fname = [basename, '.txt'];
nnode = int32(size(xc,2));
ndim = int32(size(xc,1));
nelem = int32(size(ia,2));
nvert_e = int32(3);
dlmwrite(fname,nnode,'delimiter','\t','precision',16) % number of nodes
dlmwrite(fname,ndim,'-append','delimiter','\t','precision',16) % space dimension
dlmwrite(fname,nelem,'-append','delimiter','\t','precision',16) % number of elements
dlmwrite(fname,nvert_e,'-append','delimiter','\t','precision',16) % number of vertices per element
% dlmwrite(fname,xc(:),'-append','delimiter','\t','precision',16) % coordinates
dlmwrite(fname,xc([1,2],:).','-append','delimiter','\t','precision',16) % coordinates
% no subdomain info transferred
tmp=int32(ia(1:3,:));
% dlmwrite(fname,tmp(:),'-append','delimiter','\t','precision',16) % connectivity in Matlab indexing
dlmwrite(fname,tmp(:,:).','-append','delimiter','\t','precision',16) % connectivity in Matlab indexing
% store only start and end point of boundary edges,
nbedges = size(e,2);
dlmwrite(fname,nbedges,'-append','delimiter','\t','precision',16) % number boundary edges
tmp=int32(e(1:2,:));
% dlmwrite(fname,tmp(:),'-append','delimiter','\t','precision',16) % boundary edges in Matlab indexing
dlmwrite(fname,tmp(:,:).','-append','delimiter','\t','precision',16) % boundary edges in Matlab indexing
end

View file

@ -0,0 +1,51 @@
function ascii_write_subdomains( xc, ia, e, basename)
%
% Saves the 2D triangular mesh in the minimal way (only coordinates, vertex connectivity, minimal boundary edge info)
% in an ASCII file.
% Matlab indexing is stored (starts with 1).
%
% The output file format is compatible with Mesh_2d_3_matlab:Mesh_2d_3_matlab(std::string const &fname) in jacobi_oo_stl/geom.h
%
% IN:
% coordinates xc: [2][nnode]
% connectivity ia: [4][nelem] with t(4,:) are the subdomain numbers
% edges e: [7][nedges] boundary edges
% e([1,2],:) - start/end vertex of edge
% e([3,4],:) - start/end values
% e(5,:) - segment number
% e([6,7],:) - left/right subdomain
% basename: file name without extension
%
% Data have been generated via <https://de.mathworks.com/help/pde/ug/initmesh.html initmesh>.
%
fname = [basename, '_sd.txt'];
nnode = int32(size(xc,2));
ndim = int32(size(xc,1));
nelem = int32(size(ia,2));
nvert_e = int32(3);
% dlmwrite(fname,nnode,'delimiter','\t','precision',16) % number of nodes
% dlmwrite(fname,ndim,'-append','delimiter','\t','precision',16) % space dimension
% dlmwrite(fname,nelem,'-append','delimiter','\t','precision',16) % number of elements
dlmwrite(fname,nelem,'delimiter','\t','precision',16) % number of elements
% dlmwrite(fname,nvert_e,'-append','delimiter','\t','precision',16) % number of vertices per element
% % dlmwrite(fname,xc(:),'-append','delimiter','\t','precision',16) % coordinates
% dlmwrite(fname,xc([1,2],:).','-append','delimiter','\t','precision',16) % coordinates
% subdomain info
tmp=int32(ia(4,:));
% % dlmwrite(fname,tmp(:),'-append','delimiter','\t','precision',16) % connectivity in Matlab indexing
% dlmwrite(fname,tmp(:,:).','-append','delimiter','\t','precision',16) % connectivity in Matlab indexing
dlmwrite(fname,tmp(:,:).','-append','delimiter','\t') % connectivity in Matlab indexing
% % store only start and end point of boundary edges,
% nbedges = size(e,2);
% dlmwrite(fname,nbedges,'-append','delimiter','\t','precision',16) % number boundary edges
% tmp=int32(e(1:2,:));
% % dlmwrite(fname,tmp(:),'-append','delimiter','\t','precision',16) % boundary edges in Matlab indexing
% dlmwrite(fname,tmp(:,:).','-append','delimiter','\t','precision',16) % boundary edges in Matlab indexing
end

1278
sheet7/E910111213/geom.cpp Normal file

File diff suppressed because it is too large Load diff

712
sheet7/E910111213/geom.h Normal file
View file

@ -0,0 +1,712 @@
#ifndef GEOM_FILE
#define GEOM_FILE
#include <array>
#include <functional> // function; C++11
#include <iostream>
#include <memory> // shared_ptr
#include <string>
#include <vector>
/**
* Basis class for finite element meshes.
*/
class Mesh
{
public:
/**
* Constructor initializing the members with default values.
*
* @param[in] ndim space dimensions (dimension for coordinates)
* @param[in] nvert_e number of vertices per element (dimension for connectivity)
* @param[in] ndof_e degrees of freedom per element (= @p nvert_e for linear elements)
* @param[in] nedge_e number of edges per element (= @p nvert_e for linear elements in 2D)
*/
explicit Mesh(int ndim, int nvert_e = 0, int ndof_e = 0, int nedge_e = 0);
__attribute__((noinline))
Mesh(Mesh const &) = default;
Mesh &operator=(Mesh const &) = delete;
/**
* Destructor.
*
* See clang warning on
* <a href="https://stackoverflow.com/questions/28786473/clang-no-out-of-line-virtual-method-definitions-pure-abstract-c-class/40550578">weak-vtables</a>.
*/
virtual ~Mesh();
/**
* Reads mesh data from a binary file.
*
* File format, see ascii_write_mesh.m
*
* @param[in] fname file name
*/
explicit Mesh(std::string const &fname);
/**
* Reads mesh data from a binary file.
*
* File format, see ascii_write_mesh.m
*
* @param[in] fname file name
*/
void ReadVertexBasedMesh(std::string const &fname);
/**
* Number of finite elements in (sub)domain.
* @return number of elements.
*/
int Nelems() const
{
return _nelem;
}
/**
* Global number of vertices for each finite element.
* @return number of vertices per element.
*/
int NverticesElements() const
{
return _nvert_e;
}
/**
* Global number of degrees of freedom (dof) for each finite element.
* @return degrees of freedom per element.
*/
int NdofsElement() const
{
return _ndof_e;
}
/**
* Number of vertices in mesh.
* @return number of vertices.
*/
int Nnodes() const
{
return _nnode;
}
/**
* Space dimension.
* @return number of dimensions.
*/
int Ndims() const
{
return _ndim;
}
/**
* (Re-)Allocates memory for the element connectivity and redefines the appropriate dimensions.
*
* @param[in] nelem number of elements
* @param[in] nvert_e number of vertices per element
*/
void Resize_Connectivity(int nelem, int nvert_e)
{
SetNelem(nelem); // number of elements
SetNverticesElement(nvert_e); // vertices per element
_ia.resize(nelem * nvert_e);
}
/**
* Read connectivity information (g1,g2,g3)_i.
* @return connectivity vector [nelems*ndofs].
*/
const std::vector<int> &GetConnectivity() const
{
return _ia;
}
/**
* Access/Change connectivity information (g1,g2,g3)_i.
* @return connectivity vector [nelems*ndofs].
*/
std::vector<int> &GetConnectivity()
{
return _ia;
}
/**
* (Re-)Allocates memory for the element connectivity and redefines the appropriate dimensions.
*
* @param[in] nnodes number of nodes
* @param[in] ndim space dimension
*/
void Resize_Coords(int nnodes, int ndim)
{
SetNnode(nnodes); // number of nodes
SetNdim(ndim); // space dimension
_xc.resize(nnodes * ndim);
}
/**
* Read coordinates of vertices (x,y)_i.
* @return coordinates vector [nnodes*2].
*/
const std::vector<double> &GetCoords() const
{
return _xc;
}
/**
* Access/Change coordinates of vertices (x,y)_i.
* @return coordinates vector [nnodes*2].
*/
std::vector<double> &GetCoords()
{
return _xc;
}
/**
* Calculate values in vector @p v via function @p func(x,y)
* @param[in] v vector
* @param[in] func function of (x,y) returning a double value.
*/
void SetValues(std::vector<double> &v, const std::function<double(double, double)> &func) const;
void SetBoundaryValues(std::vector<double> &v, const std::function<double(double, double)> &func) const;
void SetDirchletValues(std::vector<double> &v, const std::function<double(double, double)> &func) const;
/**
* Prints the information for a finite element mesh
*/
void Debug() const;
/**
* Prints the edge based information for a finite element mesh
*/
void DebugEdgeBased() const;
/**
* Determines the indices of those vertices with Dirichlet boundary conditions
* @return index vector.
*/
virtual std::vector<int> Index_DirichletNodes() const;
virtual std::vector<int> Index_BoundaryNodes() const;
/**
* Write vector @p v together with its mesh information to an ASCii file @p fname.
*
* The data are written in C-style.
*
* @param[in] fname file name
* @param[in] v vector
*/
void Write_ascii_matlab(std::string const &fname, std::vector<double> const &v) const;
/**
* Exports the mesh information to ASCii files @p basename + {_coords|_elements}.txt.
*
* The data are written in C-style.
*
* @param[in] basename first part of file names
*/
void Export_scicomp(std::string const &basename) const;
/**
* Visualize @p v together with its mesh information via matlab or octave.
*
* Comment/uncomment those code lines in method Mesh:Visualize (geom.cpp)
* that are supported on your system.
*
* @param[in] v vector
*
* @warning matlab files ascii_read_meshvector.m visualize_results.m
* must be in the executing directory.
*/
void Visualize(std::vector<double> const &v) const;
/**
* Global number of edges.
* @return number of edges in mesh.
*/
int Nedges() const
{
return _nedge;
}
/**
* Global number of edges for each finite element.
* @return number of edges per element.
*/
int NedgesElements() const
{
return _nedge_e;
}
/**
* Read edge connectivity information (e1,e2,e3)_i.
* @return edge connectivity vector [nelems*_nedge_e].
*/
const std::vector<int> &GetEdgeConnectivity() const
{
return _ea;
}
/**
* Access/Change edge connectivity information (e1,e2,e3)_i.
* @return edge connectivity vector [nelems*_nedge_e].
*/
std::vector<int> &GetEdgeConnectivity()
{
return _ea;
}
/**
* Read edge information (v1,v2)_i.
* @return edge connectivity vector [_nedge*2].
*/
const std::vector<int> &GetEdges() const
{
return _edges;
}
/**
* Access/Change edge information (v1,v2)_i.
* @return edge connectivity vector [_nedge*2].
*/
std::vector<int> &GetEdges()
{
return _edges;
}
/**
* Determines all node to node connections from the vertex based mesh.
*
* @return vector[k][] containing all connections of vertex k, including to itself.
*/
std::vector<std::vector<int>> Node2NodeGraph() const
{
//// Check version 2 wrt. version 1
//auto v1=Node2NodeGraph_1();
//auto v2=Node2NodeGraph_2();
//if ( equal(v1.cbegin(),v1.cend(),v2.begin()) )
//{
//std::cout << "\nidentical Versions\n";
//}
//else
//{
//std::cout << "\nE R R O R in Versions\n";
//}
//return Node2NodeGraph_1();
return Node2NodeGraph_2(); // 2 times faster than version 1
}
/**
* Accesses the father-of-nodes relation.
*
* @return vector of length 0 because no relation available.
*
*/
virtual std::vector<int> const &GetFathersOfVertices() const
{
return _dummy;
}
/**
* Deletes all edge connectivity information (saves memory).
*/
void Del_EdgeConnectivity();
protected:
//public:
void SetNelem(int nelem)
{
_nelem = nelem;
}
void SetNverticesElement(int nvert)
{
_nvert_e = nvert;
}
void SetNdofsElement(int ndof)
{
_ndof_e = ndof;
}
void SetNnode(int nnode)
{
_nnode = nnode;
}
void SetNdim(int ndim)
{
_ndim = ndim;
}
void SetNedge(int nedge)
{
_nedge = nedge;
}
/**
* Reads vertex based mesh data from a binary file.
*
* File format, see ascii_write_mesh.m
*
* @param[in] fname file name
*/
void ReadVectexBasedMesh(std::string const &fname);
/**
* The vertex based mesh data are used to derive the edge based data.
*
* @warning Exactly 3 vertices, 3 edges per element are assumed (linear triangle in 2D)
*/
void DeriveEdgeFromVertexBased()
{
//DeriveEdgeFromVertexBased_slow();
//DeriveEdgeFromVertexBased_fast();
DeriveEdgeFromVertexBased_fast_2();
}
void DeriveEdgeFromVertexBased_slow();
void DeriveEdgeFromVertexBased_fast();
void DeriveEdgeFromVertexBased_fast_2();
/**
* The edge based mesh data are used to derive the vertex based data.
*
* @warning Exactly 3 vertices, 3 edges per element are assumed (linear triangle in 2D)
*/
void DeriveVertexFromEdgeBased();
/**
* Determines the indices of those vertices with Dirichlet boundary conditions
* @return index vector.
*/
int Nnbedges() const
{
return static_cast<int>(_bedges.size());
}
/**
* Checks whether the array dimensions fit to their appropriate size parameters
* @return index vector.
*/
virtual bool Check_array_dimensions() const;
/**
* Permutes the vertex information in an edge based mesh.
*
* @param[in] old2new new indices of original vertices.
*/
void PermuteVertices_EdgeBased(std::vector<int> const &old2new);
private:
/**
* Determines all node to node connections from the vertex based mesh.
*
* @return vector[k][] containing all connections of vertex k, including to itself.
*/
std::vector<std::vector<int>> Node2NodeGraph_1() const; // is correct
/**
* Determines all node to node connections from the vertex based mesh.
*
* Faster than @p Node2NodeGraph_1().
*
* @return vector[k][] containing all connections of vertex k, including to itself.
*/
std::vector<std::vector<int>> Node2NodeGraph_2() const; // is correct
//private:
protected:
int _nelem; //!< number elements
int _nvert_e; //!< number of vertices per element
int _ndof_e; //!< degrees of freedom (d.o.f.) per element
int _nnode; //!< number nodes/vertices
int _ndim; //!< space dimension of the problem (1, 2, or 3)
std::vector<int> _ia; //!< element connectivity
std::vector<double> _xc; //!< coordinates
protected:
// B.C.
std::vector<int> _bedges; //!< boundary edges [nbedges][2] storing start/end vertex
// 2020-01-08
std::vector<int> _sdedges; //!< boundary edges [nbedges][2] with left/right subdomain number
//private:
protected:
// edge based connectivity
int _nedge; //!< number of edges in mesh
int _nedge_e; //!< number of edges per element
std::vector<int> _edges; //!< edges of mesh (vertices ordered ascending)
std::vector<int> _ea; //!< edge based element connectivity
// B.C.
std::vector<int> _ebedges; //!< boundary edges [nbedges]
private:
const std::vector<int> _dummy; //!< empty dummy vector
};
// *********************************************************************
class RefinedMesh: public Mesh
{
public:
/**
* Constructs a refined mesh according to the marked elements in @p ibref.
*
* If the vector @p ibref has size 0 then all elements will be refined.
*
* @param[in] cmesh original mesh for coarsening.
* @param[in] ibref vector containing True/False regarding refinement for each element
*
*/
//explicit RefinedMesh(Mesh const &cmesh, std::vector<bool> const &ibref = std::vector<bool>(0));
RefinedMesh(Mesh const &cmesh, std::vector<bool> const &ibref);
//RefinedMesh(Mesh const &cmesh, std::vector<bool> const &ibref);
/**
* Constructs a refined mesh by regulare refinement of all elements.
*
* @param[in] cmesh original mesh for coarsening.
*
*/
explicit RefinedMesh(Mesh const &cmesh)
: RefinedMesh(cmesh, std::vector<bool>(0))
{}
RefinedMesh(RefinedMesh const &) = delete;
//RefinedMesh(RefinedMesh const&&) = delete;
RefinedMesh &operator=(RefinedMesh const &) = delete;
//RefinedMesh& operator=(RefinedMesh const&&) = delete;
/**
* Destructor.
*/
virtual ~RefinedMesh() override;
/**
* Refines the mesh according to the marked elements.
*
* @param[in] ibref vector containing True/False regarding refinement for each element
*
* @return the refined mesh
*
*/
Mesh RefineElements(std::vector<bool> const &ibref);
/**
* Refines all elements in the actual mesh.
*
* @param[in] nref number of regular refinements to perform
*
*/
void RefineAllElements(int nref = 1);
/**
* Accesses the father-of-nodes relation.
*
* @return father-of-nodes relation [nnodes][2]
*
*/
std::vector<int> const &GetFathersOfVertices() const override
{
return _vfathers;
}
protected:
/**
* Checks whether the array dimensions fit to their appropriate size parameters
* @return index vector.
*/
bool Check_array_dimensions() const override;
/**
* Permutes the vertex information in an edge based mesh.
*
* @param[in] old2new new indices of original vertices.
*/
void PermuteVertices_EdgeBased(std::vector<int> const &old2new);
private:
//Mesh const & _cmesh; //!< coarse mesh
std::vector<bool> const _ibref; //!< refinement info
int _nref; //!< number of regular refinements performed
std::vector<int> _vfathers; //!< stores the 2 fathers of each vertex (equal fathers denote original coarse vertex)
};
// *********************************************************************
class gMesh_Hierarchy
{
public:
/**
* Constructs mesh hierarchy of @p nlevel levels starting with coarse mesh @p cmesh.
* The coarse mesh @p cmesh will be @p nlevel-1 times geometrically refined.
*
* @param[in] cmesh initial coarse mesh
* @param[in] nlevel number levels in mesh hierarchy
*
*/
gMesh_Hierarchy(Mesh const &cmesh, int nlevel);
size_t size() const
{
return _gmesh.size();
}
/**
* Access to mesh @p lev from mesh hierarchy.
*
* @return mesh @p lev
* @warning An out_of_range exception might be thrown.
*
*/
Mesh const &operator[](int lev) const
{
return *_gmesh.at(lev);
}
/**
* Access to finest mesh in mesh hierarchy.
*
* @return finest mesh
*
*/
Mesh const &finest() const
{
return *_gmesh.back();
}
/**
* Access to coarest mesh in mesh hierarchy.
*
* @return coarsest mesh
*
*/
Mesh const &coarsest() const
{
return *_gmesh.front();
}
private:
std::vector<std::shared_ptr<Mesh>> _gmesh; //!< mesh hierarchy from coarse ([0]) to fine.
};
// *********************************************************************
/**
* 2D finite element mesh of the square consisting of linear triangular elements.
*/
class Mesh_2d_3_square: public Mesh
{
public:
/**
* Generates the f.e. mesh for the unit square.
*
* @param[in] nx number of discretization intervals in x-direction
* @param[in] ny number of discretization intervals in y-direction
* @param[in] myid my MPI-rank / subdomain
* @param[in] procx number of ranks/subdomains in x-direction
* @param[in] procy number of processes in y-direction
*/
Mesh_2d_3_square(int nx, int ny, int myid = 0, int procx = 1, int procy = 1);
/**
* Destructor
*/
~Mesh_2d_3_square() override;
/**
* Set solution vector based on a tensor product grid in the rectangle.
* @param[in] u solution vector
*/
void SetU(std::vector<double> &u) const;
/**
* Set right hand side (rhs) vector on a tensor product grid in the rectangle.
* @param[in] f rhs vector
*/
void SetF(std::vector<double> &f) const;
/**
* Determines the indices of those vertices with Dirichlet boundary conditions
* @return index vector.
*/
std::vector<int> Index_DirichletNodes() const override;
std::vector<int> Index_BoundaryNodes() const override;
/**
* Stores the values of vector @p u of (sub)domain into a file @p name for further processing in gnuplot.
* The file stores rowise the x- and y- coordinates together with the value from @p u .
* The domain [@p xl, @p xr] x [@p yb, @p yt] is discretized into @p nx x @p ny intervals.
*
* @param[in] name basename of file name (file name will be extended by the rank number)
* @param[in] u local vector
*
* @warning Assumes tensor product grid in unit square; rowise numbered
* (as generated in class constructor).
* The output is provided for tensor product grid visualization
* ( similar to Matlab-surf() ).
*
* @see Mesh_2d_3_square
*/
void SaveVectorP(std::string const &name, std::vector<double> const &u) const;
// here will still need to implement in the class
// GetBound(), AddBound()
// or better a generalized way with indices and their appropriate ranks for MPI communication
private:
/**
* Determines the coordinates of the discretization nodes of the domain [@p xl, @p xr] x [@p yb, @p yt]
* which is discretized into @p nx x @p ny intervals.
* @param[in] nx number of discretization intervals in x-direction
* @param[in] ny number of discretization intervals in y-direction
* @param[in] xl x-coordinate of left boundary
* @param[in] xr x-coordinate of right boundary
* @param[in] yb y-coordinate of lower boundary
* @param[in] yt y-coordinate of upper boundary
* @param[out] xc coordinate vector of length 2n with x(2*k,2*k+1) as coordinates of node k
*/
void GetCoordsInRectangle(int nx, int ny, double xl, double xr, double yb, double yt,
double xc[]);
/**
* Determines the element connectivity of linear triangular elements of a FEM discretization
* of a rectangle using @p nx x @p ny equidistant intervals for discretization.
* @param[in] nx number of discretization intervals in x-direction
* @param[in] ny number of discretization intervals in y-direction
* @param[out] ia element connectivity matrix with ia(3*s,3*s+1,3*s+2) as node numbers od element s
*/
void GetConnectivityInRectangle(int nx, int ny, int ia[]);
private:
int _myid; //!< my MPI rank
int _procx; //!< number of MPI ranks in x-direction
int _procy; //!< number of MPI ranks in y-direction
std::array<int, 4> _neigh; //!< MPI ranks of neighbors (negative: no neighbor but b.c.)
int _color; //!< red/black coloring (checker board) of subdomains
double _xl; //!< x coordinate of lower left corner of square
double _xr; //!< x coordinate of lower right corner of square
double _yb; //!< y coordinate or lower left corner of square
double _yt; //!< y coordinate of upper right corner of square
int _nx; //!< number of intervals in x-direction
int _ny; //!< number of intervals in y-direction
};
// *********************************************************************
#endif

108
sheet7/E910111213/main.cpp Normal file
View file

@ -0,0 +1,108 @@
// MPI code in C++.
// See [Gropp/Lusk/Skjellum, "Using MPI", p.33/41 etc.]
// and /opt/mpich/include/mpi2c++/comm.h for details
#include "geom.h"
#include "par_geom.h"
#include "vdop.h"
#include <cassert>
#include <cmath>
#include <iostream>
#include <mpi.h> // MPI
#include <omp.h> // OpenMP
using namespace std;
int main(int argc, char **argv )
{
MPI_Init(&argc, &argv);
MPI_Comm const icomm(MPI_COMM_WORLD);
omp_set_num_threads(1); // don't use OMP parallelization for a start
//
int np;
MPI_Comm_size(icomm, &np);
//assert(4 == np); // example is only provided for 4 MPI processes
if(np == 4 || np ==2 || np == 1)
{
// #####################################################################
// ---- Read the f.e. mesh and the mapping of elements to MPI processes
//Mesh const mesh_c("square_4.txt"); // Files square_4.txt and square_4_sd.txt are needed
ParMesh const mesh("square",icomm);
int const numprocs = mesh.NumProcs();
int const myrank = mesh.MyRank();
if ( 0 == myrank ) {
cout << "\n There are " << numprocs << " processes running.\n \n";
}
int const check_rank=0; // choose the MPI process you would like to check the mesh
//if ( check_rank == myrank ) mesh.Debug();
//if ( check_rank == myrank ) mesh.DebugEdgeBased();
// ---- allocate local vectors and check skalar product and vector accumulation
vector<double> xl(mesh.Nnodes(), -1.0);
mesh.SetValues(xl, [](double x, double y) -> double {return x * x * std::sin(2.5 * M_PI * y);} );
if (check_rank==myrank) mesh.Visualize(xl);
cout << "nnodes: " <<mesh.Nnodes() << endl;
for (size_t k=0; k<xl.size(); ++k)
{
xl[k] = 1.0;
}
double ss = mesh.dscapr(xl,xl);
cout << myrank << " : scalar : " << ss << endl;
mesh.VecAccu(xl);
if (check_rank==myrank) mesh.Visualize(xl);
//--------------------E10
vector<int> x2(mesh.Nnodes(), -1);
mesh.VecAccu(x2);
if(myrank == check_rank)
{
for(int i: x2)
{
cout << i << " ";
}
cout << endl;
}
//-------------------E11
int global_nodes = mesh.GlobalNnodes();
if(myrank == check_rank)
{
cout << "Global number of nodes = " << global_nodes << endl;
}
//--------------------E12
vector<double> x3(mesh.Nnodes(), myrank);
// check averaging at the interfaces (by visualization)
mesh.Average(x3);
if (check_rank==myrank) mesh.Visualize(x3);
}
else
{
cout << np << endl;
}
MPI_Barrier(icomm);
MPI_Finalize();
return 0;
}

View file

@ -0,0 +1,621 @@
// see: http://llvm.org/docs/CodingStandards.html#include-style
#include "vdop.h"
//#include "geom.h"
#include "par_geom.h"
#include <algorithm>
#include <array>
#include <cassert>
#include <cmath>
#include <ctime> // contains clock()
#include <fstream>
#include <iostream>
#include <list>
#include <numeric> // accumulate()
#include <string>
#include <vector>
using namespace std;
ParMesh::ParMesh(int ndim, int nvert_e, int ndof_e, int nedge_e, MPI_Comm const &icomm)
: Mesh(ndim, nvert_e, ndof_e, nedge_e),
_icomm(icomm), _numprocs(-1), _myrank(-1),
_v_l2g(0), _t_l2g(0), _v_g2l{{}}, _t_g2l{{}}, _valence(0),
_sendbuf(0), _sendcounts(0), _sdispls(0),
_loc_itf(0), _gloc_itf(0), _buf2loc(0)
{
MPI_Comm_size(icomm, &_numprocs);
MPI_Comm_rank(icomm, &_myrank);
}
ParMesh::~ParMesh()
{}
ParMesh::ParMesh(std::string const &sname, MPI_Comm const &icomm)
: ParMesh(2, 3, 3, 3, icomm) // two dimensions, 3 vertices, 3 dofs, 3 edges per element
{
//const int numprocs = _icomm.Get_size();
const string NS = "_" + to_string(_numprocs);
const string fname = sname + NS + ".txt";
//cout << "############ " << fname << endl;
ReadVertexBasedMesh(fname);
cout << "\n End of sequential File read \n";
// ------------------------------------------------------------------------------
// Until this point a l l processes possess a l l mesh info in g l o b a l numbering
//
// Now, we have to select the data belonging to my_rank
// and we have to create the mapping local to global (l2g) and vice versa (g2l)
// ------------------------------------------------------------------------------
// save the global node mesh (maybe we need it later)
DeriveEdgeFromVertexBased(); // and even more
Mesh global_mesh(*this); // requires a l o t of memory
Del_EdgeConnectivity();
// read the subdomain info
const string dname = sname + NS + "_sd" + ".txt";
vector<int> t2d = ReadElementSubdomains(dname); // global mapping triangle to subdomain for all elements
//const int myrank = _icomm.Get_rank();
Transform_Local2Global_Vertex(_myrank, t2d); // Vertex based mesh: now in l o c a l indexing
DeriveEdgeFromVertexBased(); // Generate also the l o c a l edge based information
Generate_VectorAdd();
// Now we have to organize the MPI communication of vertices on the subdomain interfaces
return;
}
vector<int> ParMesh::ReadElementSubdomains(string const &dname)
{
ifstream ifs(dname);
if (!(ifs.is_open() && ifs.good())) {
cerr << "ParMesh::ReadElementSubdomain: Error cannot open file " << dname << endl;
assert(ifs.is_open());
}
int const OFFSET{1}; // Matlab to C indexing
cout << "ASCI file " << dname << " opened" << endl;
// Read some mesh constants
int nelem;
ifs >> nelem;
cout << nelem << " " << Nelems() << endl;
assert( Nelems() == nelem);
// Allocate memory
vector<int> t2d(nelem, -1);
// Read element mapping
for (int k = 0; k < nelem; ++k) {
int tmp;
ifs >> tmp;
//t2d[k] = tmp - OFFSET;
// 2020-01-08
t2d[k] = min(tmp, NumProcs()) - OFFSET;
}
return t2d;
}
void ParMesh::Transform_Local2Global_Vertex(int const myrank, vector<int> const &t2d)
{
// number of local elements
const int l_ne = count(t2d.cbegin(), t2d.cend(), myrank);
//cout << myrank << ":: " << lne << endl;
vector<int> l_ia(l_ne * NverticesElements(), -1); // local elements still with global vertex numbers
_t_l2g.resize(l_ne, -1);
int lk = 0;
for (size_t k = 0; k < t2d.size(); ++k) {
if (myrank == t2d[k]) {
//if (0==myrank)
//{
//cout << lk << " k " << t2d[k] << endl;
//}
l_ia[3 * lk ] = _ia[3 * k ];
l_ia[3 * lk + 1] = _ia[3 * k + 1];
l_ia[3 * lk + 2] = _ia[3 * k + 2]; // local elements still with global vertex numbers
_t_l2g[lk] = k; // elements: local to global mapping
_t_g2l[k] = lk; // global to local
++lk;
}
}
// Checks:
assert( count(l_ia.cbegin(), l_ia.cend(), -1) == 0 );
assert( count(_t_l2g.cbegin(), _t_l2g.cend(), -1) == 0 );
// Vertices: local to global mapping
auto tmp = l_ia;
sort(tmp.begin(), tmp.end());
auto ip = unique(tmp.begin(), tmp.end());
tmp.erase(ip, tmp.end());
_v_l2g = tmp; // Vertices: local to global mapping
for (size_t lkv = 0; lkv < _v_l2g.size(); ++lkv) {
_v_g2l[_v_l2g[lkv]] = lkv; // global to local
}
// Boundary edges
vector<int> l_bedges;
vector<int> l_sdedges;
for (size_t b = 0; b < _bedges.size(); b += 2) {
int const v1 = _bedges[b ]; // global vertex numbers
int const v2 = _bedges[b + 1];
try {
int const lv1 = _v_g2l.at(v1); // map[] would add that element
int const lv2 = _v_g2l.at(v2); // but at() throws an exeption
l_bedges.push_back(lv1);
l_bedges.push_back(lv2); // Boundaries: already in local indexing
// 2020-01-08
l_sdedges.push_back(_sdedges[b ]);
l_sdedges.push_back(_sdedges[b+1]);
}
catch (std::out_of_range & err) {
//cerr << ".";
}
}
// number of local vertices
const int l_nn = _v_l2g.size();
vector<double> l_xc(Ndims()*l_nn);
for (int lkk = 0; lkk < l_nn; ++lkk) {
int k = _v_l2g.at(lkk);
l_xc[2 * lkk ] = _xc[2 * k ];
l_xc[2 * lkk + 1] = _xc[2 * k + 1];
}
// Now, we represent the vertex mesh in l o c a l numbering
// elements
for (size_t i = 0; i < l_ia.size(); ++i) {
l_ia[i] = _v_g2l.at(l_ia[i]); // element vertices: global to local
}
SetNelem(l_ne);
_ia = l_ia;
// boundary
_bedges = l_bedges;
_sdedges = l_sdedges;
// coordinates
SetNnode(l_nn);
_xc = l_xc;
return;
}
void ParMesh::Generate_VectorAdd()
{
// Some checks
int lnn = Nnodes(); // local number of vertices
assert(static_cast<int>(_v_l2g.size()) == lnn);
int ierr{-12345};
// ---- Determine global largest vertex index
int gidx_max{-1}; // global largest vertex index
int lmax = *max_element(_v_l2g.cbegin(), _v_l2g.cend());
MPI_Allreduce(&lmax, &gidx_max, 1, MPI_INT, MPI_MAX, _icomm);
int gidx_min{-1}; // global smallest vertex index
int lmin = *min_element(_v_l2g.cbegin(), _v_l2g.cend());
MPI_Allreduce(&lmin, &gidx_min, 1, MPI_INT, MPI_MIN, _icomm);
//cout << gidx_min << " " << gidx_max << endl;
assert(0 == gidx_min); // global indices have to start with 0
// ---- Determine for all global vertices the number of subdomains it belongs to
vector<int> global(gidx_max+1, 0); // global scalar array for vertices
for (auto const gidx : _v_l2g) global[gidx] = 1;
// https://www.mpi-forum.org/docs/mpi-2.2/mpi22-report/node109.htm
ierr = MPI_Allreduce(MPI_IN_PLACE, global.data(), global.size(), MPI_INT, MPI_SUM, _icomm);
//if (0 == MyRank()) cout << global << endl;
//MPI_Barrier(_icomm);
//cout << _xc[2*_v_g2l.at(2)] << " , " << _xc[2*_v_g2l.at(2)+1] << endl;
//MPI_Barrier(_icomm);
// now, global[] contains the number of subdomains a global vertex belongs to
if ( count(global.cbegin(), global.cend(), 0) > 0 )
cerr << "\n !!! Non-continuous global vertex indexing !!!\n";
// ---- Determine local interface vertices ( <==> global[] > 1 )
// _loc_itf, neigh_itf
//vector<int> loc_itf; // local indices of interface vertices on this MPI process
for (size_t lk = 0; lk < _v_l2g.size(); ++lk) {
int const gk = _v_l2g[lk]; // global index of local vertex lk
if ( global[gk] > 1 ) {
_loc_itf.push_back(lk); // local indices of interface vertices on this MPI process
}
}
//MPI_Barrier(_icomm);
//if (0 == MyRank()) cout << "\n..._loc_itf...\n" << _loc_itf << "\n......\n";
//MPI_Barrier(_icomm);
// ---- global indices of local interface vertices
//auto gloc_itf(_loc_itf);
_gloc_itf=_loc_itf;
for_each(_gloc_itf.begin(), _gloc_itf.end(), [this] (auto & v) -> void { v = _v_l2g[v];} );
//MPI_Barrier(_icomm);
//if (0 == MyRank()) cout << "\n..._gloc_itf...\n" << _gloc_itf << "\n......\n";
//DebugVector(_gloc_itf,"_gloc_itf");
// ---- Determine the global length of interfaces
vector<int> vnn(NumProcs(), -1); // number of interface vertices per MPI rank
int l_itf(_loc_itf.size()); // # local interface vertices
ierr = MPI_Allgather(&l_itf, 1, MPI_INT, vnn.data(), 1, MPI_INT, _icomm);
assert(0 == ierr);
//cout << vnn << endl;
// ---- Now we consider only the inferface vertices
int snn = accumulate(vnn.cbegin(), vnn.cend(), 0); // required length of array for global interface indices
//cout << snn << " " << gnn << endl;
vector<int> dispnn(NumProcs(), 0) ; // displacement of interface vertices per MPI rank
partial_sum(vnn.cbegin(), vnn.cend() - 1, dispnn.begin() + 1);
//cout << dispnn << endl;
// ---- Get the global indices for all global interfaces
vector<int> g_itf(snn, -1); // collects all global indices of the global interfaces
// https://www.mpich.org/static//docs/v3.0.x/www3/MPI_Gatherv.html
ierr = MPI_Gatherv( _gloc_itf.data(), _gloc_itf.size(), MPI_INT,
g_itf.data(), vnn.data(), dispnn.data(), MPI_INT, 0, _icomm);
assert(0 == ierr);
// https://www.mpich.org/static/docs/v3.1/www3/MPI_Bcast.html
ierr = MPI_Bcast(g_itf.data(), g_itf.size(), MPI_INT, 0, _icomm);
assert(0 == ierr); // Now, each MPI rank has the all global indices of the global interfaces
//MPI_Barrier(_icomm);
//if (MyRank() == 0) cout << "\n...g_itf...\n" << g_itf << "\n......\n";
//MPI_Barrier(_icomm);
// ----- Determine all MPI ranks a local interface vertex belongs to
vector<vector<int>> neigh_itf(_loc_itf.size());// subdomains a local interface vertex belongs to
for (size_t lk = 0; lk < _loc_itf.size(); ++lk) {
const int gvert = _gloc_itf[lk]; // global index of local interface node lk
for (int rank = 0; rank < NumProcs(); ++rank) {
auto const startl = g_itf.cbegin() + dispnn[rank];
auto const endl = startl + vnn[rank];
if ( find( startl, endl, gvert) != endl) {
neigh_itf[lk].push_back(rank);
}
}
}
// ---- check the available info in _loc_itf[lk], _gloc_itf[lk], neigh_itf[lk]
//MPI_Barrier(_icomm);
////if (MyRank()==0) cout << "\n...neigh_itf ...\n" << neigh_itf << endl;
//if (MyRank() == 0) {
//for (size_t lk = 0; lk < _loc_itf.size(); ++lk ) {
//cout << lk << " : local idx " << _loc_itf[lk] << " , global idx " << _gloc_itf[lk];
//cout << " with MPI ranks " << neigh_itf[lk] << endl;
//}
//}
//MPI_Barrier(_icomm);
// ---- store the valence (e.g., the number of subdomains it belongs to) of all local vertices
_valence.resize(Nnodes(),1);
for (size_t lk = 0; lk < _loc_itf.size(); ++lk)
{
_valence[_loc_itf[lk]] = neigh_itf[lk].size();
}
//DebugVector(_valence,"_valence",_icomm);
// ---- We ware going to use MPI_Alltoallv for data exchange on interfaces
// https://www.mpi-forum.org/docs/mpi-3.1/mpi31-report/node109.htm#Node109
// https://www.open-mpi.org/doc/v4.0/man3/MPI_Alltoallv.3.php
//int MPI_Alltoallv(const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, void* recvbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, MPI_Comm comm)
//
// MPI_Alltoallv needs:
// vector<double> sendbuf (MPI_IN_PLACE: used also as recvbuf)
// vector<int> sendcounts (the same as for recv)
// vector<int> sdispls (the same as for recv)
//
// We need to map the interface vertices onto the sendbuffer:
// vector<int> loc_itf local index of interface vertex lk
// vector<int> gloc_itf global index of interface vertex lk
// vector<int> buf2loc local indices of sendbuffer positions (the same as for recv)
// ---- Determine sendcounts[] and sdipls[] from neigh_itf[]
//vector<int> _sendcounts(NumProcs(), 0);
_sendcounts.resize(NumProcs(), 0);
for (size_t lk = 0; lk < _loc_itf.size(); ++lk ) {
auto const &kneigh = neigh_itf[lk];
for (size_t ns = 0; ns < kneigh.size(); ++ns) {
++_sendcounts[kneigh[ns]];
}
}
//if (MyRank() == 0) cout << "\n..._sendcounts ...\n" << _sendcounts << endl;
//vector<int> _sdispls(NumProcs(), 0);
_sdispls.resize(NumProcs(), 0);
partial_sum(_sendcounts.cbegin(), _sendcounts.cend() - 1, _sdispls.begin() + 1);
//vector<int> _sdispls(NumProcs()+1, 0);
//partial_sum(_sendcounts.cbegin(), _sendcounts.cend(), _sdispls.begin() + 1);
//if (MyRank() == 0) cout << "\n..._sdispls ...\n" << _sdispls << endl;
// ---- Determine size of buffer 'nbuffer' and mapping 'buf2loc'
int const nbuffer = accumulate(_sendcounts.cbegin(), _sendcounts.cend(), 0);
//vector<int> _buf2loc(nbuffer, -1);
_buf2loc.resize(nbuffer, -1);
int buf_idx = 0; // position in buffer
for (int rank = 0; rank < NumProcs(); ++rank) {
assert( buf_idx == _sdispls[rank]);
for (size_t lk = 0; lk < _loc_itf.size(); ++lk ) {
auto const &kneigh = neigh_itf[lk];
if (find(kneigh.cbegin(),kneigh.cend(),rank)!=kneigh.cend())
{
_buf2loc[buf_idx] = _loc_itf[lk];
++buf_idx;
}
}
}
//if (MyRank() == 0) cout << "\n...buf2loc ...\n" << buf2loc << endl;
//DebugVector(buf2loc,"buf2loc",_icomm);
// ---- Allocate send/recv buffer
//vector<double> _sendbuf(nbuffer,-1.0);
_sendbuf.resize(nbuffer,-1.0);
_sendbuf_int.resize(nbuffer,-1);
assert(CheckInterfaceExchange_InPlace());
cout << " Check of data exchange (InPlace) successful!\n";
assert(CheckInterfaceExchange());
cout << " Check of data exchange successful!\n";
assert(CheckInterfaceAdd_InPlace());
cout << " Check of data add successful!\n";
assert(CheckInterfaceAdd());
cout << " Check of data add (InPlace) successful!\n";
vector<double> x(Nnodes(),-1.0);
VecAccu(x);
cout << " VecAccu (InPlace) successful!\n";
return;
}
bool ParMesh::CheckInterfaceExchange_InPlace() const
{
vector<double> x(Nnodes(),-1.0);
copy(_v_l2g.cbegin(),_v_l2g.cend(),x.begin()); // init x with global vertex indices
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
_sendbuf[ls] = x[_buf2loc.at(ls)];
}
int ierr = MPI_Alltoallv(MPI_IN_PLACE, _sendcounts.data(), _sdispls.data(), MPI_DOUBLE,
_sendbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE, _icomm);
assert(ierr==0);
//DebugVector(_sendbuf,"_sendbuf",_icomm);
vector<double> y(x);
for(size_t lk = 0; lk<_loc_itf.size(); ++lk) y[_loc_itf.at(lk)] = -1.0; // only for interface nodes
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
y[_buf2loc.at(ls)] = _sendbuf[ls];
}
double const eps=1e-10;
bool bv = equal(x.cbegin(),x.cend(),y.cbegin(),
[eps](double a, double b) -> bool
{ return std::abs(a-b)<eps*(1.0+0.5*(std::abs(a)+ std::abs(b))); }
);
return bv;
}
bool ParMesh::CheckInterfaceExchange() const
{
vector<double> x(Nnodes(),-1.0);
copy(_v_l2g.cbegin(),_v_l2g.cend(),x.begin()); // init x with global vertex indices
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
_sendbuf[ls] = x[_buf2loc.at(ls)];
}
vector<double> recvbuf(_sendbuf.size());
int ierr = MPI_Alltoallv(_sendbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE,
recvbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE, _icomm);
//DebugVector(_sendbuf,"_sendbuf",_icomm);
//DebugVector(recvbuf,"recvbuf",_icomm);
assert(ierr==0);
vector<double> y(x);
for(size_t lk = 0; lk<_loc_itf.size(); ++lk) y[_loc_itf.at(lk)] = -1.0; // only for interface nodes
for(size_t ls = 0; ls<recvbuf.size(); ++ls)
{
y[_buf2loc.at(ls)] = recvbuf[ls];
}
//cout << "WRONG : " << count(y.cbegin(),y.cend(), -1.0) << endl;
double const eps=1e-10;
bool bv = equal(x.cbegin(),x.cend(),y.cbegin(),
[eps](double a, double b) -> bool
{ return std::abs(a-b)<eps*(1.0+0.5*(std::abs(a)+ std::abs(b))); }
);
return bv;
}
bool ParMesh::CheckInterfaceAdd_InPlace() const
{
vector<double> x(Nnodes(),-1.0);
for (size_t i=0; i<x.size(); ++i)
{
x[i] = _xc[2*i]+_xc[2*i+1]; // init x with coordinate values
}
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
_sendbuf[ls] = x[_buf2loc.at(ls)];
}
int ierr = MPI_Alltoallv(MPI_IN_PLACE, _sendcounts.data(), _sdispls.data(), MPI_DOUBLE,
_sendbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE, _icomm);
assert(ierr==0);
//DebugVector(_sendbuf,"_sendbuf",_icomm);
vector<double> y(x);
for(size_t lk = 0; lk<_loc_itf.size(); ++lk) y[_loc_itf.at(lk)] = 0.0; // only for interface nodes
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
y[_buf2loc.at(ls)] += _sendbuf[ls];
}
MPI_Barrier(_icomm);
//DebugVector(x,"x",_icomm);
//DebugVector(y,"y",_icomm);
for (size_t i= 0; i<y.size(); ++i) y[i]/=_valence[i]; // divide by valence
double const eps=1e-10;
bool bv = equal(x.cbegin(),x.cend(),y.cbegin(),
[eps](double a, double b) -> bool
{ return std::abs(a-b)<eps*(1.0+0.5*(std::abs(a)+ std::abs(b))); }
);
return bv;
}
bool ParMesh::CheckInterfaceAdd() const
{
vector<double> x(Nnodes(),-1.0);
for (size_t i=0; i<x.size(); ++i)
{
//x[i] = _xc[2*i]+_xc[2*i+1]; // init x with coordinate values
x[i] = _v_l2g[i];
}
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
_sendbuf[ls] = x[_buf2loc.at(ls)];
}
vector<double> recvbuf(_sendbuf.size());
int ierr = MPI_Alltoallv(_sendbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE,
recvbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE, _icomm);
//DebugVector(_sendbuf,"_sendbuf",_icomm);
//DebugVector(recvbuf,"recvbuf",_icomm);
assert(ierr==0);
vector<double> y(x);
for(size_t lk = 0; lk<_loc_itf.size(); ++lk) y[_loc_itf.at(lk)] = 0.0; // only for interface nodes
for(size_t ls = 0; ls<recvbuf.size(); ++ls)
{
//if (0==MyRank()) cout << ls << ": " << _buf2loc.at(ls) << " " << y[_buf2loc.at(ls)] << "("<< x[_buf2loc.at(ls)] << ")" << " " << recvbuf[ls] << " (" << _sendbuf[ls] << ")" << endl;
y[_buf2loc.at(ls)] += recvbuf[ls];
}
MPI_Barrier(_icomm);
//DebugVector(x,"x",_icomm);
//DebugVector(y,"y",_icomm);
for (size_t i= 0; i<y.size(); ++i) y[i]/=_valence[i]; // divide by valence
double const eps=1e-10;
bool bv = equal(x.cbegin(),x.cend(),y.cbegin(),
[eps](double a, double b) -> bool
{ return std::abs(a-b)<eps*(1.0+0.5*(std::abs(a)+ std::abs(b))); }
);
return bv;
}
// ----------
void ParMesh::VecAccu(std::vector<double> &w) const
{
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
_sendbuf[ls] = w[_buf2loc.at(ls)];
}
int ierr = MPI_Alltoallv(MPI_IN_PLACE, _sendcounts.data(), _sdispls.data(), MPI_DOUBLE,
_sendbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE, _icomm);
assert(ierr==0);
//DebugVector(_sendbuf,"_sendbuf",_icomm);
for(size_t lk = 0; lk<_loc_itf.size(); ++lk) w[_loc_itf.at(lk)] = 0.0; // only for interface nodes
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
w[_buf2loc.at(ls)] += _sendbuf[ls];
}
return;
}
void ParMesh::VecAccu(std::vector<int> &w) const
{
for(size_t ls = 0; ls<_sendbuf_int.size(); ++ls)
{
_sendbuf_int[ls] = w[_buf2loc.at(ls)];
}
int ierr = MPI_Alltoallv(MPI_IN_PLACE, _sendcounts.data(), _sdispls.data(), MPI_INT,
_sendbuf_int.data(), _sendcounts.data(), _sdispls.data(), MPI_INT, _icomm);
assert(ierr==0);
//DebugVector(_sendbuf,"_sendbuf",_icomm);
for(size_t lk = 0; lk<_loc_itf.size(); ++lk) w[_loc_itf.at(lk)] = 0; // only for interface nodes
for(size_t ls = 0; ls<_sendbuf_int.size(); ++ls)
{
w[_buf2loc.at(ls)] += _sendbuf_int[ls];
}
return;
}
unsigned int ParMesh::GlobalNnodes() const
{
double localNodes = 0.0;
for(unsigned int i = 0; i < _valence.size(); i++)
{
localNodes += 1.0/_valence[i];
}
double globalNodes = 0.0;
MPI_Allreduce(&localNodes, &globalNodes, 1, MPI_DOUBLE, MPI_SUM, _icomm);
return static_cast<unsigned int>(std::round(globalNodes));
}
void ParMesh::Average(std::vector<double> &w) const
{
VecAccu(w);
unsigned int index;
for(size_t lk = 0; lk<_loc_itf.size(); ++lk)
{
index = _loc_itf.at(lk);
w[index] /= _valence[index];
}
}

View file

@ -0,0 +1,153 @@
#ifndef PAR_GEOM_FILE
#define PAR_GEOM_FILE
#include "geom.h"
#include "vdop.h"
#include <array>
#include <functional> // function; C++11
#include <iostream>
#include <map>
#include <memory> // shared_ptr
#include <mpi.h> // MPI
#include <string>
#include <vector>
class ParMesh: public Mesh
{
public:
/**
* Constructor initializing the members with default values.
*
* @param[in] ndim space dimensions (dimension for coordinates)
* @param[in] nvert_e number of vertices per element (dimension for connectivity)
* @param[in] ndof_e degrees of freedom per element (= @p nvert_e for linear elements)
* @param[in] nedge_e number of edges per element (= @p nvert_e for linear elements in 2D)
* @param[in] icomm MPI communicator
*/
explicit ParMesh(int ndim, int nvert_e = 0, int ndof_e = 0, int nedge_e = 0, MPI_Comm const &icomm = MPI_COMM_WORLD);
ParMesh(ParMesh const &) = default;
ParMesh &operator=(ParMesh const &) = delete;
/**
* Destructor.
*
* See clang warning on
* <a href="https://stackoverflow.com/questions/28786473/clang-no-out-of-line-virtual-method-definitions-pure-abstract-c-class/40550578">weak-vtables</a>.
*/
virtual ~ParMesh();
/**
* Reads mesh data from a binary file.
*
* @param[in] sname suffix of file name
* @param[in] icomm MPI communicator
* @see ascii_write_mesh.m for the file format.
*/
explicit ParMesh(std::string const &sname, MPI_Comm const &icomm = MPI_COMM_WORLD);
void VecAccu(std::vector<double> &w) const;
void VecAccu(std::vector<int> &w) const;
unsigned int GlobalNnodes() const;
void Average(std::vector<double> &w) const;
/** Inner product
* @param[in] x vector
* @param[in] y vector
* @return resulting Euclidian inner product <x,y>
*/
double dscapr(std::vector<double> const &x, std::vector<double> const &y) const
{
return par_scalar(x, y, _icomm);
}
private:
/**
* Reads the global triangle to subdomain mapping.
*
* @param[in] dname file name
*
* @see ascii_write_subdomains.m for the file format
*/
std::vector<int> ReadElementSubdomains(std::string const &dname);
/**
* Transform
*
* @param[in] myrank MPI rank of this process
* @param[in] t2d global mapping triangle to subdomain for all elements (vertex based)
*/
void Transform_Local2Global_Vertex(int myrank, std::vector<int> const &t2d);
/**
* Transform
*/
void Generate_VectorAdd();
bool CheckInterfaceExchange_InPlace() const;
bool CheckInterfaceExchange() const;
bool CheckInterfaceAdd_InPlace() const;
bool CheckInterfaceAdd() const;
public:
/** MPI rank of the calling process in communication group.
*
* @return MPI rank of the calling process
*/
int MyRank() const
{
return _myrank;
}
/** Number of MPI processes in communication group.
*
* @return Number of MPI processes
*/
int NumProcs() const
{
return _numprocs;
}
/** Returns recent
* @return MPI communicator
*/
MPI_Comm GetCommunicator() const
{
return _icomm;
}
private:
// Don't use &_icomm ==> Error
MPI_Comm const _icomm; //!< MPI communicator for the group of processes
int _numprocs; //!< number of MPI processes
int _myrank; //!< my MPI rank
std::vector<int> _v_l2g; //!< vertices: local to global mapping
std::vector<int> _t_l2g; //!< triangles: local to global mapping
std::map<int, int> _v_g2l; //!< vertices: global to local mapping
std::map<int, int> _t_g2l; //!< triangles: global to local mapping
//std::vector<int> e_l2g; //!< edges: local to global mapping
std::vector<int> _valence; //!< valence of local vertices, i.e. number of subdomains they belong to
// MPI_Alltoallv needs:
mutable std::vector<double> _sendbuf; //!< send buffer a n d receiving buffer (MPI_IN_PLACE)
mutable std::vector<int> _sendbuf_int;
std::vector<int> _sendcounts; //!< number of data to send to each MPI rank (the same as for recv)
std::vector<int> _sdispls; //!< offset of data to send to each MPI rank wrt. _senbuffer (the same as for recv)
//
// We need to map the interface vertices onto the sendbuffer:
std::vector<int> _loc_itf; //!< local index of interface vertex lk
std::vector<int> _gloc_itf; //!< global index of interface vertex lk
std::vector<int> _buf2loc; //!< local indices of sendbuffer positions (the same as for recv)
};
#endif

View file

@ -0,0 +1,71 @@
% Square:
% flatpak run org.octave.Octave <filename>
% or
% octave --no-window-system --no-gui -qf <filename>
clear all
clc
% %% L-shape
% g=[2 0 2 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 1 0;
% 2 2 1 1 0.5 1 0;
% 2 1 1 0.5 2 1 0;
% 2 1 0 2 2 1 0;
% 2 0 0 2 0 1 0]';
%% square
g=[2 0 1 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 1 1 0 1 1 0;
2 1 0 1 1 1 0;
2 0 0 1 0 1 0]';
% %% 2 squares
%g=[2 0 1 0 0 1 0; % 1 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 0 1 1 2;
% 2 1 0 1 1 1 0;
% 2 0 0 1 0 1 0;
% 2 1 2 0 0 2 0; % 2 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 2 0;
% 2 2 1 1 1 2 0
% ]';
%% 4 squares
%g=[2 0 1 0 0 1 0; % 1 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 0 1 1 2;
% 2 1 0 1 1 1 3;
% 2 0 0 1 0 1 0;
% 2 1 2 0 0 2 0; % 2 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 2 0;
% 2 2 1 1 1 2 4;
% 2 1 1 1 0 2 1;
% 2 0 1 1 1 3 1; % 3 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 1 2 3 4;
% 2 1 0 2 2 3 0;
% 2 0 0 2 1 3 0;
% 2 1 2 1 1 4 2; % 4 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 1 2 4 0;
% 2 2 1 2 2 4 0
% 2 1 1 2 1 4 3
% ]';
[p,e,t] = initmesh(g,'hmax',0.1);
pdemesh(p,e,t)
%% GH
% output from <https://de.mathworks.com/help/pde/ug/initmesh.html initmesh>
%
% coordinates p: [2][nnode]
% connectivity t: [4][nelem] with t(4,:) are the subdomain numbers
% edges e: [7][nedges] boundary edges
% e([1,2],:) - start/end vertex of edge
% e([3,4],:) - start/end values
% e(5,:) - segment number
% e([6,7],:) - left/right subdomain
ascii_write_mesh( p, t, e, mfilename);
ascii_write_subdomains( p, t, e, mfilename);
% tmp=t(1:3,:)

View file

@ -0,0 +1,558 @@
185
2
328
3
0 0
1 0
1 1
0 1
0.1 0
0.2 0
0.3 0
0.4 0
0.5 0
0.6 0
0.7 0
0.8 0
0.9 0
1 0.1
1 0.2
1 0.3
1 0.4
1 0.5
1 0.6
1 0.7
1 0.8
1 0.9
0.9 1
0.8 1
0.7 1
0.6 1
0.5 1
0.4 1
0.3 1
0.2 1
0.09999999999999998 1
0 0.9
0 0.8
0 0.7
0 0.6
0 0.5
0 0.4
0 0.3
0 0.2
0 0.09999999999999998
0.479527812121941 0.5130906855242849
0.04811823180383969 0.0438546166507735
0.9563795599175402 0.04804788885032887
0.04386052966676572 0.9518771386464404
0.9519837650414075 0.9564710564643847
0.7547325605803197 0.3556873639095056
0.360761237685547 0.2648713558910208
0.2679955285799354 0.6367316063076108
0.6565311368147725 0.7282810558667158
0.08977947929390173 0.1606758305515394
0.16074961553105 0.9101421685880838
0.9114032959455121 0.8441430342815788
0.8412127393896983 0.09032345243962579
0.4520016091121353 0.7693672638162853
0.5579173186257795 0.2370236678206477
0.2298339342907983 0.4531168320939027
0.6870727978845249 0.5344182069836515
0.8145967747561291 0.6882672637836746
0.2590391808211707 0.1580373572085882
0.8521442539624974 0.2588869931794255
0.1588406321984697 0.740526673693239
0.7456209517439575 0.8415774916282844
0.7341263127877148 0.09280026881354901
0.2671693065747615 0.909496199134008
0.09033464668980155 0.2671883646221719
0.8457896962288601 0.4896000777650233
0.3404825814904102 0.5045570302191981
0.4828554674400058 0.3357874026189672
0.5328318433844601 0.6502074760223741
0.6097225090876653 0.8567940574787075
0.3906433840357024 0.1354694263186264
0.1378922468965601 0.6086349988482294
0.7110817229397154 0.2318431574832541
0.3117181579370819 0.7783142400205344
0.220949681304264 0.310667563028674
0.5425918070322489 0.07560610459253665
0.4430039145575841 0.9116370271655028
0.08740928339643227 0.4436913622237557
0.7188986206847875 0.6636965188667157
0.9110251036728165 0.6437697100153658
0.9102437695032523 0.7452606401269005
0.1758720830768971 0.1044756994214669
0.8258438045125337 0.8981667834525472
0.1046890874548641 0.8239996484245049
0.8991462273231223 0.1744109197267267
0.6051175206745411 0.4230204952626953
0.3969635264466733 0.6241613575135659
0.3715057937073831 0.3880149382253817
0.8242474441869813 0.5849955263397347
0.8249925781817751 0.3740463471567774
0.524857955574242 0.7417226727211995
0.4596212700192013 0.2593236862528195
0.2616272257729989 0.5362197465141072
0.606859389427135 0.5708877499579769
0.6628384081676978 0.1354434799258081
0.1417735759502206 0.3537206398808048
0.3535213476926991 0.857695106111649
0.8243721350238892 0.7953207341692213
0.1809418706291789 0.2179884723304038
0.2184182171811329 0.8187415498053924
0.7884319570292208 0.183777415436584
0.9200690792392709 0.4495615809861258
0.4451391682507762 0.07311678395656888
0.5521636044351103 0.9216944906274662
0.07645110424394973 0.5525170940538346
0.6459882520842987 0.9291570899039412
0.06913750986171868 0.645365744095622
0.3540126618242007 0.06796940478874185
0.3350672820198897 0.1870674574637768
0.2681458646222014 0.07565948812083768
0.9304278242313601 0.2557924725551748
0.1888675945362029 0.6639844453684917
0.07615275558574391 0.7316010144871834
0.7330914849187046 0.9235761148662579
0.6708523871490314 0.8055961987547425
0.9254996827062275 0.09229062813530801
0.07558485132732408 0.9074193007828303
0.9079441512159013 0.9258426671715853
0.09255203863622692 0.07554591808558819
0.5680278383791504 0.3330895372138523
0.6999347455251212 0.4427774934447353
0.9169029401490904 0.54449677714876
0.7384739303161019 0.7539389212238149
0.3890119777777613 0.7104484710322342
0.2878025148437066 0.3851241789215418
0.629872124120922 0.6544815099998712
0.9007264265272032 0.3468323759374454
0.411279904374581 0.4567615871148493
0.4654915406621035 0.5928705227932682
0.5147995837137257 0.8309665940332217
0.1657576147654025 0.5169080635939939
0.4842020928392167 0.1607795698062726
0.7650269169529201 0.5114318451419555
0.7727886666860844 0.2740976924940586
0.2419510570158952 0.7238088796353837
0.2752622996523991 0.2404916166572419
0.4695150046459641 0.7063259741370521
0.3347920785441169 0.583686902316204
0.4092342435672485 0.3276796179871309
0.5063047985714836 0.4204246666548196
0.2916489655260076 0.4637829403109103
0.5599640954591117 0.4974158353977953
0.4058103707982076 0.2011311049665246
0.2039249392178127 0.5928750846678578
0.5939173661963685 0.7794857474330111
0.7083501471507929 0.1677776788423718
0.2871826927671263 0.8416108907006823
0.6452139711904199 0.198163050302754
0.1580509567525808 0.2869843728474166
0.06368210143114308 0.3527506715437289
0.6379941184879288 0.07475319919242245
0.352513946674906 0.9360345024951773
0.4295894166119973 0.8326060111402684
0.1662397140312891 0.4305970833461407
0.7747686535289771 0.6310503316801724
0.3210854659795955 0.1245584034703338
0.1257665255187338 0.6783828888007957
0.6810504641157901 0.8716254757826338
0.8566662522803288 0.4178384718512468
0.1420330245501709 0.04491064793897617
0.9561739529171728 0.141583384682825
0.8584457296716212 0.9560409249356761
0.04495620004107777 0.8579381142105297
0.5171767005108064 0.2908288148650228
0.7913019422018426 0.4313726220323573
0.6425026537330679 0.274463851858847
0.6633492402087484 0.3537206596142247
0.6768185989632656 0.6021176410080418
0.3093806310187861 0.7008983644164182
0.2975174797189623 0.3067216701578878
0.6319539373284636 0.4933605852039861
0.4046353295336855 0.5461031514210908
0.4362702351071284 0.3855427634700271
0.7411951440577802 0.5877461636960623
0.2088055498119787 0.3863707017530032
0.3866467015671689 0.79036800231969
0.8211383868684881 0.3213102960013891
0.3391487763640905 0.6517998595813517
0.4504917745206402 0.6572589474543384
0.3403171186033137 0.4396480629283334
0.3450512325288959 0.3342143314291603
0.5884423212101556 0.1464688161475419
0.5874820780580523 0.7111470153304259
0.5289939782208927 0.5649267052820812
0.7089627135729326 0.297207704024266
13 2 43
82 6 110
7 8 108
42 5 119
119 5 160
6 7 110
92 47 143
9 10 76
76 10 151
22 3 45
31 4 44
85 15 111
111 16 127
17 18 102
102 18 122
12 13 53
53 13 116
43 14 116
15 16 111
40 1 42
134 46 185
21 22 52
83 24 114
25 26 106
52 22 118
45 23 118
24 25 114
126 49 183
27 28 77
77 28 152
30 31 51
51 31 117
44 32 117
84 33 113
33 34 113
34 35 107
1 5 42
50 40 119
93 48 144
36 37 78
78 37 150
39 40 50
2 14 43
116 14 161
4 32 44
117 32 163
3 23 45
118 23 162
19 20 80
128 41 172
166 73 185
16 17 127
129 41 184
8 9 103
138 67 172
35 36 105
40 42 119
170 47 181
31 44 117
169 48 178
22 45 118
26 27 104
13 43 116
115 49 123
135 48 169
29 30 64
136 47 170
11 12 63
79 49 126
38 39 65
92 55 164
94 57 168
80 20 81
168 57 174
109 47 136
39 50 65
90 46 177
12 53 63
112 48 135
30 51 64
20 21 81
79 58 123
63 53 101
139 68 173
64 51 100
91 54 137
65 50 99
93 56 141
21 52 81
121 46 165
124 54 176
125 56 175
148 55 182
128 88 173
121 57 171
93 67 138
28 29 152
115 62 158
10 11 151
109 59 156
37 38 150
112 61 157
85 60 101
120 55 166
84 61 100
97 74 176
82 59 99
96 75 175
120 68 164
11 63 151
129 69 179
29 64 152
128 67 180
38 65 150
80 58 89
91 69 183
81 52 98
89 58 155
98 62 123
58 80 81
96 65 149
108 71 156
83 62 98
106 70 158
97 64 147
107 72 157
95 63 146
134 60 177
159 66 165
120 86 140
94 69 184
124 87 179
92 68 139
125 88 180
58 79 155
18 19 122
101 60 134
89 66 122
115 70 145
130 91 145
99 59 136
109 71 143
100 61 135
112 72 144
155 79 174
140 86 142
101 73 146
146 73 148
99 75 149
131 78 154
100 74 147
130 77 153
58 81 98
52 83 98
132 92 143
50 82 99
131 93 144
51 84 100
148 73 166
53 85 101
19 80 122
102 66 159
103 76 132
9 76 103
104 77 130
27 77 104
105 78 131
36 78 105
104 70 106
26 104 106
105 72 107
35 105 107
103 71 108
8 103 108
125 75 170
110 108 156
59 82 110
7 108 110
60 85 111
46 90 165
124 74 169
113 107 157
61 84 113
34 107 113
62 83 114
25 106 114
49 79 123
114 106 158
85 53 116
14 15 161
84 51 117
32 33 163
83 52 118
23 24 162
82 50 119
5 6 160
132 76 182
121 86 167
142 86 171
133 121 165
80 89 122
66 102 122
58 98 123
62 115 123
77 97 153
138 87 178
78 96 154
139 88 181
145 91 183
69 94 126
17 102 127
60 111 127
137 124 179
141 125 180
142 94 184
129 87 172
54 91 130
70 104 130
56 93 131
72 105 131
55 92 132
71 103 132
66 89 133
57 121 133
127 90 177
73 101 134
74 100 135
61 112 135
75 99 136
59 109 136
69 91 137
54 124 137
67 128 172
48 93 138
140 128 173
47 92 139
68 120 140
41 128 140
67 93 141
56 125 141
57 94 171
41 140 142
47 109 143
71 132 143
48 112 144
72 131 144
49 115 145
70 130 145
151 95 182
63 101 146
74 97 147
64 100 147
86 120 167
95 146 148
75 96 149
65 99 149
96 78 150
65 96 150
55 132 182
63 95 151
97 77 152
64 97 152
153 97 176
54 130 153
154 96 175
56 131 154
133 89 174
57 133 174
71 109 156
59 110 156
72 112 157
61 113 157
62 114 158
70 115 158
127 102 159
90 127 159
6 82 160
82 119 160
15 85 161
85 116 161
24 83 162
83 118 162
33 84 163
84 117 163
68 92 164
55 120 164
66 133 165
90 159 165
73 134 185
55 148 166
46 121 167
120 166 167
126 94 168
79 126 168
87 124 178
74 135 169
88 125 181
75 136 170
86 121 171
94 142 171
41 129 172
87 138 172
88 139 173
68 140 173
89 155 174
79 168 174
75 125 175
56 154 175
74 124 176
54 153 176
60 127 177
46 134 177
48 138 178
124 169 178
87 129 179
69 137 179
88 128 180
67 141 180
47 139 181
125 170 181
95 148 182
76 151 182
69 126 183
49 145 183
69 129 184
41 142 184
167 166 185
46 167 185
40
1 5
5 6
6 7
7 8
8 9
9 10
10 11
11 12
12 13
13 2
2 14
14 15
15 16
16 17
17 18
18 19
19 20
20 21
21 22
22 3
3 23
23 24
24 25
25 26
26 27
27 28
28 29
29 30
30 31
31 4
4 32
32 33
33 34
34 35
35 36
36 37
37 38
38 39
39 40
40 1

View file

@ -0,0 +1,329 @@
328
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1

View file

@ -0,0 +1,71 @@
% Square:
% flatpak run org.octave.Octave <filename>
% or
% octave --no-window-system --no-gui -qf <filename>
clear all
clc
% %% L-shape
% g=[2 0 2 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 1 0;
% 2 2 1 1 0.5 1 0;
% 2 1 1 0.5 2 1 0;
% 2 1 0 2 2 1 0;
% 2 0 0 2 0 1 0]';
%% square
% g=[2 0 1 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 0 1 1 0;
% 2 1 0 1 1 1 0;
% 2 0 0 1 0 1 0]';
% %% 2 squares
g=[2 0 1 0 0 1 0; % 1 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 1 1 0 1 1 2;
2 1 0 1 1 1 0;
2 0 0 1 0 1 0;
2 1 2 0 0 2 0; % 2 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 2 2 0 1 2 0;
2 2 1 1 1 2 0
]';
%% 4 squares
%g=[2 0 1 0 0 1 0; % 1 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 0 1 1 2;
% 2 1 0 1 1 1 3;
% 2 0 0 1 0 1 0;
% 2 1 2 0 0 2 0; % 2 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 2 0;
% 2 2 1 1 1 2 4;
% 2 1 1 1 0 2 1;
% 2 0 1 1 1 3 1; % 3 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 1 2 3 4;
% 2 1 0 2 2 3 0;
% 2 0 0 2 1 3 0;
% 2 1 2 1 1 4 2; % 4 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 1 2 4 0;
% 2 2 1 2 2 4 0
% 2 1 1 2 1 4 3
% ]';
[p,e,t] = initmesh(g,'hmax',0.1);
pdemesh(p,e,t)
%% GH
% output from <https://de.mathworks.com/help/pde/ug/initmesh.html initmesh>
%
% coordinates p: [2][nnode]
% connectivity t: [4][nelem] with t(4,:) are the subdomain numbers
% edges e: [7][nedges] boundary edges
% e([1,2],:) - start/end vertex of edge
% e([3,4],:) - start/end values
% e(5,:) - segment number
% e([6,7],:) - left/right subdomain
ascii_write_mesh( p, t, e, mfilename);
ascii_write_subdomains( p, t, e, mfilename);
% tmp=t(1:3,:)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,653 @@
652
1
2
1
2
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
1
2
2
1
1
2
2
2
2
2
2
2
2
1
1
1
2
1
1
1
1
2
2
2
2
1
1
1
1
2
2
2
2
1
1
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
1
1
2
2
1
1
1
1
1
1
2
2
1
1
2
2
2
2
2
2
2
2
1
1
1
1
1
1
1
1
2
2
2
2
1
1
1
1
1
1
2
2
2
2
1
1
2
2
2
2
2
2
2
2
1
1
1
1
1
1
1
1
2
2
1
1
1
1
2
2
2
2
1
1
1
1
1
1
2
2
2
2
2
2
1
1
1
1
1
1
2
2
1
1
2
2
1
1
2
2
2
2
2
2
2
2
2
2
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
1
1
2
2
1
1
1
1
2
2
2
2
2
2
2
2
2
2
1
1
2
2
2
2
1
1
1
1
1
1
1
1
2
2
2
2
1
1
2
2
2
2
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
1
1
1
1
1
1
2
2
2
2
2
2
1
1
1
1
1
1
1
1
2
2
2
2
1
1
1
1
1
1
2
2
2
2
2
2
2
2
1
1
1
1
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
1
1
1
1
1
1
1
1
2
2
2
2
2
2
1
1
2
2
1
1
2
2
1
1
2
2
2
2
2
2
1
1
2
2
1
1
1
1
1
1
1
1
2
2
2
2
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
1
1
2
2
2
2
2
2
2
2
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
1
1
2
2
2
2
2
2
2
2
1
1
1
1
1
1
1
1
1
1
1
1
1
1
2
2
2
2
1
1
1
1
1
1
2
2
2
2
2
2
1
1
1
1
2
2
1
1
2
2
1
1
1
1
1
1
2
2
2
2
2
2
1
1
2
2
2
2
2
2
2
2
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
1
1
1
1
1
1
2
2
2
2
2
2
1
1
2
2
2
2
1
1
1
1
1
1
1
1
2
2
2
2
2
2
1
1
1
1
2
2
1
1
2
2
2
2
2
2
2
2
1
1
1
1
1
1
1
1
1
1
1
1
1
1
2
2
1
1
1
1
1
1
2
2
2
2
1
1
2
2
2
2
2
1
2
1

View file

@ -0,0 +1,71 @@
% Square:
% flatpak run org.octave.Octave <filename>
% or
% octave --no-window-system --no-gui -qf <filename>
clear all
clc
% %% L-shape
% g=[2 0 2 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 1 0;
% 2 2 1 1 0.5 1 0;
% 2 1 1 0.5 2 1 0;
% 2 1 0 2 2 1 0;
% 2 0 0 2 0 1 0]';
%% square
% g=[2 0 1 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 0 1 1 0;
% 2 1 0 1 1 1 0;
% 2 0 0 1 0 1 0]';
% %% 2 squares
% g=[2 0 1 0 0 1 0; % 1 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 0 1 1 2;
% 2 1 0 1 1 1 0;
% 2 0 0 1 0 1 0;
% 2 1 2 0 0 2 0; % 2 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 2 0;
% 2 2 1 1 1 2 0
% ]';
%% 4 squares
g=[2 0 1 0 0 1 0; % 1 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 1 1 0 1 1 2;
2 1 0 1 1 1 3;
2 0 0 1 0 1 0;
2 1 2 0 0 2 0; % 2 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 2 2 0 1 2 0;
2 2 1 1 1 2 4;
% 2 1 1 1 0 2 1;
% 2 0 1 1 1 3 1; % 3 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 1 1 1 2 3 4;
2 1 0 2 2 3 0;
2 0 0 2 1 3 0;
% 2 1 2 1 1 4 2; % 4 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 2 2 1 2 4 0;
2 2 1 2 2 4 0
% 2 1 1 2 1 4 3
]';
[p,e,t] = initmesh(g,'hmax',0.1);
pdemesh(p,e,t)
%% GH
% output from <https://de.mathworks.com/help/pde/ug/initmesh.html initmesh>
%
% coordinates p: [2][nnode]
% connectivity t: [4][nelem] with t(4,:) are the subdomain numbers
% edges e: [7][nedges] boundary edges
% e([1,2],:) - start/end vertex of edge
% e([3,4],:) - start/end values
% e(5,:) - segment number
% e([6,7],:) - left/right subdomain
ascii_write_mesh( p, t, e, mfilename);
ascii_write_subdomains( p, t, e, mfilename);
% tmp=t(1:3,:)

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

705
sheet7/E910111213/uv.txt Normal file
View file

@ -0,0 +1,705 @@
187 2 330 3
0 0
1 0
1 1
0 1
0.1 0
0.2 0
0.3 0
0.4 0
0.5 0
0.6 0
0.7 0
0.8 0
0.9 0
1 0.1
1 0.2
1 0.3
1 0.4
1 0.5
1 0.6
1 0.7
1 0.8
1 0.9
0.9 1
0.8 1
0.7 1
0.6 1
0.5 1
0.4 1
0.3 1
0.2 1
0.1 1
0 0.9
0 0.8
0 0.7
0 0.6
0 0.5
0 0.4
0 0.3
0 0.2
0 0.1
0.479684 0.513096
0.0481001 0.0438139
0.965456 0.0484346
0.0438168 0.951897
0.961962 0.955138
0.754828 0.354966
0.360392 0.264508
0.267426 0.637353
0.65649 0.728531
0.0895934 0.160441
0.160484 0.910354
0.911842 0.844141
0.841625 0.0900995
0.451559 0.770056
0.557953 0.236427
0.229318 0.453048
0.687178 0.534082
0.814675 0.688238
0.258637 0.157666
0.852304 0.258371
0.158327 0.741011
0.745783 0.841799
0.734225 0.0924118
0.266805 0.909841
0.0900392 0.266867
0.845906 0.489216
0.340275 0.504784
0.482979 0.335446
0.532713 0.650471
0.609686 0.857218
0.390369 0.135169
0.137445 0.608961
0.711137 0.231036
0.311055 0.779038
0.220383 0.310202
0.542551 0.0753476
0.442762 0.912027
0.0870955 0.443563
0.71891 0.663674
0.911087 0.643693
0.910368 0.745257
0.175659 0.104224
0.826322 0.898216
0.104383 0.824244
0.899601 0.174158
0.605353 0.422553
0.396664 0.624664
0.371373 0.387899
0.824327 0.584766
0.825103 0.373429
0.524606 0.742235
0.459558 0.258909
0.261176 0.536509
0.606947 0.570757
0.662867 0.13484
0.141276 0.353323
0.353003 0.858305
0.824585 0.795396
0.180506 0.217545
0.217867 0.81926
0.7886 0.183202
0.920145 0.449301
0.445028 0.0729222
0.552084 0.922007
0.0761805 0.552601
0.646019 0.929394
0.0688853 0.645563
0.353835 0.0678044
0.33465 0.18669
0.267936 0.0754627
0.930563 0.255545
0.188297 0.664536
0.0758775 0.731844
0.733221 0.923739
0.670883 0.805934
0.927247 0.0922677
0.0754421 0.907509
0.909844 0.925626
0.0924729 0.0754185
0.568222 0.332532
0.700102 0.44222
0.916968 0.544333
0.738541 0.754101
0.388485 0.711156
0.287351 0.384911
0.629832 0.654561
0.900814 0.346453
0.41132 0.456786
0.465434 0.593118
0.514562 0.831523
0.16528 0.517016
0.484085 0.160379
0.76514 0.511037
0.772882 0.273336
0.241297 0.724487
0.274741 0.24004
0.469165 0.706885
0.334436 0.584171
0.409135 0.327415
0.506546 0.420168
0.291267 0.463861
0.560193 0.497192
0.405521 0.200759
0.203388 0.593306
0.593803 0.779935
0.708431 0.167068
0.286576 0.842217
0.645259 0.197405
0.157539 0.286478
0.0634282 0.35253
0.638015 0.0744131
0.352255 0.936338
0.429132 0.833285
0.1657 0.430369
0.774817 0.630926
0.320743 0.124262
0.125322 0.678789
0.681128 0.871955
0.856775 0.417367
0.141959 0.0447961
0.956726 0.141515
0.859027 0.95601
0.0448254 0.858021
0.51729 0.290331
0.791421 0.430793
0.642597 0.273685
0.663496 0.353015
0.676853 0.601987
0.308734 0.701655
0.297005 0.306339
0.632153 0.492996
0.404572 0.546354
0.436385 0.385368
0.741254 0.587512
0.208229 0.386035
0.386065 0.791119
0.821222 0.320652
0.33864 0.652457
0.450215 0.657712
0.340125 0.439662
0.344719 0.333947
0.588452 0.145922
0.587363 0.711465
0.529103 0.564926
0.709038 0.296367
1 0.95
1 0.05
13 2 43
82 6 110
7 8 108
43 2 187
42 5 119
119 5 160
6 7 110
92 47 143
9 10 76
76 10 151
31 4 44
85 15 111
111 16 127
17 18 102
102 18 122
12 13 53
53 13 116
43 14 116
15 16 111
40 1 42
134 46 185
21 22 52
83 24 114
25 26 106
52 22 118
45 23 118
24 25 114
126 49 183
27 28 77
77 28 152
30 31 51
51 31 117
44 32 117
84 33 113
33 34 113
34 35 107
1 5 42
50 40 119
3 23 45
93 48 144
36 37 78
78 37 150
39 40 50
116 14 161
4 32 44
117 32 163
118 23 162
45 22 186
19 20 80
128 41 172
166 73 185
16 17 127
129 41 184
8 9 103
138 67 172
35 36 105
40 42 119
170 47 181
31 44 117
169 48 178
22 45 118
26 27 104
13 43 116
115 49 123
135 48 169
29 30 64
136 47 170
11 12 63
79 49 126
38 39 65
92 55 164
94 57 168
80 20 81
168 57 174
109 47 136
39 50 65
90 46 177
12 53 63
112 48 135
30 51 64
20 21 81
79 58 123
63 53 101
139 68 173
64 51 100
91 54 137
65 50 99
93 56 141
21 52 81
121 46 165
124 54 176
125 56 175
148 55 182
128 88 173
121 57 171
93 67 138
28 29 152
115 62 158
10 11 151
109 59 156
37 38 150
112 61 157
85 60 101
120 55 166
84 61 100
97 74 176
82 59 99
96 75 175
120 68 164
11 63 151
129 69 179
29 64 152
128 67 180
38 65 150
80 58 89
91 69 183
81 52 98
89 58 155
98 62 123
58 80 81
96 65 149
108 71 156
83 62 98
106 70 158
97 64 147
107 72 157
95 63 146
134 60 177
159 66 165
120 86 140
94 69 184
124 87 179
92 68 139
125 88 180
58 79 155
18 19 122
101 60 134
89 66 122
115 70 145
130 91 145
99 59 136
109 71 143
100 61 135
112 72 144
155 79 174
140 86 142
101 73 146
146 73 148
99 75 149
131 78 154
100 74 147
130 77 153
58 81 98
52 83 98
132 92 143
50 82 99
131 93 144
51 84 100
148 73 166
53 85 101
19 80 122
102 66 159
103 76 132
9 76 103
104 77 130
27 77 104
105 78 131
36 78 105
104 70 106
26 104 106
105 72 107
35 105 107
103 71 108
8 103 108
125 75 170
110 108 156
59 82 110
7 108 110
60 85 111
46 90 165
124 74 169
113 107 157
61 84 113
34 107 113
62 83 114
25 106 114
49 79 123
114 106 158
85 53 116
14 15 161
84 51 117
32 33 163
83 52 118
23 24 162
82 50 119
5 6 160
132 76 182
121 86 167
142 86 171
133 121 165
80 89 122
66 102 122
58 98 123
62 115 123
77 97 153
138 87 178
78 96 154
139 88 181
145 91 183
69 94 126
17 102 127
60 111 127
137 124 179
141 125 180
142 94 184
129 87 172
54 91 130
70 104 130
56 93 131
72 105 131
55 92 132
71 103 132
66 89 133
57 121 133
127 90 177
73 101 134
74 100 135
61 112 135
75 99 136
59 109 136
69 91 137
54 124 137
67 128 172
48 93 138
140 128 173
47 92 139
68 120 140
41 128 140
67 93 141
56 125 141
57 94 171
41 140 142
47 109 143
71 132 143
48 112 144
72 131 144
49 115 145
70 130 145
151 95 182
63 101 146
74 97 147
64 100 147
86 120 167
95 146 148
75 96 149
65 99 149
96 78 150
65 96 150
55 132 182
63 95 151
97 77 152
64 97 152
153 97 176
54 130 153
154 96 175
56 131 154
133 89 174
57 133 174
71 109 156
59 110 156
72 112 157
61 113 157
62 114 158
70 115 158
127 102 159
90 127 159
6 82 160
82 119 160
15 85 161
85 116 161
24 83 162
83 118 162
33 84 163
84 117 163
68 92 164
55 120 164
66 133 165
90 159 165
73 134 185
55 148 166
46 121 167
120 166 167
126 94 168
79 126 168
87 124 178
74 135 169
88 125 181
75 136 170
86 121 171
94 142 171
41 129 172
87 138 172
88 139 173
68 140 173
89 155 174
79 168 174
75 125 175
56 154 175
74 124 176
54 153 176
60 127 177
46 134 177
48 138 178
124 169 178
87 129 179
69 137 179
88 128 180
67 141 180
47 139 181
125 170 181
95 148 182
76 151 182
69 126 183
49 145 183
69 129 184
41 142 184
167 166 185
46 167 185
3 45 186
14 43 187
0
0.5
0.5
0
0
0
0
0
0
0
0
0
0
0.5
0.5
0.5
0.5
0.5
0.5
0.5
0.5
0.5
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0.5
0.5

135
sheet7/E910111213/vdop.cpp Normal file
View file

@ -0,0 +1,135 @@
#include "vdop.h"
#include <cassert> // assert()
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;
void vddiv(vector<double> & x, vector<double> const& y,
vector<double> const& z)
{
assert( x.size()==y.size() && y.size()==z.size() );
size_t n = x.size();
#pragma omp parallel for
for (size_t k = 0; k < n; ++k)
{
x[k] = y[k] / z[k];
}
return;
}
//******************************************************************************
void vdaxpy(std::vector<double> & x, std::vector<double> const& y,
double alpha, std::vector<double> const& z )
{
assert( x.size()==y.size() && y.size()==z.size() );
size_t n = x.size();
#pragma omp parallel for
for (size_t k = 0; k < n; ++k)
{
x[k] = y[k] + alpha * z[k];
}
return;
}
//******************************************************************************
double dscapr(std::vector<double> const& x, std::vector<double> const& y)
{
assert( x.size()==y.size());
size_t n = x.size();
double s = 0.0;
//#pragma omp parallel for reduction(+:s)
for (size_t k = 0; k < n; ++k)
{
s += x[k] * y[k];
}
return s;
}
//******************************************************************************
//void DebugVector(vector<double> const &v)
//{
//cout << "\nVector (nnode = " << v.size() << ")\n";
//for (size_t j = 0; j < v.size(); ++j)
//{
//cout.setf(ios::right, ios::adjustfield);
//cout << v[j] << " ";
//}
//cout << endl;
//return;
//}
//******************************************************************************
bool CompareVectors(std::vector<double> const& x, int const n, double const y[], double const eps)
{
bool bn = (static_cast<int>(x.size())==n);
if (!bn)
{
cout << "######### Error: " << "number of elements" << endl;
}
//bool bv = equal(x.cbegin(),x.cend(),y);
bool bv = equal(x.cbegin(),x.cend(),y,
[eps](double a, double b) -> bool
{ return std::abs(a-b)<eps*(1.0+0.5*(std::abs(a)+ std::abs(b))); }
);
if (!bv)
{
assert(static_cast<int>(x.size())==n);
cout << "######### Error: " << "values" << endl;
}
return bn && bv;
}
//******************************************************************************
double par_scalar(vector<double> const &x, vector<double> const &y, MPI_Comm const& icomm)
{
const double s = dscapr(x,y);
double sg;
MPI_Allreduce(&s,&sg,1,MPI_DOUBLE,MPI_SUM,icomm);
return(sg);
}
//******************************************************************************
void ExchangeAll(vector<double> const &xin, vector<double> &yout, MPI_Comm const &icomm)
{
int myrank, numprocs,ierr(-1);
MPI_Comm_rank(icomm, &myrank); // my MPI-rank
MPI_Comm_size(icomm, &numprocs);
int const N=xin.size();
int const sendcount = N/numprocs; // equal sized junks
assert(sendcount*numprocs==N); // really all junk sized?
assert(xin.size()==yout.size());
auto sendbuf = xin.data();
auto recvbuf = yout.data();
ierr = MPI_Alltoall(sendbuf, sendcount, MPI_DOUBLE,
recvbuf, sendcount, MPI_DOUBLE, icomm);
assert(0==ierr);
return;
}
//******************************************************************************
void ExchangeAllInPlace(vector<double> &xin, MPI_Comm const &icomm)
{
int myrank, numprocs,ierr(-1);
MPI_Comm_rank(icomm, &myrank); // my MPI-rank
MPI_Comm_size(icomm, &numprocs);
int const N=xin.size();
int const sendcount = N/numprocs; // equal sized junks
assert(sendcount*numprocs==N); // really all junk sized?
auto sendbuf = xin.data();
ierr = MPI_Alltoall(MPI_IN_PLACE, sendcount, MPI_DOUBLE,
sendbuf, sendcount, MPI_DOUBLE, icomm);
assert(0==ierr);
return;
}

166
sheet7/E910111213/vdop.h Normal file
View file

@ -0,0 +1,166 @@
#ifndef VDOP_FILE
#define VDOP_FILE
#include <iostream>
#include <mpi.h> // MPI
#include <string>
#include <vector>
/** @brief Element-wise vector divison x_k = y_k/z_k.
*
* @param[out] x target vector
* @param[in] y source vector
* @param[in] z source vector
*
*/
void vddiv(std::vector<double> &x, std::vector<double> const &y,
std::vector<double> const &z);
/** @brief Element-wise daxpy operation x(k) = y(k) + alpha*z(k).
*
* @param[out] x target vector
* @param[in] y source vector
* @param[in] alpha scalar
* @param[in] z source vector
*
*/
void vdaxpy(std::vector<double> &x, std::vector<double> const &y,
double alpha, std::vector<double> const &z );
/** @brief Calculates the Euclidean inner product of two vectors.
*
* @param[in] x vector
* @param[in] y vector
* @return Euclidean inner product @f$\langle x,y \rangle@f$
*
*/
double dscapr(std::vector<double> const &x, std::vector<double> const &y);
inline
double L2_scapr(std::vector<double> const &x, std::vector<double> const &y)
{
return dscapr(x, y) / x.size();
}
/** Parallel inner product
@param[in] x vector
@param[in] y vector
@param[in] icomm MPI communicator
@return resulting Euclidian inner product <x,y>
*/
double par_scalar(std::vector<double> const &x, std::vector<double> const &y,
MPI_Comm const& icomm=MPI_COMM_WORLD);
/* ReadId : Input and broadcast of an integer */
inline
int ReadIn(std::string const &ss = std::string(), MPI_Comm const &icomm = MPI_COMM_WORLD)
{
MPI_Barrier(icomm);
int myrank; /* my rank number */
MPI_Comm_rank(icomm, &myrank);
int id;
if (myrank == 0) {
std::cout << "\n\n " << ss << " : Which process do you want to debug ? \n";
std::cin >> id;
}
MPI_Bcast(&id, 1, MPI_INT, 0, icomm);
return id;
}
/**
* Print entries of a vector to standard output.
*
* @param[in] v vector values
* @param[in] ss string containing the vector name
* @param[in] icomm communicator group for MPI
*
*/
//void DebugVector(std::vector<double> const &v);
template <class T>
void DebugVector(std::vector<T> const &v, std::string const &ss = std::string(), MPI_Comm const &icomm = MPI_COMM_WORLD)
{
MPI_Barrier(icomm);
int numprocs; /* # processes */
MPI_Comm_size(icomm, &numprocs);
int myrank; /* my rank number */
MPI_Comm_rank(icomm, &myrank);
int readid = ReadIn(ss); /* Read readid */
while ( (0 <= readid) && (readid < numprocs) ) {
if (myrank == readid) {
std::cout << "\n\n process " << readid;
std::cout << "\n .... " << ss << " (nnode = " << v.size() << ")\n";
for (size_t j = 0; j < v.size(); ++j) {
std::cout.setf(std::ios::right, std::ios::adjustfield);
std::cout << v[j] << " ";
}
std::cout << std::endl;
fflush(stdout);
}
readid = ReadIn(ss, icomm); /* Read readid */
}
MPI_Barrier(icomm);
return;
}
/** @brief Compares an STL vector with POD vector.
*
* The accuracy criteria @f$ |x_k-y_k| < \varepsilon \left({1+0.5(|x_k|+|y_k|)}\right) @f$
* follows the book by
* <a href="https://www.springer.com/la/book/9783319446592">Stoyan/Baran</a>, p.8.
*
* @param[in] x STL vector
* @param[in] n length of POD vector
* @param[in] y POD vector
* @param[in] eps relative accuracy criteria (default := 0.0).
* @return true iff pairwise vector elements are relatively close to each other.
*
*/
bool CompareVectors(std::vector<double> const &x, int n, double const y[], double const eps = 0.0);
/** Output operator for vector
* @param[in,out] s output stream, e.g. @p cout
* @param[in] v vector
*
* @return output stream
*/
template <class T>
std::ostream &operator<<(std::ostream &s, std::vector<T> const &v)
{
for (auto vp : v) {
s << vp << " ";
}
return s;
}
/** Exchanges equal size partions of vector @p xin with all MPI processes.
* The received data are return in vector @p yout .
*
* @param[in] xin input vector
* @param[out] yout output vector
* @param[in] icomm MPI communicator
*
*/
void ExchangeAll(std::vector<double> const &xin, std::vector<double> &yout, MPI_Comm const &icomm = MPI_COMM_WORLD);
/** Exchanges equal size partions of vector @p xin with all MPI processes.
* The received data are return in vector @p xin .
*
* @param[in,out] xin input/output vector
* @param[in] icomm MPI communicator
*
*/
void ExchangeAllInPlace(std::vector<double> &xin, MPI_Comm const &icomm = MPI_COMM_WORLD);
#endif

View file

@ -0,0 +1,20 @@
%% Visualize results
%
% flatpak run org.octave.Octave <filename>
% or
% octave --no-window-system --no-gui -qf <filename>
%
% or
% matlab -nosplash < <filename>
clear all
clc
%%
fname = 'uv.txt';
[xc,ia,v] = ascii_read_meshvector(fname);
h = trisurf(ia, xc(:,1), xc(:,2), v);
waitfor(h) % wait for closing the figure

154
sheet7/GCC_default.mk Normal file
View file

@ -0,0 +1,154 @@
# Basic Defintions for using GNU-compiler suite sequentially
# requires setting of COMPILER=GCC_
#startmake as follows to avoid warnings caused by OpenMPI code
# make 2>&1 | grep -v openmpi
MPI_ROOT=/usr/bin/
CC = ${MPI_ROOT}mpicc
CXX = ${MPI_ROOT}mpicxx
F77 = ${MPI_ROOT}mpif77
LINKER = ${CXX}
# If you 'mpirun ...' reports some error "... not enough slots .." then use the option '--oversubscribe'
MPIRUN = ${MPI_ROOT}mpirun --oversubscribe -display-map
#MPIRUN = ${MPI_ROOT}mpiexec
# 2023, Oct 23: ""WARNING: There is at least non-excluded one OpenFabrics device found,"
# solution according to https://github.com/open-mpi/ompi/issues/11063
MPIRUN += -mca btl ^openib
# KFU:sauron
CXXFLAGS += -I/software/boost/1_72_0/include
WARNINGS = -Wall -pedantic -Woverloaded-virtual -Wfloat-equal -Wshadow \
-Wredundant-decls -Wunreachable-code -Winline -fmax-errors=1
# WARNINGS += -Weffc++ -Wextra
# -Wno-pragmas
CXXFLAGS += -std=c++17 -ffast-math -O3 -march=native ${WARNINGS}
# -ftree-vectorizer-verbose=5 -DNDEBUG
# -ftree-vectorizer-verbose=2
# CFLAGS = -ffast-math -O3 -DNDEBUG -msse3 -fopenmp -fdump-tree-vect-details
# CFLAGS = -ffast-math -O3 -funroll-loops -DNDEBUG -msse3 -fopenmp -ftree-vectorizer-verbose=2
# info on vectorization
#VECTORIZE = -ftree-vectorize -fdump-tree-vect-blocks=foo.dump
#-fdump-tree-pre=stderr
VECTORIZE = -ftree-vectorize -fopt-info -ftree-vectorizer-verbose=5
#CXXFLAGS += ${VECTORIZE}
# -funroll-all-loops -msse3
#GCC -march=knl -march=broadwell -march=haswell
# for debugging purpose (save code)
# -fsanitize=leak # only one out the trhee can be used
# -fsanitize=address
# -fsanitize=thread
SANITARY = -fsanitize=address -fsanitize=undefined -fsanitize=null -fsanitize=return \
-fsanitize=bounds -fsanitize=alignment -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow \
-fsanitize=bool -fsanitize=enum -fsanitize=vptr
#CXXFLAGS += ${SANITARY}
#LINKFLAGS +=${SANITARY}
# OpenMP
CXXFLAGS += -fopenmp
LINKFLAGS += -fopenmp
default: ${PROGRAM}
${PROGRAM}: ${OBJECTS}
$(LINKER) $^ ${LINKFLAGS} -o $@
@echo
@echo "Start with : $(MPIRUN) -np num_proc $(MPIFLAGS) $(PROGRAM)"
@echo
clean:
@rm -f ${PROGRAM} ${OBJECTS} gmon.out
clean_all:: clean
@rm -f *_ *~ *.bak *.log *.out *.tar *.orig
@rm -rf html latex
run: ${PROGRAM}
${MPIRUN} -np 4 ./$^
# tar the current directory
MY_DIR = `basename ${PWD}`
tar: clean_all
@echo "Tar the directory: " ${MY_DIR}
@cd .. ;\
tar cf ${MY_DIR}.tar ${MY_DIR} *default.mk ;\
cd ${MY_DIR}
# tar cf `basename ${PWD}`.tar *
zip: clean
@echo "Zip the directory: " ${MY_DIR}
@cd .. ;\
zip -r ${MY_DIR}.zip ${MY_DIR} *default.mk ;\
cd ${MY_DIR}
doc:
doxygen Doxyfile
#########################################################################
.cpp.o:
$(CXX) -c $(CXXFLAGS) -o $@ $<
# 2>&1 | grep -v openmpi
# special: get rid of compiler warnings genereate by openmpi-files
#.cpp.o:
# @$(CXX) -c $(CXXFLAGS) $< 2>/tmp/t.txt || grep -sv openmpi /tmp/t.txt
# |grep -sv openmpi
.c.o:
$(CC) -c $(CFLAGS) -o $@ $<
.f.o:
$(F77) -c $(FFLAGS) -o $@ $<
##################################################################################################
# some tools
# Cache behaviour (CXXFLAGS += -g tracks down to source lines; no -pg in linkflags)
cache: ${PROGRAM}
valgrind --tool=callgrind --simulate-cache=yes ./$^
# kcachegrind callgrind.out.<pid> &
kcachegrind `ls -1tr callgrind.out.* |tail -1`
# Check for wrong memory accesses, memory leaks, ...
# use smaller data sets
# no "-pg" in compile/link options
mem: ${PROGRAM}
valgrind -v --leak-check=yes --tool=memcheck --undef-value-errors=yes --track-origins=yes --log-file=$^.addr.out --show-reachable=yes mpirun -np 4 ./$^
# Graphical interface
# valkyrie
# Simple run time profiling of your code
# CXXFLAGS += -g -pg
# LINKFLAGS += -pg
prof: ${PROGRAM}
perf record ./$^
perf report
# gprof -b ./$^ > gp.out
# kprof -f gp.out -p gprof &
#Trace your heap:
#> heaptrack ./main.GCC_
#> heaptrack_gui heaptrack.main.GCC_.<pid>.gz
heap: ${PROGRAM}
heaptrack ./$^ 11
heaptrack_gui `ls -1tr heaptrack.$^.* |tail -1` &
codecheck: $(SOURCES)
cppcheck --enable=all --inconclusive --std=c++17 --suppress=missingIncludeSystem $^
########################################################################
# get the detailed status of all optimization flags
info:
echo "detailed status of all optimization flags"
$(CXX) --version
$(CXX) -Q $(CXXFLAGS) --help=optimizers

View file

@ -0,0 +1,107 @@
# Basic Defintions for using INTEL-MPI with its compilers
# requires setting of COMPILER=ICC_NATIVE_
# MPI_ROOT should be defined by shell
# path to icpc is contained in $PATH
MPI_BIN = $(shell dirname `which icpc` | sed 's/bin\/intel64/mpi\/intel64\/bin/g')/
MPI_LIB = $(shell echo ${MPI_BIN} | sed 's/bin/lib/g')
# Intel-MPI wrappers used gcc as default !!
CC = ${MPI_BIN}mpicc -cc=icc
CXX = ${MPI_BIN}mpicxx -cxx=icpc
F77 = ${MPI_BIN}mpif77 -f77=ifort
LINKER = ${CXX}
MPIRUN = ${MPI_BIN}mpirun
WARNINGS = -Wall -Wextra -pedantic -Woverloaded-virtual -Wfloat-equal -Wshadow
# -Weffc++ -Wunreachable-code -Winline
CXXFLAGS += -O3 -fargument-noalias -DNDEBUG -std=c++17 ${WARNINGS} ${MPI_COMPILE_FLAGS}
CFLAGS += -O3 -fargument-noalias -DNDEBUG -Wall -Wextra -pedantic -Wfloat-equal \
-Wshadow ${MPI_COMPILE_FLAGS}
# -vec-report=3 -mkl
# -guide -parallel
# -guide-opts=string -guide-par[=n] -guide-vec[=n]
# -auto-p32 -simd
# use MKL by INTEL
LINKFLAGS += -mkl ${MPI_LINK_FLAGS}
default: ${PROGRAM}
${PROGRAM}: ${OBJECTS}
$(LINKER) $^ ${LINKFLAGS} -o $@
@echo
@echo "Start with : $(MPIRUN) -np num_proc $(MPIFLAGS) $(PROGRAM)"
@echo
clean:
rm -f ${PROGRAM} ${OBJECTS}
clean_all:: clean
@rm -f *_ *~ *.bak *.log *.out *.tar
run: ${PROGRAM}
(export LD_LIBRARY_PATH=${MPI_LIB}:${LD_LIBRARY_PATH} ;${MPIRUN} -np 4 ./$^ ${PROG_ARGS})
# tar the current directory
MY_DIR = `basename ${PWD}`
tar: clean_all
@echo "Tar the directory: " ${MY_DIR}
@cd .. ;\
tar cf ${MY_DIR}.tar ${MY_DIR} *default.mk ;\
cd ${MY_DIR}
# tar cf `basename ${PWD}`.tar *
doc:
doxygen Doxyfile
#########################################################################
.cpp.o:
$(CXX) -c $(CXXFLAGS) -o $@ $<
.c.o:
$(CC) -c $(CFLAGS) -o $@ $<
.f.o:
$(F77) -c $(FFLAGS) -o $@ $<
##################################################################################################
# # some tools
# # Cache behaviour (CXXFLAGS += -g tracks down to source lines)
# cache: ${PROGRAM}
# valgrind --tool=callgrind --simulate-cache=yes ./$^
# # kcachegrind callgrind.out.<pid> &
#
# # Check for wrong memory accesses, memory leaks, ...
# # use smaller data sets
# mem: ${PROGRAM}
# valgrind -v --leak-check=yes --tool=memcheck --undef-value-errors=yes --track-origins=yes --log-file=$^.addr.out --show-reachable=yes ./$^
#
# # Simple run time profiling of your code
# # CXXFLAGS += -g -pg
# # LINKFLAGS += -pg
# prof: ${PROGRAM}
# ./$^
# gprof -b ./$^ > gp.out
# # kprof -f gp.out -p gprof &
#
mem: inspector
prof: amplifier
cache: amplifier
gap_par_report:
${CXX} -c -guide -parallel $(SOURCES) 2> gap.txt
# GUI for performance report
amplifier: ${PROGRAM}
${BINDIR}../vtune_amplifier_xe_2013/bin64/amplxe-gui &
# GUI for Memory and Thread analyzer (race condition)
inspector: ${PROGRAM}
# http://askubuntu.com/questions/41629/after-upgrade-gdb-wont-attach-to-process
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
${BINDIR}../inspector_xe_2013/bin64/inspxe-gui &

112
sheet7/ICC_default.mk Normal file
View file

@ -0,0 +1,112 @@
# Basic Defintions for using INTEL compilers with OpenMPI headers and libraries
# requires setting of COMPILER=ICC_
# MPI_ROOT should be defined by shell
MPI_ROOT=/usr
CC = icc
CXX = icpc
F77 = ifort
LINKER = ${CXX}
MPIRUN = ${MPI_ROOT}/bin/mpirun
# no differences when C or C++ is used !! (always used options from mpicxx)
MPI_COMPILE_FLAGS = `${MPI_ROOT}/bin/mpicxx -showme:compile`
MPI_LINK_FLAGS = `${MPI_ROOT}/bin/mpicxx -showme:link`
# MPI_LINK_FLAGS = -pthread -L/usr/lib/openmpi/lib -lmpi_cxx -lmpi -lopen-rte -lopen-pal -ldl -Wl,--export-dynamic -lnsl -lutil -lm -ldl
WARNINGS = -Wall -Wextra -pedantic -Woverloaded-virtual -Wfloat-equal -Wshadow
# -Weffc++ -Wunreachable-code -Winline
CXXFLAGS += -O3 -std=c++17 -fargument-noalias -DNDEBUG ${WARNINGS} ${MPI_COMPILE_FLAGS}
CFLAGS += -O3 -fargument-noalias -DNDEBUG -Wall -Wextra -pedantic -Wfloat-equal \
-Wshadow ${MPI_COMPILE_FLAGS}
# -vec-report=3 -mkl
# -guide -parallel
# -guide-opts=string -guide-par[=n] -guide-vec[=n]
# -auto-p32 -simd
# use MKL by INTEL
LINKFLAGS += -mkl
# use MPI by Compiler
LINKFLAGS += ${MPI_LINK_FLAGS}
default: ${PROGRAM}
${PROGRAM}: ${OBJECTS}
$(LINKER) $^ ${LINKFLAGS} -o $@
@echo
@echo "Start with : $(MPIRUN) -np num_proc $(MPIFLAGS) $(PROGRAM)"
@echo
clean:
rm -f ${PROGRAM} ${OBJECTS}
clean_all:: clean
@rm -f *_ *~ *.bak *.log *.out *.tar
run: ${PROGRAM}
${MPIRUN} -np 4 ./$^
# tar the current directory
MY_DIR = `basename ${PWD}`
tar: clean_all
@echo "Tar the directory: " ${MY_DIR}
@cd .. ;\
tar cf ${MY_DIR}.tar ${MY_DIR} *default.mk ;\
cd ${MY_DIR}
# tar cf `basename ${PWD}`.tar *
doc:
doxygen Doxyfile
#########################################################################
.cpp.o:
$(CXX) -c $(CXXFLAGS) -o $@ $<
.c.o:
$(CC) -c $(CFLAGS) -o $@ $<
.f.o:
$(F77) -c $(FFLAGS) -o $@ $<
##################################################################################################
# # some tools
# # Cache behaviour (CXXFLAGS += -g tracks down to source lines)
# cache: ${PROGRAM}
# valgrind --tool=callgrind --simulate-cache=yes ./$^
# # kcachegrind callgrind.out.<pid> &
#
# # Check for wrong memory accesses, memory leaks, ...
# # use smaller data sets
# mem: ${PROGRAM}
# valgrind -v --leak-check=yes --tool=memcheck --undef-value-errors=yes --track-origins=yes --log-file=$^.addr.out --show-reachable=yes ./$^
#
# # Simple run time profiling of your code
# # CXXFLAGS += -g -pg
# # LINKFLAGS += -pg
# prof: ${PROGRAM}
# ./$^
# gprof -b ./$^ > gp.out
# # kprof -f gp.out -p gprof &
#
mem: inspector
prof: amplifier
cache: amplifier
gap_par_report:
${CXX} -c -guide -parallel $(SOURCES) 2> gap.txt
# GUI for performance report
amplifier: ${PROGRAM}
${BINDIR}../vtune_amplifier_xe_2013/bin64/amplxe-gui &
# GUI for Memory and Thread analyzer (race condition)
inspector: ${PROGRAM}
# http://askubuntu.com/questions/41629/after-upgrade-gdb-wont-attach-to-process
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
${BINDIR}../inspector_xe_2013/bin64/inspxe-gui &

View file

@ -0,0 +1,128 @@
# Basic Defintions for using OpenMPI with CLANG compilers
# requires setting of COMPILER=OPENMPI_CLANG_
# Pass CLANG Compilers to the OpenMPI wrappers
# see: https://www.open-mpi.org/faq/?category=mpi-apps#override-wrappers-after-v1.0
EXPORT = export OMPI_CXX=clang++; export OMPI_CC=clang; export OMPI_mpifort=flang
CC = mpicc
CXX = mpicxx
F77 = mpifort
LINKER = ${CXX}
MPIRUN = ${MPI_BIN}mpirun
#http://clang.llvm.org/docs/UsersManual.html#options-to-control-error-and-warning-messages
SILENCE_MPI = -Wno-weak-vtables -Wno-old-style-cast -Wno-cast-align -Wno-deprecated
SILENCE_MPI+= -Wno-sign-conversion -Wno-reserved-id-macro -Wno-c++98-compat-pedantic
SILENCE_MPI+= -Wno-zero-as-null-pointer-constant -Wno-source-uses-openmp
WARNINGS = -Weverything -Wno-c++98-compat -Wno-weak-vtables -ferror-limit=3 ${SILENCE_MPI}
#-fsyntax-only -Wdocumentation -Wconversion -Wshadow -Wfloat-conversion -pedantic
CXXFLAGS += -Ofast -std=c++17 ${WARNINGS}
#CXXFLAGS += -Ofast -std=c++17
# -ftrapv
#
CFLAGS += -Ofast -Weverything -ferror-limit=3 ${MPI_COMPILE_FLAGS}
# OpenMP
#CXXFLAGS += -fopenmp
#LINKFLAGS += -fopenmp
# tidy_check
SWITCH_OFF=,-readability-magic-numbers,-readability-redundant-control-flow,-readability-redundant-member-init
SWITCH_OFF+=,-readability-redundant-member-init,-readability-isolate-declaration
#READABILITY=,readability*${SWITCH_OFF}
#TIDYFLAGS = -checks=llvm-*,-llvm-header-guard -header-filter=.* -enable-check-profile -extra-arg="-std=c++17" -extra-arg="-fopenmp"
TIDYFLAGS = -checks=llvm-*,-llvm-header-guard${READABILITY} -header-filter=.* -enable-check-profile -extra-arg="-std=c++17" -extra-arg="-fopenmp"
#TIDYFLAGS += -checks='modernize*
MPI_COMPILE_FLAGS = `${MPI_BIN}mpicxx -showme:compile`
MPI_LINK_FLAGS = `${MPI_BIN}mpicxx -showme:link`
#TIDYFLAGS += ${MPI_COMPILE_FLAGS}
TIDYFLAGS += -extra-arg="-I/usr/lib/x86_64-linux-gnu/openmpi/include"
#check:
# echo ${MPI_COMPILE_FLAGS}
default: ${PROGRAM}
${PROGRAM}: ${OBJECTS}
@( ${EXPORT}; $(LINKER) $^ ${LINKFLAGS} -o $@ )
@echo
@echo "Start with : $(MPIRUN) -np num_proc $(MPIFLAGS) $(PROGRAM)"
@echo
clean:
rm -f ${PROGRAM} ${OBJECTS}
clean_all:: clean
@rm -f *_ *~ *.bak *.log *.out *.tar
codecheck: tidy_check
tidy_check:
clang-tidy ${SOURCES} ${TIDYFLAGS} -- ${SOURCES}
# see also http://clang-developers.42468.n3.nabble.com/Error-while-trying-to-load-a-compilation-database-td4049722.html
run: ${PROGRAM}
${MPIRUN} -np 4 ./$^ ${PROG_ARGS}
# tar the current directory
MY_DIR = `basename ${PWD}`
tar: clean_all
@echo "Tar the directory: " ${MY_DIR}
@cd .. ;\
tar cf ${MY_DIR}.tar ${MY_DIR} *default.mk ;\
cd ${MY_DIR}
# tar cf `basename ${PWD}`.tar *
doc:
doxygen Doxyfile
#########################################################################
.cpp.o:
@( ${EXPORT}; $(CXX) -c $(CXXFLAGS) -o $@ $< )
.c.o:
@( ${EXPORT}; $(CC) -c $(CFLAGS) -o $@ $< )
.f.o:
$(F77) -c $(FFLAGS) -o $@ $<
##################################################################################################
# # some tools
# # Cache behaviour (CXXFLAGS += -g tracks down to source lines)
# cache: ${PROGRAM}
# valgrind --tool=callgrind --simulate-cache=yes ./$^
# # kcachegrind callgrind.out.<pid> &
#
# # Check for wrong memory accesses, memory leaks, ...
# # use smaller data sets
# mem: ${PROGRAM}
# valgrind -v --leak-check=yes --tool=memcheck --undef-value-errors=yes --track-origins=yes --log-file=$^.addr.out --show-reachable=yes ./$^
#
# # Simple run time profiling of your code
# # CXXFLAGS += -g -pg
# # LINKFLAGS += -pg
# prof: ${PROGRAM}
# ./$^
# gprof -b ./$^ > gp.out
# # kprof -f gp.out -p gprof &
#
mem: inspector
prof: amplifier
cache: amplifier
gap_par_report:
${CXX} -c -guide -parallel $(SOURCES) 2> gap.txt
# GUI for performance report
amplifier: ${PROGRAM}
${BINDIR}../vtune_amplifier_xe_2013/bin64/amplxe-gui &
# GUI for Memory and Thread analyzer (race condition)
inspector: ${PROGRAM}
# http://askubuntu.com/questions/41629/after-upgrade-gdb-wont-attach-to-process
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
${BINDIR}../inspector_xe_2013/bin64/inspxe-gui &

View file

@ -0,0 +1,107 @@
# Basic Defintions for using OpenMPI with Intel compilers
# requires setting of COMPILER=OPENMPI_ICC_
# Pass Intel Compilers to the OpenMPI wrappers
# see: https://www.open-mpi.org/faq/?category=mpi-apps#override-wrappers-after-v1.0
EXPORT = export OMPI_CXX=icpc; export OMPI_CC=icc; export OMPI_mpifort=ifort
CC = mpicc
CXX = mpicxx
F77 = mpifort
LINKER = ${CXX}
MPIRUN = ${MPI_BIN}mpirun
WARNINGS = -Wall -Wextra -pedantic -Woverloaded-virtual -Wfloat-equal -Wshadow
# -Weffc++ -Wunreachable-code -Winline
CXXFLAGS += -fast -fargument-noalias -DNDEBUG -std=c++17 ${WARNINGS}
CFLAGS += -O3 -fargument-noalias -DNDEBUG -Wall -Wextra -pedantic -Wfloat-equal -Wshadow
# -vec-report=3 -mkl
# -guide -parallel
# -guide-opts=string -guide-par[=n] -guide-vec[=n]
# -auto-p32 -simd
# use MKL by INTEL
LINKFLAGS += -O3 -mkl ${MPI_LINK_FLAGS}
# ipo: warning #11021: unresolved __GI_memset
# see: https://software.intel.com/en-us/articles/ipo-warning-11021-unresolved-symbols-referenced-a-dynamic-library
LINKFLAGS +=
default: ${PROGRAM}
${PROGRAM}: ${OBJECTS}
@( ${EXPORT}; $(LINKER) $^ ${LINKFLAGS} -o $@ )
@echo
@echo "Start with : $(MPIRUN) -np num_proc $(MPIFLAGS) $(PROGRAM)"
@echo
clean:
rm -f ${PROGRAM} ${OBJECTS}
clean_all:: clean
@rm -f *_ *~ *.bak *.log *.out *.tar
run: ${PROGRAM}
${MPIRUN} -np 4 ./$^ ${PROG_ARGS}
# tar the current directory
MY_DIR = `basename ${PWD}`
tar: clean_all
@echo "Tar the directory: " ${MY_DIR}
@cd .. ;\
tar cf ${MY_DIR}.tar ${MY_DIR} *default.mk ;\
cd ${MY_DIR}
# tar cf `basename ${PWD}`.tar *
doc:
doxygen Doxyfile
#########################################################################
.cpp.o:
@( ${EXPORT}; $(CXX) -c $(CXXFLAGS) -o $@ $< )
.c.o:
@( ${EXPORT}; $(CC) -c $(CFLAGS) -o $@ $< )
.f.o:
$(F77) -c $(FFLAGS) -o $@ $<
##################################################################################################
# # some tools
# # Cache behaviour (CXXFLAGS += -g tracks down to source lines)
# cache: ${PROGRAM}
# valgrind --tool=callgrind --simulate-cache=yes ./$^
# # kcachegrind callgrind.out.<pid> &
#
# # Check for wrong memory accesses, memory leaks, ...
# # use smaller data sets
# mem: ${PROGRAM}
# valgrind -v --leak-check=yes --tool=memcheck --undef-value-errors=yes --track-origins=yes --log-file=$^.addr.out --show-reachable=yes ./$^
#
# # Simple run time profiling of your code
# # CXXFLAGS += -g -pg
# # LINKFLAGS += -pg
# prof: ${PROGRAM}
# ./$^
# gprof -b ./$^ > gp.out
# # kprof -f gp.out -p gprof &
#
mem: inspector
prof: amplifier
cache: amplifier
gap_par_report:
${CXX} -c -guide -parallel $(SOURCES) 2> gap.txt
# GUI for performance report
amplifier: ${PROGRAM}
${BINDIR}../vtune_amplifier_xe_2013/bin64/amplxe-gui &
# GUI for Memory and Thread analyzer (race condition)
inspector: ${PROGRAM}
# http://askubuntu.com/questions/41629/after-upgrade-gdb-wont-attach-to-process
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
${BINDIR}../inspector_xe_2013/bin64/inspxe-gui &

View file

@ -0,0 +1,125 @@
# Use the MPI-wrappers from the PGI compiler suite.
# requires setting of COMPILER=PGI_MPI_
#
# requires
# sudo apt install librdmacm1
# Details for run time information
# export PGI_ACC_TIME=1
# unset PGI_ACC_TIME
# export PGI_ACC_NOTIFY=1
# export PGI_ACC_NOTIFY=3
# unset PGI_ACC_NOTIFY
PGI_PATH = /opt/pgi/linux86-64/2019/bin
#ifeq "$(HOSTNAME)" "mephisto.uni-graz.at"
# # mephisto
# PGI_PATH = /share/apps/pgi/linux86-64/2016/bin
#endif
#MPI_ROOT=${PGI_PATH}mpi/mpich/bin/
MPI_ROOT= ${PGI_PATH}/../mpi/openmpi-3.1.3/bin/
MPIRUN = ${MPI_ROOT}mpirun
CC = ${MPI_ROOT}mpicc
CXX = ${MPI_ROOT}mpicxx
#F77 = ${MPI_ROOT}mpif77
ifndef LINKER
LINKER = ${CC}
endif
LINKER = ${CXX}
WARNINGS = -Minform=warn
PGI_PROFILING += -Minfo=loop,vect,opt,intensity,mp,accel
#PGI_PROFILING += -Mprof=lines Minfo=ccff
CXXFLAGS += -e3 -std=c++17 -fast ${PGI_PROFILING} ${WARNINGS} -Mnodepchk
CFLAGS += -fast ${PGI_PROFILING} ${WARNINGS} -Mnodepchk
#
# for OpenACC
# Target architecture (nvidia,host)
TA_ARCH = host
#TA_ARCH = nvidia,host
#TA_ARCH = -ta=nvidia:cc2+,cuda5.5,fastmath
#TA_ARCH = -acc -DNDEBUG -ta=nvidia:cc2+,cuda5.5,fastmath,keepgpu
#TA_ARCH = -acc -DNDEBUG -ta=nvidia:cc2+,fastmath,keepgpu
#,keepgpu
# CFLAGS = -O3 -ta=$(TA_ARCH)
#CFLAGS += -B -gopt $(TA_ARCH)
#CXXFLAGS += -B -gopt $(TA_ARCH)
# -Minfo=all
# libcudart.a is needed for direct CUDA calls
#LINKFLAGS = -gopt $(TA_ARCH) -L${BINDIR}../lib $(PGI_PROFILING)
# -lcudart
default: ${PROGRAM}
${PROGRAM}: ${OBJECTS}
$(LINKER) $^ ${LINKFLAGS} -o $@
clean:
rm -f ${PROGRAM} ${OBJECTS} *.gpu *gprof.out
clean_all:: clean
@rm -f *_ *~ *.bak *.log *.out *.tar
#run: clean ${PROGRAM}
run: ${PROGRAM}
${MPIRUN} -np 4 ${OPTIRUN} ./${PROGRAM}
# tar the current directory
MY_DIR = `basename ${PWD}`
tar: clean_all
@echo "Tar the directory: " ${MY_DIR}
@cd .. ;\
tar cf ${MY_DIR}.tar ${MY_DIR} *default.mk ;\
cd ${MY_DIR}
# tar cf `basename ${PWD}`.tar *
doc:
doxygen Doxyfile
#########################################################################
.cpp.o:
$(CXX) -c $(CXXFLAGS) -o $@ $<
.c.o:
$(CC) -c $(CFLAGS) -o $@ $<
.f.o:
$(F77) -c $(FFLAGS) -o $@ $<
##################################################################################################
# # some tools
# # Simple run time profiling of your code
# # CXXFLAGS += -g -pg
# # LINKFLAGS += -pg
# Profiling options PGI, see: pgcollect -help
CPU_PROF = -allcache
GPU_PROF = -cuda=gmem,branch,cc13 -cudainit
#GPU_PROF = -cuda=branch:cc20
#
PROF_FILE = pgprof.out
prof: ${PROGRAM}
# ./$^
# $(CUDA_HOME)/bin/nvvp &
# export LD_LIBRARY_PATH=/state/partition1/apps/pgi/linux86-64/12.9/lib:$LD_LIBRARY_PATH
${OPTIRUN} ${BINDIR}pgcollect $(GPU_PROF) ./$^
${OPTIRUN} ${BINDIR}pgprof -exe ./$^ $(PROF_FILE) &
# Memory checker (slooooow!!!):
# see doc at /usr/local/cuda/doc/cuda-memcheck.pdf
# mem: ${PROGRAM}
# $(CUDA_HOME)memcheck ./$^

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,56 @@
#
# use GNU-Compiler tools
COMPILER=GCC_
# COMPILER=GCC_SEQ_
# alternatively from the shell
# export COMPILER=GCC_
# or, alternatively from the shell
# make COMPILER=GCC_
MAIN = main
SOURCES = ${MAIN}.cpp vdop.cpp geom.cpp par_geom.cpp\
getmatrix.cpp jacsolve.cpp userset.cpp
# cuthill_mckee_ordering.cpp
OBJECTS = $(SOURCES:.cpp=.o)
PROGRAM = ${MAIN}.${COMPILER}
# uncomment the next to lines for debugging and detailed performance analysis
CXXFLAGS += -g
# -DNDEBUG
# -pg slows down the code on my laptop when using CLANG_
LINKFLAGS += -g
#-pg
#CXXFLAGS += -Q --help=optimizers
#CXXFLAGS += -fopt-info
include ../${COMPILER}default.mk
#############################################################################
# additional specific cleaning in this directory
clean_all::
@rm -f t.dat*
#############################################################################
# special testing
# NPROCS = 4
#
TFILE = t.dat
# TTMP = t.tmp
#
graph: $(PROGRAM)
# @rm -f $(TFILE).*
# next two lines only sequentially
./$(PROGRAM)
@mv $(TFILE).000 $(TFILE)
# $(MPIRUN) $(MPIFLAGS) -np $(NPROCS) $(PROGRAM)
# @echo " "; echo "Manipulate data for graphics."; echo " "
# @cat $(TFILE).* > $(TTMP)
# @sort -b -k 2 $(TTMP) -o $(TTMP).1
# @sort -b -k 1 $(TTMP).1 -o $(TTMP).2
# @awk -f nl.awk $(TTMP).2 > $(TFILE)
# @rm -f $(TTMP).* $(TTMP) $(TFILE).*
#
-gnuplot jac.dem

View file

@ -0,0 +1,43 @@
function [ xc, ia, v ] = ascii_read_meshvector( fname )
%
% Loads the 2D triangular mesh (coordinates, vertex connectivity)
% together with values on its vertices from an ASCII file.
% Matlab indexing is stored (starts with 1).
%
% The input file format is compatible
% with Mesh_2d_3_matlab:Write_ascii_matlab(..) in jacobi_oo_stl/geom.h
%
%
% IN: fname - filename
% OUT: xc - coordinates
% ia - mesh connectivity
% v - solution vector
DELIMETER = ' ';
fprintf('Read file %s\n',fname)
% Read mesh constants
nn = dlmread(fname,DELIMETER,[0 0 0 3]); %% row_1, col_1, row_2, col_2 in C indexing!!!
nnode = nn(1);
ndim = nn(2);
nelem = nn(3);
nvert = nn(4);
% Read coordinates
row_start = 0+1;
row_end = 0+nnode;
xc = dlmread(fname,DELIMETER,[row_start 0 row_end ndim-1]);
% Read connectivity
row_start = row_end+1;
row_end = row_end+nelem;
ia = dlmread(fname,DELIMETER,[row_start 0 row_end nvert-1]);
% Read solution
row_start = row_end+1;
row_end = row_end+nnode;
v = dlmread(fname,DELIMETER,[row_start 0 row_end 0]);
end

View file

@ -0,0 +1,54 @@
function ascii_write_mesh( xc, ia, e, basename)
%
% Saves the 2D triangular mesh in the minimal way (only coordinates, vertex connectivity, minimal boundary edge info)
% in an ASCII file.
% Matlab indexing is stored (starts with 1).
%
% The output file format is compatible with Mesh_2d_3_matlab:Mesh_2d_3_matlab(std::string const &fname) in jacobi_oo_stl/geom.h
%
% IN:
% coordinates xc: [2][nnode]
% connectivity ia: [4][nelem] with t(4,:) are the subdomain numbers
% edges e: [7][nedges] boundary edges
% e([1,2],:) - start/end vertex of edge
% e([3,4],:) - start/end values
% e(5,:) - segment number
% e([6,7],:) - left/right subdomain
% basename: file name without extension
%
% Data have been generated via <https://de.mathworks.com/help/pde/ug/initmesh.html initmesh>.
%
fname = [basename, '.txt'];
nnode = int32(size(xc,2));
ndim = int32(size(xc,1));
nelem = int32(size(ia,2));
nvert_e = int32(3);
dlmwrite(fname,nnode,'delimiter','\t','precision',16) % number of nodes
dlmwrite(fname,ndim,'-append','delimiter','\t','precision',16) % space dimension
dlmwrite(fname,nelem,'-append','delimiter','\t','precision',16) % number of elements
dlmwrite(fname,nvert_e,'-append','delimiter','\t','precision',16) % number of vertices per element
%% dlmwrite(fname,xc(:),'-append','delimiter','\t','precision',16) % coordinates
dlmwrite(fname,xc([1,2],:).','-append','delimiter','\t','precision',16) % coordinates
%% no subdomain info transferred
tmp=int32(ia(1:3,:));
% dlmwrite(fname,tmp(:),'-append','delimiter','\t','precision',16) % connectivity in Matlab indexing
dlmwrite(fname,tmp(:,:).','-append','delimiter','\t','precision',16) % connectivity in Matlab indexing
%% store only start and end point of boundary edges,
nbedges = size(e,2);
dlmwrite(fname,nbedges,'-append','delimiter','\t','precision',16) % number boundary edges
tmp=int32(e(1:2,:));
% dlmwrite(fname,tmp(:),'-append','delimiter','\t','precision',16) % boundary edges in Matlab indexing
dlmwrite(fname,tmp(:,:).','-append','delimiter','\t','precision',16) % boundary edges in Matlab indexing
%% Add subdomain information to edges
tmp=int32(e(6:7,:));
dlmwrite(fname,tmp(:,:).','-append','delimiter','\t','precision',16) % subdomain information to edges
end

View file

@ -0,0 +1,51 @@
function ascii_write_subdomains( xc, ia, e, basename)
%
% Saves the 2D triangular mesh in the minimal way (only coordinates, vertex connectivity, minimal boundary edge info)
% in an ASCII file.
% Matlab indexing is stored (starts with 1).
%
% The output file format is compatible with Mesh_2d_3_matlab:Mesh_2d_3_matlab(std::string const &fname) in jacobi_oo_stl/geom.h
%
% IN:
% coordinates xc: [2][nnode]
% connectivity ia: [4][nelem] with t(4,:) are the subdomain numbers
% edges e: [7][nedges] boundary edges
% e([1,2],:) - start/end vertex of edge
% e([3,4],:) - start/end values
% e(5,:) - segment number
% e([6,7],:) - left/right subdomain
% basename: file name without extension
%
% Data have been generated via <https://de.mathworks.com/help/pde/ug/initmesh.html initmesh>.
%
fname = [basename, '_sd.txt'];
nnode = int32(size(xc,2));
ndim = int32(size(xc,1));
nelem = int32(size(ia,2))
nvert_e = int32(3);
% dlmwrite(fname,nnode,'delimiter','\t','precision',16) % number of nodes
% dlmwrite(fname,ndim,'-append','delimiter','\t','precision',16) % space dimension
% dlmwrite(fname,nelem,'-append','delimiter','\t','precision',16) % number of elements
dlmwrite(fname,nelem,'delimiter','\t','precision',16) % number of elements
% dlmwrite(fname,nvert_e,'-append','delimiter','\t','precision',16) % number of vertices per element
% % dlmwrite(fname,xc(:),'-append','delimiter','\t','precision',16) % coordinates
% dlmwrite(fname,xc([1,2],:).','-append','delimiter','\t','precision',16) % coordinates
% subdomain info
tmp=int32(ia(4,:));
% % dlmwrite(fname,tmp(:),'-append','delimiter','\t','precision',16) % connectivity in Matlab indexing
% dlmwrite(fname,tmp(:,:).','-append','delimiter','\t','precision',16) % connectivity in Matlab indexing
dlmwrite(fname,tmp(:,:).','-append','delimiter','\t') % connectivity in Matlab indexing
% % store only start and end point of boundary edges,
% nbedges = size(e,2);
% dlmwrite(fname,nbedges,'-append','delimiter','\t','precision',16) % number boundary edges
% tmp=int32(e(1:2,:));
% % dlmwrite(fname,tmp(:),'-append','delimiter','\t','precision',16) % boundary edges in Matlab indexing
% dlmwrite(fname,tmp(:,:).','-append','delimiter','\t','precision',16) % boundary edges in Matlab indexing
end

View file

@ -0,0 +1,218 @@
//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// This file is part of the Boost Graph Library
//
// You should have received a copy of the License Agreement for the
// Boost Graph Library along with the software; see the file LICENSE.
// If not, contact Office of Research, University of Notre Dame, Notre
// Dame, IN 46556.
//
// Permission to modify the code and to distribute modified code is
// granted, provided the text of this NOTICE is retained, a notice that
// the code was modified is included with the above COPYRIGHT NOTICE and
// with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
// file is distributed with the modified code.
//
// LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
// By way of example, but not limitation, Licensor MAKES NO
// REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
// OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
// OR OTHER RIGHTS.
// Modyfied 2019 by Gundolf Haase, University of Graz
//=======================================================================
#include "cuthill_mckee_ordering.h"
#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/bandwidth.hpp>
#include <boost/graph/cuthill_mckee_ordering.hpp>
#include <boost/graph/properties.hpp>
#include <iostream>
#include <vector>
/*
Sample Output
original bandwidth: 8
Reverse Cuthill-McKee ordering starting at: 6
8 3 0 9 2 5 1 4 7 6
bandwidth: 4
Reverse Cuthill-McKee ordering starting at: 0
9 1 4 6 7 2 8 5 3 0
bandwidth: 4
Reverse Cuthill-McKee ordering:
0 8 5 7 3 6 4 2 1 9
bandwidth: 4
*/
/*
int main(int, char *[])
{
using namespace boost;
using namespace std;
typedef adjacency_list<vecS, vecS, undirectedS,
property<vertex_color_t, default_color_type,
property<vertex_degree_t, int> > > Graph;
typedef graph_traits<Graph>::vertex_descriptor Vertex;
typedef graph_traits<Graph>::vertices_size_type size_type;
typedef std::pair<std::size_t, std::size_t> Pair;
Pair edges[14] = { Pair(0, 3), //a-d
Pair(0, 5), //a-f
Pair(1, 2), //b-c
Pair(1, 4), //b-e
Pair(1, 6), //b-g
Pair(1, 9), //b-j
Pair(2, 3), //c-d
Pair(2, 4), //c-e
Pair(3, 5), //d-f
Pair(3, 8), //d-i
Pair(4, 6), //e-g
Pair(5, 6), //f-g
Pair(5, 7), //f-h
Pair(6, 7)
}; //g-h
Graph G(10);
for (int i = 0; i < 14; ++i)
add_edge(edges[i].first, edges[i].second, G);
graph_traits<Graph>::vertex_iterator ui, ui_end;
property_map<Graph, vertex_degree_t>::type deg = get(vertex_degree, G);
for (boost::tie(ui, ui_end) = vertices(G); ui != ui_end; ++ui)
deg[*ui] = degree(*ui, G);
property_map<Graph, vertex_index_t>::type
index_map = get(vertex_index, G);
std::cout << "original bandwidth: " << bandwidth(G) << std::endl;
std::vector<Vertex> inv_perm(num_vertices(G));
std::vector<size_type> perm(num_vertices(G));
{
Vertex s = vertex(6, G);
//reverse cuthill_mckee_ordering
cuthill_mckee_ordering(G, s, inv_perm.rbegin(), get(vertex_color, G),
get(vertex_degree, G));
cout << "Reverse Cuthill-McKee ordering starting at: " << s << endl;
cout << " ";
for (std::vector<Vertex>::const_iterator i = inv_perm.begin();
i != inv_perm.end(); ++i)
cout << index_map[*i] << " ";
cout << endl;
for (size_type c = 0; c != inv_perm.size(); ++c)
perm[index_map[inv_perm[c]]] = c;
std::cout << " bandwidth: "
<< bandwidth(G, make_iterator_property_map(&perm[0], index_map, perm[0]))
<< std::endl;
}
{
Vertex s = vertex(0, G);
//reverse cuthill_mckee_ordering
cuthill_mckee_ordering(G, s, inv_perm.rbegin(), get(vertex_color, G),
get(vertex_degree, G));
cout << "Reverse Cuthill-McKee ordering starting at: " << s << endl;
cout << " ";
for (std::vector<Vertex>::const_iterator i = inv_perm.begin();
i != inv_perm.end(); ++i)
cout << index_map[*i] << " ";
cout << endl;
for (size_type c = 0; c != inv_perm.size(); ++c)
perm[index_map[inv_perm[c]]] = c;
std::cout << " bandwidth: "
<< bandwidth(G, make_iterator_property_map(&perm[0], index_map, perm[0]))
<< std::endl;
}
{
//reverse cuthill_mckee_ordering
cuthill_mckee_ordering(G, inv_perm.rbegin(), get(vertex_color, G),
make_degree_map(G));
cout << "Reverse Cuthill-McKee ordering:" << endl;
cout << " ";
for (std::vector<Vertex>::const_iterator i = inv_perm.begin();
i != inv_perm.end(); ++i)
cout << index_map[*i] << " ";
cout << endl;
for (size_type c = 0; c != inv_perm.size(); ++c)
perm[index_map[inv_perm[c]]] = c;
std::cout << " bandwidth: "
<< bandwidth(G, make_iterator_property_map(&perm[0], index_map, perm[0]))
<< std::endl;
}
return 0;
}
*/
// ------------- Modifications by Gundolf Haase
// std::vector<int> _edges; //!< edges of mesh (vertices ordered ascending)
using namespace boost;
using namespace std;
typedef adjacency_list<vecS, vecS, undirectedS,
property<vertex_color_t, default_color_type,
property<vertex_degree_t, int> > > Graph;
typedef graph_traits<Graph>::vertex_descriptor Vertex;
typedef graph_traits<Graph>::vertices_size_type size_type;
typedef std::pair<std::size_t, std::size_t> Pair;
vector<int> cuthill_mckee_reordering(vector<int> const &_edges)
{
size_t const nnodes = *max_element(cbegin(_edges), cend(_edges)) + 1;
cout << "NNODES = " << nnodes << endl;
//size_t const nedges = _edges.size()/2;
Graph G(nnodes);
for (size_t i = 0; i < _edges.size(); i+=2)
add_edge(_edges[i], _edges[i+1], G);
graph_traits<Graph>::vertex_iterator ui, ui_end;
property_map<Graph, vertex_degree_t>::type deg = get(vertex_degree, G);
for (boost::tie(ui, ui_end) = vertices(G); ui != ui_end; ++ui)
deg[*ui] = degree(*ui, G);
property_map<Graph, vertex_index_t>::type
index_map = get(vertex_index, G);
std::cout << "original bandwidth: " << bandwidth(G) << std::endl;
std::vector<Vertex> inv_perm(num_vertices(G));
//std::vector<size_type> perm(num_vertices(G));
std::vector<int> perm(num_vertices(G));
{
Vertex s = vertex(nnodes/2, G);
//reverse cuthill_mckee_ordering
cuthill_mckee_ordering(G, s, inv_perm.rbegin(), get(vertex_color, G),
get(vertex_degree, G));
//cout << "Reverse Cuthill-McKee ordering starting at: " << s << endl;
//cout << " ";
//for (std::vector<Vertex>::const_iterator i = inv_perm.begin(); i != inv_perm.end(); ++i)
//cout << index_map[*i] << " ";
//cout << endl;
for (size_type c = 0; c != inv_perm.size(); ++c)
perm[index_map[inv_perm[c]]] = c;
std::cout << "improved bandwidth: "
<< bandwidth(G, make_iterator_property_map(&perm[0], index_map, perm[0]))
<< std::endl;
}
assert(perm.size()==nnodes);
return perm;
}
// ------------- end Modifications

View file

@ -0,0 +1,8 @@
#ifndef CUTHILL_MCKEE_ORDERING
#define CUTHILL_MCKEE_ORDERING
#include <vector>
std::vector<int> cuthill_mckee_reordering(std::vector<int> const &_edges);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,712 @@
#ifndef GEOM_FILE
#define GEOM_FILE
#include <array>
#include <functional> // function; C++11
#include <iostream>
#include <memory> // shared_ptr
#include <string>
#include <vector>
/**
* Basis class for finite element meshes.
*/
class Mesh
{
public:
/**
* Constructor initializing the members with default values.
*
* @param[in] ndim space dimensions (dimension for coordinates)
* @param[in] nvert_e number of vertices per element (dimension for connectivity)
* @param[in] ndof_e degrees of freedom per element (= @p nvert_e for linear elements)
* @param[in] nedge_e number of edges per element (= @p nvert_e for linear elements in 2D)
*/
explicit Mesh(int ndim, int nvert_e = 0, int ndof_e = 0, int nedge_e = 0);
__attribute__((noinline))
Mesh(Mesh const &) = default;
Mesh &operator=(Mesh const &) = delete;
/**
* Destructor.
*
* See clang warning on
* <a href="https://stackoverflow.com/questions/28786473/clang-no-out-of-line-virtual-method-definitions-pure-abstract-c-class/40550578">weak-vtables</a>.
*/
virtual ~Mesh();
/**
* Reads mesh data from a binary file.
*
* File format, see ascii_write_mesh.m
*
* @param[in] fname file name
*/
explicit Mesh(std::string const &fname);
/**
* Reads mesh data from a binary file.
*
* File format, see ascii_write_mesh.m
*
* @param[in] fname file name
*/
void ReadVertexBasedMesh(std::string const &fname);
/**
* Number of finite elements in (sub)domain.
* @return number of elements.
*/
int Nelems() const
{
return _nelem;
}
/**
* Global number of vertices for each finite element.
* @return number of vertices per element.
*/
int NverticesElements() const
{
return _nvert_e;
}
/**
* Global number of degrees of freedom (dof) for each finite element.
* @return degrees of freedom per element.
*/
int NdofsElement() const
{
return _ndof_e;
}
/**
* Number of vertices in mesh.
* @return number of vertices.
*/
int Nnodes() const
{
return _nnode;
}
/**
* Space dimension.
* @return number of dimensions.
*/
int Ndims() const
{
return _ndim;
}
/**
* (Re-)Allocates memory for the element connectivity and redefines the appropriate dimensions.
*
* @param[in] nelem number of elements
* @param[in] nvert_e number of vertices per element
*/
void Resize_Connectivity(int nelem, int nvert_e)
{
SetNelem(nelem); // number of elements
SetNverticesElement(nvert_e); // vertices per element
_ia.resize(nelem * nvert_e);
}
/**
* Read connectivity information (g1,g2,g3)_i.
* @return connectivity vector [nelems*ndofs].
*/
const std::vector<int> &GetConnectivity() const
{
return _ia;
}
/**
* Access/Change connectivity information (g1,g2,g3)_i.
* @return connectivity vector [nelems*ndofs].
*/
std::vector<int> &GetConnectivity()
{
return _ia;
}
/**
* (Re-)Allocates memory for the element connectivity and redefines the appropriate dimensions.
*
* @param[in] nnodes number of nodes
* @param[in] ndim space dimension
*/
void Resize_Coords(int nnodes, int ndim)
{
SetNnode(nnodes); // number of nodes
SetNdim(ndim); // space dimension
_xc.resize(nnodes * ndim);
}
/**
* Read coordinates of vertices (x,y)_i.
* @return coordinates vector [nnodes*2].
*/
const std::vector<double> &GetCoords() const
{
return _xc;
}
/**
* Access/Change coordinates of vertices (x,y)_i.
* @return coordinates vector [nnodes*2].
*/
std::vector<double> &GetCoords()
{
return _xc;
}
/**
* Calculate values in vector @p v via function @p func(x,y)
* @param[in] v vector
* @param[in] func function of (x,y) returning a double value.
*/
void SetValues(std::vector<double> &v, const std::function<double(double, double)> &func) const;
void SetBoundaryValues(std::vector<double> &v, const std::function<double(double, double)> &func) const;
void SetDirchletValues(std::vector<double> &v, const std::function<double(double, double)> &func) const;
/**
* Prints the information for a finite element mesh
*/
void Debug() const;
/**
* Prints the edge based information for a finite element mesh
*/
void DebugEdgeBased() const;
/**
* Determines the indices of those vertices with Dirichlet boundary conditions
* @return index vector.
*/
virtual std::vector<int> Index_DirichletNodes() const;
virtual std::vector<int> Index_BoundaryNodes() const;
/**
* Write vector @p v together with its mesh information to an ASCii file @p fname.
*
* The data are written in C-style.
*
* @param[in] fname file name
* @param[in] v vector
*/
void Write_ascii_matlab(std::string const &fname, std::vector<double> const &v) const;
/**
* Exports the mesh information to ASCii files @p basename + {_coords|_elements}.txt.
*
* The data are written in C-style.
*
* @param[in] basename first part of file names
*/
void Export_scicomp(std::string const &basename) const;
/**
* Visualize @p v together with its mesh information via matlab or octave.
*
* Comment/uncomment those code lines in method Mesh:Visualize (geom.cpp)
* that are supported on your system.
*
* @param[in] v vector
*
* @warning matlab files ascii_read_meshvector.m visualize_results.m
* must be in the executing directory.
*/
virtual void Visualize(std::vector<double> const &v) const;
/**
* Global number of edges.
* @return number of edges in mesh.
*/
int Nedges() const
{
return _nedge;
}
/**
* Global number of edges for each finite element.
* @return number of edges per element.
*/
int NedgesElements() const
{
return _nedge_e;
}
/**
* Read edge connectivity information (e1,e2,e3)_i.
* @return edge connectivity vector [nelems*_nedge_e].
*/
const std::vector<int> &GetEdgeConnectivity() const
{
return _ea;
}
/**
* Access/Change edge connectivity information (e1,e2,e3)_i.
* @return edge connectivity vector [nelems*_nedge_e].
*/
std::vector<int> &GetEdgeConnectivity()
{
return _ea;
}
/**
* Read edge information (v1,v2)_i.
* @return edge connectivity vector [_nedge*2].
*/
const std::vector<int> &GetEdges() const
{
return _edges;
}
/**
* Access/Change edge information (v1,v2)_i.
* @return edge connectivity vector [_nedge*2].
*/
std::vector<int> &GetEdges()
{
return _edges;
}
/**
* Determines all node to node connections from the vertex based mesh.
*
* @return vector[k][] containing all connections of vertex k, including to itself.
*/
std::vector<std::vector<int>> Node2NodeGraph() const
{
//// Check version 2 wrt. version 1
//auto v1=Node2NodeGraph_1();
//auto v2=Node2NodeGraph_2();
//if ( equal(v1.cbegin(),v1.cend(),v2.begin()) )
//{
//std::cout << "\nidentical Versions\n";
//}
//else
//{
//std::cout << "\nE R R O R in Versions\n";
//}
//return Node2NodeGraph_1();
return Node2NodeGraph_2(); // 2 times faster than version 1
}
/**
* Accesses the father-of-nodes relation.
*
* @return vector of length 0 because no relation available.
*
*/
virtual std::vector<int> const &GetFathersOfVertices() const
{
return _dummy;
}
/**
* Deletes all edge connectivity information (saves memory).
*/
void Del_EdgeConnectivity();
protected:
//public:
void SetNelem(int nelem)
{
_nelem = nelem;
}
void SetNverticesElement(int nvert)
{
_nvert_e = nvert;
}
void SetNdofsElement(int ndof)
{
_ndof_e = ndof;
}
void SetNnode(int nnode)
{
_nnode = nnode;
}
void SetNdim(int ndim)
{
_ndim = ndim;
}
void SetNedge(int nedge)
{
_nedge = nedge;
}
/**
* Reads vertex based mesh data from a binary file.
*
* File format, see ascii_write_mesh.m
*
* @param[in] fname file name
*/
void ReadVectexBasedMesh(std::string const &fname);
/**
* The vertex based mesh data are used to derive the edge based data.
*
* @warning Exactly 3 vertices, 3 edges per element are assumed (linear triangle in 2D)
*/
void DeriveEdgeFromVertexBased()
{
//DeriveEdgeFromVertexBased_slow();
//DeriveEdgeFromVertexBased_fast();
DeriveEdgeFromVertexBased_fast_2();
}
void DeriveEdgeFromVertexBased_slow();
void DeriveEdgeFromVertexBased_fast();
void DeriveEdgeFromVertexBased_fast_2();
/**
* The edge based mesh data are used to derive the vertex based data.
*
* @warning Exactly 3 vertices, 3 edges per element are assumed (linear triangle in 2D)
*/
void DeriveVertexFromEdgeBased();
/**
* Determines the indices of those vertices with Dirichlet boundary conditions
* @return index vector.
*/
int Nnbedges() const
{
return static_cast<int>(_bedges.size());
}
/**
* Checks whether the array dimensions fit to their appropriate size parameters
* @return index vector.
*/
virtual bool Check_array_dimensions() const;
/**
* Permutes the vertex information in an edge based mesh.
*
* @param[in] old2new new indices of original vertices.
*/
void PermuteVertices_EdgeBased(std::vector<int> const &old2new);
private:
/**
* Determines all node to node connections from the vertex based mesh.
*
* @return vector[k][] containing all connections of vertex k, including to itself.
*/
std::vector<std::vector<int>> Node2NodeGraph_1() const; // is correct
/**
* Determines all node to node connections from the vertex based mesh.
*
* Faster than @p Node2NodeGraph_1().
*
* @return vector[k][] containing all connections of vertex k, including to itself.
*/
std::vector<std::vector<int>> Node2NodeGraph_2() const; // is correct
//private:
protected:
int _nelem; //!< number elements
int _nvert_e; //!< number of vertices per element
int _ndof_e; //!< degrees of freedom (d.o.f.) per element
int _nnode; //!< number nodes/vertices
int _ndim; //!< space dimension of the problem (1, 2, or 3)
std::vector<int> _ia; //!< element connectivity
std::vector<double> _xc; //!< coordinates
protected:
// B.C.
std::vector<int> _bedges; //!< boundary edges [nbedges][2] storing start/end vertex
// 2020-01-08
std::vector<int> _sdedges; //!< boundary edges [nbedges][2] with left/right subdomain number
//private:
protected:
// edge based connectivity
int _nedge; //!< number of edges in mesh
int _nedge_e; //!< number of edges per element
std::vector<int> _edges; //!< edges of mesh (vertices ordered ascending)
std::vector<int> _ea; //!< edge based element connectivity
// B.C.
std::vector<int> _ebedges; //!< boundary edges [nbedges]
private:
const std::vector<int> _dummy; //!< empty dummy vector
};
// *********************************************************************
class RefinedMesh: public Mesh
{
public:
/**
* Constructs a refined mesh according to the marked elements in @p ibref.
*
* If the vector @p ibref has size 0 then all elements will be refined.
*
* @param[in] cmesh original mesh for coarsening.
* @param[in] ibref vector containing True/False regarding refinement for each element
*
*/
//explicit RefinedMesh(Mesh const &cmesh, std::vector<bool> const &ibref = std::vector<bool>(0));
RefinedMesh(Mesh const &cmesh, std::vector<bool> const &ibref);
//RefinedMesh(Mesh const &cmesh, std::vector<bool> const &ibref);
/**
* Constructs a refined mesh by regulare refinement of all elements.
*
* @param[in] cmesh original mesh for coarsening.
*
*/
explicit RefinedMesh(Mesh const &cmesh)
: RefinedMesh(cmesh, std::vector<bool>(0))
{}
RefinedMesh(RefinedMesh const &) = delete;
//RefinedMesh(RefinedMesh const&&) = delete;
RefinedMesh &operator=(RefinedMesh const &) = delete;
//RefinedMesh& operator=(RefinedMesh const&&) = delete;
/**
* Destructor.
*/
virtual ~RefinedMesh() override;
/**
* Refines the mesh according to the marked elements.
*
* @param[in] ibref vector containing True/False regarding refinement for each element
*
* @return the refined mesh
*
*/
Mesh RefineElements(std::vector<bool> const &ibref);
/**
* Refines all elements in the actual mesh.
*
* @param[in] nref number of regular refinements to perform
*
*/
void RefineAllElements(int nref = 1);
/**
* Accesses the father-of-nodes relation.
*
* @return father-of-nodes relation [nnodes][2]
*
*/
std::vector<int> const &GetFathersOfVertices() const override
{
return _vfathers;
}
protected:
/**
* Checks whether the array dimensions fit to their appropriate size parameters
* @return index vector.
*/
bool Check_array_dimensions() const override;
/**
* Permutes the vertex information in an edge based mesh.
*
* @param[in] old2new new indices of original vertices.
*/
void PermuteVertices_EdgeBased(std::vector<int> const &old2new);
private:
//Mesh const & _cmesh; //!< coarse mesh
std::vector<bool> const _ibref; //!< refinement info
int _nref; //!< number of regular refinements performed
std::vector<int> _vfathers; //!< stores the 2 fathers of each vertex (equal fathers denote original coarse vertex)
};
// *********************************************************************
class gMesh_Hierarchy
{
public:
/**
* Constructs mesh hierarchy of @p nlevel levels starting with coarse mesh @p cmesh.
* The coarse mesh @p cmesh will be @p nlevel-1 times geometrically refined.
*
* @param[in] cmesh initial coarse mesh
* @param[in] nlevel number levels in mesh hierarchy
*
*/
gMesh_Hierarchy(Mesh const &cmesh, int nlevel);
size_t size() const
{
return _gmesh.size();
}
/**
* Access to mesh @p lev from mesh hierarchy.
*
* @return mesh @p lev
* @warning An out_of_range exception might be thrown.
*
*/
Mesh const &operator[](int lev) const
{
return *_gmesh.at(lev);
}
/**
* Access to finest mesh in mesh hierarchy.
*
* @return finest mesh
*
*/
Mesh const &finest() const
{
return *_gmesh.back();
}
/**
* Access to coarest mesh in mesh hierarchy.
*
* @return coarsest mesh
*
*/
Mesh const &coarsest() const
{
return *_gmesh.front();
}
private:
std::vector<std::shared_ptr<Mesh>> _gmesh; //!< mesh hierarchy from coarse ([0]) to fine.
};
// *********************************************************************
/**
* 2D finite element mesh of the square consisting of linear triangular elements.
*/
class Mesh_2d_3_square: public Mesh
{
public:
/**
* Generates the f.e. mesh for the unit square.
*
* @param[in] nx number of discretization intervals in x-direction
* @param[in] ny number of discretization intervals in y-direction
* @param[in] myid my MPI-rank / subdomain
* @param[in] procx number of ranks/subdomains in x-direction
* @param[in] procy number of processes in y-direction
*/
Mesh_2d_3_square(int nx, int ny, int myid = 0, int procx = 1, int procy = 1);
/**
* Destructor
*/
~Mesh_2d_3_square() override;
/**
* Set solution vector based on a tensor product grid in the rectangle.
* @param[in] u solution vector
*/
void SetU(std::vector<double> &u) const;
/**
* Set right hand side (rhs) vector on a tensor product grid in the rectangle.
* @param[in] f rhs vector
*/
void SetF(std::vector<double> &f) const;
/**
* Determines the indices of those vertices with Dirichlet boundary conditions
* @return index vector.
*/
std::vector<int> Index_DirichletNodes() const override;
std::vector<int> Index_BoundaryNodes() const override;
/**
* Stores the values of vector @p u of (sub)domain into a file @p name for further processing in gnuplot.
* The file stores rowise the x- and y- coordinates together with the value from @p u .
* The domain [@p xl, @p xr] x [@p yb, @p yt] is discretized into @p nx x @p ny intervals.
*
* @param[in] name basename of file name (file name will be extended by the rank number)
* @param[in] u local vector
*
* @warning Assumes tensor product grid in unit square; rowise numbered
* (as generated in class constructor).
* The output is provided for tensor product grid visualization
* ( similar to Matlab-surf() ).
*
* @see Mesh_2d_3_square
*/
void SaveVectorP(std::string const &name, std::vector<double> const &u) const;
// here will still need to implement in the class
// GetBound(), AddBound()
// or better a generalized way with indices and their appropriate ranks for MPI communication
private:
/**
* Determines the coordinates of the discretization nodes of the domain [@p xl, @p xr] x [@p yb, @p yt]
* which is discretized into @p nx x @p ny intervals.
* @param[in] nx number of discretization intervals in x-direction
* @param[in] ny number of discretization intervals in y-direction
* @param[in] xl x-coordinate of left boundary
* @param[in] xr x-coordinate of right boundary
* @param[in] yb y-coordinate of lower boundary
* @param[in] yt y-coordinate of upper boundary
* @param[out] xc coordinate vector of length 2n with x(2*k,2*k+1) as coordinates of node k
*/
void GetCoordsInRectangle(int nx, int ny, double xl, double xr, double yb, double yt,
double xc[]);
/**
* Determines the element connectivity of linear triangular elements of a FEM discretization
* of a rectangle using @p nx x @p ny equidistant intervals for discretization.
* @param[in] nx number of discretization intervals in x-direction
* @param[in] ny number of discretization intervals in y-direction
* @param[out] ia element connectivity matrix with ia(3*s,3*s+1,3*s+2) as node numbers od element s
*/
void GetConnectivityInRectangle(int nx, int ny, int ia[]);
private:
int _myid; //!< my MPI rank
int _procx; //!< number of MPI ranks in x-direction
int _procy; //!< number of MPI ranks in y-direction
std::array<int, 4> _neigh; //!< MPI ranks of neighbors (negative: no neighbor but b.c.)
int _color; //!< red/black coloring (checker board) of subdomains
double _xl; //!< x coordinate of lower left corner of square
double _xr; //!< x coordinate of lower right corner of square
double _yb; //!< y coordinate or lower left corner of square
double _yt; //!< y coordinate of upper right corner of square
int _nx; //!< number of intervals in x-direction
int _ny; //!< number of intervals in y-direction
};
// *********************************************************************
#endif

View file

@ -0,0 +1,720 @@
#include "getmatrix.h"
#include "userset.h"
#include "omp.h"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <ctime> // contains clock()
#include <iomanip>
#include <iostream>
#include <list>
#include <vector>
using namespace std;
// ####################################################################
Matrix::Matrix(int const nrows, int const ncols)
: _nrows(nrows), _ncols(ncols), _dd(0)
{}
//Matrix::Matrix()
//: Matrix(0,0)
//{}
Matrix::~Matrix()
{}
//vector<double> const & Matrix::GetDiag() const
//{
//if ( 0==_dd.size() )
//{
//_dd.resize(Nrows());
//this->GetDiag(_dd);
//}
//assert( Nrows()==static_cast<int>(_dd.size()) );
//return _dd;
//}
// ####################################################################
CRS_Matrix::CRS_Matrix()
: Matrix(0,0), _nnz(0), _id(0), _ik(0), _sk(0)
{}
CRS_Matrix::~CRS_Matrix()
{}
void CRS_Matrix::Mult(vector<double> &w, vector<double> const &u) const
{
assert( _ncols==static_cast<int>(u.size()) ); // compatibility of inner dimensions
assert( _nrows==static_cast<int>(w.size()) ); // compatibility of outer dimensions
for (int row = 0; row < _nrows; ++row)
{
double wi = 0.0;
for (int ij = _id[row]; ij < _id[row + 1]; ++ij)
{
wi += _sk[ij] * u[ _ik[ij] ];
}
w[row] = wi;
}
return;
}
void CRS_Matrix::Defect(vector<double> &w,
vector<double> const &f, vector<double> const &u) const
{
assert( _ncols==static_cast<int>(u.size()) ); // compatibility of inner dimensions
assert( _nrows==static_cast<int>(w.size()) ); // compatibility of outer dimensions
assert( w.size()==f.size() );
#pragma omp parallel for
for (int row = 0; row < _nrows; ++row)
{
double wi = f[row];
for (int ij = _id[row]; ij < _id[row + 1]; ++ij)
{
wi -= _sk[ij] * u[ _ik[ij] ];
}
w[row] = wi;
}
return;
}
void CRS_Matrix::GetDiag(vector<double> &d) const
{
// be carefull when using a rectangular matrix
int const nm = min(_nrows, _ncols);
assert( nm==static_cast<int>(d.size()) ); // instead of stopping we could resize d and warn the user
for (int row = 0; row < nm; ++row)
{
const int ia = fetch(row, row); // Find diagonal entry of row
assert(ia >= 0);
d[row] = _sk[ia];
}
return;
}
inline
int CRS_Matrix::fetch(int const row, int const col) const
{
int const id2 = _id[row + 1]; // end and
int ip = _id[row]; // start of recent row (global index)
while (ip < id2 && _ik[ip] != col) // find index col (global index)
{
++ip;
}
if (ip >= id2)
{
ip = -1;
#ifndef NDEBUG // compiler option -DNDEBUG switches off the check
cout << "No column " << col << " in row " << row << endl;
assert(ip >= id2);
#endif
}
return ip;
}
void CRS_Matrix::Debug() const
{
// ID points to first entry of row
// no symmetry assumed
cout << "\nMatrix (" << _nrows << " x " << _ncols << " with nnz = " << _id[_nrows] << ")\n";
for (int row = 0; row < _nrows; ++row)
{
cout << "Row " << row << " : ";
int const id1 = _id[row];
int const id2 = _id[row + 1];
for (int j = id1; j < id2; ++j)
{
cout.setf(ios::right, ios::adjustfield);
cout << "[" << setw(2) << _ik[j] << "] " << setw(4) << _sk[j] << " ";
}
cout << endl;
}
return;
}
bool CRS_Matrix::Compare2Old(int nnode, int const id[], int const ik[], double const sk[]) const
{
bool bn = (nnode==_nrows); // number of rows
if (!bn)
{
cout << "######### Error: " << "number of rows" << endl;
}
bool bz = (id[nnode]==_nnz); // number of non zero elements
if (!bz)
{
cout << "######### Error: " << "number of non zero elements" << endl;
}
bool bd = equal(id,id+nnode+1,_id.cbegin()); // row starts
if (!bd)
{
cout << "######### Error: " << "row starts" << endl;
}
bool bk = equal(ik,ik+id[nnode],_ik.cbegin()); // column indices
if (!bk)
{
cout << "######### Error: " << "column indices" << endl;
}
bool bv = equal(sk,sk+id[nnode],_sk.cbegin()); // values
if (!bv)
{
cout << "######### Error: " << "values" << endl;
}
return bn && bz && bd && bk && bv;
}
// ####################################################################
FEM_Matrix::FEM_Matrix(Mesh const & mesh)
: CRS_Matrix(), _mesh(mesh)
{
Derive_Matrix_Pattern();
return;
}
FEM_Matrix::~FEM_Matrix()
{}
void FEM_Matrix::Derive_Matrix_Pattern_fast()
{
cout << "\n############ FEM_Matrix::Derive_Matrix_Pattern ";
double tstart = clock();
int const nelem(_mesh.Nelems());
int const ndof_e(_mesh.NdofsElement());
auto const &ia(_mesh.GetConnectivity());
// Determine the number of matrix rows
_nrows = *max_element(ia.cbegin(), ia.cbegin() + ndof_e * nelem);
++_nrows; // node numberng: 0 ... nnode-1
assert(*min_element(ia.cbegin(), ia.cbegin() + ndof_e * nelem) == 0); // numbering starts with 0 ?
// CSR data allocation
_id.resize(_nrows + 1); // Allocate memory for CSR row pointer
//##########################################################################
auto const v2v=_mesh.Node2NodeGraph();
_nnz=0; // number of connections
_id[0] = 0; // start of matrix row zero
for (size_t v = 0; v<v2v.size(); ++v )
{
_id[v+1] = _id[v] + v2v[v].size();
_nnz += v2v[v].size();
}
assert(_nnz == _id[_nrows]);
_sk.resize(_nnz); // Allocate memory for CSR column index vector
// CSR data allocation
_ik.resize(_nnz); // Allocate memory for CSR column index vector
// Copy column indices
int kk = 0;
for (size_t v = 0; v<v2v.size(); ++v )
{
for (size_t vi=0; vi<v2v[v].size(); ++vi)
{
_ik[kk] = v2v[v][vi];
++kk;
}
}
_ncols = *max_element(_ik.cbegin(), _ik.cend()); // maximal column number
++_ncols; // node numbering: 0 ... nnode-1
//cout << _nrows << " " << _ncols << endl;
assert(_ncols==_nrows);
double duration = (clock() - tstart) / CLOCKS_PER_SEC;
cout << "finished in " << duration << " sec. ########\n";
return;
}
void FEM_Matrix::Derive_Matrix_Pattern_slow()
{
cout << "\n############ FEM_Matrix::Derive_Matrix_Pattern slow ";
double tstart = clock();
int const nelem(_mesh.Nelems());
int const ndof_e(_mesh.NdofsElement());
auto const &ia(_mesh.GetConnectivity());
// Determine the number of matrix rows
_nrows = *max_element(ia.cbegin(), ia.cbegin() + ndof_e * nelem);
++_nrows; // node numberng: 0 ... nnode-1
assert(*min_element(ia.cbegin(), ia.cbegin() + ndof_e * nelem) == 0); // numbering starts with 0 ?
// Collect for each node those nodes it is connected to (multiple entries)
// Detect the neighboring nodes
vector< list<int> > cc(_nrows); // cc[i] is the list of nodes a node i is connected to
for (int i = 0; i < nelem; ++i)
{
int const idx = ndof_e * i;
for (int k = 0; k < ndof_e; ++k)
{
list<int> &cck = cc[ia[idx + k]];
cck.insert( cck.end(), ia.cbegin() + idx, ia.cbegin() + idx + ndof_e );
}
}
// Delete the multiple entries
_nnz = 0;
for (auto &it : cc)
{
it.sort();
it.unique();
_nnz += it.size();
// cout << it.size() << " :: "; copy(it->begin(),it->end(), ostream_iterator<int,char>(cout," ")); cout << endl;
}
// CSR data allocation
_id.resize(_nrows + 1); // Allocate memory for CSR row pointer
_ik.resize(_nnz); // Allocate memory for CSR column index vector
// copy CSR data
_id[0] = 0; // begin of first row
for (size_t i = 0; i < cc.size(); ++i)
{
//cout << i << " " << nid.at(i) << endl;;
const list<int> &ci = cc[i];
const auto nci = static_cast<int>(ci.size());
_id[i + 1] = _id[i] + nci; // begin of next line
copy(ci.begin(), ci.end(), _ik.begin() + _id[i] );
}
assert(_nnz == _id[_nrows]);
_sk.resize(_nnz); // Allocate memory for CSR column index vector
_ncols = *max_element(_ik.cbegin(), _ik.cend()); // maximal column number
++_ncols; // node numbering: 0 ... nnode-1
//cout << _nrows << " " << _ncols << endl;
assert(_ncols==_nrows);
double duration = (clock() - tstart) / CLOCKS_PER_SEC;
cout << "finished in " << duration << " sec. ########\n";
return;
}
void FEM_Matrix::CalculateLaplace(vector<double> &f)
{
cout << "\n############ FEM_Matrix::CalculateLaplace ";
//double tstart = clock();
double tstart = omp_get_wtime(); // OpenMP
assert(_mesh.NdofsElement() == 3); // only for triangular, linear elements
//cout << _nnz << " vs. " << _id[_nrows] << " " << _nrows<< endl;
assert(_nnz == _id[_nrows]);
for (int k = 0; k < _nrows; ++k)
{
_sk[k] = 0.0;
}
for (int k = 0; k < _nrows; ++k)
{
f[k] = 0.0;
}
double ske[3][3], fe[3];
// Loop over all elements
auto const nelem = _mesh.Nelems();
auto const &ia = _mesh.GetConnectivity();
auto const &xc = _mesh.GetCoords();
#pragma omp parallel for private(ske,fe)
for (int i = 0; i < nelem; ++i)
{
CalcElem(ia.data()+3 * i, xc.data(), ske, fe);
//AddElem(ia.data()+3 * i, ske, fe, _id.data(), _ik.data(), _sk.data(), f.data()); // GH: deprecated
AddElem_3(ia.data()+3 * i, ske, fe, f);
}
//double duration = (clock() - tstart) / CLOCKS_PER_SEC;
double duration = omp_get_wtime() - tstart; // OpenMP
cout << "finished in " << duration << " sec. ########\n";
//Debug();
return;
}
//void FEM_Matrix::ApplyDirichletBC(std::vector<double> const &u, std::vector<double> &f)
//{
//double const PENALTY = 1e6;
//auto const idx = _mesh.Index_DirichletNodes();
//int const nidx = idx.size();
//for (int i=0; i<nidx; ++i)
//{
//int const k = idx[i];
//int const id1 = fetch(k, k); // Find diagonal entry of k
//assert(id1 >= 0);
//_sk[id1] += PENALTY; // matrix weighted scaling feasible
//f[k] += PENALTY * u[k];
//}
//return;
//}
void FEM_Matrix::ApplyDirichletBC(std::vector<double> const &u, std::vector<double> &f)
{
auto const idx = _mesh.Index_DirichletNodes();
int const nidx = idx.size();
for (int i=0; i<nidx; ++i)
{
int const row = idx[i];
for (int ij=_id[row]; ij<_id[row+1]; ++ij)
{
int const col=_ik[ij];
if (col==row)
{
_sk[ij] = 1.0;
f[row] = u[row];
}
else
{
int const id1 = fetch(col, row); // Find entry (col,row)
assert(id1 >= 0);
f[col] -= _sk[id1]*u[row];
_sk[id1] = 0.0;
_sk[ij] = 0.0;
}
}
}
return;
}
void FEM_Matrix::AddElem_3(int const ial[3], double const ske[3][3], double const fe[3], vector<double> & f)
{
for (int i = 0; i < 3; ++i)
{
const int ii = ial[i]; // row ii (global index)
for (int j = 0; j < 3; ++j) // no symmetry assumed
{
const int jj = ial[j]; // column jj (global index)
const int ip = fetch(ii,jj); // find column entry jj in row ii
#ifndef NDEBUG // compiler option -DNDEBUG switches off the check
if (ip<0) // no entry found !!
{
cout << "Error in AddElem: (" << ii << "," << jj << ") ["
<< ial[0] << "," << ial[1] << "," << ial[2] << "]\n";
assert(ip>=0);
}
#endif
#pragma omp atomic
_sk[ip] += ske[i][j];
}
#pragma omp atomic
f[ii] += fe[i];
}
}
// ####################################################################
//Prolongation::Prolongation(Mesh const & cmesh, Mesh const & fmesh)
//: CRS_Matrix(), _cmesh(cmesh), _fmesh(fmesh)
//{
//Derive_Matrix_Pattern();
//return;
//}
//void Prolongation::Derive_Matrix_Pattern()
//{
//cout << "\n ***** Subject to ongoing imlementation. *****\n";
//}
// ####################################################################
// *********************************************************************
// general routine for lin. triangular elements
void CalcElem(int const ial[3], double const xc[], double ske[3][3], double fe[3])
//void CalcElem(const int* __restrict__ ial, const double* __restrict__ xc, double* __restrict__ ske[3], double* __restrict__ fe)
{
const int i1 = 2 * ial[0], i2 = 2 * ial[1], i3 = 2 * ial[2];
const double x13 = xc[i3 + 0] - xc[i1 + 0], y13 = xc[i3 + 1] - xc[i1 + 1],
x21 = xc[i1 + 0] - xc[i2 + 0], y21 = xc[i1 + 1] - xc[i2 + 1],
x32 = xc[i2 + 0] - xc[i3 + 0], y32 = xc[i2 + 1] - xc[i3 + 1];
const double jac = fabs(x21 * y13 - x13 * y21);
ske[0][0] = 0.5 / jac * (y32 * y32 + x32 * x32);
ske[0][1] = 0.5 / jac * (y13 * y32 + x13 * x32);
ske[0][2] = 0.5 / jac * (y21 * y32 + x21 * x32);
ske[1][0] = ske[0][1];
ske[1][1] = 0.5 / jac * (y13 * y13 + x13 * x13);
ske[1][2] = 0.5 / jac * (y21 * y13 + x21 * x13);
ske[2][0] = ske[0][2];
ske[2][1] = ske[1][2];
ske[2][2] = 0.5 / jac * (y21 * y21 + x21 * x21);
const double xm = (xc[i1 + 0] + xc[i2 + 0] + xc[i3 + 0]) / 3.0,
ym = (xc[i1 + 1] + xc[i2 + 1] + xc[i3 + 1]) / 3.0;
//fe[0] = fe[1] = fe[2] = 0.5 * jac * FunctF(xm, ym) / 3.0;
fe[0] = fe[1] = fe[2] = 0.5 * jac * fNice(xm, ym) / 3.0;
}
void CalcElem_Masse(int const ial[3], double const xc[], double const cm, double ske[3][3])
{
const int i1 = 2 * ial[0], i2 = 2 * ial[1], i3 = 2 * ial[2];
const double x13 = xc[i3 + 0] - xc[i1 + 0], y13 = xc[i3 + 1] - xc[i1 + 1],
x21 = xc[i1 + 0] - xc[i2 + 0], y21 = xc[i1 + 1] - xc[i2 + 1];
//x32 = xc[i2 + 0] - xc[i3 + 0], y32 = xc[i2 + 1] - xc[i3 + 1];
const double jac = fabs(x21 * y13 - x13 * y21);
ske[0][0] += jac/12.0;
ske[0][1] += jac/24.0;
ske[0][2] += jac/24.0;
ske[1][0] += jac/24.0;
ske[1][1] += jac/12.0;
ske[1][2] += jac/24.0;
ske[2][0] += jac/24.0;
ske[2][1] += jac/24.0;
ske[2][2] += jac/12.0;
return;
}
// general routine for lin. triangular elements,
// non-symm. matrix
// node numbering in element: a s c e n d i n g indices !!
// GH: deprecated
void AddElem(int const ial[3], double const ske[3][3], double const fe[3],
int const id[], int const ik[], double sk[], double f[])
{
for (int i = 0; i < 3; ++i)
{
const int ii = ial[i], // row ii (global index)
id1 = id[ii], // start and
id2 = id[ii + 1]; // end of row ii in matrix
int ip = id1;
for (int j = 0; j < 3; ++j) // no symmetry assumed
{
const int jj = ial[j];
bool not_found = true;
do // find entry jj (global index) in row ii
{
not_found = (ik[ip] != jj);
++ip;
}
while (not_found && ip < id2);
#ifndef NDEBUG // compiler option -DNDEBUG switches off the check
if (not_found) // no entry found !!
{
cout << "Error in AddElem: (" << ii << "," << jj << ") ["
<< ial[0] << "," << ial[1] << "," << ial[2] << "]\n";
assert(!not_found);
}
#endif
sk[ip - 1] += ske[i][j];
}
f[ii] += fe[i];
}
}
// ----------------------------------------------------------------------------
// #####################################################################
BisectInterpolation::BisectInterpolation()
: Matrix( 0, 0 ), _iv(), _vv()
{
}
BisectInterpolation::BisectInterpolation(std::vector<int> const & fathers)
: Matrix( static_cast<int>(fathers.size())/2, 1+*max_element(fathers.cbegin(),fathers.cend()) ),
_iv(fathers), _vv(fathers.size(),0.5)
{
}
BisectInterpolation::~BisectInterpolation()
{}
void BisectInterpolation::GetDiag(vector<double> &d) const
{
assert( Nrows()==static_cast<int>(d.size()) );
for (int k=0; k<Nrows(); ++k)
{
if ( _iv[2*k]==_iv[2*k+1] )
{
d[k] = 1.0;
}
else
{
d[k] = 0.0;
}
}
return;
}
void BisectInterpolation::Mult(vector<double> &wf, vector<double> const &uc) const
{
assert( Nrows()==static_cast<int>(wf.size()) );
assert( Ncols()==static_cast<int>(uc.size()) );
#pragma omp parallel for
for (int k=0; k<Nrows(); ++k)
{
wf[k] = _vv[2*k]*uc[_iv[2*k]] + _vv[2*k+1]*uc[_iv[2*k+1]];
}
return;
}
//void BisectInterpolation::MultT(vector<double> const &wf, vector<double> &uc) const
//{
//assert( Nrows()==static_cast<int>(wf.size()) );
//assert( Ncols()==static_cast<int>(uc.size()) );
//// GH: atomic slows down the code ==> use different storage for MultT operation (CRS-matrix?)
////#pragma omp parallel for
//for (int k=0; k<Ncols(); ++k) uc[k] = 0.0;
//#pragma omp parallel for
//for (int k=0; k<Nrows(); ++k)
//{
//#pragma omp atomic
//uc[_iv[2*k] ] += _vv[2*k ]*wf[k];
//#pragma omp atomic
//uc[_iv[2*k+1]] += _vv[2*k+1]*wf[k];
//}
//return;
//}
void BisectInterpolation::MultT(vector<double> const &wf, vector<double> &uc) const
{
assert( Nrows()==static_cast<int>(wf.size()) );
assert( Ncols()==static_cast<int>(uc.size()) );
// GH: atomic slows down the code ==> use different storage for MultT operation (CRS-matrix?)
//#pragma omp parallel for
for (int k=0; k<Ncols(); ++k) uc[k] = 0.0;
#pragma omp parallel for
for (int k=0; k<Nrows(); ++k)
{
if (_iv[2*k]!=_iv[2*k+1])
{
#pragma omp atomic
uc[_iv[2*k] ] += _vv[2*k ]*wf[k];
#pragma omp atomic
uc[_iv[2*k+1]] += _vv[2*k+1]*wf[k];
}
else
{
#pragma omp atomic
uc[_iv[2*k] ] += 2.0*_vv[2*k ]*wf[k]; // uses a property of class BisectInterpolation
}
}
return;
}
void BisectInterpolation::Defect(vector<double> &w,
vector<double> const &f, vector<double> const &u) const
{
assert( Nrows()==static_cast<int>(w.size()) );
assert( Ncols()==static_cast<int>(u.size()) );
assert( w.size()==f.size() );
for (int k=0; k<Nrows(); ++k)
{
w[k] = f[k] - _vv[2*k]*u[_iv[2*k]] + _vv[2*k+1]*u[_iv[2*k+1]];
}
return;
}
void BisectInterpolation::Debug() const
{
for (int k=0; k<Nrows(); ++k)
{
cout << k << " : fathers(" << _iv[2*k] << "," << _iv[2*k+1] << ") ";
cout << "weights(" << _vv[2*k] << "," << _vv[2*k+1] << endl;
}
cout << endl;
return;
}
int BisectInterpolation::fetch(int row, int col) const
{
int idx(-1);
if (_iv[2*row ] == col) idx = 2*row;
if (_iv[2*row+1] == col) idx = 2*row+1;
assert(idx>=0);
return idx;
}
// #####################################################################
BisectIntDirichlet::BisectIntDirichlet(std::vector<int> const & fathers, std::vector<int> const & idxc_dir)
: BisectInterpolation(fathers)
{
vector<bool> bdir(Ncols(), false); // Indicator for Dirichlet coarse nodes
for (size_t kc=0; kc<idxc_dir.size(); ++kc)
{
bdir.at(idxc_dir[kc]) = true; // Mark Dirichlet node from coarse mesh
}
for (size_t j=0; j<_iv.size(); ++j)
{
if ( bdir.at(_iv[j]) ) _vv[j] = 0.0; // set weight to zero iff (at least) one father is Dirichlet node
}
return;
}
BisectIntDirichlet::~BisectIntDirichlet()
{}
// #####################################################################
void DefectRestrict(CRS_Matrix const & SK, BisectInterpolation const& P,
vector<double> &fc, vector<double> &ff, vector<double> &uf)
{
assert( P.Nrows()==static_cast<int>(ff.size()) );
assert( P.Ncols()==static_cast<int>(fc.size()) );
assert( ff.size()==uf.size() );
assert( P.Nrows()==SK.Nrows() );
//#pragma omp parallel for
for (int k=0; k<P.Ncols(); ++k) fc[k] = 0.0;
// GH: atomic slows down the code ==> use different storage for MultT operation (CRS-matrix?)
#pragma omp parallel for
for (int row = 0; row < SK._nrows; ++row)
{
double wi = ff[row];
for (int ij = SK._id[row]; ij < SK._id[row + 1]; ++ij)
{
wi -= SK._sk[ij] * uf[ SK._ik[ij] ];
}
const int i1=P._iv[2*row];
const int i2=P._iv[2*row+1];
if (i1!=i2)
{
#pragma omp atomic
fc[i1] += P._vv[2*row ]*wi;
#pragma omp atomic
fc[i2] += P._vv[2*row +1]*wi;
}
else
{
#pragma omp atomic
fc[i1] += 2.0*P._vv[2*row ]*wi; // uses a property of class BisectInterpolation
}
}
return;
}

View file

@ -0,0 +1,545 @@
#ifndef GETMATRIX_FILE
#define GETMATRIX_FILE
#include "geom.h"
#include <cassert>
#include <vector>
// #####################################################################
/**
* Abstract matrix class.
*/
class Matrix
{
public:
/**
* Constructor for abstract matrix class.
*
* No memory is allocated.
*
* @param[in] nrows number of matrix rows.
* @param[in] ncols number of matrix columns.
*/
Matrix(int nrows, int ncols);
//Matrix();
Matrix(Matrix const &) = default;
/**
* Destructor.
*
* No memory is allocated.
*/
virtual ~Matrix();
/**
* Checks whether the matrix is a square matrix.
*
* @return True iff square matrix.
*/
bool isSquare() const
{
return _nrows == _ncols;
}
/**
* Number of rows in matrix.
* @return number of rows.
*/
int Nrows() const
{
return _nrows;
}
/**
* Number of columns in matrix.
* @return number of columns.
*/
int Ncols() const
{
return _ncols;
}
/**
* Show the matrix entries.
*/
virtual void Debug() const = 0;
/**
* Extracts the diagonal elements of an inherited matrix.
*
* @param[in,out] d (prellocated) vector of diagonal elements
*/
virtual void GetDiag(std::vector<double> &d) const = 0;
/**
* Extracts the diagonal elements of the matrix.
*
* @return d vector of diagonal elements
*/
std::vector<double> const &GetDiag() const
{
if ( 0 == _dd.size() ) // GH: better? Nrows()>static_cast<int>(_dd.size())
{
_dd.resize(Nrows());
this->GetDiag(_dd);
}
assert( Nrows() == static_cast<int>(_dd.size()) );
return _dd;
}
/**
* Performs the matrix-vector product w := K*u.
*
* @param[in,out] w resulting vector (preallocated)
* @param[in] u vector
*/
virtual void Mult(std::vector<double> &w, std::vector<double> const &u) const = 0;
/**
* Calculates the defect/residuum w := f - K*u.
*
* @param[in,out] w resulting vector (preallocated)
* @param[in] f load vector
* @param[in] u vector
*/
virtual void Defect(
std::vector<double> &w,
std::vector<double> const &f, std::vector<double> const &u) const = 0;
/**
* Finds in a CRS matrix the access index for an entry at row @p row and column @p col.
*
* @param[in] row row index
* @param[in] col column index
* @return index for element (@p row, @p col). If no appropriate entry exists then -1 will be returned.
*
* @warning assert() stops the function in case that matrix element (@p row, @p col) doesn't exist.
*/
virtual int fetch(int row, int col) const = 0;
protected:
int _nrows; //!< number of rows in matrix
int _ncols; //!< number of columns in matrix
mutable std::vector<double> _dd; //!< diagonal matrix elements
};
// #####################################################################
class BisectInterpolation; // class forward declaration
/**
* Matrix in CRS format (compressed row storage; also named CSR),
* see an <a href="https://en.wikipedia.org/wiki/Sparse_matrix">introduction</a>.
*/
class CRS_Matrix: public Matrix
{
public:
/**
* Constructor
*
*/
CRS_Matrix();
CRS_Matrix(CRS_Matrix const &) = default;
/**
* Destructor.
*/
virtual ~CRS_Matrix() override;
/**
* Extracts the diagonal elements of the sparse matrix.
*
* @param[in,out] d (prellocated) vector of diagonal elements
*/
void GetDiag(std::vector<double> &d) const override;
///**
//* Extracts the diagonal elements of the sparse matrix.
//*
//* @return d vector of diagonal elements
//*/
//std::vector<double> const & GetDiag() const override;
/**
* Performs the matrix-vector product w := K*u.
*
* @param[in,out] w resulting vector (preallocated)
* @param[in] u vector
*/
void Mult(std::vector<double> &w, std::vector<double> const &u) const override;
/**
* Calculates the defect/residuum w := f - K*u.
*
* @param[in,out] w resulting vector (preallocated)
* @param[in] f load vector
* @param[in] u vector
*/
void Defect(std::vector<double> &w,
std::vector<double> const &f, std::vector<double> const &u) const override;
/**
* Show the matrix entries.
*/
void Debug() const override;
/**
* Finds in a CRS matrix the access index for an entry at row @p row and column @p col.
*
* @param[in] row row index
* @param[in] col column index
* @return index for element (@p row, @p col). If no appropriate entry exists then -1 will be returned.
*
* @warning assert() stops the function in case that matrix element (@p row, @p col) doesn't exist.
*/
int fetch(int row, int col) const override;
/**
* Compare @p this CRS matrix with an external CRS matrix stored in C-Style.
*
* The method prints statements on differences found.
*
* @param[in] nnode row number of external matrix
* @param[in] id start indices of matrix rows of external matrix
* @param[in] ik column indices of external matrix
* @param[in] sk non-zero values of external matrix
*
* @return true iff all data are identical.
*/
bool Compare2Old(int nnode, int const id[], int const ik[], double const sk[]) const;
/**
* Calculates the defect and projects it to the next coarser level @f$ f_C := P^T \cdot (f_F - SK\cdot u_F) @f$.
*
* @param[in] SK matrix on fine mesh
* @param[in] P prolongation operator
* @param[in,out] fc resulting coarse mesh vector (preallocated)
* @param[in] ff r.h.s. on fine mesh
* @param[in] uf status vector on fine mesh
*
*/
friend void DefectRestrict(CRS_Matrix const &SK, BisectInterpolation const &P,
std::vector<double> &fc, std::vector<double> &ff, std::vector<double> &uf);
protected:
//int _nrows; //!< number of rows in matrix
//int _ncols; //!< number of columns in matrix
int _nnz; //!< number of non-zero entries
std::vector<int> _id; //!< start indices of matrix rows
std::vector<int> _ik; //!< column indices
std::vector<double> _sk; //!< non-zero values
};
/**
* FEM Matrix in CRS format (compressed row storage; also named CSR),
* see an <a href="https://en.wikipedia.org/wiki/Sparse_matrix">introduction</a>.
*/
class FEM_Matrix: public CRS_Matrix
{
public:
/**
* Initializes the CRS matrix structure from the given discretization in @p mesh.
*
* The sparse matrix pattern is generated but the values are 0.
*
* @param[in] mesh given discretization
*
* @warning A reference to the discretization @p mesh is stored inside this class.
* Therefore, changing @p mesh outside requires also
* to call method @p Derive_Matrix_Pattern explicitly.
*
* @see Derive_Matrix_Pattern
*/
explicit FEM_Matrix(Mesh const &mesh);
FEM_Matrix(FEM_Matrix const &) = default;
/**
* Destructor.
*/
~FEM_Matrix() override;
/**
* Generates the sparse matrix pattern and overwrites the existing pattern.
*
* The sparse matrix pattern is generated but the values are 0.
*/
void Derive_Matrix_Pattern()
{
//Derive_Matrix_Pattern_slow();
Derive_Matrix_Pattern_fast();
}
void Derive_Matrix_Pattern_fast();
void Derive_Matrix_Pattern_slow();
/**
* Calculates the entries of f.e. stiffness matrix and load/rhs vector @p f for the Laplace operator in 2D.
* No memory is allocated.
*
* @param[in,out] f (preallocated) rhs/load vector
*/
void CalculateLaplace(std::vector<double> &f);
/**
* Applies Dirichlet boundary conditions to stiffness matrix and to load vector @p f.
* The <a href="https://www.jstor.org/stable/2005611?seq=1#metadata_info_tab_contents">penalty method</a>
* is used for incorporating the given values @p u.
*
* @param[in] u (global) vector with Dirichlet data
* @param[in,out] f load vector
*/
void ApplyDirichletBC(std::vector<double> const &u, std::vector<double> &f);
///**
//* Extracts the diagonal elements of the sparse matrix.
//*
//* @param[in,out] d (prellocated) vector of diagonal elements
//*/
//void GetDiag(std::vector<double> &d) const; // override in MPI parallel
/**
* Adds the element stiffness matrix @p ske and the element load vector @p fe
* of one triangular element with linear shape functions to the appropriate positions in
* the stiffness matrix, stored as CSR matrix K(@p sk,@p id, @p ik).
*
* @param[in] ial node indices of the three element vertices
* @param[in] ske element stiffness matrix
* @param[in] fe element load vector
* @param[in,out] f distributed local vector storing the right hand side
*
* @warning Algorithm assumes linear triangular elements (ndof_e==3).
*/
void AddElem_3(int const ial[3], double const ske[3][3], double const fe[3], std::vector<double> &f);
private:
Mesh const &_mesh; //!< reference to discretization
};
///**
//* Prolongation matrix in CRS format (compressed row storage; also named CSR),
//* see an <a href="https://en.wikipedia.org/wiki/Sparse_matrix">introduction</a>.
//*
//* The prolongation is applied for each node from the coarse mesh to the fine mesh and
//* is derived only geometrically (no operator weighted prolongation).
//*/
//class Prolongation: public CRS_Matrix
//{
//public:
///**
//* Intializes the CRS matrix structure from the given discetization in @p mesh.
//*
//* The sparse matrix pattern is generated but the values are 0.
//*
//* @param[in] cmesh coarse mesh
//* @param[in] fmesh fine mesh
//*
//* @warning A reference to the discretizations @p fmesh @p cmesh are stored inside this class.
//* Therefore, changing these meshes outside requires also
//* to call method @p Derive_Matrix_Pattern explicitely.
//*
//* @see Derive_Matrix_Pattern
//*/
//Prolongation(Mesh const & cmesh, Mesh const & fmesh);
///**
//* Destructor.
//*/
//~Prolongation() override
//{}
///**
//* Generates the sparse matrix pattern and overwrites the existing pattern.
//*
//* The sparse matrix pattern is generated but the values are 0.
//*/
//void Derive_Matrix_Pattern() override;
//private:
//Mesh const & _cmesh; //!< reference to coarse discretization
//Mesh const & _fmesh; //!< reference to fine discretization
//};
// *********************************************************************
/**
* Interpolation matrix for prolongation coarse mesh (C) to a fine mesh (F)
* generated by bisecting edges.
*
* All interpolation weights are 0.5 (injection points contribute twice).
*/
class BisectInterpolation: public Matrix
{
public:
/**
* Generates the interpolation matrix for prolongation coarse mesh to a fine mesh
* generated by bisecting edges.
* The interpolation weights are all 0.5.
*
* @param[in] fathers vector[nnodes][2] containing
* the two coarse grid fathers of a fine grid vertex
*
*/
explicit BisectInterpolation(std::vector<int> const &fathers);
BisectInterpolation();
BisectInterpolation(BisectInterpolation const &) = default;
/**
* Destructor.
*/
~BisectInterpolation() override;
/**
* Extracts the diagonal elements of the matrix.
*
* @param[in,out] d (prellocated) vector of diagonal elements
*/
void GetDiag(std::vector<double> &d) const override;
///**
//* Extracts the diagonal elements of the sparse matrix.
//*
//* @return d vector of diagonal elements
//*/
//std::vector<double> const & GetDiag() const override;
/**
* Performs the prolongation @f$ w_F := P*u_C @f$.
*
* @param[in,out] wf resulting fine vector (preallocated)
* @param[in] uc coarse vector
*/
void Mult(std::vector<double> &wf, std::vector<double> const &uc) const override;
/**
* Performs the restriction @f$ u_C := P^T*w_F @f$.
*
* @param[in] wf fine vector
* @param[in,out] uc resulting coarse vector (preallocated)
*/
void MultT(std::vector<double> const &wf, std::vector<double> &uc) const;
/**
* Calculates the defect/residuum w := f - P*u.
*
* @param[in,out] w resulting vector (preallocated)
* @param[in] f load vector
* @param[in] u coarse vector
*/
void Defect(std::vector<double> &w,
std::vector<double> const &f, std::vector<double> const &u) const override;
/**
* Show the matrix entries.
*/
void Debug() const override;
/**
* Finds in this matrix the access index for an entry at row @p row and column @p col.
*
* @param[in] row row index
* @param[in] col column index
* @return index for element (@p row, @p col). If no appropriate entry exists then -1 will be returned.
*
* @warning assert() stops the function in case that matrix element (@p row, @p col) doesn't exist.
*/
int fetch(int row, int col) const override;
/**
* Calculates the defect and projects it to the next coarser level @f$ f_C := P^T \cdot (f_F - SK\cdot u_F) @f$.
*
* @param[in] SK matrix on fine mesh
* @param[in] P prolongation operator
* @param[in,out] fc resulting coarse mesh vector (preallocated)
* @param[in] ff r.h.s. on fine mesh
* @param[in] uf status vector on fine mesh
*
*/
friend void DefectRestrict(CRS_Matrix const &SK, BisectInterpolation const &P,
std::vector<double> &fc, std::vector<double> &ff, std::vector<double> &uf);
protected:
std::vector<int> _iv; //!< fathers[nnode][2] of fine grid nodes, double entries denote injection points
std::vector<double> _vv; //!< weights[nnode][2] of fathers for grid nodes
};
/**
* Interpolation matrix for prolongation from coarse mesh (C)) to a fine mesh (F)
* generated by bisecting edges.
*
* We take into account that values at Dirichlet nodes have to be preserved, i.e.,
* @f$ w_F = P \cdot I_D \cdot w_C @f$ and @f$ d_C = I_D \cdot P^T \cdot d_F@f$
* with @f$ I_D @f$ as @f$ n_C \times n_C @f$ diagonal matrix and entries
* @f$ I_{D(j,j)} := \left\{{\begin{array}{l@{\qquad}l} 0 & x_{j}\;\; \textrm{is Dirichlet node} \\ 1 & \textrm{else} \end{array}}\right. @f$
*
* Interpolation weights are eighter 0.5 or 0.0 in case of coarse Dirichlet nodes
* (injection points contribute twice),
* Sets weight to zero iff (at least) one father nodes is a Dirichlet node.
*/
class BisectIntDirichlet: public BisectInterpolation
{
public:
/**
* Default constructor.
*/
BisectIntDirichlet()
: BisectInterpolation()
{}
/**
* Constructs interpolation from father-@p row and column @p col.
*
* @param[in] fathers two father nodes from each fine node [nnode_f*2].
* @param[in] idxc_dir vector containing the indices of coarse mesh Dirichlet nodes.
*
*/
BisectIntDirichlet(std::vector<int> const &fathers, std::vector<int> const &idxc_dir);
BisectIntDirichlet(BisectIntDirichlet const &) = default;
/**
* Destructor.
*/
~BisectIntDirichlet() override;
};
// *********************************************************************
/**
* Calculates the element stiffness matrix @p ske and the element load vector @p fe
* of one triangular element with linear shape functions.
* @param[in] ial node indices of the three element vertices
* @param[in] xc vector of node coordinates with x(2*k,2*k+1) as coordinates of node k
* @param[out] ske element stiffness matrix
* @param[out] fe element load vector
*/
void CalcElem(int const ial[3], double const xc[], double ske[3][3], double fe[3]);
/**
* Adds the element stiffness matrix @p ske and the element load vector @p fe
* of one triangular element with linear shape functions to the appropriate positions in
* the symmetric stiffness matrix, stored as CSR matrix K(@p sk,@p id, @p ik)
*
* @param[in] ial node indices of the three element vertices
* @param[in] ske element stiffness matrix
* @param[in] fe element load vector
* @param[out] sk vector non-zero entries of CSR matrix
* @param[in] id index vector containing the first entry in a CSR row
* @param[in] ik column index vector of CSR matrix
* @param[out] f distributed local vector storing the right hand side
*
* @warning Algorithm requires indices in connectivity @p ial in ascending order.
* Currently deprecated.
*/
void AddElem(int const ial[3], double const ske[3][3], double const fe[3],
int const id[], int const ik[], double sk[], double f[]);
#endif

View file

@ -0,0 +1,235 @@
#include "vdop.h"
#include "geom.h"
#include "getmatrix.h"
#include "jacsolve.h"
#include "userset.h"
#include <cassert>
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;
// #####################################################################
// ParMesh const & mesh,
void JacobiSolve(CRS_Matrix const &SK, vector<double> const &f, vector<double> &u)
{
const double omega = 1.0;
const int maxiter = 1000;
const double tol = 1e-6, // tolerance
tol2 = tol * tol; // tolerance^2
int nrows = SK.Nrows(); // number of rows == number of columns
assert( nrows == static_cast<int>(f.size()) && f.size() == u.size() );
cout << endl << " Start Jacobi solver for " << nrows << " d.o.f.s" << endl;
// Choose initial guess
for (int k = 0; k < nrows; ++k) {
u[k] = 0.0; // u := 0
}
vector<double> dd(nrows); // matrix diagonal
vector<double> r(nrows); // residual
vector<double> w(nrows); // correction
SK.GetDiag(dd); // dd := diag(K)
////DebugVector(dd);{int ijk; cin >> ijk;}
// Initial sweep
SK.Defect(r, f, u); // r := f - K*u
vddiv(w, r, dd); // w := D^{-1}*r
const double sigma0 = dscapr(w, r); // s0 := <w,r>
// Iteration sweeps
int iter = 0;
double sigma = sigma0;
while ( sigma > tol2 * sigma0 && maxiter > iter) // relative error
//while ( sigma > tol2 && maxiter > iter) // absolute error
{
++iter;
vdaxpy(u, u, omega, w ); // u := u + om*w
SK.Defect(r, f, u); // r := f - K*u
vddiv(w, r, dd); // w := D^{-1}*r
sigma = dscapr(w, r); // s0 := <w,r>
// cout << "Iteration " << iter << " : " << sqrt(sigma/sigma0) << endl;
}
cout << "aver. Jacobi rate : " << exp(log(sqrt(sigma / sigma0)) / iter) << " (" << iter << " iter)" << endl;
cout << "final error: " << sqrt(sigma / sigma0) << " (rel) " << sqrt(sigma) << " (abs)\n";
return;
}
void JacobiSmoother(Matrix const &SK, std::vector<double> const &f, std::vector<double> &u,
std::vector<double> &r, int nsmooth, double const omega, bool zero)
{
// ToDO: ensure compatible dimensions
int const nnodes = static_cast<int>(u.size());
if (zero) { // assumes initial solution is zero
DiagPrecond(SK, f, u, omega);
--nsmooth; // first smoothing sweep done
}
auto const &D = SK.GetDiag(); // accumulated diagonal of matrix @p SK.
for (int ns = 1; ns <= nsmooth; ++ns) {
SK.Defect(r, f, u); // r := f - K*u
#pragma omp parallel for
for (int k = 0; k < nnodes; ++k) {
// u := u + om*D^{-1}*r
u[k] = u[k] + omega * r[k] / D[k]; // MPI: distributed to accumulated vector needed
}
}
return;
}
void DiagPrecond(Matrix const &SK, std::vector<double> const &r, std::vector<double> &w,
double const omega)
{
// ToDO: ensure compatible dimensions
auto const &D = SK.GetDiag(); // accumulated diagonal of matrix @p SK.
int const nnodes = static_cast<int>(w.size());
#pragma omp parallel for
for (int k = 0; k < nnodes; ++k) {
w[k] = omega * r[k] / D[k]; // MPI: distributed to accumulated vector needed
}
return;
}
Multigrid::Multigrid(Mesh const &cmesh, int const nlevel)
: _meshes(cmesh, nlevel),
_SK(), _u(_meshes.size()), _f(_meshes.size()), _d(_meshes.size()), _w(_meshes.size()),
_Pc2f()
{
cout << "\n........................ in Multigrid::Multigrid ..................\n";
// Allocate Memory for matrices/vectors on all levels
for (size_t lev = 0; lev < Nlevels(); ++lev) {
_SK.push_back( FEM_Matrix(_meshes[lev]) ); // CRS matrix
const auto nn = _SK[lev].Nrows();
_u[lev].resize(nn);
_f[lev].resize(nn);
_d[lev].resize(nn);
_w[lev].resize(nn);
auto vv = _meshes[lev].GetFathersOfVertices();
cout << vv.size() << endl;
}
// Intergrid transfer operators
//cout << "\n........................ in Multigrid::Multigrid Prolongation ..................\n";
//_Pc2f.push_back( BisectInterpolation(vector<int>(0)) ); // no prolongation to coarsest grid
_Pc2f.push_back( BisectIntDirichlet() ); // no prolongation to coarsest grid
for (size_t lev = 1; lev < Nlevels(); ++lev) {
//cout << lev << endl;
//cout << _meshes[lev].GetFathersOfVertices () << endl;
_Pc2f.push_back( BisectIntDirichlet( _meshes[lev].GetFathersOfVertices (), _meshes[lev-1].Index_DirichletNodes () ) );
//cout << _Pc2f.back().Nrows() << " " << _Pc2f.back().Ncols() << endl;
}
cout << "\n..........................................\n";
}
Multigrid::~Multigrid()
{}
void Multigrid::DefineOperators()
{
for (size_t lev = 0; lev < Nlevels(); ++lev) {
DefineOperator(lev);
}
return;
}
// GH: Hack
void Multigrid::DefineOperator(size_t lev)
{
_SK[lev].CalculateLaplace(_f[lev]); // fNice() in userset.h
if (lev == Nlevels() - 1) { // fine mesh
_meshes[lev].SetValues(_u[lev], [](double x, double y) -> double
{ return x *x * std::sin(2.5 * M_PI * y); }
);
}
else {
_meshes[lev].SetValues(_u[lev], f_zero);
}
_SK[lev].ApplyDirichletBC(_u[lev], _f[lev]);
return;
}
void Multigrid::JacobiSolve(size_t lev)
{
assert(lev < Nlevels());
::JacobiSolve(_SK[lev], _f[lev], _u[lev]);
}
void Multigrid::MG_Step(size_t lev, int const pre_smooth, bool const bzero, int nu)
{
assert(lev < Nlevels());
int const post_smooth = pre_smooth;
if (lev == 0) { // coarse level
JacobiSmoother(_SK[lev], _f[lev], _u[lev], _d[lev], 100, 1.0, false);
}
else {
JacobiSmoother(_SK[lev], _f[lev], _u[lev], _d[lev], pre_smooth, 0.85, bzero);
if (nu > 0) {
_SK[lev].Defect(_d[lev], _f[lev], _u[lev]); // d := f - K*u
_Pc2f[lev].MultT(_d[lev], _f[lev - 1]); // f_H := R*d
//DefectRestrict(_SK[lev], _Pc2f[lev], _f[lev - 1], _f[lev], _u[lev]); // f_H := R*(f - K*u)
//_meshes[lev-1].Visualize(_f[lev - 1]); // GH: Visualize: f_H should be 0 on Dirichlet B.C.
MG_Step(lev - 1, pre_smooth, true, nu); // solve K_H * u_H =f_H with u_H:=0
for (int k = 1; k < nu; ++k) {
// W-cycle
MG_Step(lev - 1, pre_smooth, false, nu); // solve K_H * u_H =f_H
}
_Pc2f[lev].Mult(_w[lev], _u[lev - 1]); // w := P*u_H
vdaxpy(_u[lev], _u[lev], 1.0, _w[lev] ); // u := u + tau*w
}
JacobiSmoother(_SK[lev], _f[lev], _u[lev], _d[lev], post_smooth, 0.85, false);
}
return;
}
void Multigrid::MG_Solve(int pre_smooth, double eps, int nu)
{
size_t lev=Nlevels()-1; // fine level
// start with zero guess
DiagPrecond(_SK[lev], _f[lev], _w[lev], 1.0); // w := D^{-1]*f
//double s0 = L2_scapr(_f[lev],_w[lev]); // s_0 := <f,w>
double s0 = dscapr(_f[lev],_w[lev]); // s_0 := <f,w>
double si;
bool bzero = true; // start with zero guess
int iter = 0;
do
{
MG_Step(lev, pre_smooth, bzero, nu);
bzero=false;
_SK[lev].Defect(_d[lev], _f[lev], _u[lev]); // d := f - K*u
DiagPrecond(_SK[lev], _d[lev], _w[lev], 1.0); // w := D^{-1]*d
//si = L2_scapr(_d[lev],_w[lev]); // s_i := <d,w>
si = dscapr(_d[lev],_w[lev]); // s_i := <d,w>
++iter;
} while (si>s0*eps*eps);
cout << "\nrel. error: " << sqrt(si/s0) << " ( " << iter << " iter.)" << endl;
return;
}

View file

@ -0,0 +1,154 @@
#ifndef JACSOLVE_FILE
#define JACSOLVE_FILE
#include "geom.h"
#include "getmatrix.h"
#include <vector>
/**
* Solves linear system of equations K @p u = @p f via the Jacobi iteration.
* We use a distributed symmetric CSR matrix @p SK and initial guess of the
* solution is set to 0.
* @param[in] SK CSR matrix
* @param[in] f distributed local vector storing the right hand side
* @param[out] u accumulated local vector storing the solution.
*/
void JacobiSolve(CRS_Matrix const &SK, std::vector<double> const &f, std::vector<double> &u);
/**
* Solves linear system of equations K @p u = @p f via the Jacobi iteration.
* We use a distributed symmetric CSR matrix @p SK and initial guess of the
* solution is set to 0.
*
* In each smoothing step: @f$ \widehat{u} := u + \omega D^{-1}\left({f-K*u}\right) @f$
*
* @param[in] SK CSR matrix
* @param[in] f distributed local vector storing the right hand side
* @param[out] u accumulated local vector storing the solution.
* @param[in,out] r auxiliary local vector.
* @param[in] nsmooth number of smoothing steps.
* @param[in] omega relaxation parameter.
* @param[in] zero initial solution @p u is assumed to be zero.
*/
void JacobiSmoother(Matrix const &SK, std::vector<double> const &f, std::vector<double> &u,
std::vector<double> & r, int nsmooth=1, double const omega=1.0, bool zero=false);
/**
* @brief Simple diagonale preconditioning.
*
* The residuum @p r scaled by the inverse diagonal of matríx @p SK results in the correction @p w.
*
* @f$ w := \omega D^{-1}*r @f$
*
* @param[in] SK matrix
* @param[in] r distributed local vector storing the residuum
* @param[out] w accumulated local vector storing the correction.
* @param[in] omega relaxation parameter.
*/
void DiagPrecond(Matrix const &SK, std::vector<double> const &r, std::vector<double> &w,
double const omega=1.0);
/**
* @brief The Multigrid hierarchy including meshes, vectors and matrices, prolongations is stored.
*/
class Multigrid
{
public:
/**
* Generates the mesh hierachy with @p nlevel meshes starting from coarse mesh @p cmesg .
*
* The refined meshes are generated by edge bisection.
* All memory is allocated but stiffness matrices are yet not calculated
*
* @param[in] cmesh initial coarse mesh
* @param[in] nlevel number of meshes in hierarchy, including the initial coarse mesh
*
*/
Multigrid(Mesh const& cmesh, int nlevel);
Multigrid(Multigrid const&) = delete;
Multigrid& operator=(Multigrid const&) = delete;
~Multigrid();
/**
* @return Number of meshes in hierarchy.
*/
size_t Nlevels() const
{return _meshes.size(); }
/**
* @return Number of Unknowns.
*/
int Ndofs() const
{return _meshes[Nlevels()-1].Nnodes(); }
/**
* @return Meshes number @p lev .
*/
Mesh const& GetMesh(int lev) const
{ return _meshes[lev]; }
/**
* @return Solution vector at level @p lev .
*/
std::vector<double> const& GetSolution(int lev) const
{ return _u.at(lev); }
/**
* Calculates PDE matrices for all levels.
*/
void DefineOperators();
/**
* Calculates PDE matrix for level @p lev.
*
* @param[in] lev level in hierachy
*/
void DefineOperator(size_t lev);
/**
* Solves the system of equations at level @p lev via Jacobi iterations
*
* @param[in] lev level in hierachy
*/
void JacobiSolve(size_t lev);
/**
* Peformes one multigrid step at level @p lev .
*
* @param[in] lev level in hierachy
* @param[in] pre_smooth number of pre/post-smoothing sweeps
* @param[in] bzero start with zero-vector as solution
* @param[in] nu defines the multigrid cycle (1/2 = V/W)
*/
void MG_Step(size_t lev, int pre_smooth=1, bool const bzero=false, int nu=1);
/**
* Solves the system of equations at finest level via multigrid
*
* @param[in] pre_smooth number of pre/post-smoothing sweeps
* @param[in] eps stopping criteria (relative error)
* @param[in] nu defines the multigrid cycle (1/2 = V/W)
*/
void MG_Solve(int pre_smooth=1, double eps=1e-6, int nu=1);
private:
gMesh_Hierarchy _meshes; //!< mesh hierarchy from coarse (level 0) to fine.
std::vector<FEM_Matrix> _SK; //!< Sparse matrix on each level.
std::vector<std::vector<double>> _u; //!< Solution vector on each level.
std::vector<std::vector<double>> _f; //!< Right hand side vector on each level.
std::vector<std::vector<double>> _d; //!< Defect vector on each level.
std::vector<std::vector<double>> _w; //!< Correction vector on each level.
std::vector<BisectIntDirichlet> _Pc2f; //!< Interpolation to level from next coarser level.
};
#endif

View file

@ -0,0 +1,8 @@
%% calculate -Laplacian of a function
syms x y c ;
u = x*x*sin(2.5*pi*y)
f = simplify(-laplacian(u,[x,y]))
fsurf(u,[0,1,0,1])
xlabel("x");ylabel("y");

View file

@ -0,0 +1,94 @@
// MPI code in C++.
// See [Gropp/Lusk/Skjellum, "Using MPI", p.33/41 etc.]
// and /opt/mpich/include/mpi2c++/comm.h for details
#include "geom.h"
#include "getmatrix.h"
#include "jacsolve.h"
#include "par_geom.h"
#include "userset.h"
#include "vdop.h"
#include <cmath>
#include <iostream>
#include <mpi.h> // MPI
#include <omp.h> // OpenMP
using namespace std;
int main(int argc , char **argv )
{
MPI_Init(&argc, &argv);
MPI_Comm const icomm(MPI_COMM_WORLD);
omp_set_num_threads(1); // don't use OMP parallelization for a start
//
{
int np;
MPI_Comm_size(icomm, &np);
//assert(4 == np); // example is only provided for 4 MPI processes
}
// #####################################################################
// ---- Read the f.e. mesh and the mapping of elements to MPI processes
//Mesh const mesh_c("square_4.txt"); // Files square_4.m and square_4_sd.txt are needed
//ParMesh const mesh("square_bb");
ParMesh const mesh("../generate_mesh/rec");
//ParMesh const mesh("../generate_mesh/rec_a");
int const numprocs = mesh.NumProcs();
int const myrank = mesh.MyRank();
if ( 0 == myrank )
{
cout << "\n There are " << numprocs << " processes running.\n \n";
}
int const check_rank = 0; // choose the MPI process you would like to check the mesh
//if ( check_rank == myrank ) mesh.Debug();
//if ( check_rank == myrank ) mesh.DebugEdgeBased();
FEM_Matrix SK(mesh); // CRS matrix
//SK.Debug();
vector<double> uv(SK.Nrows(), 0.0); // temperature
vector<double> fv(SK.Nrows(), 0.0); // r.h.s.
mesh.VecAccu(uv); // Check MPI comm.
SK.CalculateLaplace(fv);
//SK.Debug();
//
mesh.SetValues(uv, [](double x, double y) -> double
{
return x *x * std::sin(2.5 * M_PI * y);
} );
////mesh.SetU(uv); // deprecated
//// Two ways to initialize the vector
////mesh.SetValues(uv,f_zero); // user function
////mesh.SetValues(uv, [](double x, double y) -> double {return 0.0*x*y;} ); // lambda function
////mesh.SetValues(uv, [](double x, double y) -> double {return 5e-3*(x+1)*(y+1);} ); // lambda function
////
//mesh.SetValues(uv, [](double x, double y) -> double {
//return x * x * std::sin(2.5 * M_PI * y);
//} );
SK.ApplyDirichletBC(uv, fv);
//SK.Debug();
double tstart = MPI_Wtime(); // Wall clock
JacobiSolve(SK, fv, uv ); // solve the system of equations
//JacobiSolve(mesh, SK, fv, uv ); // MPI: solve the system of equations
double t1 = MPI_Wtime() - tstart; // Wall clock
cout << "JacobiSolve: timing in sec. : " << t1 << endl;
//if (2==myrank || (1==numprocs && 0==myrank) ) mesh.Mesh::Visualize(uv); // Visualize only one subdomain
mesh.Visualize(uv); // Visualize all subdomains
MPI_Finalize();
return 0;
}

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_file>
<FileVersion major="1" minor="6" />
<Project>
<Option title="mgrid" />
<Option pch_mode="2" />
<Option compiler="gcc" />
<Build>
<Target title="Debug">
<Option output="bin/Debug/mgrid" prefix_auto="1" extension_auto="1" />
<Option object_output="obj/Debug/" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-Wshadow" />
<Add option="-Winit-self" />
<Add option="-Wredundant-decls" />
<Add option="-Wcast-align" />
<Add option="-Wundef" />
<Add option="-Wunreachable-code" />
<Add option="-Wmissing-declarations" />
<Add option="-Wswitch-enum" />
<Add option="-Wswitch-default" />
<Add option="-Weffc++" />
<Add option="-pedantic" />
<Add option="-Wextra" />
<Add option="-Wall" />
<Add option="-g" />
</Compiler>
</Target>
<Target title="Release">
<Option output="bin/Release/mgrid" prefix_auto="1" extension_auto="1" />
<Option object_output="obj/Release/" />
<Option type="1" />
<Option compiler="gcc" />
<Compiler>
<Add option="-O2" />
</Compiler>
<Linker>
<Add option="-s" />
</Linker>
</Target>
</Build>
<Compiler>
<Add option="-std=c++11" />
<Add option="-fexceptions" />
</Compiler>
<Unit filename="geom.cpp" />
<Unit filename="geom.h" />
<Unit filename="getmatrix.cpp" />
<Unit filename="getmatrix.h" />
<Unit filename="jacsolve.cpp" />
<Unit filename="jacsolve.h" />
<Unit filename="main.cpp" />
<Unit filename="userset.cpp" />
<Unit filename="userset.h" />
<Unit filename="vdop.cpp" />
<Unit filename="vdop.h" />
<Extensions>
<envvars />
<code_completion />
<lib_finder disable_auto="1" />
<debugger />
<DoxyBlocks>
<comment_style block="1" line="1" />
<doxyfile_project />
<doxyfile_build extract_all="1" />
<doxyfile_warnings />
<doxyfile_output />
<doxyfile_dot class_diagrams="1" have_dot="1" />
<general />
</DoxyBlocks>
</Extensions>
</Project>
</CodeBlocks_project_file>

View file

@ -0,0 +1,625 @@
// see: http://llvm.org/docs/CodingStandards.html#include-style
#include "vdop.h"
//#include "geom.h"
#include "par_geom.h"
#include <algorithm>
#include <array>
#include <cassert>
#include <cmath>
#include <ctime> // contains clock()
#include <fstream>
#include <iostream>
#include <list>
#include <numeric> // accumulate()
#include <string>
#include <vector>
using namespace std;
ParMesh::ParMesh(int ndim, int nvert_e, int ndof_e, int nedge_e, MPI_Comm const &icomm)
: Mesh(ndim, nvert_e, ndof_e, nedge_e),
_icomm(icomm), _numprocs(-1), _myrank(-1),
_v_l2g(0), _t_l2g(0), _v_g2l{{}}, _t_g2l{{}}, _valence(0),
_sendbuf(0), _sendcounts(0), _sdispls(0),
_loc_itf(0), _gloc_itf(0), _buf2loc(0)
{
MPI_Comm_size(icomm, &_numprocs);
MPI_Comm_rank(icomm, &_myrank);
}
ParMesh::~ParMesh()
{}
ParMesh::ParMesh(std::string const &sname, MPI_Comm const &icomm)
: ParMesh(2, 3, 3, 3, icomm) // two dimensions, 3 vertices, 3 dofs, 3 edges per element
{
//const int numprocs = _icomm.Get_size();
const string NS = "_" + to_string(_numprocs);
const string fname = sname + NS + ".txt";
//cout << "############ " << fname << endl;
ReadVertexBasedMesh(fname);
// ------------------------------------------------------------------------------
// Until this point a l l processes possess a l l mesh info in g l o b a l numbering
//
// Now, we have to select the data belonging to my_rank
// and we have to create the mapping local to global (l2g) and vice versa (g2l)
// ------------------------------------------------------------------------------
// save the global node mesh (maybe we need it later)
DeriveEdgeFromVertexBased(); // and even more
Mesh global_mesh(*this); // requires a l o t of memory
Del_EdgeConnectivity();
// read the subdomain info
const string dname = sname + NS + "_sd" + ".txt";
vector<int> t2d = ReadElementSubdomains(dname); // global mapping triangle to subdomain for all elements
//const int myrank = _icomm.Get_rank();
Transform_Local2Global_Vertex(_myrank, t2d); // Vertex based mesh: now in l o c a l indexing
DeriveEdgeFromVertexBased(); // Generate also the l o c a l edge based information
Generate_VectorAdd();
// Now we have to organize the MPI communication of vertices on the subdomain interfaces
return;
}
vector<int> ParMesh::ReadElementSubdomains(string const &dname)
{
ifstream ifs(dname);
if (!(ifs.is_open() && ifs.good())) {
cerr << "ParMesh::ReadElementSubdomain: Error cannot open file " << dname << endl;
assert(ifs.is_open());
}
int const OFFSET{1}; // Matlab to C indexing
cout << "ASCI file " << dname << " opened" << endl;
// Read some mesh constants
int nelem;
ifs >> nelem;
cout << nelem << " " << Nelems() << endl;
assert( Nelems() == nelem);
// Allocate memory
vector<int> t2d(nelem, -1);
// Read element mapping
for (int k = 0; k < nelem; ++k) {
int tmp;
ifs >> tmp;
//t2d[k] = tmp - OFFSET;
// 2020-01-08
t2d[k] = min(tmp, NumProcs()) - OFFSET;
}
return t2d;
}
void ParMesh::Transform_Local2Global_Vertex(int const myrank, vector<int> const &t2d)
{
// number of local elements
const int l_ne = count(t2d.cbegin(), t2d.cend(), myrank);
//cout << myrank << ":: " << lne << endl;
vector<int> l_ia(l_ne * NverticesElements(), -1); // local elements still with global vertex numbers
_t_l2g.resize(l_ne, -1);
int lk = 0;
for (size_t k = 0; k < t2d.size(); ++k) {
if (myrank == t2d[k]) {
//if (0==myrank)
//{
//cout << lk << " k " << t2d[k] << endl;
//}
l_ia[3 * lk ] = _ia[3 * k ];
l_ia[3 * lk + 1] = _ia[3 * k + 1];
l_ia[3 * lk + 2] = _ia[3 * k + 2]; // local elements still with global vertex numbers
_t_l2g[lk] = k; // elements: local to global mapping
_t_g2l[k] = lk; // global to local
++lk;
}
}
// Checks:
assert( count(l_ia.cbegin(), l_ia.cend(), -1) == 0 );
assert( count(_t_l2g.cbegin(), _t_l2g.cend(), -1) == 0 );
// Vertices: local to global mapping
auto tmp = l_ia;
sort(tmp.begin(), tmp.end());
auto ip = unique(tmp.begin(), tmp.end());
tmp.erase(ip, tmp.end());
_v_l2g = tmp; // Vertices: local to global mapping
for (size_t lkv = 0; lkv < _v_l2g.size(); ++lkv) {
_v_g2l[_v_l2g[lkv]] = lkv; // global to local
}
// Boundary edges
vector<int> l_bedges;
vector<int> l_sdedges;
for (size_t b = 0; b < _bedges.size(); b += 2) {
int const v1 = _bedges[b ]; // global vertex numbers
int const v2 = _bedges[b + 1];
try {
int const lv1 = _v_g2l.at(v1); // map[] would add that element
int const lv2 = _v_g2l.at(v2); // but at() throws an exeption
l_bedges.push_back(lv1);
l_bedges.push_back(lv2); // Boundaries: already in local indexing
// 2020-01-08
l_sdedges.push_back(_sdedges[b ]);
l_sdedges.push_back(_sdedges[b+1]);
}
catch (std::out_of_range &err) {
//cerr << ".";
}
}
// number of local vertices
const int l_nn = _v_l2g.size();
vector<double> l_xc(Ndims()*l_nn);
for (int lkk = 0; lkk < l_nn; ++lkk) {
int k = _v_l2g.at(lkk);
l_xc[2 * lkk ] = _xc[2 * k ];
l_xc[2 * lkk + 1] = _xc[2 * k + 1];
}
// Now, we represent the vertex mesh in l o c a l numbering
// elements
for (size_t i = 0; i < l_ia.size(); ++i) {
l_ia[i] = _v_g2l.at(l_ia[i]); // element vertices: global to local
}
SetNelem(l_ne);
_ia = l_ia;
// boundary
_bedges = l_bedges;
_sdedges = l_sdedges;
// coordinates
SetNnode(l_nn);
_xc = l_xc;
return;
}
void ParMesh::Generate_VectorAdd()
{
// Some checks
int lnn = Nnodes(); // local number of vertices
assert(static_cast<int>(_v_l2g.size()) == lnn);
int ierr{-12345};
// ---- Determine global largest vertex index
int gidx_max{-1}; // global largest vertex index
int lmax = *max_element(_v_l2g.cbegin(), _v_l2g.cend());
MPI_Allreduce(&lmax, &gidx_max, 1, MPI_INT, MPI_MAX, _icomm);
int gidx_min{-1}; // global smallest vertex index
int lmin = *min_element(_v_l2g.cbegin(), _v_l2g.cend());
MPI_Allreduce(&lmin, &gidx_min, 1, MPI_INT, MPI_MIN, _icomm);
//cout << gidx_min << " " << gidx_max << endl;
assert(0 == gidx_min); // global indices have to start with 0
// ---- Determine for all global vertices the number of subdomains it belongs to
vector<int> global(gidx_max+1, 0); // global scalar array for vertices
for (auto const gidx : _v_l2g) global[gidx] = 1;
// https://www.mpi-forum.org/docs/mpi-2.2/mpi22-report/node109.htm
ierr = MPI_Allreduce(MPI_IN_PLACE, global.data(), global.size(), MPI_INT, MPI_SUM, _icomm);
//if (0 == MyRank()) cout << global << endl;
//MPI_Barrier(_icomm);
//cout << _xc[2*_v_g2l.at(2)] << " , " << _xc[2*_v_g2l.at(2)+1] << endl;
//MPI_Barrier(_icomm);
// now, global[] contains the number of subdomains a global vertex belongs to
if ( count(global.cbegin(), global.cend(), 0) > 0 )
cerr << "\n !!! Non-continuous global vertex indexing !!!\n";
// ---- Determine local interface vertices ( <==> global[] > 1 )
// _loc_itf, neigh_itf
//vector<int> loc_itf; // local indices of interface vertices on this MPI process
for (size_t lk = 0; lk < _v_l2g.size(); ++lk) {
int const gk = _v_l2g[lk]; // global index of local vertex lk
if ( global[gk] > 1 ) {
_loc_itf.push_back(lk); // local indices of interface vertices on this MPI process
}
}
//MPI_Barrier(_icomm);
//if (0 == MyRank()) cout << "\n..._loc_itf...\n" << _loc_itf << "\n......\n";
//MPI_Barrier(_icomm);
// ---- global indices of local interface vertices
//auto gloc_itf(_loc_itf);
_gloc_itf=_loc_itf;
for_each(_gloc_itf.begin(), _gloc_itf.end(), [this] (auto & v) -> void { v = _v_l2g[v];} );
//MPI_Barrier(_icomm);
//if (0 == MyRank()) cout << "\n..._gloc_itf...\n" << _gloc_itf << "\n......\n";
//DebugVector(_gloc_itf,"_gloc_itf");
// ---- Determine the global length of interfaces
vector<int> vnn(NumProcs(), -1); // number of interface vertices per MPI rank
int l_itf(_loc_itf.size()); // # local interface vertices
ierr = MPI_Allgather(&l_itf, 1, MPI_INT, vnn.data(), 1, MPI_INT, _icomm);
assert(0 == ierr);
//cout << vnn << endl;
// ---- Now we consider only the inferface vertices
int snn = accumulate(vnn.cbegin(), vnn.cend(), 0); // required length of array for global interface indices
//cout << snn << " " << gnn << endl;
vector<int> dispnn(NumProcs(), 0) ; // displacement of interface vertices per MPI rank
partial_sum(vnn.cbegin(), vnn.cend() - 1, dispnn.begin() + 1);
//cout << dispnn << endl;
// ---- Get the global indices for all global interfaces
vector<int> g_itf(snn, -1); // collects all global indices of the global interfaces
// https://www.mpich.org/static//docs/v3.0.x/www3/MPI_Gatherv.html
ierr = MPI_Gatherv( _gloc_itf.data(), _gloc_itf.size(), MPI_INT,
g_itf.data(), vnn.data(), dispnn.data(), MPI_INT, 0, _icomm);
assert(0 == ierr);
// https://www.mpich.org/static/docs/v3.1/www3/MPI_Bcast.html
ierr = MPI_Bcast(g_itf.data(), g_itf.size(), MPI_INT, 0, _icomm);
assert(0 == ierr); // Now, each MPI rank has the all global indices of the global interfaces
//MPI_Barrier(_icomm);
//if (MyRank() == 0) cout << "\n...g_itf...\n" << g_itf << "\n......\n";
//MPI_Barrier(_icomm);
// ----- Determine all MPI ranks a local interface vertex belongs to
vector<vector<int>> neigh_itf(_loc_itf.size());// subdomains a local interface vertex belongs to
for (size_t lk = 0; lk < _loc_itf.size(); ++lk) {
const int gvert = _gloc_itf[lk]; // global index of local interface node lk
for (int rank = 0; rank < NumProcs(); ++rank) {
auto const startl = g_itf.cbegin() + dispnn[rank];
auto const endl = startl + vnn[rank];
if ( find( startl, endl, gvert) != endl) {
neigh_itf[lk].push_back(rank);
}
}
}
// ---- check the available info in _loc_itf[lk], _gloc_itf[lk], neigh_itf[lk]
//MPI_Barrier(_icomm);
////if (MyRank()==0) cout << "\n...neigh_itf ...\n" << neigh_itf << endl;
//if (MyRank() == 0) {
//for (size_t lk = 0; lk < _loc_itf.size(); ++lk ) {
//cout << lk << " : local idx " << _loc_itf[lk] << " , global idx " << _gloc_itf[lk];
//cout << " with MPI ranks " << neigh_itf[lk] << endl;
//}
//}
//MPI_Barrier(_icomm);
// ---- store the valence (e.g., the number of subdomains it belongs to) of all local vertices
_valence.resize(Nnodes(),1);
for (size_t lk = 0; lk < _loc_itf.size(); ++lk)
{
_valence[_loc_itf[lk]] = neigh_itf[lk].size();
}
//DebugVector(_valence,"_valence",_icomm);
// ---- We ware going to use MPI_Alltoallv for data exchange on interfaces
// https://www.mpi-forum.org/docs/mpi-3.1/mpi31-report/node109.htm#Node109
// https://www.open-mpi.org/doc/v4.0/man3/MPI_Alltoallv.3.php
//int MPI_Alltoallv(const void* sendbuf, const int sendcounts[], const int sdispls[], MPI_Datatype sendtype, void* recvbuf, const int recvcounts[], const int rdispls[], MPI_Datatype recvtype, MPI_Comm comm)
//
// MPI_Alltoallv needs:
// vector<double> sendbuf (MPI_IN_PLACE: used also as recvbuf)
// vector<int> sendcounts (the same as for recv)
// vector<int> sdispls (the same as for recv)
//
// We need to map the interface vertices onto the sendbuffer:
// vector<int> loc_itf local index of interface vertex lk
// vector<int> gloc_itf global index of interface vertex lk
// vector<int> buf2loc local indices of sendbuffer positions (the same as for recv)
// ---- Determine sendcounts[] and sdipls[] from neigh_itf[]
//vector<int> _sendcounts(NumProcs(), 0);
_sendcounts.resize(NumProcs(), 0);
for (size_t lk = 0; lk < _loc_itf.size(); ++lk ) {
auto const &kneigh = neigh_itf[lk];
for (size_t ns = 0; ns < kneigh.size(); ++ns) {
++_sendcounts[kneigh[ns]];
}
}
//if (MyRank() == 0) cout << "\n..._sendcounts ...\n" << _sendcounts << endl;
//vector<int> _sdispls(NumProcs(), 0);
_sdispls.resize(NumProcs(), 0);
partial_sum(_sendcounts.cbegin(), _sendcounts.cend() - 1, _sdispls.begin() + 1);
//vector<int> _sdispls(NumProcs()+1, 0);
//partial_sum(_sendcounts.cbegin(), _sendcounts.cend(), _sdispls.begin() + 1);
//if (MyRank() == 0) cout << "\n..._sdispls ...\n" << _sdispls << endl;
// ---- Determine size of buffer 'nbuffer' and mapping 'buf2loc'
int const nbuffer = accumulate(_sendcounts.cbegin(), _sendcounts.cend(), 0);
//vector<int> _buf2loc(nbuffer, -1);
_buf2loc.resize(nbuffer, -1);
int buf_idx = 0; // position in buffer
for (int rank = 0; rank < NumProcs(); ++rank) {
assert( buf_idx == _sdispls[rank]);
for (size_t lk = 0; lk < _loc_itf.size(); ++lk ) {
auto const &kneigh = neigh_itf[lk];
if (find(kneigh.cbegin(),kneigh.cend(),rank)!=kneigh.cend())
{
_buf2loc[buf_idx] = _loc_itf[lk];
++buf_idx;
}
}
}
//if (MyRank() == 0) cout << "\n...buf2loc ...\n" << buf2loc << endl;
//DebugVector(buf2loc,"buf2loc",_icomm);
// ---- Allocate send/recv buffer
//vector<double> _sendbuf(nbuffer,-1.0);
_sendbuf.resize(nbuffer,-1.0);
assert(CheckInterfaceExchange_InPlace());
cout << " Check of data exchange (InPlace) successful!\n";
assert(CheckInterfaceExchange());
cout << " Check of data exchange successful!\n";
assert(CheckInterfaceAdd_InPlace());
cout << " Check of data add successful!\n";
assert(CheckInterfaceAdd());
cout << " Check of data add (InPlace) successful!\n";
vector<double> x(Nnodes(),-1.0);
VecAccu(x);
cout << " VecAccu (InPlace) successful!\n";
return;
}
bool ParMesh::CheckInterfaceExchange_InPlace() const
{
vector<double> x(Nnodes(),-1.0);
copy(_v_l2g.cbegin(),_v_l2g.cend(),x.begin()); // init x with global vertex indices
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
_sendbuf[ls] = x[_buf2loc.at(ls)];
}
int ierr = MPI_Alltoallv(MPI_IN_PLACE, _sendcounts.data(), _sdispls.data(), MPI_DOUBLE,
_sendbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE, _icomm);
assert(ierr==0);
//DebugVector(_sendbuf,"_sendbuf",_icomm);
vector<double> y(x);
for(size_t lk = 0; lk<_loc_itf.size(); ++lk) y[_loc_itf.at(lk)] = -1.0; // only for interface nodes
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
y[_buf2loc.at(ls)] = _sendbuf[ls];
}
double const eps=1e-10;
bool bv = equal(x.cbegin(),x.cend(),y.cbegin(),
[eps](double a, double b) -> bool
{ return std::abs(a-b)<eps*(1.0+0.5*(std::abs(a)+ std::abs(b))); }
);
return bv;
}
bool ParMesh::CheckInterfaceExchange() const
{
vector<double> x(Nnodes(),-1.0);
copy(_v_l2g.cbegin(),_v_l2g.cend(),x.begin()); // init x with global vertex indices
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
_sendbuf[ls] = x[_buf2loc.at(ls)];
}
vector<double> recvbuf(_sendbuf.size());
int ierr = MPI_Alltoallv(_sendbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE,
recvbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE, _icomm);
//DebugVector(_sendbuf,"_sendbuf",_icomm);
//DebugVector(recvbuf,"recvbuf",_icomm);
assert(ierr==0);
vector<double> y(x);
for(size_t lk = 0; lk<_loc_itf.size(); ++lk) y[_loc_itf.at(lk)] = -1.0; // only for interface nodes
for(size_t ls = 0; ls<recvbuf.size(); ++ls)
{
y[_buf2loc.at(ls)] = recvbuf[ls];
}
//cout << "WRONG : " << count(y.cbegin(),y.cend(), -1.0) << endl;
double const eps=1e-10;
bool bv = equal(x.cbegin(),x.cend(),y.cbegin(),
[eps](double a, double b) -> bool
{ return std::abs(a-b)<eps*(1.0+0.5*(std::abs(a)+ std::abs(b))); }
);
return bv;
}
bool ParMesh::CheckInterfaceAdd_InPlace() const
{
vector<double> x(Nnodes(),-1.0);
for (size_t i=0; i<x.size(); ++i)
{
x[i] = _xc[2*i]+_xc[2*i+1]; // init x with coordinate values
}
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
_sendbuf[ls] = x[_buf2loc.at(ls)];
}
int ierr = MPI_Alltoallv(MPI_IN_PLACE, _sendcounts.data(), _sdispls.data(), MPI_DOUBLE,
_sendbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE, _icomm);
assert(ierr==0);
//DebugVector(_sendbuf,"_sendbuf",_icomm);
vector<double> y(x);
for(size_t lk = 0; lk<_loc_itf.size(); ++lk) y[_loc_itf.at(lk)] = 0.0; // only for interface nodes
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
y[_buf2loc.at(ls)] += _sendbuf[ls];
}
MPI_Barrier(_icomm);
//DebugVector(x,"x",_icomm);
//DebugVector(y,"y",_icomm);
for (size_t i= 0; i<y.size(); ++i) y[i]/=_valence[i]; // divide by valence
double const eps=1e-10;
bool bv = equal(x.cbegin(),x.cend(),y.cbegin(),
[eps](double a, double b) -> bool
{ return std::abs(a-b)<eps*(1.0+0.5*(std::abs(a)+ std::abs(b))); }
);
return bv;
}
bool ParMesh::CheckInterfaceAdd() const
{
vector<double> x(Nnodes(),-1.0);
for (size_t i=0; i<x.size(); ++i)
{
//x[i] = _xc[2*i]+_xc[2*i+1]; // init x with coordinate values
x[i] = _v_l2g[i];
}
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
_sendbuf[ls] = x[_buf2loc.at(ls)];
}
vector<double> recvbuf(_sendbuf.size());
int ierr = MPI_Alltoallv(_sendbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE,
recvbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE, _icomm);
//DebugVector(_sendbuf,"_sendbuf",_icomm);
//DebugVector(recvbuf,"recvbuf",_icomm);
assert(ierr==0);
vector<double> y(x);
for(size_t lk = 0; lk<_loc_itf.size(); ++lk) y[_loc_itf.at(lk)] = 0.0; // only for interface nodes
for(size_t ls = 0; ls<recvbuf.size(); ++ls)
{
//if (0==MyRank()) cout << ls << ": " << _buf2loc.at(ls) << " " << y[_buf2loc.at(ls)] << "("<< x[_buf2loc.at(ls)] << ")" << " " << recvbuf[ls] << " (" << _sendbuf[ls] << ")" << endl;
y[_buf2loc.at(ls)] += recvbuf[ls];
}
MPI_Barrier(_icomm);
//DebugVector(x,"x",_icomm);
//DebugVector(y,"y",_icomm);
for (size_t i= 0; i<y.size(); ++i) y[i]/=_valence[i]; // divide by valence
double const eps=1e-10;
bool bv = equal(x.cbegin(),x.cend(),y.cbegin(),
[eps](double a, double b) -> bool
{ return std::abs(a-b)<eps*(1.0+0.5*(std::abs(a)+ std::abs(b))); }
);
return bv;
}
// ----------
void ParMesh::VecAccu(std::vector<double> &w) const
{
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
_sendbuf[ls] = w[_buf2loc.at(ls)];
}
int ierr = MPI_Alltoallv(MPI_IN_PLACE, _sendcounts.data(), _sdispls.data(), MPI_DOUBLE,
_sendbuf.data(), _sendcounts.data(), _sdispls.data(), MPI_DOUBLE, _icomm);
assert(ierr==0);
//DebugVector(_sendbuf,"_sendbuf",_icomm);
for(size_t lk = 0; lk<_loc_itf.size(); ++lk) w[_loc_itf.at(lk)] = 0.0; // only for interface nodes
for(size_t ls = 0; ls<_sendbuf.size(); ++ls)
{
w[_buf2loc.at(ls)] += _sendbuf[ls];
}
return;
}
// ----------------------
/*
manjaro> matlab
MATLAB is selecting SOFTWARE OPENGL rendering.
/usr/local/MATLAB/R2019a/bin/glnxa64/MATLAB: error while loading shared libraries: libcrypt.so.1: cannot open shared object file: No such file or directory
SOLUTION: sudo pacman -S libxcrypt-compat + reboot
*/
void ParMesh::Visualize(vector<double> const &v) const
{
// define external command, but we have to pass the number of subdomains
string const MatlabScript{"visualize_par_results("+ to_string(_numprocs) + ")"};
// define the command to be executed
string const exec_m("matlab -nosplash -nodesktop -r '" + MatlabScript + "; quit'"); // Matlab
//string const exec_m("octave --no-window-system --no-gui '"+MatlabScript+"'"); // Octave, until version 6.3
//string const exec_m("octave --no-gui --eval '"+MatlabScript+"'"); // Octave since version 6.4
//string const exec_m("flatpak run org.octave.Octave --eval '"+MatlabScript+"'"); // Octave (flatpak): desktop GH
// old calls
//const string exec_m("matlab -nosplash -nodesktop -r 'try visualize_par_results("+ to_string(_numprocs) + "); catch; end; quit'"); // Matlab old
//const string exec_m("octave --no-window-system --no-gui visualize_par_results.m"); // Octave old
const string pre{"uv_"};
const string post{".txt"};
const string fname(pre + to_string(_myrank) + post);
if (0 == _myrank)
{
cout << exec_m << endl;
cout << fname << endl;
}
for (int p = 0; p <= NumProcs(); ++p) {
if (MyRank() == p) Write_ascii_matlab(fname, v);
MPI_Barrier(_icomm);
}
MPI_Barrier(_icomm);
if (0 == _myrank)
{
int ierror = system(exec_m.c_str()); // call external command
if (ierror != 0)
{
cout << endl << "Check path to Matlab/octave on your system" << endl;
}
cout << endl;
}
MPI_Barrier(_icomm);
return;
}

View file

@ -0,0 +1,160 @@
#ifndef PAR_GEOM_FILE
#define PAR_GEOM_FILE
#include "geom.h"
#include "vdop.h"
#include <array>
#include <functional> // function; C++11
#include <iostream>
#include <map>
#include <memory> // shared_ptr
#include <mpi.h> // MPI
#include <string>
#include <vector>
class ParMesh: public Mesh
{
public:
/**
* Constructor initializing the members with default values.
*
* @param[in] ndim space dimensions (dimension for coordinates)
* @param[in] nvert_e number of vertices per element (dimension for connectivity)
* @param[in] ndof_e degrees of freedom per element (= @p nvert_e for linear elements)
* @param[in] nedge_e number of edges per element (= @p nvert_e for linear elements in 2D)
* @param[in] icomm MPI communicator
*/
explicit ParMesh(int ndim, int nvert_e = 0, int ndof_e = 0, int nedge_e = 0, MPI_Comm const &icomm = MPI_COMM_WORLD);
ParMesh(ParMesh const &) = default;
ParMesh &operator=(ParMesh const &) = delete;
/**
* Destructor.
*
* See clang warning on
* <a href="https://stackoverflow.com/questions/28786473/clang-no-out-of-line-virtual-method-definitions-pure-abstract-c-class/40550578">weak-vtables</a>.
*/
virtual ~ParMesh();
/**
* Reads mesh data from a binary file.
*
* @param[in] sname suffix of file name
* @param[in] icomm MPI communicator
* @see ascii_write_mesh.m for the file format.
*/
explicit ParMesh(std::string const &sname, MPI_Comm const &icomm = MPI_COMM_WORLD);
void VecAccu(std::vector<double> &w) const;
/** Inner product
* @param[in] x vector
* @param[in] y vector
* @return resulting Euclidian inner product <x,y>
*/
double dscapr(std::vector<double> const &x, std::vector<double> const &y) const
{
return par_scalar(x, y, _icomm);
}
/**
* Visualize @p v together with its mesh information via matlab or octave.
*
* Comment/uncomment those code lines in method Mesh:Visualize (geom.cpp)
* that are supported on your system.
*
* @param[in] v vector
*
* @warning matlab files ascii_read_meshvector.m visualize_results.m
* must be in the executing directory.
*/
void Visualize(std::vector<double> const &v) const override;
private:
/**
* Reads the global triangle to subdomain mapping.
*
* @param[in] dname file name
*
* @see ascii_write_subdomains.m for the file format
*/
std::vector<int> ReadElementSubdomains(std::string const &dname);
/**
* Transform
*
* @param[in] myrank MPI rank of this process
* @param[in] t2d global mapping triangle to subdomain for all elements (vertex based)
*/
void Transform_Local2Global_Vertex(int myrank, std::vector<int> const &t2d);
/**
* Transform
*/
void Generate_VectorAdd();
bool CheckInterfaceExchange_InPlace() const;
bool CheckInterfaceExchange() const;
bool CheckInterfaceAdd_InPlace() const;
bool CheckInterfaceAdd() const;
public:
/** MPI rank of the calling process in communication group.
*
* @return MPI rank of the calling process
*/
int MyRank() const
{
return _myrank;
}
/** Number of MPI processes in communication group.
*
* @return Number of MPI processes
*/
int NumProcs() const
{
return _numprocs;
}
/** Returns recent
* @return MPI communicator
*/
MPI_Comm GetCommunicator() const
{
return _icomm;
}
private:
// Don't use &_icomm ==> Error
MPI_Comm const _icomm; //!< MPI communicator for the group of processes
int _numprocs; //!< number of MPI processes
int _myrank; //!< my MPI rank
std::vector<int> _v_l2g; //!< vertices: local to global mapping
std::vector<int> _t_l2g; //!< triangles: local to global mapping
std::map<int, int> _v_g2l; //!< vertices: global to local mapping
std::map<int, int> _t_g2l; //!< triangles: global to local mapping
//std::vector<int> e_l2g; //!< edges: local to global mapping
std::vector<int> _valence; //!< valence of local vertices, i.e. number of subdomains they belong to
// MPI_Alltoallv needs:
mutable std::vector<double> _sendbuf; //!< send buffer a n d receiving buffer (MPI_IN_PLACE)
std::vector<int> _sendcounts; //!< number of data to send to each MPI rank (the same as for recv)
std::vector<int> _sdispls; //!< offset of data to send to each MPI rank wrt. _senbuffer (the same as for recv)
//
// We need to map the interface vertices onto the sendbuffer:
std::vector<int> _loc_itf; //!< local index of interface vertex lk
std::vector<int> _gloc_itf; //!< global index of interface vertex lk
std::vector<int> _buf2loc; //!< local indices of sendbuffer positions (the same as for recv)
};
#endif

View file

@ -0,0 +1,42 @@
% Square:
% flatpak run org.octave.Octave <filename>
% or
% octave --no-window-system --no-gui -qf <filename>
clear all
clc
% %% L-shape
% g=[2 0 2 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 1 0;
% 2 2 1 1 0.5 1 0;
% 2 1 1 0.5 2 1 0;
% 2 1 0 2 2 1 0;
% 2 0 0 2 0 1 0]';
%% square
g=[2 0 1 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 1 1 0 1 1 0;
2 1 0 1 1 1 0;
2 0 0 1 0 1 0]';
%[p,e,t] = initmesh(g,'hmax',0.01);
[p,e,t] = initmesh(g,'hmax',0.5);
pdemesh(p,e,t)
%% GH
% output from <https://de.mathworks.com/help/pde/ug/initmesh.html initmesh>
%
% coordinates p: [2][nnode]
% connectivity t: [4][nelem] with t(4,:) are the subdomain numbers
% edges e: [7][nedges] boundary edges
% e([1,2],:) - start/end vertex of edge
% e([3,4],:) - start/end values
% e(5,:) - segment number
% e([6,7],:) - left/right subdomain
ascii_write_mesh( p, t, e, mfilename);
% tmp=t(1:3,:)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,71 @@
% Square:
% flatpak run org.octave.Octave <filename>
% or
% octave --no-window-system --no-gui -qf <filename>
clear all
clc
% %% L-shape
% g=[2 0 2 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 1 0;
% 2 2 1 1 0.5 1 0;
% 2 1 1 0.5 2 1 0;
% 2 1 0 2 2 1 0;
% 2 0 0 2 0 1 0]';
%% square
% g=[2 0 1 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 0 1 1 0;
% 2 1 0 1 1 1 0;
% 2 0 0 1 0 1 0]';
% %% 2 squares
% g=[2 0 1 0 0 1 0; % 1 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 0 1 1 2;
% 2 1 0 1 1 1 0;
% 2 0 0 1 0 1 0;
% 2 1 2 0 0 2 0; % 2 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 2 0;
% 2 2 1 1 1 2 0
% ]';
%% 4 squares
g=[2 0 1 0 0 1 0; % 1 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 1 1 0 1 1 2;
2 1 0 1 1 1 3;
2 0 0 1 0 1 0;
2 1 2 0 0 2 0; % 2 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 2 2 0 1 2 0;
2 2 1 1 1 2 4;
% 2 1 1 1 0 2 1;
% 2 0 1 1 1 3 1; % 3 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 1 1 1 2 3 4;
2 1 0 2 2 3 0;
2 0 0 2 1 3 0;
% 2 1 2 1 1 4 2; % 4 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 2 2 1 2 4 0;
2 2 1 2 2 4 0
% 2 1 1 2 1 4 3
]';
[p,e,t] = initmesh(g,'hmax',0.1);
pdemesh(p,e,t)
%% GH
% output from <https://de.mathworks.com/help/pde/ug/initmesh.html initmesh>
%
% coordinates p: [2][nnode]
% connectivity t: [4][nelem] with t(4,:) are the subdomain numbers
% edges e: [7][nedges] boundary edges
% e([1,2],:) - start/end vertex of edge
% e([3,4],:) - start/end values
% e(5,:) - segment number
% e([6,7],:) - left/right subdomain
ascii_write_mesh( p, t, e, mfilename);
ascii_write_subdomains( p, t, e, mfilename);
% tmp=t(1:3,:)

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,106 @@
% Square:
% flatpak run org.octave.Octave <filename>
% or
% octave --no-window-system --no-gui -qf <filename>
clear all
clc
% %% L-shape
% g=[2 0 2 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 1 0;
% 2 2 1 1 0.5 1 0;
% 2 1 1 0.5 2 1 0;
% 2 1 0 2 2 1 0;
% 2 0 0 2 0 1 0]';
%% square
% g=[2 0 1 0 0 1 0; % #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 0 1 1 0;
% 2 1 0 1 1 1 0;
% 2 0 0 1 0 1 0]';
% %% 2 squares
% g=[2 0 1 0 0 1 0; % 1 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 1 1 0 1 1 2;
% 2 1 0 1 1 1 0;
% 2 0 0 1 0 1 0;
% 2 1 2 0 0 2 0; % 2 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
% 2 2 2 0 1 2 0;
% 2 2 1 1 1 2 0
% ]';
%% 4 squares
g=[2 0 1 0 0 1 0; % 1 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 1 1 0 1 1 2;
2 1 0 1 1 1 3;
2 0 0 1 0 1 0;
2 1 2 0 0 2 0; % 2 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 2 2 0 1 2 0;
2 2 1 1 1 2 4;
% 2 1 1 1 0 2 1;
% 2 0 1 1 1 3 1; % 3 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 1 1 1 2 3 4;
2 1 0 2 2 3 0;
2 0 0 2 1 3 0;
% 2 1 2 1 1 4 2; % 4 #vertices,v_1x, v_2x, v_1y, v_2y, subdomain_left, subdomain_right
2 2 2 1 2 4 0;
2 2 1 2 2 4 0
% 2 1 1 2 1 4 3
]';
%% Generate mesh from geometry
%
[p,e,t] = initmesh(g,'hmax',1); % works correctly
% p(1,15) = 1.51; %% angle in trangle > pi/2 ==> now the second refinement produces irregular meshes!
% p(1,15) = 1.7; %% angle in trangle > pi/2 ==> now the second refinement produces irregular meshes!
% ??
% https://de.mathworks.com/help/pde/ug/mesh-data-pet-triples.html
% generateMesh(...)
% mesh2Pet(...)
%
% [p,e,t] = initmesh(g); % problems in solution after 2 refinements
% [p,e,t] = initmesh(g,'hmax',0.5); % problems in solution after 2 refinements (peaks with h=0.5, oscillations in (1,1) for h=0.1
% [p,e,t] = initmesh(g,'hmax',0.1/4); % no problems in solution with 0 refinemnet steps
%% Show mesh
pdemesh(p,e,t)
% pdemesh(p,e,t,'NodeLabels','on')
%% Improve mesh
% min(pdetriq(p,t))
% p = jigglemesh(p,e,t,'opt','minimum','iter',inf);
% min(pdetriq(p,t))
% pdemesh(p,e,t)
%% Refine mesh, see comments in "Generate mesh from geometry"
%
% nrefine=8;
nrefine=2; %
for k=1:nrefine
[p,e,t] = refinemesh(g,p,e,t);
% p = jigglemesh(p,e,t,'opt','minimum','iter',inf); % improve mesh
min(pdetriq(p,t))
fprintf('refinement: %i nodes: %i triangles: %i \n', k, size(p,2), size(t,2))
end
% figure; pdemesh(p,e,t,'NodeLabels','on')
%
%% GH
% output from <https://de.mathworks.com/help/pde/ug/initmesh.html initmesh>
%
% coordinates p: [2][nnode]
% connectivity t: [4][nelem] with t(4,:) are the subdomain numbers
% edges e: [7][nedges] boundary edges
% e([1,2],:) - start/end vertex of edge
% e([3,4],:) - start/end values
% e(5,:) - segment number
% e([6,7],:) - left/right subdomain
ascii_write_mesh( p, t, e, mfilename);
ascii_write_subdomains( p, t, e, mfilename);
% tmp=t(1:3,:)

View file

@ -0,0 +1,726 @@
209
2
384
3
0 0
1 0
1 1
0 1
2 0
2 1
1 2
0 2
2 2
1.5 1.5
0.5 1.5
1.5 0.5
0.5 0.5
1 0.5
1.5 1
0.5 1
1 1.5
0.5 0
1 0.25
0.75 1
0 0.5
1.5 0
2 0.5
1.75 1
1 1.25
0.5 2
0 1.5
2 1.5
1.5 2
1 0.75
1.25 1
0.25 1
1 1.75
1.75 1.25
1.25 1.75
1.75 1.75
0.25 1.25
0.75 1.75
0.25 1.75
1.25 0.25
1.75 0.25
1.75 0.75
0.25 0.25
0.75 0.25
0.25 0.75
1.25 0.5
0.75 0.5
1.5 1.25
1.5 0.75
1.25 0.75
0.5 1.25
0.5 0.75
0.75 0.75
1.25 1.5
0.75 1.5
1.25 1.25
0.75 1.25
0.25 0
1 0.125
0.875 1
0 0.75
1.25 0
2 0.25
1.875 1
1 1.125
0.75 2
0 1.75
2 1.25
1.75 2
1 0.625
1.375 1
0.375 1
1 1.625
0.75 0
1 0.375
0.625 1
0 0.25
1.75 0
2 0.75
1.625 1
1 1.375
0.25 2
0 1.25
2 1.75
1.25 2
1 0.875
1.125 1
0.125 1
1 1.875
0.875 1.125
0.875 0.875
1.125 1.125
1.125 0.875
1.875 1.125
1.625 1.375
1.75 1.125
1.875 1.375
1.125 1.875
1.375 1.625
1.375 1.875
1.125 1.75
1.875 1.875
1.625 1.625
1.875 1.625
1.625 1.875
1.75 1.5
1.5 1.75
0.125 1.125
0.375 1.375
0.125 1.375
0.25 1.125
0.875 1.875
0.625 1.625
0.625 1.875
0.875 1.75
0.125 1.875
0.375 1.625
0.375 1.875
0.125 1.625
0.25 1.5
0.5 1.75
1.125 0.125
1.375 0.375
1.125 0.25
1.375 0.125
1.875 0.125
1.625 0.375
1.625 0.125
1.875 0.375
1.5 0.25
1.875 0.875
1.625 0.625
1.875 0.625
1.75 0.875
1.75 0.5
0.125 0.125
0.375 0.375
0.375 0.125
0.125 0.375
0.875 0.125
0.625 0.375
0.625 0.125
0.875 0.25
0.5 0.25
0.125 0.875
0.375 0.625
0.125 0.625
0.25 0.875
0.25 0.5
1.375 0.5
1.125 0.5
1.125 0.375
1.25 0.375
0.625 0.5
0.875 0.5
0.875 0.375
0.75 0.375
1.5 1.375
1.5 1.125
1.625 1.125
1.625 1.25
1.5 0.625
1.5 0.875
1.625 0.875
1.625 0.75
1.375 0.625
1.125 0.625
1.375 0.875
1.125 0.75
1.25 0.875
1.25 0.625
1.375 0.75
0.5 1.375
0.5 1.125
0.375 1.125
0.375 1.25
0.5 0.625
0.5 0.875
0.375 0.875
0.375 0.75
0.625 0.625
0.875 0.625
0.625 0.875
0.75 0.875
0.875 0.75
0.75 0.625
0.625 0.75
1.375 1.5
1.125 1.5
1.125 1.625
1.25 1.625
1.375 1.375
0.625 1.5
0.875 1.5
0.875 1.625
0.75 1.625
0.625 1.375
1.375 1.125
1.125 1.375
1.125 1.25
1.25 1.125
1.375 1.25
1.25 1.375
0.625 1.125
0.875 1.375
0.75 1.125
0.875 1.25
0.625 1.25
0.75 1.375
4 61 145
1 58 136
13 141 154
17 205 81
16 183 76
8 67 116
10 99 188
2 62 122
9 69 102
6 68 94
7 66 112
17 199 189
5 63 126
12 132 162
11 109 173
6 94 64
2 122 59
7 112 89
15 168 163
14 167 70
4 145 88
14 182 155
15 198 71
16 204 174
21 77 139
18 74 142
44 140 143
57 204 206
53 182 185
27 83 110
35 98 101
22 78 128
29 85 100
28 84 104
26 82 118
56 198 202
23 79 133
42 131 134
37 108 111
34 95 161
40 123 153
38 113 196
50 167 171
50 168 170
45 146 180
53 183 187
56 199 200
57 205 209
45 149 146
43 144 137
47 156 155
25 90 65
20 91 60
39 120 117
54 190 189
40 130 123
36 107 103
34 106 95
38 121 113
54 192 188
41 135 127
49 164 163
51 175 174
24 160 80
19 152 75
33 195 73
49 166 162
30 93 86
32 179 72
47 181 154
31 92 87
51 197 173
43 149 139
44 144 142
19 156 143
20 90 206
30 91 185
37 120 110
33 190 101
41 130 128
35 107 100
36 106 104
39 121 118
48 192 202
42 135 133
24 164 134
32 175 111
48 160 161
46 152 153
55 195 196
46 166 171
31 93 170
52 179 180
52 181 187
25 92 200
55 197 209
21 147 61
18 138 58
44 157 141
57 207 205
53 184 183
27 119 67
35 191 99
22 125 62
29 105 69
28 97 68
26 114 66
56 203 199
23 129 63
42 165 132
37 176 109
34 96 94
40 124 122
38 115 112
50 172 168
50 169 167
45 148 145
53 186 182
56 201 198
57 208 204
1 136 77
2 140 74
2 59 140
16 76 204
14 70 182
4 108 83
7 89 98
5 126 78
7 98 85
9 102 84
8 116 82
15 159 198
6 131 79
6 64 131
4 88 108
10 158 95
12 150 123
11 193 113
14 151 167
15 71 168
13 177 146
16 178 183
17 81 199
17 194 205
43 137 149
44 141 144
19 75 156
20 60 90
30 86 91
37 109 120
33 73 190
41 127 130
35 99 107
36 103 106
39 117 121
48 158 192
42 132 135
24 80 164
32 72 175
48 159 160
46 151 152
55 194 195
46 150 166
31 87 93
52 178 179
52 177 181
25 65 92
55 193 197
45 147 149
43 138 144
47 157 156
25 207 90
20 184 91
39 119 120
54 191 190
40 125 130
36 105 107
34 97 106
38 114 121
54 203 192
41 129 135
49 165 164
51 176 175
24 96 160
19 124 152
33 115 195
49 172 166
30 169 93
32 148 179
47 186 181
31 201 92
51 208 197
45 145 147
43 136 138
47 154 157
25 81 207
20 76 184
39 116 119
54 188 191
40 122 125
36 102 105
34 94 97
38 112 114
54 189 203
41 126 129
49 162 165
51 173 176
24 64 96
19 59 124
33 89 115
49 163 172
30 70 169
32 88 148
47 155 186
31 71 201
51 174 208
43 139 136
44 142 140
19 143 59
20 206 76
30 185 70
37 110 108
33 101 89
41 128 126
35 100 98
36 104 102
39 118 116
48 202 159
42 133 131
24 134 64
32 111 88
48 161 158
46 153 150
55 196 193
46 171 151
31 170 71
52 180 177
52 187 178
25 200 81
55 209 194
13 146 137
13 137 141
14 155 75
3 65 60
3 60 86
11 117 109
17 189 73
12 123 127
10 103 99
10 95 103
11 113 117
10 188 158
12 127 132
15 163 80
16 174 72
15 80 159
14 75 151
17 73 194
12 162 150
3 86 87
16 72 178
13 154 177
3 87 65
11 173 193
21 139 147
18 142 138
44 143 157
57 206 207
53 185 184
27 110 119
35 101 191
22 128 125
29 100 105
28 104 97
26 118 114
56 202 203
23 133 129
42 134 165
37 111 176
34 161 96
40 153 124
38 196 115
50 171 172
50 170 169
45 180 148
53 187 186
56 200 201
57 209 208
147 145 61
138 136 58
157 154 141
207 81 205
184 76 183
119 116 67
191 188 99
125 122 62
105 102 69
97 94 68
114 112 66
203 189 199
129 126 63
165 162 132
176 173 109
96 64 94
124 59 122
115 89 112
172 163 168
169 70 167
148 88 145
186 155 182
201 71 198
208 174 204
136 139 77
140 142 74
59 143 140
76 206 204
70 185 182
108 110 83
89 101 98
126 128 78
98 100 85
102 104 84
116 118 82
159 202 198
131 133 79
64 134 131
88 111 108
158 161 95
150 153 123
193 196 113
151 171 167
71 170 168
177 180 146
178 187 183
81 200 199
194 209 205
137 146 149
141 137 144
75 155 156
60 65 90
86 60 91
109 117 120
73 189 190
127 123 130
99 103 107
103 95 106
117 113 121
158 188 192
132 127 135
80 163 164
72 174 175
159 80 160
151 75 152
194 73 195
150 162 166
87 86 93
178 72 179
177 154 181
65 87 92
193 173 197
147 139 149
138 142 144
157 143 156
207 206 90
184 185 91
119 110 120
191 101 190
125 128 130
105 100 107
97 104 106
114 118 121
203 202 192
129 133 135
165 134 164
176 111 175
96 161 160
124 153 152
115 196 195
172 171 166
169 170 93
148 180 179
186 187 181
201 200 92
208 209 197
64
1 58
2 59
3 60
4 61
2 62
5 63
6 64
3 65
7 66
8 67
6 68
9 69
14 70
15 71
16 72
17 73
18 74
19 75
20 76
21 77
22 78
23 79
24 80
25 81
26 82
27 83
28 84
29 85
30 86
31 87
32 88
33 89
58 18
59 19
60 20
61 21
62 22
63 23
64 24
65 25
66 26
67 27
68 28
69 29
70 30
71 31
72 32
73 33
74 2
75 14
76 16
77 1
78 5
79 6
80 15
81 17
82 8
83 4
84 9
85 7
86 3
87 3
88 4
89 7
1 0
1 2
1 3
1 0
2 0
2 0
2 4
3 4
3 0
3 0
4 0
4 0
1 2
2 4
1 3
3 4
1 0
1 2
1 3
1 0
2 0
2 0
2 4
3 4
3 0
3 0
4 0
4 0
1 2
2 4
1 3
3 4
1 0
1 2
1 3
1 0
2 0
2 0
2 4
3 4
3 0
3 0
4 0
4 0
1 2
2 4
1 3
3 4
1 0
1 2
1 3
1 0
2 0
2 0
2 4
3 4
3 0
3 0
4 0
4 0
1 2
2 4
1 3
3 4

View file

@ -0,0 +1,385 @@
384
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3
1
1
1
3
1
3
4
2
4
4
3
4
2
2
3
4
2
3
2
2
1
1
4
3

Binary file not shown.

View file

@ -0,0 +1,95 @@
13
2
16
3
0
0
1
0
1
1
0
1
0.5
0
1
0.5
0.5
1
0
0.5
0.4999999999999999
0.4999999999999999
0.3333333333333333
0.6666666666666666
0.6666666666666666
0.6666666666666666
0.6666666666666666
0.3333333333333333
0.3333333333333333
0.3333333333333333
8
1
13
5
2
12
6
3
11
7
4
10
1
5
13
10
8
13
2
6
12
3
7
11
4
8
10
12
9
13
10
9
11
7
10
11
11
9
12
6
11
12
9
10
13
5
12
13
8
1
5
5
2
2
6
6
3
3
7
7
4
4
8
8
1

View file

@ -0,0 +1,16 @@
#include "userset.h"
#include <cmath>
double FunctF(double const x , double const y)
{
// return std::sin(3.14159*1*x)*std::sin(3.14159*1*y);
// return 16.0*1024. ;
// return (double)1.0 ;
return x * x * std::sin(2.5 * 3.14159 * y);
}
double FunctU(const double /* x */, double const /* y */)
{
return 1.0 ;
}

View file

@ -0,0 +1,47 @@
#ifndef USERSET_FILE
#define USERSET_FILE
#include <cmath>
/**
* User function: f(@p x,@p y)
* @param[in] x x-coordinate of discretization point
* @param[in] y y-coordinate of discretization point
* @return value for right hand side f(@p x,@p y)
*/
double FunctF(double const x, double const y);
/**
* User function: u(@p x,@p y)
* @param[in] x x-coordinate of discretization point
* @param[in] y y-coordinate of discretization point
* @return value for solution vector u(@p x,@p y)
*/
double FunctU(double const x, double const y);
/**
* User function: f(@p x,@p y) = @f$ x^2 \sin(2.5\pi y)@f$.
* @param[in] x x-coordinate of discretization point
* @param[in] y y-coordinate of discretization point
* @return value f(@p x,@p y)
*/
inline
double fNice(double const x, double const y)
{
//return x * x * std::sin(2.5 * M_PI * y); // solution u
return std::sin(M_PI*2.5*y)*(M_PI*M_PI*2.5*2.5*x*x - 2); // -Laplacian(u)
}
/**
* User function: f(@p x,@p y) = 0$.
* @param[in] x x-coordinate of discretization point
* @param[in] y y-coordinate of discretization point
* @return value 0
*/
inline
double f_zero(double const x, double const y)
//double f_zero(double const /*x*/, double const /*y*/)
{
return 0.0 + 0.0*(x+y);
}
#endif

View file

@ -0,0 +1,122 @@
#include "vdop.h"
#include <cassert> // assert()
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;
void vddiv(vector<double> & x, vector<double> const& y,
vector<double> const& z)
{
assert( x.size()==y.size() && y.size()==z.size() );
size_t n = x.size();
#pragma omp parallel for
for (size_t k = 0; k < n; ++k)
{
x[k] = y[k] / z[k];
}
return;
}
//******************************************************************************
void vdaxpy(std::vector<double> & x, std::vector<double> const& y,
double alpha, std::vector<double> const& z )
{
assert( x.size()==y.size() && y.size()==z.size() );
size_t n = x.size();
#pragma omp parallel for
for (size_t k = 0; k < n; ++k)
{
x[k] = y[k] + alpha * z[k];
}
return;
}
//******************************************************************************
double dscapr(std::vector<double> const& x, std::vector<double> const& y)
{
assert( x.size()==y.size());
size_t n = x.size();
double s = 0.0;
#pragma omp parallel for reduction(+:s)
for (size_t k = 0; k < n; ++k)
{
s += x[k] * y[k];
}
return s;
}
//******************************************************************************
bool CompareVectors(std::vector<double> const& x, int const n, double const y[], double const eps)
{
bool bn = (static_cast<int>(x.size())==n);
if (!bn)
{
cout << "######### Error: " << "number of elements" << endl;
}
//bool bv = equal(x.cbegin(),x.cend(),y);
bool bv = equal(x.cbegin(),x.cend(),y,
[eps](double a, double b) -> bool
{ return std::abs(a-b)<eps*(1.0+0.5*(std::abs(a)+ std::abs(b))); }
);
if (!bv)
{
assert(static_cast<int>(x.size())==n);
cout << "######### Error: " << "values" << endl;
}
return bn && bv;
}
//******************************************************************************
double par_scalar(vector<double> const &x, vector<double> const &y, MPI_Comm const& icomm)
{
const double s = dscapr(x,y);
double sg;
MPI_Allreduce(&s,&sg,1,MPI_DOUBLE,MPI_SUM,icomm);
return(sg);
}
//******************************************************************************
void ExchangeAll(vector<double> const &xin, vector<double> &yout, MPI_Comm const &icomm)
{
int myrank, numprocs,ierr(-1);
MPI_Comm_rank(icomm, &myrank); // my MPI-rank
MPI_Comm_size(icomm, &numprocs);
int const N=xin.size();
int const sendcount = N/numprocs; // equal sized junks
assert(sendcount*numprocs==N); // really all junk sized?
assert(xin.size()==yout.size());
auto sendbuf = xin.data();
auto recvbuf = yout.data();
ierr = MPI_Alltoall(sendbuf, sendcount, MPI_DOUBLE,
recvbuf, sendcount, MPI_DOUBLE, icomm);
assert(0==ierr);
return;
}
//******************************************************************************
void ExchangeAllInPlace(vector<double> &xin, MPI_Comm const &icomm)
{
int myrank, numprocs,ierr(-1);
MPI_Comm_rank(icomm, &myrank); // my MPI-rank
MPI_Comm_size(icomm, &numprocs);
int const N=xin.size();
int const sendcount = N/numprocs; // equal sized junks
assert(sendcount*numprocs==N); // really all junk sized?
auto sendbuf = xin.data();
ierr = MPI_Alltoall(MPI_IN_PLACE, sendcount, MPI_DOUBLE,
sendbuf, sendcount, MPI_DOUBLE, icomm);
assert(0==ierr);
return;
}

View file

@ -0,0 +1,167 @@
#ifndef VDOP_FILE
#define VDOP_FILE
#include <iostream>
#include <mpi.h> // MPI
#include <string>
#include <vector>
/** @brief Element-wise vector divison x_k = y_k/z_k.
*
* @param[out] x target vector
* @param[in] y source vector
* @param[in] z source vector
*
*/
void vddiv(std::vector<double> &x, std::vector<double> const &y,
std::vector<double> const &z);
/** @brief Element-wise daxpy operation x(k) = y(k) + alpha*z(k).
*
* @param[out] x target vector
* @param[in] y source vector
* @param[in] alpha scalar
* @param[in] z source vector
*
*/
void vdaxpy(std::vector<double> &x, std::vector<double> const &y,
double alpha, std::vector<double> const &z );
/** @brief Calculates the Euclidean inner product of two vectors.
*
* @param[in] x vector
* @param[in] y vector
* @return Euclidean inner product @f$\langle x,y \rangle@f$
*
*/
double dscapr(std::vector<double> const &x, std::vector<double> const &y);
inline
double L2_scapr(std::vector<double> const &x, std::vector<double> const &y)
{
return dscapr(x, y) / x.size();
}
/** Parallel inner product
@param[in] x vector
@param[in] y vector
@param[in] icomm MPI communicator
@return resulting Euclidian inner product <x,y>
*/
double par_scalar(std::vector<double> const &x, std::vector<double> const &y,
MPI_Comm const& icomm=MPI_COMM_WORLD);
/* ReadId : Input and broadcast of an integer */
inline
int ReadIn(std::string const &ss = std::string(), MPI_Comm const &icomm = MPI_COMM_WORLD)
{
MPI_Barrier(icomm);
int myrank; /* my rank number */
MPI_Comm_rank(icomm, &myrank);
int id;
if (myrank == 0) {
std::cout << "\n\n " << ss << " : Which process do you want to debug ? \n";
std::cin >> id;
}
MPI_Bcast(&id, 1, MPI_INT, 0, icomm);
return id;
}
/**
* Print entries of a vector to standard output.
*
* @param[in] v vector values
* @param[in] ss string containing the vector name
* @param[in] icomm communicator group for MPI
*
*/
template <class T>
void DebugVector(std::vector<T> const &v, std::string const &ss = std::string(), MPI_Comm const &icomm = MPI_COMM_WORLD)
{
MPI_Barrier(icomm);
std::cout.flush();
int numprocs; /* # processes */
MPI_Comm_size(icomm, &numprocs);
int myrank; /* my rank number */
MPI_Comm_rank(icomm, &myrank);
int readid = ReadIn(ss); /* Read readid */
while ( (0 <= readid) && (readid < numprocs) ) {
if (myrank == readid) {
std::cout << "\n\n process " << readid;
std::cout << "\n .... " << ss << " (nnode = " << v.size() << ")\n";
for (size_t j = 0; j < v.size(); ++j) {
std::cout.setf(std::ios::right, std::ios::adjustfield);
std::cout << "[" << j << "] "<< v[j] << " ";
}
std::cout << std::endl;
std::cout.flush();
}
readid = ReadIn(ss, icomm); /* Read readid */
}
MPI_Barrier(icomm);
return;
}
/** @brief Compares an STL vector with POD vector.
*
* The accuracy criteria @f$ |x_k-y_k| < \varepsilon \left({1+0.5(|x_k|+|y_k|)}\right) @f$
* follows the book by
* <a href="https://www.springer.com/la/book/9783319446592">Stoyan/Baran</a>, p.8.
*
* @param[in] x STL vector
* @param[in] n length of POD vector
* @param[in] y POD vector
* @param[in] eps relative accuracy criteria (default := 0.0).
* @return true iff pairwise vector elements are relatively close to each other.
*
*/
bool CompareVectors(std::vector<double> const &x, int n, double const y[], double const eps = 0.0);
/** Output operator for vector
* @param[in,out] s output stream, e.g. @p cout
* @param[in] v vector
*
* @return output stream
*/
template <class T>
std::ostream& operator<<(std::ostream &s, std::vector<T> const &v)
{
for (auto vp: v)
{
s << vp << " ";
}
return s;
}
/** Exchanges equal size partions of vector @p xin with all MPI processes.
* The received data are return in vector @p yout .
*
* @param[in] xin input vector
* @param[out] yout output vector
* @param[in] icomm MPI communicator
*
*/
void ExchangeAll(std::vector<double> const &xin, std::vector<double> &yout, MPI_Comm const &icomm = MPI_COMM_WORLD);
/** Exchanges equal size partions of vector @p xin with all MPI processes.
* The received data are return in vector @p xin .
*
* @param[in,out] xin input/output vector
* @param[in] icomm MPI communicator
*
*/
void ExchangeAllInPlace(std::vector<double> &xin, MPI_Comm const &icomm = MPI_COMM_WORLD);
#endif

View file

@ -0,0 +1,52 @@
%% Visualize results
%
% flatpak run org.octave.Octave <filename>
% or
% octave --no-window-system --no-gui -qf <filename>
%
% or
%
% matlab -nosplash -nodesktop -r 'try visualize_par_results(4); catch; end; quit'
%
function visualize_par_results(nprocs)
%%
if nargin<1
nprocs = 4;
end
fprintf('# procs = %d\n',nprocs)
pre = 'uv_';
post = '.txt';
xc = []; nnodes = [];
ia = []; nelems = [];
v = [];
node_offset = 0;
elem_offset = 0;
for rank=0:nprocs-1
fname = [pre,num2str(rank,'%2u'),post];
[lxc,lia,lv] = ascii_read_meshvector(fname);
% whos lxc lia lv
nnodes = [nnodes size(lxc,1)];
nelems = [nelems size(lia,1)];
%[xc,ia,v]
xc = [xc; lxc];
v = [v ; lv ];
ia = [ia; lia+node_offset];
% node_offset
% lia = lia + node_offset
% ia = [ia; lia];
% index offsets for next subdomain
node_offset = node_offset + nnodes(end);
elem_offset = elem_offset + nelems(end);
end
% fname = 'uv.txt';
% [xc,ia,v] = ascii_read_meshvector(fname);
h = trisurf(ia, xc(:,1), xc(:,2), v);
xlabel('x'),ylabel('y'),zlabel('z')
shading interp
waitfor(h) % wait for closing the figure

View file

@ -0,0 +1,20 @@
%% Visualize results
%
% flatpak run org.octave.Octave <filename>
% or
% octave --no-window-system --no-gui -qf <filename>
%
% or
% matlab -nosplash < <filename>
clear all
clc
%%
fname = 'uv.txt';
[xc,ia,v] = ascii_read_meshvector(fname);
h = trisurf(ia, xc(:,1), xc(:,2), v);
waitfor(h) % wait for closing the figure