Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
tsoc
openmm
Commits
46f7b76b
"serialization/vscode:/vscode.git/clone" did not exist on "5b074f80caec64e890037a192d176f9209c5b553"
Commit
46f7b76b
authored
Jan 08, 2015
by
Peter Eastman
Browse files
AmoebaVdwForce works with triclinic boxes
parent
53690fef
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
162 additions
and
52 deletions
+162
-52
plugins/amoeba/platforms/cuda/tests/TestCudaAmoebaVdwForce.cpp
...ns/amoeba/platforms/cuda/tests/TestCudaAmoebaVdwForce.cpp
+62
-0
plugins/amoeba/platforms/reference/src/AmoebaReferenceKernels.cpp
...amoeba/platforms/reference/src/AmoebaReferenceKernels.cpp
+4
-4
plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.cpp
.../reference/src/SimTKReference/AmoebaReferenceVdwForce.cpp
+29
-34
plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.h
...ms/reference/src/SimTKReference/AmoebaReferenceVdwForce.h
+5
-14
plugins/amoeba/platforms/reference/tests/TestReferenceAmoebaVdwForce.cpp
...platforms/reference/tests/TestReferenceAmoebaVdwForce.cpp
+62
-0
No files found.
plugins/amoeba/platforms/cuda/tests/TestCudaAmoebaVdwForce.cpp
View file @
46f7b76b
...
...
@@ -39,6 +39,7 @@
#include "openmm/System.h"
#include "openmm/AmoebaVdwForce.h"
#include "openmm/LangevinIntegrator.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
#include <stdlib.h>
...
...
@@ -50,6 +51,7 @@
using
namespace
OpenMM
;
using
namespace
std
;
extern
"C"
void
registerAmoebaCudaKernelFactories
();
...
...
@@ -1981,6 +1983,62 @@ void testVdwWater( int includeVdwDispersionCorrection, FILE* log ) {
}
}
void
testTriclinic
()
{
System
system
;
system
.
addParticle
(
1.0
);
system
.
addParticle
(
1.0
);
Vec3
a
(
3.1
,
0
,
0
);
Vec3
b
(
0.4
,
3.5
,
0
);
Vec3
c
(
-
0.1
,
-
0.5
,
4.0
);
system
.
setDefaultPeriodicBoxVectors
(
a
,
b
,
c
);
LangevinIntegrator
integrator
(
0.0
,
0.1
,
0.01
);
AmoebaVdwForce
*
vdw
=
new
AmoebaVdwForce
();
vdw
->
setUseDispersionCorrection
(
false
);
vdw
->
addParticle
(
0
,
0.5
,
1.0
,
0.0
);
vdw
->
addParticle
(
1
,
0.5
,
1.0
,
0.0
);
vdw
->
setNonbondedMethod
(
AmoebaVdwForce
::
CutoffPeriodic
);
const
double
cutoff
=
1.5
;
vdw
->
setCutoff
(
cutoff
);
system
.
addForce
(
vdw
);
Context
context
(
system
,
integrator
,
Platform
::
getPlatformByName
(
"CUDA"
));
vector
<
Vec3
>
positions
(
2
);
OpenMM_SFMT
::
SFMT
sfmt
;
init_gen_rand
(
0
,
sfmt
);
for
(
int
iteration
=
0
;
iteration
<
50
;
iteration
++
)
{
// Generate random positions for the two particles.
positions
[
0
]
=
a
*
genrand_real2
(
sfmt
)
+
b
*
genrand_real2
(
sfmt
)
+
c
*
genrand_real2
(
sfmt
);
positions
[
1
]
=
a
*
genrand_real2
(
sfmt
)
+
b
*
genrand_real2
(
sfmt
)
+
c
*
genrand_real2
(
sfmt
);
context
.
setPositions
(
positions
);
// Loop over all possible periodic copies and find the nearest one.
Vec3
delta
;
double
distance2
=
100.0
;
for
(
int
i
=
-
1
;
i
<
2
;
i
++
)
for
(
int
j
=
-
1
;
j
<
2
;
j
++
)
for
(
int
k
=
-
1
;
k
<
2
;
k
++
)
{
Vec3
d
=
positions
[
1
]
-
positions
[
0
]
+
a
*
i
+
b
*
j
+
c
*
k
;
if
(
d
.
dot
(
d
)
<
distance2
)
{
delta
=
d
;
distance2
=
d
.
dot
(
d
);
}
}
double
distance
=
sqrt
(
distance2
);
// See if the energy is correct.
State
state
=
context
.
getState
(
State
::
Energy
);
if
(
distance
>=
cutoff
)
{
ASSERT_EQUAL
(
0.0
,
state
.
getPotentialEnergy
());
}
else
if
(
distance
<
0.9
*
cutoff
)
{
const
double
energy
=
pow
(
1.07
/
(
distance
+
0.07
),
7.0
)
*
(
1.12
/
(
pow
(
distance
,
7.0
)
+
0.12
)
-
2
);
ASSERT_EQUAL_TOL
(
energy
,
state
.
getPotentialEnergy
(),
TOL
);
}
}
}
int
main
(
int
argc
,
char
*
argv
[])
{
try
{
std
::
cout
<<
"TestCudaAmoebaVdwForce running test..."
<<
std
::
endl
;
...
...
@@ -2029,6 +2087,10 @@ int main(int argc, char* argv[]) {
includeVdwDispersionCorrection
=
1
;
testVdwWater
(
includeVdwDispersionCorrection
,
log
);
// test triclinic boxes
testTriclinic
();
}
catch
(
const
std
::
exception
&
e
)
{
std
::
cout
<<
"exception: "
<<
e
.
what
()
<<
std
::
endl
;
...
...
plugins/amoeba/platforms/reference/src/AmoebaReferenceKernels.cpp
View file @
46f7b76b
...
...
@@ -976,14 +976,14 @@ double ReferenceCalcAmoebaVdwForceKernel::execute(ContextImpl& context, bool inc
computeNeighborListVoxelHash
(
*
neighborList
,
numParticles
,
posData
,
allExclusions
,
extractBoxVectors
(
context
),
usePBC
,
cutoff
,
0.0
);
if
(
usePBC
){
vdwForce
.
setNonbondedMethod
(
AmoebaReferenceVdwForce
::
CutoffPeriodic
);
RealVec
&
box
=
extractBox
Size
(
context
);
RealVec
*
box
Vectors
=
extractBox
Vectors
(
context
);
double
minAllowedSize
=
1.999999
*
cutoff
;
if
(
box
[
0
]
<
minAllowedSize
||
box
[
1
]
<
minAllowedSize
||
box
[
2
]
<
minAllowedSize
){
if
(
box
Vectors
[
0
]
[
0
]
<
minAllowedSize
||
box
Vectors
[
1
]
[
1
]
<
minAllowedSize
||
box
Vectors
[
2
]
[
2
]
<
minAllowedSize
){
throw
OpenMMException
(
"The periodic box size has decreased to less than twice the cutoff."
);
}
vdwForce
.
setPeriodicBox
(
box
);
vdwForce
.
setPeriodicBox
(
box
Vectors
);
energy
=
vdwForce
.
calculateForceAndEnergy
(
numParticles
,
posData
,
indexIVs
,
sigmas
,
epsilons
,
reductions
,
*
neighborList
,
forceData
);
energy
+=
dispersionCoefficient
/
(
box
[
0
]
*
box
[
1
]
*
box
[
2
]);
energy
+=
dispersionCoefficient
/
(
box
Vectors
[
0
][
0
]
*
boxVectors
[
1
][
1
]
*
boxVectors
[
2
]
[
2
]);
}
else
{
vdwForce
.
setNonbondedMethod
(
AmoebaReferenceVdwForce
::
CutoffNonPeriodic
);
}
...
...
plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.cpp
View file @
46f7b76b
...
...
@@ -24,18 +24,18 @@
#include "AmoebaReferenceForce.h"
#include "AmoebaReferenceVdwForce.h"
#include "ReferenceForce.h"
#include <algorithm>
#include <cctype>
using
std
::
vector
;
using
OpenMM
::
RealVec
;
using
namespace
OpenMM
;
AmoebaReferenceVdwForce
::
AmoebaReferenceVdwForce
(
)
:
_nonbondedMethod
(
NoCutoff
),
_cutoff
(
1.0e+10
),
_taperCutoffFactor
(
0.9
)
{
setTaperCoefficients
(
_cutoff
);
setSigmaCombiningRule
(
"ARITHMETIC"
);
setEpsilonCombiningRule
(
"GEOMETRIC"
);
_periodicBoxDimensions
=
RealVec
(
0.0
,
0.0
,
0.0
);
}
...
...
@@ -44,7 +44,6 @@ AmoebaReferenceVdwForce::AmoebaReferenceVdwForce( const std::string& sigmaCombin
setTaperCoefficients
(
_cutoff
);
setSigmaCombiningRule
(
sigmaCombiningRule
);
setEpsilonCombiningRule
(
epsilonCombiningRule
);
_periodicBoxDimensions
=
RealVec
(
0.0
,
0.0
,
0.0
);
}
AmoebaReferenceVdwForce
::
NonbondedMethod
AmoebaReferenceVdwForce
::
getNonbondedMethod
(
void
)
const
{
...
...
@@ -77,12 +76,10 @@ double AmoebaReferenceVdwForce::getCutoff( void ) const {
return
_cutoff
;
}
void
AmoebaReferenceVdwForce
::
setPeriodicBox
(
const
RealVec
&
box
){
_periodicBoxDimensions
=
box
;
}
RealVec
AmoebaReferenceVdwForce
::
getPeriodicBox
(
void
)
const
{
return
_periodicBoxDimensions
;
void
AmoebaReferenceVdwForce
::
setPeriodicBox
(
OpenMM
::
RealVec
*
vectors
)
{
_periodicBoxVectors
[
0
]
=
vectors
[
0
];
_periodicBoxVectors
[
1
]
=
vectors
[
1
];
_periodicBoxVectors
[
2
]
=
vectors
[
2
];
}
void
AmoebaReferenceVdwForce
::
setSigmaCombiningRule
(
const
std
::
string
&
sigmaCombiningRule
){
...
...
@@ -181,7 +178,7 @@ void AmoebaReferenceVdwForce::addReducedForce( unsigned int particleI, unsigned
forces
[
particleIV
][
2
]
+=
sign
*
force
[
2
]
*
(
one
-
reduction
);
}
RealOpenMM
AmoebaReferenceVdwForce
::
calculatePairIxn
(
RealOpenMM
combin
d
edSigma
,
RealOpenMM
combin
d
edEpsilon
,
RealOpenMM
AmoebaReferenceVdwForce
::
calculatePairIxn
(
RealOpenMM
combinedSigma
,
RealOpenMM
combinedEpsilon
,
const
Vec3
&
particleIPosition
,
const
Vec3
&
particleJPosition
,
Vec3
&
force
)
const
{
...
...
@@ -197,36 +194,34 @@ RealOpenMM AmoebaReferenceVdwForce::calculatePairIxn( RealOpenMM combindedSigma,
// ---------------------------------------------------------------------------------------
RealOpenMM
xr
=
particleIPosition
[
0
]
-
particleJPosition
[
0
];
RealOpenMM
yr
=
particleIPosition
[
1
]
-
particleJPosition
[
1
];
RealOpenMM
zr
=
particleIPosition
[
2
]
-
particleJPosition
[
2
];
// get deltaR, R2, and R between 2 atoms
if
(
_nonbondedMethod
==
CutoffPeriodic
){
xr
-=
static_cast
<
RealOpenMM
>
((
floor
(
xr
/
_periodicBoxDimensions
[
0
]
+
0.5
)
*
_periodicBoxDimensions
[
0
]));
yr
-=
static_cast
<
RealOpenMM
>
((
floor
(
yr
/
_periodicBoxDimensions
[
1
]
+
0.5
)
*
_periodicBoxDimensions
[
1
])
);
zr
-=
static_cast
<
RealOpenMM
>
((
floor
(
zr
/
_periodicBoxDimensions
[
2
]
+
0.5
)
*
_periodicBoxDimensions
[
2
]));
}
RealOpenMM
deltaR
[
ReferenceForce
::
LastDeltaRIndex
];
if
(
_nonbondedMethod
==
CutoffPeriodic
)
ReferenceForce
::
getDeltaRPeriodic
(
particleJPosition
,
particleIPosition
,
_periodicBoxVectors
,
deltaR
);
else
ReferenceForce
::
getDeltaR
(
particleJPosition
,
particleIPosition
,
deltaR
);
RealOpenMM
r_ij_2
=
xr
*
xr
+
yr
*
yr
+
zr
*
zr
;
RealOpenMM
r_ij
=
SQRT
(
r_ij_2
)
;
RealOpenMM
sigma_7
=
combin
d
edSigma
*
combin
d
edSigma
*
combin
d
edSigma
;
sigma_7
=
sigma_7
*
sigma_7
*
combin
d
edSigma
;
RealOpenMM
r_ij_2
=
deltaR
[
ReferenceForce
::
R2Index
]
;
RealOpenMM
r_ij
=
deltaR
[
ReferenceForce
::
RIndex
]
;
RealOpenMM
sigma_7
=
combinedSigma
*
combinedSigma
*
combinedSigma
;
sigma_7
=
sigma_7
*
sigma_7
*
combinedSigma
;
RealOpenMM
r_ij_6
=
r_ij_2
*
r_ij_2
*
r_ij_2
;
RealOpenMM
r_ij_7
=
r_ij_6
*
r_ij
;
RealOpenMM
rho
=
r_ij_7
+
ghal
*
sigma_7
;
RealOpenMM
tau
=
(
dhal
+
one
)
/
(
r_ij
+
dhal
*
combin
d
edSigma
);
RealOpenMM
tau
=
(
dhal
+
one
)
/
(
r_ij
+
dhal
*
combinedSigma
);
RealOpenMM
tau_7
=
tau
*
tau
*
tau
;
tau_7
=
tau_7
*
tau_7
*
tau
;
RealOpenMM
dtau
=
tau
/
(
dhal
+
one
);
RealOpenMM
ratio
=
(
sigma_7
/
rho
);
RealOpenMM
gtau
=
combin
d
edEpsilon
*
tau_7
*
r_ij_6
*
(
ghal
+
one
)
*
ratio
*
ratio
;
RealOpenMM
gtau
=
combinedEpsilon
*
tau_7
*
r_ij_6
*
(
ghal
+
one
)
*
ratio
*
ratio
;
RealOpenMM
energy
=
combin
d
edEpsilon
*
tau_7
*
sigma_7
*
(
(
ghal
+
one
)
*
sigma_7
/
rho
-
two
);
RealOpenMM
energy
=
combinedEpsilon
*
tau_7
*
sigma_7
*
(
(
ghal
+
one
)
*
sigma_7
/
rho
-
two
);
RealOpenMM
dEdR
=
-
seven
*
(
dtau
*
energy
+
gtau
);
...
...
@@ -242,9 +237,9 @@ RealOpenMM AmoebaReferenceVdwForce::calculatePairIxn( RealOpenMM combindedSigma,
dEdR
/=
r_ij
;
force
[
0
]
=
dEdR
*
xr
;
force
[
1
]
=
dEdR
*
yr
;
force
[
2
]
=
dEdR
*
zr
;
force
[
0
]
=
dEdR
*
deltaR
[
0
]
;
force
[
1
]
=
dEdR
*
deltaR
[
1
]
;
force
[
2
]
=
dEdR
*
deltaR
[
2
]
;
return
energy
;
...
...
@@ -315,11 +310,11 @@ RealOpenMM AmoebaReferenceVdwForce::calculateForceAndEnergy( int numParticles,
for
(
unsigned
int
jj
=
ii
+
1
;
jj
<
static_cast
<
unsigned
int
>
(
numParticles
);
jj
++
){
if
(
exclusions
[
jj
]
==
0
){
RealOpenMM
combin
d
edSigma
=
(
this
->*
_combineSigmas
)(
sigmaI
,
sigmas
[
jj
]
);
RealOpenMM
combin
d
edEpsilon
=
(
this
->*
_combineEpsilons
)(
epsilonI
,
epsilons
[
jj
]
);
RealOpenMM
combinedSigma
=
(
this
->*
_combineSigmas
)(
sigmaI
,
sigmas
[
jj
]
);
RealOpenMM
combinedEpsilon
=
(
this
->*
_combineEpsilons
)(
epsilonI
,
epsilons
[
jj
]
);
Vec3
force
;
energy
+=
calculatePairIxn
(
combin
d
edSigma
,
combin
d
edEpsilon
,
energy
+=
calculatePairIxn
(
combinedSigma
,
combinedEpsilon
,
reducedPositions
[
ii
],
reducedPositions
[
jj
],
force
);
...
...
@@ -384,11 +379,11 @@ RealOpenMM AmoebaReferenceVdwForce::calculateForceAndEnergy( int numParticles,
int
siteI
=
pair
.
first
;
int
siteJ
=
pair
.
second
;
RealOpenMM
combin
d
edSigma
=
(
this
->*
_combineSigmas
)(
sigmas
[
siteI
],
sigmas
[
siteJ
]
);
RealOpenMM
combin
d
edEpsilon
=
(
this
->*
_combineEpsilons
)(
epsilons
[
siteI
],
epsilons
[
siteJ
]
);
RealOpenMM
combinedSigma
=
(
this
->*
_combineSigmas
)(
sigmas
[
siteI
],
sigmas
[
siteJ
]
);
RealOpenMM
combinedEpsilon
=
(
this
->*
_combineEpsilons
)(
epsilons
[
siteI
],
epsilons
[
siteJ
]
);
Vec3
force
;
energy
+=
calculatePairIxn
(
combin
d
edSigma
,
combin
d
edEpsilon
,
energy
+=
calculatePairIxn
(
combinedSigma
,
combinedEpsilon
,
reducedPositions
[
siteI
],
reducedPositions
[
siteJ
],
force
);
if
(
indexIVs
[
siteI
]
==
siteI
){
...
...
plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.h
View file @
46f7b76b
...
...
@@ -31,7 +31,7 @@
#include <string>
#include <vector>
using
namespace
OpenMM
;
namespace
OpenMM
{
class
AmoebaReferenceVdwForce
;
typedef
RealOpenMM
(
AmoebaReferenceVdwForce
::*
CombiningFunction
)(
RealOpenMM
x
,
RealOpenMM
y
)
const
;
...
...
@@ -174,21 +174,11 @@ public:
Set box dimensions
@param
box box dimensions
@param
vectors the vectors defining the periodic box
--------------------------------------------------------------------------------------- */
void
setPeriodicBox
(
const
RealVec
&
box
);
/**---------------------------------------------------------------------------------------
Get box dimensions
@return box dimensions
--------------------------------------------------------------------------------------- */
RealVec
getPeriodicBox
(
void
)
const
;
void
setPeriodicBox
(
OpenMM
::
RealVec
*
vectors
);
/**---------------------------------------------------------------------------------------
...
...
@@ -253,7 +243,7 @@ private:
double
_taperCutoffFactor
;
double
_taperCutoff
;
RealOpenMM
_taperCoefficients
[
3
];
RealVec
_periodicBox
Dimensions
;
RealVec
_periodicBox
Vectors
[
3
]
;
CombiningFunction
_combineSigmas
;
RealOpenMM
arithmeticSigmaCombiningRule
(
RealOpenMM
sigmaI
,
RealOpenMM
sigmaJ
)
const
;
RealOpenMM
geometricSigmaCombiningRule
(
RealOpenMM
sigmaI
,
RealOpenMM
sigmaJ
)
const
;
...
...
@@ -333,6 +323,7 @@ private:
};
}
// ---------------------------------------------------------------------------------------
#endif // _AmoebaReferenceVdwForce___
plugins/amoeba/platforms/reference/tests/TestReferenceAmoebaVdwForce.cpp
View file @
46f7b76b
...
...
@@ -39,6 +39,7 @@
#include "openmm/System.h"
#include "openmm/AmoebaVdwForce.h"
#include "openmm/LangevinIntegrator.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
#include <stdlib.h>
...
...
@@ -50,6 +51,7 @@
using
namespace
OpenMM
;
using
namespace
std
;
extern
"C"
OPENMM_EXPORT
void
registerAmoebaReferenceKernelFactories
();
...
...
@@ -1981,6 +1983,62 @@ void testVdwWater( int includeVdwDispersionCorrection, FILE* log ) {
}
}
void
testTriclinic
()
{
System
system
;
system
.
addParticle
(
1.0
);
system
.
addParticle
(
1.0
);
Vec3
a
(
3.1
,
0
,
0
);
Vec3
b
(
0.4
,
3.5
,
0
);
Vec3
c
(
-
0.1
,
-
0.5
,
4.0
);
system
.
setDefaultPeriodicBoxVectors
(
a
,
b
,
c
);
LangevinIntegrator
integrator
(
0.0
,
0.1
,
0.01
);
AmoebaVdwForce
*
vdw
=
new
AmoebaVdwForce
();
vdw
->
setUseDispersionCorrection
(
false
);
vdw
->
addParticle
(
0
,
0.5
,
1.0
,
0.0
);
vdw
->
addParticle
(
1
,
0.5
,
1.0
,
0.0
);
vdw
->
setNonbondedMethod
(
AmoebaVdwForce
::
CutoffPeriodic
);
const
double
cutoff
=
1.5
;
vdw
->
setCutoff
(
cutoff
);
system
.
addForce
(
vdw
);
Context
context
(
system
,
integrator
,
Platform
::
getPlatformByName
(
"Reference"
));
vector
<
Vec3
>
positions
(
2
);
OpenMM_SFMT
::
SFMT
sfmt
;
init_gen_rand
(
0
,
sfmt
);
for
(
int
iteration
=
0
;
iteration
<
50
;
iteration
++
)
{
// Generate random positions for the two particles.
positions
[
0
]
=
a
*
genrand_real2
(
sfmt
)
+
b
*
genrand_real2
(
sfmt
)
+
c
*
genrand_real2
(
sfmt
);
positions
[
1
]
=
a
*
genrand_real2
(
sfmt
)
+
b
*
genrand_real2
(
sfmt
)
+
c
*
genrand_real2
(
sfmt
);
context
.
setPositions
(
positions
);
// Loop over all possible periodic copies and find the nearest one.
Vec3
delta
;
double
distance2
=
100.0
;
for
(
int
i
=
-
1
;
i
<
2
;
i
++
)
for
(
int
j
=
-
1
;
j
<
2
;
j
++
)
for
(
int
k
=
-
1
;
k
<
2
;
k
++
)
{
Vec3
d
=
positions
[
1
]
-
positions
[
0
]
+
a
*
i
+
b
*
j
+
c
*
k
;
if
(
d
.
dot
(
d
)
<
distance2
)
{
delta
=
d
;
distance2
=
d
.
dot
(
d
);
}
}
double
distance
=
sqrt
(
distance2
);
// See if the energy is correct.
State
state
=
context
.
getState
(
State
::
Energy
);
if
(
distance
>=
cutoff
)
{
ASSERT_EQUAL
(
0.0
,
state
.
getPotentialEnergy
());
}
else
if
(
distance
<
0.9
*
cutoff
)
{
const
double
energy
=
pow
(
1.07
/
(
distance
+
0.07
),
7.0
)
*
(
1.12
/
(
pow
(
distance
,
7.0
)
+
0.12
)
-
2
);
ASSERT_EQUAL_TOL
(
energy
,
state
.
getPotentialEnergy
(),
TOL
);
}
}
}
int
main
(
int
numberOfArguments
,
char
*
argv
[]
)
{
try
{
...
...
@@ -2029,6 +2087,10 @@ int main( int numberOfArguments, char* argv[] ) {
includeVdwDispersionCorrection
=
1
;
testVdwWater
(
includeVdwDispersionCorrection
,
log
);
// test triclinic boxes
testTriclinic
();
}
catch
(
const
std
::
exception
&
e
)
{
std
::
cout
<<
"exception: "
<<
e
.
what
()
<<
std
::
endl
;
...
...
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