Commit caacc205 authored by Jan Eichstaedt's avatar Jan Eichstaedt
Browse files

Merge branch 'feature/jan_masterupdates' of gitlab.nektar.info:meshing/nektar...

Merge branch 'feature/jan_masterupdates' of gitlab.nektar.info:meshing/nektar into feature/jan_masterupdates
some previous merge
parents e5fa870e 4288270e
......@@ -34,7 +34,11 @@ v4.4.0
- Enabled MUMPS support in PETSc if a Fortran compiler was found and added 3D
support to the Helmholtz smoother used e.g. in FieldConverts C0Projection
module (!714)
- Fix bug in `Vmath::FillWhiteNoise` which caused `ForcingNoise` to have
a repeated pattern (!718)
- Fix bug in the calculation of the RHS magnitude in CG solver (!721)
- Fix bug in CMake Homebrew and MacPorts detection for OS X (!729)
- Fix bug in FieldUtils when using half mode expansions (!734)
**ADRSolver:**
- Add a projection equation system for C^0 projections (!675)
......@@ -84,7 +88,7 @@ v4.4.0
- 2D to 3D mesh extrusion module (!715)
- Add new two-dimensional mesher from NACA code or step file (!720)
- Fix inverted boundary layer in 2D (!736)
- More sensible element sizing with boudnary layers in 2D (!736)
- More sensible element sizing with boundary layers in 2D (!736)
- Change variable names in mcf file to make more sense (!736)
- Fix issues in varopti module so that in can be compiled without meshgen on
(!736)
......
......@@ -190,7 +190,10 @@ state of the solution fields at at given timestep. This can subsequently be used
for restarting the simulation or examining time-dependent behaviour. This
produces a sequence of files, by default named \inltt{session\_*.chk}, where
\inltt{*} is replaced by a counter. The initial condition is written to
\inltt{session\_0.chk}.
\inltt{session\_0.chk}. Existing files are not overwritten, but renamed to e.g.
\inltt{session\_0.bak0.chk}. In case this file already exists, too, the \inltt{chk}-file
is renamed to \inltt{session\_0.bak*.chk} and so on.
\begin{notebox}
This functionality is equivalent to setting the \inltt{IO\_CheckSteps}
......
......@@ -281,7 +281,9 @@ struct Field
// Define Homogeneous expansion
int nplanes;
NekDouble lz;
LibUtilities::BasisType btype;
LibUtilities::BasisType btype;
LibUtilities::PointsType ptype =
LibUtilities::eFourierEvenlySpaced;
if (fldfilegiven)
{
......@@ -299,6 +301,11 @@ struct Field
nplanes = 4;
}
}
else if (btype == LibUtilities::eFourierHalfModeRe &&
nplanes == 1)
{
ptype = LibUtilities::ePolyEvenlySpaced;
}
}
else
{
......@@ -310,7 +317,7 @@ struct Field
// Choose points to be at evenly spaced points at
// nplanes points
const LibUtilities::PointsKey Pkey(
nplanes, LibUtilities::eFourierEvenlySpaced);
nplanes, ptype);
const LibUtilities::BasisKey Bkey(btype, nplanes, Pkey);
......
......@@ -100,19 +100,19 @@ class ThreadJob
{
public:
/// Base constructor
LIB_UTILITIES_EXPORT ThreadJob();
LIB_UTILITIES_EXPORT ThreadJob();
/// Base destructor.
LIB_UTILITIES_EXPORT virtual ~ThreadJob();
LIB_UTILITIES_EXPORT virtual ~ThreadJob();
/**
* This method will be called when the task is loaded
* onto a worker thread and is ready to run. When Run
* has finished this instance will be destructed.
*/
LIB_UTILITIES_EXPORT virtual void Run() = 0;
LIB_UTILITIES_EXPORT virtual void Run() = 0;
/// Set number of worker threads.
LIB_UTILITIES_EXPORT void SetWorkerNum(unsigned int num);
LIB_UTILITIES_EXPORT void SetWorkerNum(unsigned int num);
protected:
/**
......@@ -168,7 +168,7 @@ class ThreadManager : public boost::enable_shared_from_this<ThreadManager>
{
public:
/// Destructor.
LIB_UTILITIES_EXPORT virtual ~ThreadManager();
LIB_UTILITIES_EXPORT virtual ~ThreadManager();
/**
* @brief Pass a list of tasklets to the master queue.
* @param joblist Vector of ThreadJob pointers.
......@@ -181,7 +181,7 @@ class ThreadManager : public boost::enable_shared_from_this<ThreadManager>
*
* @see SchedType
*/
LIB_UTILITIES_EXPORT virtual void QueueJobs(std::vector<ThreadJob*>& joblist) = 0;
LIB_UTILITIES_EXPORT virtual void QueueJobs(std::vector<ThreadJob*>& joblist) = 0;
/**
* @brief Pass a single job to the master queue.
* @param job A pointer to a ThreadJob subclass.
......@@ -190,14 +190,14 @@ class ThreadManager : public boost::enable_shared_from_this<ThreadManager>
* issue then suspend the workers with SetNumWorkers(0) until the jobs
* are queued.
*/
LIB_UTILITIES_EXPORT virtual void QueueJob(ThreadJob* job) = 0;
LIB_UTILITIES_EXPORT virtual void QueueJob(ThreadJob* job) = 0;
/**
* @brief Return the number of active workers.
*
* Active workers are threads that are either running jobs
* or are waiting for jobs to be queued.
*/
LIB_UTILITIES_EXPORT virtual unsigned int GetNumWorkers() = 0;
LIB_UTILITIES_EXPORT virtual unsigned int GetNumWorkers() = 0;
/**
* @brief Returns the worker number of the executing thread.
*
......@@ -216,7 +216,7 @@ class ThreadManager : public boost::enable_shared_from_this<ThreadManager>
*
* Returns 0 if called by non-thread.
*/
LIB_UTILITIES_EXPORT virtual unsigned int GetWorkerNum() = 0;
LIB_UTILITIES_EXPORT virtual unsigned int GetWorkerNum() = 0;
/**
* @brief Sets the number of active workers.
* @param num The number of active workers.
......@@ -227,18 +227,18 @@ class ThreadManager : public boost::enable_shared_from_this<ThreadManager>
* If num is greater than the maximum allowed number of active workers,
* then the maximum value will be used instead.
*/
LIB_UTILITIES_EXPORT virtual void SetNumWorkers(const unsigned int num) = 0;
LIB_UTILITIES_EXPORT virtual void SetNumWorkers(const unsigned int num) = 0;
/**
* @brief Sets the number of active workers to the maximum.
*
* Sets the number of active workers to the maximum available.
*/
LIB_UTILITIES_EXPORT virtual void SetNumWorkers() = 0;
LIB_UTILITIES_EXPORT virtual void SetNumWorkers() = 0;
/**
* @brief Gets the maximum available number of threads.
* @return The maximum number of workers.
*/
LIB_UTILITIES_EXPORT virtual unsigned int GetMaxNumWorkers() = 0;
LIB_UTILITIES_EXPORT virtual unsigned int GetMaxNumWorkers() = 0;
/**
* @brief Waits until all queued jobs are finished.
*
......@@ -261,7 +261,7 @@ class ThreadManager : public boost::enable_shared_from_this<ThreadManager>
* number of worker threads, implementations should increase the number
* of active workers by 1 on entering Wait().
*/
LIB_UTILITIES_EXPORT virtual void Wait() = 0;
LIB_UTILITIES_EXPORT virtual void Wait() = 0;
/**
* @brief Controls how many jobs are sent to each worker at a time.
*
......@@ -271,17 +271,17 @@ class ThreadManager : public boost::enable_shared_from_this<ThreadManager>
* @see SchedType
* @see SetSchedType()
*/
LIB_UTILITIES_EXPORT virtual void SetChunkSize(unsigned int chnk) = 0;
LIB_UTILITIES_EXPORT virtual void SetChunkSize(unsigned int chnk) = 0;
/**
* @brief Sets the current scheduling algorithm.
* @see SetChunkSize()
*/
LIB_UTILITIES_EXPORT virtual void SetSchedType(SchedType s) = 0;
LIB_UTILITIES_EXPORT virtual void SetSchedType(SchedType s) = 0;
/**
* @brief Indicates whether the code is in a worker thread or not.
* @return True if the caller is in a worker thread.
*/
LIB_UTILITIES_EXPORT virtual bool InThread() = 0;
LIB_UTILITIES_EXPORT virtual bool InThread() = 0;
/**
* @brief A calling threads holds until all active threads call this
* method.
......@@ -294,16 +294,16 @@ class ThreadManager : public boost::enable_shared_from_this<ThreadManager>
* is altered after a thread has called this method. It is only safe to
* call SetNumWorkers() when no threads are holding.
*/
LIB_UTILITIES_EXPORT virtual void Hold() = 0;
LIB_UTILITIES_EXPORT virtual void Hold() = 0;
/**
* @brief Returns a description of the type of threading.
*
* E.g. "Threading with Boost"
*/
LIB_UTILITIES_EXPORT virtual const std::string& GetType() const = 0;
LIB_UTILITIES_EXPORT virtual const std::string& GetType() const = 0;
/// ThreadManager implementation.
LIB_UTILITIES_EXPORT virtual bool IsInitialised();
LIB_UTILITIES_EXPORT virtual bool IsInitialised();
inline int GetThrFromPartition(int pPartition)
{
......@@ -354,17 +354,17 @@ class ThreadMaster
THREADMANAGER_MAX
};
/// Constructor
LIB_UTILITIES_EXPORT ThreadMaster();
LIB_UTILITIES_EXPORT ThreadMaster();
/// Destructor
LIB_UTILITIES_EXPORT ~ThreadMaster();
LIB_UTILITIES_EXPORT ~ThreadMaster();
/// Sets what ThreadManagers will be created in CreateInstance.
LIB_UTILITIES_EXPORT void SetThreadingType(const std::string &p_type);
LIB_UTILITIES_EXPORT void SetThreadingType(const std::string &p_type);
/// Gets the ThreadManager associated with string s.
LIB_UTILITIES_EXPORT ThreadManagerSharedPtr& GetInstance(const ThreadManagerName t);
LIB_UTILITIES_EXPORT ThreadManagerSharedPtr& GetInstance(const ThreadManagerName t);
/// Creates an instance of a ThreadManager (which one is determined by
/// a previous call to SetThreadingType) and associates it with
/// the string s.
LIB_UTILITIES_EXPORT ThreadManagerSharedPtr CreateInstance(const ThreadManagerName t,
LIB_UTILITIES_EXPORT ThreadManagerSharedPtr CreateInstance(const ThreadManagerName t,
unsigned int nThr);
};
......
......@@ -135,30 +135,44 @@ namespace Vmath
template LIB_UTILITIES_EXPORT Nektar::NekDouble ran2 (long* idum);
/// \brief Fills a vector with white noise.
template<class T> void FillWhiteNoise( int n, const T eps, T *x, const int incx, int outseed)
template<class T> void FillWhiteNoise( int n, const T eps, T *x,
const int incx, int outseed)
{
// Protect the static vars here and in ran2
boost::mutex::scoped_lock l(mutex);
// Define static variables for generating random numbers
static int iset = 0;
static T gset;
static long seed = 0;
// Bypass seed if outseed was specified
if( outseed != 9999)
{
seed = long(outseed);
}
while( n-- )
{
static int iset = 0;
static T gset;
long seed = long(outseed);
T fac, rsq, v1, v2;
if (iset == 0) {
do {
v1 = 2.0 * ran2<T> (&seed) - 1.0;
v2 = 2.0 * ran2<T> (&seed) - 1.0;
rsq = v1*v1 + v2*v2;
} while (rsq >= 1.0 || rsq == 0.0);
fac = sqrt(-2.0 * log (rsq) / rsq);
gset = v1 * fac;
iset = 1;
*x = eps * v2 * fac;
} else {
iset = 0;
*x = eps * gset;
if (iset == 0)
{
do
{
v1 = 2.0 * ran2<T> (&seed) - 1.0;
v2 = 2.0 * ran2<T> (&seed) - 1.0;
rsq = v1*v1 + v2*v2;
} while (rsq >= 1.0 || rsq == 0.0);
fac = sqrt(-2.0 * log (rsq) / rsq);
gset = v1 * fac;
iset = 1;
*x = eps * v2 * fac;
}
else
{
iset = 0;
*x = eps * gset;
}
x += incx;
}
......
......@@ -54,7 +54,8 @@ namespace Vmath
/// \brief Fills a vector with white noise.
template<class T> LIB_UTILITIES_EXPORT void FillWhiteNoise( int n, const T eps, T *x, const int incx, int seed = 0);
template<class T> LIB_UTILITIES_EXPORT void FillWhiteNoise(
int n, const T eps, T *x, const int incx, int seed = 9999);
/// \brief Multiply vector z = x*y
template<class T> LIB_UTILITIES_EXPORT void Vmul( int n, const T *x, const int incx, const T *y,
......
......@@ -53,7 +53,9 @@
Fill(n,alpha,&x[0],incx);
}
template<class T> void FillWhiteNoise( int n, const T eps, Array<OneD, T> &x, const int incx, int outseed)
template<class T> void FillWhiteNoise(
int n, const T eps, Array<OneD, T> &x,
const int incx, int outseed = 9999)
{
ASSERTL1(n*incx <= x.num_elements()+x.GetOffset(),"Out of bounds");
......
......@@ -418,6 +418,8 @@ namespace Nektar
Set_Rhs_Magnitude(inGlob);
}
m_totalIterations = 0;
// If input residual is less than tolerance skip solve.
if (eps < m_tolerance * m_tolerance * m_rhs_magnitude)
{
......@@ -432,7 +434,6 @@ namespace Nektar
return;
}
m_totalIterations = 1;
m_precon->DoPreconditioner(r_A, tmp = w_A + nDir);
v_DoMatrixMultiply(w_A, s_A);
......@@ -451,11 +452,12 @@ namespace Nektar
vComm->AllReduce(vExchange, Nektar::LibUtilities::ReduceSum);
rho = vExchange[0];
mu = vExchange[1];
min_resid = m_rhs_magnitude;
beta = 0.0;
alpha = rho/mu;
rho = vExchange[0];
mu = vExchange[1];
min_resid = m_rhs_magnitude;
beta = 0.0;
alpha = rho/mu;
m_totalIterations = 1;
// Continue until convergence
while (true)
......@@ -541,8 +543,12 @@ namespace Nektar
void GlobalLinSysIterative::Set_Rhs_Magnitude(
const NekVector<NekDouble> &pIn)
{
Array<OneD, NekDouble> vExchange(1);
vExchange[0] = Vmath::Dot(pIn.GetDimension(),&pIn[0],&pIn[0]);
Array<OneD, NekDouble> vExchange(1, 0.0);
if (m_map.num_elements() > 0)
{
vExchange[0] = Vmath::Dot2(pIn.GetDimension(),
&pIn[0],&pIn[0],&m_map[0]);
}
m_expList.lock()->GetComm()->GetRowComm()->AllReduce(
vExchange, Nektar::LibUtilities::ReduceSum);
......
......@@ -56,7 +56,7 @@ void Face::GetCurvedNodes(
int n2 = m_edgeList[1]->GetNodeCount();
int n3 = m_edgeList[2]->GetNodeCount();
bool same = (n == n2 ? (n2==n3) : false);
bool same = (n == n2 ? (n2 == n3) : false);
ASSERTL0(same, "Edges are not consistent");
nodeList.insert(
......
......@@ -227,7 +227,7 @@ public:
return an;
}
// fucntions for cad information
// functions for cad information
void SetCADCurve(int i, CADCurveSharedPtr c, NekDouble t)
{
......
......@@ -103,7 +103,7 @@ void Octree::Process()
NekDouble Octree::QueryR(Array<OneD, NekDouble> loc)
{
NekDouble d = Query(loc);
return d/2.0/(sqrt(m_eps * (2.0 - m_eps)));
return d / 2.0 / (sqrt(m_eps * (2.0 - m_eps)));
}
NekDouble Octree::Query(Array<OneD, NekDouble> loc)
......@@ -681,8 +681,10 @@ void Octree::PropagateDomain()
for (int i = 0; i < m_octants.size(); i++)
{
ASSERTL0(m_octants[i]->IsDeltaKnown(),
"does not know delta after propergation");
if (!m_octants[i]->IsDeltaKnown())
{
m_octants[i]->SetDelta(m_maxDelta);
}
}
}
......
......@@ -97,11 +97,14 @@ namespace SolverUtils
m_Forcing = Array<OneD, Array<OneD, NekDouble> > (m_NumVariable);
// Fill forcing: use -i as seed to avoid repeated results
for (int i = 0; i < m_NumVariable; ++i)
// Fill forcing: use rank in seed to avoid repeated results
int seed = - m_session->GetComm()->GetRank();
m_Forcing[0] = Array<OneD, NekDouble> (nq, 0.0);
Vmath::FillWhiteNoise(nq,m_noise,m_Forcing[0],1,seed);
for (int i = 1; i < m_NumVariable; ++i)
{
m_Forcing[i] = Array<OneD, NekDouble> (nq, 0.0);
Vmath::FillWhiteNoise(nq,m_noise,&m_Forcing[i][0],1,-i);
Vmath::FillWhiteNoise(nq,m_noise,m_Forcing[i],1);
}
m_index = 0;
......@@ -125,8 +128,7 @@ namespace SolverUtils
for (int i = 0; i < m_NumVariable; ++i)
{
Vmath::FillWhiteNoise(outarray[i].num_elements(),
m_noise,&m_Forcing[i][0],1,
-m_index*m_NumVariable-i);
m_noise,m_Forcing[i],1);
}
}
......
......@@ -484,6 +484,7 @@ set(CPACK_SOURCE_IGNORE_FILES
"/library/Demos/LocalRegions/XmlFiles/"
"/solvers/ImageWarpingSolver/"
"/solvers/VortexWaveInteraction/"
"/solvers/IncNavierStokesSolver/Utilities/HybridPerformanceModel/"
"/utilities/Extras/"
)
......
......@@ -709,10 +709,12 @@ namespace Nektar
if (Noise > 0.0)
{
int seed = - m_comm->GetRank()*m_nConvectiveFields;
for (int i = 0; i < m_nConvectiveFields; i++)
{
Vmath::FillWhiteNoise(phystot, Noise, noise, 1,
m_comm->GetColumnComm()->GetRank()+1);
seed);
--seed;
Vmath::Vadd(phystot, m_fields[i]->GetPhys(), 1,
noise, 1, m_fields[i]->UpdatePhys(), 1);
m_fields[i]->FwdTrans_IterPerExp(m_fields[i]->GetPhys(),
......
......@@ -8,17 +8,17 @@
</files>
<metrics>
<metric type="L2" id="1">
<value variable="rho" tolerance="1e-12">0.000483502</value>
<value variable="rho" tolerance="1e-12">0.000483148</value>
<value variable="rhou" tolerance="1e-12">48.1265</value>
<value variable="rhov" tolerance="1e-12">0.17737</value>
<value variable="rhow" tolerance="1e-12">7.15155e-06</value>
<value variable="rhov" tolerance="1e-12">0.177369</value>
<value variable="rhow" tolerance="1e-12">7.074e-06</value>
<value variable="E" tolerance="1e-12">17518.9</value>
</metric>
<metric type="Linf" id="2">
<value variable="rho" tolerance="1e-12">0.00208788</value>
<value variable="rho" tolerance="1e-12">0.00207205</value>
<value variable="rhou" tolerance="1e-12">83.3318</value>
<value variable="rhov" tolerance="1e-12">0.752415</value>
<value variable="rhow" tolerance="1e-12">4.89968e-05</value>
<value variable="rhov" tolerance="1e-12">0.7524</value>
<value variable="rhow" tolerance="1e-12">3.34657e-05</value>
<value variable="E" tolerance="1e-12">18726.1</value>
</metric>
</metrics>
......
......@@ -8,17 +8,17 @@
</files>
<metrics>
<metric type="L2" id="1">
<value variable="rho" tolerance="1e-12">0.000483502</value>
<value variable="rho" tolerance="1e-12">0.000483148</value>
<value variable="rhou" tolerance="1e-12">48.1265</value>
<value variable="rhov" tolerance="1e-12">0.17737</value>
<value variable="rhow" tolerance="1e-12"> 7.15155e-06</value>
<value variable="rhov" tolerance="1e-12">0.177369</value>
<value variable="rhow" tolerance="1e-12"> 7.074e-06</value>
<value variable="E" tolerance="1e-12">17518.9</value>
</metric>
<metric type="Linf" id="2">
<value variable="rho" tolerance="1e-12">0.00208788</value>
<value variable="rho" tolerance="1e-12">0.00207205</value>
<value variable="rhou" tolerance="1e-12">83.3318</value>
<value variable="rhov" tolerance="1e-12">0.752415</value>
<value variable="rhow" tolerance="1e-12">4.89968e-05</value>
<value variable="rhov" tolerance="1e-12">0.7524</value>
<value variable="rhow" tolerance="1e-12">3.34657e-05</value>
<value variable="E" tolerance="1e-12">18726.1</value>
</metric>
</metrics>
......
......@@ -8,17 +8,17 @@
</files>
<metrics>
<metric type="L2" id="1">
<value variable="rho" tolerance="1e-12">0.000397706</value>
<value variable="rho" tolerance="1e-12">0.000397131</value>
<value variable="rhou" tolerance="1e-12">48.1295</value>
<value variable="rhov" tolerance="1e-8">0.145836</value>
<value variable="rhow" tolerance="1e-8">8.79652e-06</value>
<value variable="rhov" tolerance="1e-8">0.145835</value>
<value variable="rhow" tolerance="1e-8">9.3349e-06</value>
<value variable="E" tolerance="1e-12">17519.9</value>
</metric>
<metric type="Linf" id="2">
<value variable="rho" tolerance="1e-12">0.00139611</value>
<value variable="rhou" tolerance="1e-12">83.3516</value>
<value variable="rhov" tolerance="1e-8">0.505196</value>
<value variable="rhow" tolerance="1e-8">3.177e-05</value>
<value variable="rho" tolerance="1e-12">0.00139966</value>
<value variable="rhou" tolerance="1e-12">83.3517</value>
<value variable="rhov" tolerance="1e-8">0.50519</value>
<value variable="rhow" tolerance="1e-8">3.11563e-05</value>
<value variable="E" tolerance="1e-12">18953</value>
</metric>
</metrics>
......
......@@ -8,17 +8,17 @@
</files>
<metrics>
<metric type="L2" id="1">
<value variable="rho" tolerance="1e-12">0.000397706</value>
<value variable="rho" tolerance="1e-12">0.000397131</value>
<value variable="rhou" tolerance="1e-12">48.1295</value>
<value variable="rhov" tolerance="1e-8">0.145836</value>
<value variable="rhow" tolerance="1e-8">8.79652e-06</value>
<value variable="rhov" tolerance="1e-8">0.145835</value>
<value variable="rhow" tolerance="1e-8">9.3349e-06</value>
<value variable="E" tolerance="1e-12">17519.9</value>
</metric>
<metric type="Linf" id="2">
<value variable="rho" tolerance="1e-12">0.00139611</value>
<value variable="rhou" tolerance="1e-12">83.3516</value>
<value variable="rhov" tolerance="1e-8">0.505196</value>
<value variable="rhow" tolerance="1e-8">3.177e-05</value>
<value variable="rho" tolerance="1e-12">0.00139966</value>
<value variable="rhou" tolerance="1e-12">83.3517</value>
<value variable="rhov" tolerance="1e-8">0.50519</value>
<value variable="rhow" tolerance="1e-8">3.11563e-05</value>
<value variable="E" tolerance="1e-12">18953</value>
</metric>
</metrics>
......
......@@ -8,17 +8,17 @@
</files>
<metrics>
<metric type="L2" id="1">
<value variable="rho" tolerance="1e-12">0.000391612</value>
<value variable="rho" tolerance="1e-12">0.000391074</value>
<value variable="rhou" tolerance="1e-12">48.1285</value>
<value variable="rhov" tolerance="1e-12">0.143446</value>
<value variable="rhow" tolerance="1e-12"> 6.98852e-06</value>
<value variable="rhov" tolerance="1e-12">0.143445</value>
<value variable="rhow" tolerance="1e-12"> 6.90409e-06</value>
<value variable="E" tolerance="1e-12">17519.5</value>
</metric>
<metric type="Linf" id="2">
<value variable="rho" tolerance="1e-12">0.00164134</value>
<value variable="rho" tolerance="1e-12">0.00162551</value>
<value variable="rhou" tolerance="1e-12">83.3449</value>
<value variable="rhov" tolerance="1e-12">0.5882</value>
<value variable="rhow" tolerance="1e-12">4.62189e-05</value>
<value variable="rhov" tolerance="1e-12">0.588196</value>
<value variable="rhow" tolerance="1e-12">3.33264e-05</value>
<value variable="E" tolerance="1e-12">18878.3</value>
</metric>
</metrics>
......