Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
KiT-RT
KiT-RT
Commits
20273d0b
Commit
20273d0b
authored
Dec 01, 2020
by
Steffen Schotthöfer
Browse files
renamed createQuadrature. commented out lookup tables. added docu
Former-commit-id:
e5169b19
parent
373053ed
Changes
14
Hide whitespace changes
Inline
Side-by-side
code/include/quadratures/quadraturebase.h
View file @
20273d0b
...
...
@@ -15,7 +15,8 @@ class QuadratureBase
{
public:
/*! @brief: Constructor using settings class. This is the recommended constructor.
* @param: Config* settings: Settings class storing all important options */
* @param: Config* settings: Settings class storing all important options.
*/
QuadratureBase
(
Config
*
settings
);
/*! @brief: Constructor using directly the order of the quadrature. Not applicable for GaussLegendre, that need additional options.
It sets member _settings = nulltpr.*/
...
...
@@ -46,13 +47,13 @@ class QuadratureBase
/*! @brief Creates a quadrature rule with a given name and a given order.
* @param: Config* settings: Settings to handle quadrature options
* @returns Quadrature* quadrature: returns pointer to instance of the given derived quadrature class */
static
QuadratureBase
*
Create
Quadrature
(
Config
*
settings
);
static
QuadratureBase
*
Create
(
Config
*
settings
);
/*! @brief Creates a quadrature rule with a given name and a given order.
* @param: name: name of quadrature as enum
* @param: quadOrder: order of quadrature
* @returns Quadrature* quadrature: returns pointer to instance of the given derived quadrature class */
static
QuadratureBase
*
Create
Quadrature
(
QUAD_NAME
name
,
unsigned
quadOrder
);
static
QuadratureBase
*
Create
(
QUAD_NAME
name
,
unsigned
quadOrder
);
// Getter
inline
std
::
string
GetName
()
const
{
return
_name
;
}
/*! @returns std::string _name: name of the quadrature */
...
...
code/src/optimizers/newtonoptimizer.cpp
View file @
20273d0b
...
...
@@ -6,7 +6,7 @@
#include "toolboxes/errormessages.h"
NewtonOptimizer
::
NewtonOptimizer
(
Config
*
settings
)
:
OptimizerBase
(
settings
)
{
_quadrature
=
QuadratureBase
::
Create
Quadrature
(
settings
);
_quadrature
=
QuadratureBase
::
Create
(
settings
);
_nq
=
_quadrature
->
GetNq
();
_weights
=
_quadrature
->
GetWeights
();
_quadPointsSphere
=
_quadrature
->
GetPointsSphere
();
...
...
code/src/quadratures/qldfesa.cpp
View file @
20273d0b
#include "quadratures/qldfesa.h"
#include "quadratures/lookuptable_ldfesa.h"
//
#include "quadratures/lookuptable_ldfesa.h"
#include "toolboxes/errormessages.h"
QLDFESA
::
QLDFESA
(
Config
*
settings
)
:
QLookupQuadrature
(
settings
)
{
...
...
@@ -34,9 +34,9 @@ std::string QLDFESA::GetLookupTable() {
unsigned
char
*
lookupTable
=
nullptr
;
switch
(
_order
)
{
case
1
:
lookupTable
=
__1_ldfesa_txt
;
break
;
case
2
:
lookupTable
=
__2_ldfesa_txt
;
break
;
case
3
:
lookupTable
=
__3_ldfesa_txt
;
break
;
//
case 1: lookupTable = __1_ldfesa_txt; break;
//
case 2: lookupTable = __2_ldfesa_txt; break;
//
case 3: lookupTable = __3_ldfesa_txt; break;
default:
ErrorMessages
::
Error
(
"Invalid quadrature order chosen!"
,
CURRENT_FUNCTION
);
}
...
...
code/src/quadratures/qlebedev.cpp
View file @
20273d0b
#include "quadratures/qlebedev.h"
#include "quadratures/lookuptable_lebedev.h"
//
#include "quadratures/lookuptable_lebedev.h"
#include "toolboxes/errormessages.h"
QLebedev
::
QLebedev
(
Config
*
settings
)
:
QLookupQuadrature
(
settings
)
{
...
...
@@ -37,38 +37,38 @@ std::string QLebedev::GetLookupTable() {
unsigned
char
*
lookupTable
=
nullptr
;
switch
(
_order
)
{
case
3
:
lookupTable
=
__3_lebedev_txt
;
break
;
case
5
:
lookupTable
=
__5_lebedev_txt
;
break
;
case
7
:
lookupTable
=
__7_lebedev_txt
;
break
;
case
9
:
lookupTable
=
__9_lebedev_txt
;
break
;
case
11
:
lookupTable
=
__11_lebedev_txt
;
break
;
case
13
:
lookupTable
=
__13_lebedev_txt
;
break
;
case
15
:
lookupTable
=
__15_lebedev_txt
;
break
;
case
17
:
lookupTable
=
__17_lebedev_txt
;
break
;
case
19
:
lookupTable
=
__19_lebedev_txt
;
break
;
case
21
:
lookupTable
=
__21_lebedev_txt
;
break
;
case
23
:
lookupTable
=
__23_lebedev_txt
;
break
;
case
25
:
lookupTable
=
__25_lebedev_txt
;
break
;
case
27
:
lookupTable
=
__27_lebedev_txt
;
break
;
case
29
:
lookupTable
=
__29_lebedev_txt
;
break
;
case
31
:
lookupTable
=
__31_lebedev_txt
;
break
;
case
35
:
lookupTable
=
__35_lebedev_txt
;
break
;
case
41
:
lookupTable
=
__41_lebedev_txt
;
break
;
case
47
:
lookupTable
=
__47_lebedev_txt
;
break
;
case
53
:
lookupTable
=
__53_lebedev_txt
;
break
;
case
59
:
lookupTable
=
__59_lebedev_txt
;
break
;
case
65
:
lookupTable
=
__65_lebedev_txt
;
break
;
case
71
:
lookupTable
=
__71_lebedev_txt
;
break
;
case
77
:
lookupTable
=
__77_lebedev_txt
;
break
;
case
83
:
lookupTable
=
__83_lebedev_txt
;
break
;
case
89
:
lookupTable
=
__89_lebedev_txt
;
break
;
case
95
:
lookupTable
=
__95_lebedev_txt
;
break
;
case
101
:
lookupTable
=
__101_lebedev_txt
;
break
;
case
107
:
lookupTable
=
__107_lebedev_txt
;
break
;
case
113
:
lookupTable
=
__113_lebedev_txt
;
break
;
case
119
:
lookupTable
=
__119_lebedev_txt
;
break
;
case
125
:
lookupTable
=
__125_lebedev_txt
;
break
;
case
131
:
lookupTable
=
__131_lebedev_txt
;
break
;
//
case 3: lookupTable = __3_lebedev_txt; break;
//
case 5: lookupTable = __5_lebedev_txt; break;
//
case 7: lookupTable = __7_lebedev_txt; break;
//
case 9: lookupTable = __9_lebedev_txt; break;
//
case 11: lookupTable = __11_lebedev_txt; break;
//
case 13: lookupTable = __13_lebedev_txt; break;
//
case 15: lookupTable = __15_lebedev_txt; break;
//
case 17: lookupTable = __17_lebedev_txt; break;
//
case 19: lookupTable = __19_lebedev_txt; break;
//
case 21: lookupTable = __21_lebedev_txt; break;
//
case 23: lookupTable = __23_lebedev_txt; break;
//
case 25: lookupTable = __25_lebedev_txt; break;
//
case 27: lookupTable = __27_lebedev_txt; break;
//
case 29: lookupTable = __29_lebedev_txt; break;
//
case 31: lookupTable = __31_lebedev_txt; break;
//
case 35: lookupTable = __35_lebedev_txt; break;
//
case 41: lookupTable = __41_lebedev_txt; break;
//
case 47: lookupTable = __47_lebedev_txt; break;
//
case 53: lookupTable = __53_lebedev_txt; break;
//
case 59: lookupTable = __59_lebedev_txt; break;
//
case 65: lookupTable = __65_lebedev_txt; break;
//
case 71: lookupTable = __71_lebedev_txt; break;
//
case 77: lookupTable = __77_lebedev_txt; break;
//
case 83: lookupTable = __83_lebedev_txt; break;
//
case 89: lookupTable = __89_lebedev_txt; break;
//
case 95: lookupTable = __95_lebedev_txt; break;
//
case 101: lookupTable = __101_lebedev_txt; break;
//
case 107: lookupTable = __107_lebedev_txt; break;
//
case 113: lookupTable = __113_lebedev_txt; break;
//
case 119: lookupTable = __119_lebedev_txt; break;
//
case 125: lookupTable = __125_lebedev_txt; break;
//
case 131: lookupTable = __131_lebedev_txt; break;
default:
ErrorMessages
::
Error
(
"Invalid quadrature order chosen!"
,
CURRENT_FUNCTION
);
}
...
...
code/src/quadratures/qlevelsymmetric.cpp
View file @
20273d0b
#include "quadratures/qlevelsymmetric.h"
#include "quadratures/lookuptable_levelsymmetric.h"
//
#include "quadratures/lookuptable_levelsymmetric.h"
#include "toolboxes/errormessages.h"
QLevelSymmetric
::
QLevelSymmetric
(
Config
*
settings
)
:
QLookupQuadrature
(
settings
)
{
...
...
@@ -35,16 +35,16 @@ std::string QLevelSymmetric::GetLookupTable() {
unsigned
char
*
lookupTable
=
nullptr
;
switch
(
_order
)
{
case
2
:
lookupTable
=
__2_levelsym_txt
;
break
;
case
4
:
lookupTable
=
__4_levelsym_txt
;
break
;
case
6
:
lookupTable
=
__6_levelsym_txt
;
break
;
case
8
:
lookupTable
=
__8_levelsym_txt
;
break
;
case
10
:
lookupTable
=
__10_levelsym_txt
;
break
;
case
12
:
lookupTable
=
__12_levelsym_txt
;
break
;
case
14
:
lookupTable
=
__14_levelsym_txt
;
break
;
case
16
:
lookupTable
=
__16_levelsym_txt
;
break
;
case
18
:
lookupTable
=
__18_levelsym_txt
;
break
;
case
20
:
lookupTable
=
__20_levelsym_txt
;
break
;
//
case 2: lookupTable = __2_levelsym_txt; break;
//
case 4: lookupTable = __4_levelsym_txt; break;
//
case 6: lookupTable = __6_levelsym_txt; break;
//
case 8: lookupTable = __8_levelsym_txt; break;
//
case 10: lookupTable = __10_levelsym_txt; break;
//
case 12: lookupTable = __12_levelsym_txt; break;
//
case 14: lookupTable = __14_levelsym_txt; break;
//
case 16: lookupTable = __16_levelsym_txt; break;
//
case 18: lookupTable = __18_levelsym_txt; break;
//
case 20: lookupTable = __20_levelsym_txt; break;
default:
ErrorMessages
::
Error
(
"Invalid quadrature order chosen!"
,
CURRENT_FUNCTION
);
}
...
...
code/src/quadratures/quadraturebase.cpp
View file @
20273d0b
...
...
@@ -16,7 +16,7 @@ QuadratureBase::QuadratureBase( Config* settings ) {
QuadratureBase
::
QuadratureBase
(
unsigned
order
)
:
_order
(
order
)
{
_settings
=
nullptr
;
}
QuadratureBase
*
QuadratureBase
::
Create
Quadrature
(
Config
*
settings
)
{
QuadratureBase
*
QuadratureBase
::
Create
(
Config
*
settings
)
{
QUAD_NAME
name
=
settings
->
GetQuadName
();
switch
(
name
)
{
...
...
@@ -26,12 +26,12 @@ QuadratureBase* QuadratureBase::CreateQuadrature( Config* settings ) {
case
QUAD_LevelSymmetric
:
return
new
QLevelSymmetric
(
settings
);
case
QUAD_LDFESA
:
return
new
QLDFESA
(
settings
);
case
QUAD_Lebedev
:
return
new
QLebedev
(
settings
);
case
QUAD_Product
:
return
new
Product
Quadrature
(
settings
);
case
QUAD_Product
:
return
new
Q
Product
(
settings
);
default:
ErrorMessages
::
Error
(
"Creator for the chose quadrature does not yet exist. This is is the fault of the coder!"
,
CURRENT_FUNCTION
);
}
}
QuadratureBase
*
QuadratureBase
::
Create
Quadrature
(
QUAD_NAME
name
,
unsigned
quadOrder
)
{
QuadratureBase
*
QuadratureBase
::
Create
(
QUAD_NAME
name
,
unsigned
quadOrder
)
{
switch
(
name
)
{
case
QUAD_MonteCarlo
:
return
new
QMonteCarlo
(
quadOrder
);
...
...
@@ -42,7 +42,7 @@ QuadratureBase* QuadratureBase::CreateQuadrature( QUAD_NAME name, unsigned quadO
case
QUAD_LevelSymmetric
:
return
new
QLevelSymmetric
(
quadOrder
);
case
QUAD_LDFESA
:
return
new
QLDFESA
(
quadOrder
);
case
QUAD_Lebedev
:
return
new
QLebedev
(
quadOrder
);
case
QUAD_Product
:
return
new
Product
Quadrature
(
quadOrder
);
case
QUAD_Product
:
return
new
Q
Product
(
quadOrder
);
default:
ErrorMessages
::
Error
(
"Creator for the chose quadrature does not yet exist. This is is the fault of the coder!"
,
CURRENT_FUNCTION
);
}
}
...
...
code/src/solvers/csdsnsolverfp.cpp
View file @
20273d0b
...
...
@@ -32,7 +32,7 @@ CSDSNSolverFP::CSDSNSolverFP( Config* settings ) : SNSolver( settings ) {
// create 1D quadrature
unsigned
nq
=
_settings
->
GetNQuadPoints
();
QuadratureBase
*
quad1D
=
QuadratureBase
::
Create
Quadrature
(
QUAD_GaussLegendre1D
,
nq
);
QuadratureBase
*
quad1D
=
QuadratureBase
::
Create
(
QUAD_GaussLegendre1D
,
nq
);
Vector
w
=
quad1D
->
GetWeights
();
VectorVector
muVec
=
quad1D
->
GetPoints
();
Vector
mu
(
nq
);
...
...
code/src/solvers/csdsolvertrafofp.cpp
View file @
20273d0b
...
...
@@ -23,7 +23,7 @@ CSDSolverTrafoFP::CSDSolverTrafoFP( Config* settings ) : SNSolver( settings ) {
// create 1D quadrature
unsigned
nq
=
_settings
->
GetNQuadPoints
();
QuadratureBase
*
quad1D
=
QuadratureBase
::
Create
Quadrature
(
QUAD_GaussLegendre1D
,
nq
);
QuadratureBase
*
quad1D
=
QuadratureBase
::
Create
(
QUAD_GaussLegendre1D
,
nq
);
Vector
w
=
quad1D
->
GetWeights
();
VectorVector
muVec
=
quad1D
->
GetPoints
();
Vector
mu
(
nq
);
...
...
code/src/solvers/csdsolvertrafofp2d.cpp
View file @
20273d0b
...
...
@@ -4,7 +4,7 @@
#include "fluxes/numericalflux.h"
#include "kernels/scatteringkernelbase.h"
#include "problems/problembase.h"
#include "quadratures/product
quadrature
.h"
#include "quadratures/
q
product.h"
#include "quadratures/quadraturebase.h"
// externals
...
...
@@ -36,7 +36,7 @@ CSDSolverTrafoFP2D::CSDSolverTrafoFP2D( Config* settings ) : SNSolver( settings
_wa
=
Vector
(
2
*
order
);
// create quadrature 1D to compute mu grid
QuadratureBase
*
quad1D
=
QuadratureBase
::
Create
Quadrature
(
QUAD_GaussLegendre1D
,
2
*
order
);
QuadratureBase
*
quad1D
=
QuadratureBase
::
Create
(
QUAD_GaussLegendre1D
,
2
*
order
);
Vector
w
=
quad1D
->
GetWeights
();
VectorVector
muVec
=
quad1D
->
GetPoints
();
for
(
unsigned
k
=
0
;
k
<
2
*
order
;
++
k
)
{
...
...
code/src/solvers/csdsolvertrafofpsh2d.cpp
View file @
20273d0b
...
...
@@ -25,7 +25,7 @@ CSDSolverTrafoFPSH2D::CSDSolverTrafoFPSH2D( Config* settings ) : SNSolver( setti
_wa
=
Vector
(
2
*
order
);
// create quadrature 1D to compute mu grid
QuadratureBase
*
quad1D
=
QuadratureBase
::
Create
Quadrature
(
QUAD_GaussLegendre1D
,
2
*
order
);
QuadratureBase
*
quad1D
=
QuadratureBase
::
Create
(
QUAD_GaussLegendre1D
,
2
*
order
);
Vector
w
=
quad1D
->
GetWeights
();
VectorVector
muVec
=
quad1D
->
GetPoints
();
for
(
unsigned
k
=
0
;
k
<
2
*
order
;
++
k
)
{
...
...
code/src/solvers/solverbase.cpp
View file @
20273d0b
...
...
@@ -25,7 +25,7 @@ Solver::Solver( Config* settings ) : _settings( settings ) {
_settings
->
SetNCells
(
_nCells
);
// build quadrature object and store frequently used params
_quadrature
=
QuadratureBase
::
Create
Quadrature
(
settings
);
_quadrature
=
QuadratureBase
::
Create
(
settings
);
_nq
=
_quadrature
->
GetNq
();
_settings
->
SetNQuadPoints
(
_nq
);
...
...
code/tests/test_kernel.cpp
View file @
20273d0b
...
...
@@ -11,7 +11,7 @@ TEST_CASE( "test all scattering kernels", "[kernel]" ) {
// Load Settings from File
Config
*
config
=
new
Config
(
filename
);
QuadratureBase
*
quad
=
QuadratureBase
::
Create
Quadrature
(
config
);
//@TODO: swap out for different quadrature rule
QuadratureBase
*
quad
=
QuadratureBase
::
Create
(
config
);
//@TODO: swap out for different quadrature rule
SECTION
(
"isotropic scattering kernel"
)
{
...
...
code/tests/test_optimizer.cpp
View file @
20273d0b
...
...
@@ -16,7 +16,7 @@ TEST_CASE( "Test the Newton Optimizer", "[optimizers]" ) {
SphericalHarmonics
basis
(
config
->
GetMaxMomentDegree
()
);
// Get Quadrature
QuadratureBase
*
quad
=
QuadratureBase
::
Create
Quadrature
(
config
);
QuadratureBase
*
quad
=
QuadratureBase
::
Create
(
config
);
// Get Optimizer (Newton)
OptimizerBase
*
optimizer
=
OptimizerBase
::
Create
(
config
);
...
...
code/tests/test_quadrature.cpp
View file @
20273d0b
...
...
@@ -52,7 +52,7 @@ TEST_CASE( "Quadrature Tests", "[quadrature]" ) {
// Set quadOrder
config
->
SetQuadOrder
(
quadratureorder
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
Quadrature
(
config
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
(
config
);
if
(
quadraturename
==
QUAD_GaussLegendre1D
)
{
if
(
!
approxequal
(
Q
->
SumUpWeights
(),
2
,
lowAccuracyTesting
)
)
{
...
...
@@ -79,7 +79,7 @@ TEST_CASE( "Quadrature Tests", "[quadrature]" ) {
// Special case for Gauss Legendre with half weights
if
(
quadraturename
==
QUAD_GaussLegendreTensorized
)
{
config
->
SetSNAllGaussPts
(
false
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
Quadrature
(
config
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
(
config
);
if
(
!
approxequal
(
Q
->
SumUpWeights
(),
4
*
M_PI
,
lowAccuracyTesting
)
)
{
printf
(
"Quadrature %d at order %d . Error : %.15f (low accuracy testing was set to %d). Reduced number of quadrature "
"points used.
\n
"
,
...
...
@@ -116,7 +116,7 @@ TEST_CASE( "Quadrature Tests", "[quadrature]" ) {
bool
errorWithinBounds
=
true
;
QuadratureBase
*
Q
=
QuadratureBase
::
Create
Quadrature
(
config
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
(
config
);
VectorVector
points
=
Q
->
GetPoints
();
for
(
unsigned
i
=
0
;
i
<
Q
->
GetNq
();
i
++
)
{
if
(
!
approxequal
(
1.0
,
norm
(
points
[
i
]
),
lowAccuracyTesting
)
)
{
...
...
@@ -134,7 +134,7 @@ TEST_CASE( "Quadrature Tests", "[quadrature]" ) {
// Special case for Gauss Legendre with half weights
if
(
quadraturename
==
QUAD_GaussLegendreTensorized
)
{
config
->
SetSNAllGaussPts
(
false
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
Quadrature
(
config
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
(
config
);
VectorVector
points
=
Q
->
GetPoints
();
for
(
unsigned
i
=
0
;
i
<
Q
->
GetNq
();
i
++
)
{
...
...
@@ -169,14 +169,14 @@ TEST_CASE( "Quadrature Tests", "[quadrature]" ) {
// Set quadOrder
config
->
SetQuadOrder
(
quadratureorder
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
Quadrature
(
config
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
(
config
);
REQUIRE
(
Q
->
GetNq
()
==
size
(
Q
->
GetWeights
()
)
);
// Special case for Gauss Legendre with half weights
if
(
quadraturename
==
QUAD_GaussLegendreTensorized
)
{
config
->
SetSNAllGaussPts
(
false
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
Quadrature
(
config
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
(
config
);
REQUIRE
(
Q
->
GetNq
()
==
size
(
Q
->
GetWeights
()
)
);
config
->
SetSNAllGaussPts
(
true
);
}
...
...
@@ -194,13 +194,13 @@ TEST_CASE( "Quadrature Tests", "[quadrature]" ) {
// Set quadOrder
config
->
SetQuadOrder
(
quadratureorder
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
Quadrature
(
config
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
(
config
);
REQUIRE
(
Q
->
GetNq
()
==
size
(
Q
->
GetPoints
()
)
);
// Special case for Gauss Legendre with half weights
if
(
quadraturename
==
QUAD_GaussLegendreTensorized
)
{
config
->
SetSNAllGaussPts
(
false
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
Quadrature
(
config
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
(
config
);
REQUIRE
(
Q
->
GetNq
()
==
size
(
Q
->
GetPoints
()
)
);
config
->
SetSNAllGaussPts
(
true
);
}
...
...
@@ -224,7 +224,7 @@ TEST_CASE( "Quadrature Tests", "[quadrature]" ) {
// Set quadOrder
config
->
SetQuadOrder
(
quadratureorder
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
Quadrature
(
config
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
(
config
);
if
(
quadraturename
==
QUAD_GaussLegendre1D
)
{
if
(
!
approxequal
(
Q
->
Integrate
(
sin
),
0
,
lowAccuracyTesting
)
)
{
...
...
@@ -252,7 +252,7 @@ TEST_CASE( "Quadrature Tests", "[quadrature]" ) {
// Special case for Gauss Legendre with half weights
if
(
quadraturename
==
QUAD_GaussLegendreTensorized
)
{
config
->
SetSNAllGaussPts
(
false
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
Quadrature
(
config
);
QuadratureBase
*
Q
=
QuadratureBase
::
Create
(
config
);
if
(
!
approxequal
(
Q
->
Integrate
(
f
),
4.0
*
M_PI
,
lowAccuracyTesting
)
)
{
printf
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment