Over-wrote common/ with version from ScaLBL (AA model refactor)
This commit is contained in:
parent
282c296935
commit
db8a1bfba9
@ -4,7 +4,7 @@
|
||||
#include "common/Communication.h"
|
||||
#include "common/MPI_Helpers.h"
|
||||
#include "common/Utilities.h"
|
||||
#include "ProfilerApp.h"
|
||||
//#include "ProfilerApp.h"
|
||||
|
||||
|
||||
/********************************************************
|
||||
@ -104,7 +104,7 @@ fillHalo<TYPE>::~fillHalo( )
|
||||
template<class TYPE>
|
||||
void fillHalo<TYPE>::fill( Array<TYPE>& data )
|
||||
{
|
||||
PROFILE_START("fillHalo::fill",1);
|
||||
//PROFILE_START("fillHalo::fill",1);
|
||||
int depth2 = data.size(3);
|
||||
ASSERT((int)data.size(0)==nx+2*ngx);
|
||||
ASSERT((int)data.size(1)==ny+2*ngy);
|
||||
@ -156,7 +156,7 @@ void fillHalo<TYPE>::fill( Array<TYPE>& data )
|
||||
}
|
||||
}
|
||||
}
|
||||
PROFILE_STOP("fillHalo::fill",1);
|
||||
//PROFILE_STOP("fillHalo::fill",1);
|
||||
}
|
||||
template<class TYPE>
|
||||
void fillHalo<TYPE>::pack( const Array<TYPE>& data, int i0, int j0, int k0, TYPE *buffer )
|
||||
@ -207,7 +207,7 @@ template<class TYPE>
|
||||
template<class TYPE1, class TYPE2>
|
||||
void fillHalo<TYPE>::copy( const Array<TYPE1>& src, Array<TYPE2>& dst )
|
||||
{
|
||||
PROFILE_START("fillHalo::copy",1);
|
||||
//PROFILE_START("fillHalo::copy",1);
|
||||
ASSERT( (int)src.size(0)==nx || (int)src.size(0)==nx+2*ngx );
|
||||
ASSERT( (int)dst.size(0)==nx || (int)dst.size(0)==nx+2*ngx );
|
||||
bool src_halo = (int)src.size(0)==nx+2*ngx;
|
||||
@ -254,7 +254,7 @@ void fillHalo<TYPE>::copy( const Array<TYPE1>& src, Array<TYPE2>& dst )
|
||||
}
|
||||
fill(dst);
|
||||
}
|
||||
PROFILE_STOP("fillHalo::copy",1);
|
||||
//PROFILE_STOP("fillHalo::copy",1);
|
||||
}
|
||||
|
||||
|
||||
|
1814
common/Domain.cpp
1814
common/Domain.cpp
File diff suppressed because it is too large
Load Diff
187
common/Domain.h
187
common/Domain.h
@ -24,6 +24,7 @@ void read_domain( int rank, int nprocs, MPI_Comm comm,
|
||||
int& nprocx, int& nprocy, int& nprocz, int& nx, int& ny, int& nz,
|
||||
int& nspheres, double& Lx, double& Ly, double& Lz );
|
||||
|
||||
|
||||
//! Class to hold domain info
|
||||
struct Domain{
|
||||
// Default constructor
|
||||
@ -89,16 +90,15 @@ struct Domain{
|
||||
|
||||
// Solid indicator function
|
||||
char *id;
|
||||
// Blob information
|
||||
IntArray BlobLabel;
|
||||
IntArray BlobGraph;
|
||||
|
||||
void InitializeRanks();
|
||||
void CommInit(MPI_Comm comm);
|
||||
void CommunicateMeshHalo(DoubleArray &Mesh);
|
||||
void BlobComm(MPI_Comm comm);
|
||||
void AssignComponentLabels(double *phase);
|
||||
|
||||
void AssignBlobConnections();
|
||||
void TestCommInit(MPI_Comm comm);
|
||||
|
||||
//void MemoryOptimizedLayout(IntArray &Map, int *neighborList, int Np);
|
||||
|
||||
private:
|
||||
|
||||
@ -109,28 +109,6 @@ private:
|
||||
int k2 = (k+nprocz)%nprocz;
|
||||
return i2 + j2*nprocx + k2*nprocx*nprocy;
|
||||
}
|
||||
inline void PackBlobData(int *list, int count, int *sendbuf, int *data){
|
||||
// Fill in the phase ID values from neighboring processors
|
||||
// This packs up the values that need to be sent from one processor to another
|
||||
int idx,n;
|
||||
for (idx=0; idx<count; idx++){
|
||||
n = list[idx];
|
||||
sendbuf[idx] = data[n];
|
||||
}
|
||||
}
|
||||
inline void UnpackBlobData(int *list, int count, int *recvbuf, int *data){
|
||||
// Fill in the phase ID values from neighboring processors
|
||||
// This unpacks the values once they have been recieved from neighbors
|
||||
int idx,n;
|
||||
for (idx=0; idx<count; idx++){
|
||||
n = list[idx];
|
||||
data[n] = recvbuf[idx];
|
||||
}
|
||||
}
|
||||
|
||||
int VoxelConnection(int n);
|
||||
|
||||
void getBlobConnections(int * List, int count, int neighbor, int direction);
|
||||
};
|
||||
|
||||
|
||||
@ -596,161 +574,6 @@ inline void ReadBinaryFile(char *FILENAME, double *Data, int N)
|
||||
File.close();
|
||||
|
||||
}
|
||||
inline double minmod(double &a, double &b){
|
||||
|
||||
double value;
|
||||
|
||||
value = a;
|
||||
if ( a*b < 0.0) value=0.0;
|
||||
else if (fabs(a) > fabs(b)) value = b;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline double Eikonal(DoubleArray &Distance, char *ID, Domain &Dm, int timesteps){
|
||||
|
||||
/*
|
||||
* This routine converts the data in the Distance array to a signed distance
|
||||
* by solving the equation df/dt = sign(1-|grad f|), where Distance provides
|
||||
* the values of f on the mesh associated with domain Dm
|
||||
* It has been tested with segmented data initialized to values [-1,1]
|
||||
* and will converge toward the signed distance to the surface bounding the associated phases
|
||||
*
|
||||
* Reference:
|
||||
* Min C (2010) On reinitializing level set functions, Journal of Computational Physics 229
|
||||
*/
|
||||
|
||||
int i,j,k;
|
||||
double dt=0.1;
|
||||
double Dx,Dy,Dz;
|
||||
double Dxp,Dxm,Dyp,Dym,Dzp,Dzm;
|
||||
double Dxxp,Dxxm,Dyyp,Dyym,Dzzp,Dzzm;
|
||||
double sign,norm;
|
||||
double LocalVar,GlobalVar,LocalMax,GlobalMax;
|
||||
|
||||
int xdim,ydim,zdim;
|
||||
xdim=Dm.Nx-2;
|
||||
ydim=Dm.Ny-2;
|
||||
zdim=Dm.Nz-2;
|
||||
fillHalo<double> fillData(Dm.Comm, Dm.rank_info,xdim,ydim,zdim,1,1,1,0,1);
|
||||
|
||||
// Arrays to store the second derivatives
|
||||
DoubleArray Dxx(Dm.Nx,Dm.Ny,Dm.Nz);
|
||||
DoubleArray Dyy(Dm.Nx,Dm.Ny,Dm.Nz);
|
||||
DoubleArray Dzz(Dm.Nx,Dm.Ny,Dm.Nz);
|
||||
|
||||
int count = 0;
|
||||
while (count < timesteps){
|
||||
|
||||
// Communicate the halo of values
|
||||
fillData.fill(Distance);
|
||||
|
||||
// Compute second order derivatives
|
||||
for (k=1;k<Dm.Nz-1;k++){
|
||||
for (j=1;j<Dm.Ny-1;j++){
|
||||
for (i=1;i<Dm.Nx-1;i++){
|
||||
Dxx(i,j,k) = Distance(i+1,j,k) + Distance(i-1,j,k) - 2*Distance(i,j,k);
|
||||
Dyy(i,j,k) = Distance(i,j+1,k) + Distance(i,j-1,k) - 2*Distance(i,j,k);
|
||||
Dzz(i,j,k) = Distance(i,j,k+1) + Distance(i,j,k-1) - 2*Distance(i,j,k);
|
||||
}
|
||||
}
|
||||
}
|
||||
fillData.fill(Dxx);
|
||||
fillData.fill(Dyy);
|
||||
fillData.fill(Dzz);
|
||||
|
||||
LocalMax=LocalVar=0.0;
|
||||
// Execute the next timestep
|
||||
for (k=1;k<Dm.Nz-1;k++){
|
||||
for (j=1;j<Dm.Ny-1;j++){
|
||||
for (i=1;i<Dm.Nx-1;i++){
|
||||
|
||||
int n = k*Dm.Nx*Dm.Ny + j*Dm.Nx + i;
|
||||
|
||||
sign = 1;
|
||||
if (ID[n] == 0) sign = -1;
|
||||
|
||||
// local second derivative terms
|
||||
Dxxp = minmod(Dxx(i,j,k),Dxx(i+1,j,k));
|
||||
Dyyp = minmod(Dyy(i,j,k),Dyy(i,j+1,k));
|
||||
Dzzp = minmod(Dzz(i,j,k),Dzz(i,j,k+1));
|
||||
Dxxm = minmod(Dxx(i,j,k),Dxx(i-1,j,k));
|
||||
Dyym = minmod(Dyy(i,j,k),Dyy(i,j-1,k));
|
||||
Dzzm = minmod(Dzz(i,j,k),Dzz(i,j,k-1));
|
||||
|
||||
/* //............Compute upwind derivatives ...................
|
||||
Dxp = Distance(i+1,j,k) - Distance(i,j,k) + 0.5*Dxxp;
|
||||
Dyp = Distance(i,j+1,k) - Distance(i,j,k) + 0.5*Dyyp;
|
||||
Dzp = Distance(i,j,k+1) - Distance(i,j,k) + 0.5*Dzzp;
|
||||
|
||||
|
||||
Dxm = Distance(i,j,k) - Distance(i-1,j,k) + 0.5*Dxxm;
|
||||
Dym = Distance(i,j,k) - Distance(i,j-1,k) + 0.5*Dyym;
|
||||
Dzm = Distance(i,j,k) - Distance(i,j,k-1) + 0.5*Dzzm;
|
||||
*/
|
||||
Dxp = Distance(i+1,j,k)- Distance(i,j,k) - 0.5*Dxxp;
|
||||
Dyp = Distance(i,j+1,k)- Distance(i,j,k) - 0.5*Dyyp;
|
||||
Dzp = Distance(i,j,k+1)- Distance(i,j,k) - 0.5*Dzzp;
|
||||
|
||||
Dxm = Distance(i,j,k) - Distance(i-1,j,k) + 0.5*Dxxm;
|
||||
Dym = Distance(i,j,k) - Distance(i,j-1,k) + 0.5*Dyym;
|
||||
Dzm = Distance(i,j,k) - Distance(i,j,k-1) + 0.5*Dzzm;
|
||||
|
||||
// Compute upwind derivatives for Godunov Hamiltonian
|
||||
if (sign < 0.0){
|
||||
if (Dxp + Dxm > 0.f) Dx = Dxp*Dxp;
|
||||
else Dx = Dxm*Dxm;
|
||||
|
||||
if (Dyp + Dym > 0.f) Dy = Dyp*Dyp;
|
||||
else Dy = Dym*Dym;
|
||||
|
||||
if (Dzp + Dzm > 0.f) Dz = Dzp*Dzp;
|
||||
else Dz = Dzm*Dzm;
|
||||
}
|
||||
else{
|
||||
|
||||
if (Dxp + Dxm < 0.f) Dx = Dxp*Dxp;
|
||||
else Dx = Dxm*Dxm;
|
||||
|
||||
if (Dyp + Dym < 0.f) Dy = Dyp*Dyp;
|
||||
else Dy = Dym*Dym;
|
||||
|
||||
if (Dzp + Dzm < 0.f) Dz = Dzp*Dzp;
|
||||
else Dz = Dzm*Dzm;
|
||||
}
|
||||
|
||||
//Dx = max(Dxp*Dxp,Dxm*Dxm);
|
||||
//Dy = max(Dyp*Dyp,Dym*Dym);
|
||||
//Dz = max(Dzp*Dzp,Dzm*Dzm);
|
||||
|
||||
norm=sqrt(Dx + Dy + Dz);
|
||||
if (norm > 1.0) norm=1.0;
|
||||
|
||||
Distance(i,j,k) += dt*sign*(1.0 - norm);
|
||||
LocalVar += dt*sign*(1.0 - norm);
|
||||
|
||||
if (fabs(dt*sign*(1.0 - norm)) > LocalMax)
|
||||
LocalMax = fabs(dt*sign*(1.0 - norm));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MPI_Allreduce(&LocalVar,&GlobalVar,1,MPI_DOUBLE,MPI_SUM,Dm.Comm);
|
||||
MPI_Allreduce(&LocalMax,&GlobalMax,1,MPI_DOUBLE,MPI_MAX,Dm.Comm);
|
||||
GlobalVar /= (Dm.Nx-2)*(Dm.Ny-2)*(Dm.Nz-2)*Dm.nprocx*Dm.nprocy*Dm.nprocz;
|
||||
count++;
|
||||
|
||||
|
||||
if (count%50 == 0 && Dm.rank==0 )
|
||||
printf("Time=%i, Max variation=%f, Global variation=%f \n",count,GlobalMax,GlobalVar);
|
||||
|
||||
if (fabs(GlobalMax) < 1e-5){
|
||||
if (Dm.rank==0) printf("Exiting with max tolerance of 1e-5 \n");
|
||||
count=timesteps;
|
||||
}
|
||||
}
|
||||
return GlobalVar;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -27,7 +27,7 @@ void pack( const std::vector<TYPE>& rhs, char *buffer )
|
||||
size_t size = rhs.size();
|
||||
memcpy(buffer,&size,sizeof(size_t));
|
||||
size_t pos = sizeof(size_t);
|
||||
for (size_t i=0; i<rhs.size(); i++) {
|
||||
for (int i=0; i<rhs.size(); i++) {
|
||||
pack(rhs[i],&buffer[pos]);
|
||||
pos += packsize(rhs[i]);
|
||||
}
|
||||
@ -40,7 +40,7 @@ void unpack( std::vector<TYPE>& data, const char *buffer )
|
||||
data.clear();
|
||||
data.resize(size);
|
||||
size_t pos = sizeof(size_t);
|
||||
for (size_t i=0; i<data.size(); i++) {
|
||||
for (int i=0; i<data.size(); i++) {
|
||||
unpack(data[i],&buffer[pos]);
|
||||
pos += packsize(data[i]);
|
||||
}
|
||||
|
3425
common/ScaLBL.h
3425
common/ScaLBL.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,31 +1,12 @@
|
||||
#ifndef included_AtomicStackTrace
|
||||
#define included_AtomicStackTrace
|
||||
#ifndef included_StackTrace
|
||||
#define included_StackTrace
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
|
||||
// Check for and include MPI
|
||||
// clang-format off
|
||||
#if defined(USE_MPI) || defined(USE_EXT_MPI)
|
||||
#include "mpi.h"
|
||||
#elif defined(__has_include)
|
||||
#if __has_include("mpi.h")
|
||||
#include "mpi.h"
|
||||
#else
|
||||
typedef int MPI_Comm;
|
||||
#endif
|
||||
#else
|
||||
typedef int MPI_Comm;
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
|
||||
namespace StackTrace {
|
||||
|
||||
@ -38,179 +19,29 @@ struct stack_info {
|
||||
std::string filename;
|
||||
int line;
|
||||
//! Default constructor
|
||||
stack_info() : address( nullptr ), address2( nullptr ), line( 0 ) {}
|
||||
//! Operator==
|
||||
bool operator==( const stack_info& rhs ) const;
|
||||
//! Operator!=
|
||||
bool operator!=( const stack_info& rhs ) const;
|
||||
stack_info(): address(NULL), address2(NULL), line(0) {}
|
||||
//! Print the stack info
|
||||
std::string print() const;
|
||||
//! Compute the number of bytes needed to store the object
|
||||
size_t size() const;
|
||||
//! Pack the data to a byte array, returning a pointer to the end of the data
|
||||
char* pack( char* ptr ) const;
|
||||
//! Unpack the data from a byte array, returning a pointer to the end of the data
|
||||
const char* unpack( const char* ptr );
|
||||
//! Pack a vector of data to a memory block
|
||||
static std::vector<char> packArray( const std::vector<stack_info>& data );
|
||||
//! Unpack a vector of data from a memory block
|
||||
static std::vector<stack_info> unpackArray( const char* data );
|
||||
};
|
||||
|
||||
|
||||
struct multi_stack_info {
|
||||
int N;
|
||||
stack_info stack;
|
||||
std::vector<multi_stack_info> children;
|
||||
//! Default constructor
|
||||
multi_stack_info() : N( 0 ) {}
|
||||
//! Add the given stack to the multistack
|
||||
void add( size_t N, const stack_info *stack );
|
||||
//! Print the stack info
|
||||
std::vector<std::string> print( const std::string& prefix=std::string() ) const;
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Get the current call stack
|
||||
* @details This function returns the current call stack for the current thread
|
||||
* @return Returns vector containing the stack
|
||||
*/
|
||||
//! Function to return the current call stack
|
||||
std::vector<stack_info> getCallStack();
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Get the current call stack for a thread
|
||||
* @details This function returns the current call stack for the given thread
|
||||
* @param[in] id The thread id of the stack we want to return
|
||||
* @return Returns vector containing the stack
|
||||
*/
|
||||
std::vector<stack_info> getCallStack( std::thread::native_handle_type id );
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Get the current call stack for all threads
|
||||
* @details This function returns the current call stack for all threads
|
||||
* in the current process.
|
||||
* Note: This functionality may not be availible on all platforms
|
||||
* @return Returns vector containing the stack
|
||||
*/
|
||||
multi_stack_info getAllCallStacks( );
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Get the current call stack for all threads/processes
|
||||
* @details This function returns the current call stack for all threads
|
||||
* for all processes in the current process. This function requires
|
||||
* the user to call globalCallStackInitialize() before calling this
|
||||
* routine, and globalCallStackFinalize() before exiting.
|
||||
* Note: This functionality may not be availible on all platforms
|
||||
* @return Returns vector containing the stack
|
||||
*/
|
||||
multi_stack_info getGlobalCallStacks( );
|
||||
|
||||
|
||||
//! Function to return the current call stack for the current thread
|
||||
std::vector<void *> backtrace();
|
||||
|
||||
//! Function to return the current call stack for the given thread
|
||||
std::vector<void *> backtrace( std::thread::native_handle_type id );
|
||||
|
||||
//! Function to return the current call stack for all threads
|
||||
std::vector<std::vector<void *>> backtraceAll();
|
||||
|
||||
|
||||
//! Function to return the stack info for a given address
|
||||
stack_info getStackInfo( void *address );
|
||||
|
||||
|
||||
//! Function to return the stack info for a given address
|
||||
std::vector<stack_info> getStackInfo( const std::vector<void *> &address );
|
||||
|
||||
|
||||
//! Function to return the signal name
|
||||
std::string signalName( int signal );
|
||||
stack_info getStackInfo( void* address );
|
||||
|
||||
|
||||
/*!
|
||||
* Return the symbols from the current executable (not availible for all platforms)
|
||||
* @return Returns 0 if sucessful
|
||||
*/
|
||||
int getSymbols(
|
||||
std::vector<void *> &address, std::vector<char> &type, std::vector<std::string> &obj );
|
||||
|
||||
|
||||
/*!
|
||||
* Return the name of the executable
|
||||
* @return Returns the name of the executable (usually the full path)
|
||||
*/
|
||||
std::string getExecutable();
|
||||
|
||||
|
||||
/*!
|
||||
* Return the search path for the symbols
|
||||
* @return Returns the search path for the symbols
|
||||
*/
|
||||
std::string getSymPaths();
|
||||
|
||||
|
||||
//!< Terminate type
|
||||
enum class terminateType { signal, exception };
|
||||
|
||||
/*!
|
||||
* Set the error handlers
|
||||
* @param[in] Function to terminate the program: abort(msg,type)
|
||||
*/
|
||||
void setErrorHandlers( std::function<void( std::string, terminateType )> abort );
|
||||
|
||||
|
||||
/*!
|
||||
* Set the given signals to the handler
|
||||
* @param[in] Function to terminate the program: abort(msg,type)
|
||||
*/
|
||||
void setSignals( const std::vector<int>& signals, void (*handler) (int) );
|
||||
|
||||
|
||||
//! Clear a signal set by setSignals
|
||||
void clearSignal( int signal );
|
||||
|
||||
|
||||
//! Clear all signals set by setSignals
|
||||
void clearSignals( );
|
||||
|
||||
|
||||
//! Return a list of all signals that can be caught
|
||||
std::vector<int> allSignalsToCatch( );
|
||||
|
||||
//! Return a default list of signals to catch
|
||||
std::vector<int> defaultSignalsToCatch( );
|
||||
|
||||
|
||||
//! Get a list of the active threads
|
||||
std::set<std::thread::native_handle_type> activeThreads( );
|
||||
|
||||
//! Get a handle to this thread
|
||||
std::thread::native_handle_type thisThread( );
|
||||
|
||||
|
||||
//! Initialize globalCallStack functionallity
|
||||
void globalCallStackInitialize( MPI_Comm comm );
|
||||
|
||||
//! Clean up globalCallStack functionallity
|
||||
void globalCallStackFinalize( );
|
||||
|
||||
|
||||
/*!
|
||||
* @brief Call system command
|
||||
* @details This function calls a system command, waits for the program
|
||||
* to execute, captures and returns the output and exit code.
|
||||
* @param[in] cmd Command to execute
|
||||
* @param[out] exit_code Exit code returned from child process
|
||||
* @return Returns string containing the output
|
||||
*/
|
||||
std::string exec( const std::string& cmd, int& exit_code );
|
||||
int getSymbols( std::vector<void*>& address, std::vector<char>& type, std::vector<std::string>& obj );
|
||||
|
||||
|
||||
} // namespace StackTrace
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -236,9 +236,9 @@ size_t Utilities::getMemoryUsage()
|
||||
size_t N_bytes = 0;
|
||||
#if defined(USE_LINUX)
|
||||
struct mallinfo meminfo = mallinfo();
|
||||
size_t size_hblkhd = static_cast<unsigned int>( meminfo.hblkhd );
|
||||
size_t size_uordblks = static_cast<unsigned int>( meminfo.uordblks );
|
||||
N_bytes = size_hblkhd + size_uordblks;
|
||||
size_t size_hblkhd = static_cast<size_t>( meminfo.hblkhd );
|
||||
size_t size_uordblks = static_cast<size_t>( meminfo.uordblks );
|
||||
N_bytes = static_cast<size_t>( size_hblkhd + size_uordblks );
|
||||
#elif defined(USE_MAC)
|
||||
struct task_basic_info t_info;
|
||||
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
|
||||
@ -347,11 +347,3 @@ std::vector<int> Utilities::factor(size_t number)
|
||||
std::sort( factors.begin(), factors.end() );
|
||||
return factors;
|
||||
}
|
||||
|
||||
|
||||
// Dummy function to prevent compiler from optimizing away variable
|
||||
void Utilities::nullUse( void* data )
|
||||
{
|
||||
NULL_USE(data);
|
||||
}
|
||||
|
||||
|
@ -61,9 +61,6 @@ namespace Utilities
|
||||
//! Factor a number into it's prime factors
|
||||
std::vector<int> factor(size_t number);
|
||||
|
||||
//! Print AMP Banner
|
||||
void nullUse( void* );
|
||||
|
||||
} // namespace Utilities
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user