septernion: Changed definition of the forward transformation for consistency with spatialTransform

inline Foam::vector Foam::septernion::transformPoint(const vector& v) const
{
    return r().transform(v - t());
}

Now there is a 1:1 correspondence between septernion and
spatialTransform and a septernion constructor from spatialTransform
provided.

Additionally "septernion::transform" has been renamed
"septernion::transformPoint" to clarify that it transforms coordinate
points rather than displacements or other relative vectors.
This commit is contained in:
Henry Weller
2016-04-15 11:27:18 +01:00
parent b66675ed58
commit 23a8779379
20 changed files with 108 additions and 83 deletions

View File

@ -39,10 +39,10 @@ using namespace Foam;
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
quaternion q(vector(1, 0, 0), 0.7853981); quaternion q(vector(1, 2, 3), 0.7853981);
Info<< "q " << q << endl; Info<< "q " << q << endl;
vector v(0, 1, 0); vector v(0.1, 0.4, 2.1);
Info<< "v " << v << endl; Info<< "v " << v << endl;
Info<< "inv(q)*q " << inv(q)*q << endl; Info<< "inv(q)*q " << inv(q)*q << endl;
@ -50,8 +50,11 @@ int main(int argc, char *argv[])
Info<< "q*quaternion(0, v)*conjugate(q) " Info<< "q*quaternion(0, v)*conjugate(q) "
<< q*quaternion(0, v)*conjugate(q) << endl; << q*quaternion(0, v)*conjugate(q) << endl;
Info<< "q.R() " << q.R() << endl;
Info<< "q.transform(v) " << q.transform(v) << endl; Info<< "q.transform(v) " << q.transform(v) << endl;
Info<< "q.R() & v " << (q.R() & v) << endl; Info<< "q.R() & v " << (q.R() & v) << endl;
Info<< "quaternion(q.R()).transform(v) "
<< (quaternion(q.R()).transform(v)) << endl;
Info<< "q.invTransform(v) " << q.invTransform(v) << endl; Info<< "q.invTransform(v) " << q.invTransform(v) << endl;
@ -60,13 +63,16 @@ int main(int argc, char *argv[])
Info<< "inv(tr)*tr " << inv(tr)*tr << endl; Info<< "inv(tr)*tr " << inv(tr)*tr << endl;
Info<< "tr.transform(v) " << tr.transform(v) << endl; Info<< "tr.transform(v) " << tr.transformPoint(v) << endl;
Info<< "(septernion(vector(0, -1, 0))*q*septernion(vector(0, 1, 0)))" vector origin(1, 2, 4);
Info<< "(septernion(-origin)*q*septernion(origin))"
<< ".transform(v) " << ".transform(v) "
<< (septernion(vector(0, -1, 0)) << (septernion(-origin)*q*septernion(origin)).transformPoint(v)
*q << " "
*septernion(vector(0, 1, 0))).transform(v) << septernion(-origin)
.transformPoint(q.transform(septernion(origin).transformPoint(v)))
<< endl; << endl;
Info<< "Test conversion from and to Euler-angles" << endl; Info<< "Test conversion from and to Euler-angles" << endl;

View File

@ -66,7 +66,7 @@ Foam::tmp<Foam::vectorField> Foam::transform
} }
void Foam::transform void Foam::transformPoints
( (
vectorField& rtf, vectorField& rtf,
const septernion& tr, const septernion& tr,
@ -75,50 +75,44 @@ void Foam::transform
{ {
vector T = tr.t(); vector T = tr.t();
// Check if any rotation // Check if any translation
if (mag(tr.r().R() - I) > SMALL) if (mag(T) > VSMALL)
{ {
transform(rtf, tr.r(), tf); TFOR_ALL_F_OP_F_OP_S(vector, rtf, =, vector, tf, -, vector, T);
if (mag(T) > VSMALL)
{
rtf += T;
}
} }
else else
{ {
if (mag(T) > VSMALL) rtf = tf;
{ }
TFOR_ALL_F_OP_S_OP_F(vector, rtf, =, vector, T, +, vector, tf);
} // Check if any rotation
else if (mag(tr.r().R() - I) > SMALL)
{ {
rtf = tf; transform(rtf, tr.r(), rtf);
}
} }
} }
Foam::tmp<Foam::vectorField> Foam::transform Foam::tmp<Foam::vectorField> Foam::transformPoints
( (
const septernion& tr, const septernion& tr,
const vectorField& tf const vectorField& tf
) )
{ {
tmp<vectorField > tranf(new vectorField(tf.size())); tmp<vectorField > tranf(new vectorField(tf.size()));
transform(tranf.ref(), tr, tf); transformPoints(tranf.ref(), tr, tf);
return tranf; return tranf;
} }
Foam::tmp<Foam::vectorField> Foam::transform Foam::tmp<Foam::vectorField> Foam::transformPoints
( (
const septernion& tr, const septernion& tr,
const tmp<vectorField>& ttf const tmp<vectorField>& ttf
) )
{ {
tmp<vectorField > tranf = New(ttf); tmp<vectorField > tranf = New(ttf);
transform(tranf.ref(), tr, ttf()); transformPoints(tranf.ref(), tr, ttf());
ttf.clear(); ttf.clear();
return tranf; return tranf;
} }

View File

@ -117,14 +117,14 @@ tmp<vectorField> transform(const quaternion&, const vectorField&);
tmp<vectorField> transform(const quaternion&, const tmp<vectorField>&); tmp<vectorField> transform(const quaternion&, const tmp<vectorField>&);
//- Transform given vectorField with the given septernion //- Transform given vectorField of coordinates with the given septernion
void transform(vectorField&, const septernion&, const vectorField&); void transformPoints(vectorField&, const septernion&, const vectorField&);
//- Transform given vectorField with the given septernion //- Transform given vectorField of coordinates with the given septernion
tmp<vectorField> transform(const septernion&, const vectorField&); tmp<vectorField> transformPoints(const septernion&, const vectorField&);
//- Transform given tmp<vectorField> with the given septernion //- Transform given tmp<vectorField> of coordinates with the given septernion
tmp<vectorField> transform(const septernion&, const tmp<vectorField>&); tmp<vectorField> transformPoints(const septernion&, const tmp<vectorField>&);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -66,7 +66,7 @@ Foam::septernion Foam::slerp
const scalar t const scalar t
) )
{ {
return septernion((1.0-t)*qa.t()+t*qb.t(), slerp(qa.r(), qb.r(), t)); return septernion((1 - t)*qa.t() + t*qb.t(), slerp(qa.r(), qb.r(), t));
} }

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -43,6 +43,7 @@ SourceFiles
#include "vector.H" #include "vector.H"
#include "quaternion.H" #include "quaternion.H"
#include "spatialTransform.H"
#include "word.H" #include "word.H"
#include "contiguous.H" #include "contiguous.H"
@ -97,6 +98,9 @@ public:
//- Construct a pure rotation septernion given a rotation quaternion //- Construct a pure rotation septernion given a rotation quaternion
inline explicit septernion(const quaternion& r); inline explicit septernion(const quaternion& r);
//- Construct a general septernion from the given spatialTransform
inline explicit septernion(const spatialTransform& st);
//- Construct from Istream //- Construct from Istream
septernion(Istream&); septernion(Istream&);
@ -117,11 +121,11 @@ public:
// Transform // Transform
//- Transform the given vector //- Transform the given coordinate point
inline vector transform(const vector& v) const; inline vector transformPoint(const vector& v) const;
//- Inverse Transform the given vector //- Inverse Transform the given coordinate point
inline vector invTransform(const vector& v) const; inline vector invTransformPoint(const vector& v) const;
// Member operators // Member operators
@ -153,7 +157,6 @@ public:
//- Return the inverse of the given septernion //- Return the inverse of the given septernion
inline septernion inv(const septernion& tr); inline septernion inv(const septernion& tr);
//- Return a string representation of a septernion //- Return a string representation of a septernion
word name(const septernion&); word name(const septernion&);

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -42,10 +42,16 @@ inline Foam::septernion::septernion(const vector& t)
inline Foam::septernion::septernion(const quaternion& r) inline Foam::septernion::septernion(const quaternion& r)
: :
t_(vector::zero), t_(Zero),
r_(r) r_(r)
{} {}
inline Foam::septernion::septernion(const spatialTransform& st)
:
t_(st.r()),
r_(st.E())
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
@ -73,15 +79,15 @@ inline Foam::quaternion& Foam::septernion::r()
} }
inline Foam::vector Foam::septernion::transform(const vector& v) const inline Foam::vector Foam::septernion::transformPoint(const vector& v) const
{ {
return t() + r().transform(v); return r().transform(v - t());
} }
inline Foam::vector Foam::septernion::invTransform(const vector& v) const inline Foam::vector Foam::septernion::invTransformPoint(const vector& v) const
{ {
return r().invTransform(v - t()); return t() + r().invTransform(v);
} }
@ -95,7 +101,7 @@ inline void Foam::septernion::operator=(const septernion& tr)
inline void Foam::septernion::operator*=(const septernion& tr) inline void Foam::septernion::operator*=(const septernion& tr)
{ {
t_ += r().transform(tr.t()); t_ = tr.t() + tr.r().invTransform(t_);
r_ *= tr.r(); r_ *= tr.r();
} }
@ -103,6 +109,7 @@ inline void Foam::septernion::operator*=(const septernion& tr)
inline void Foam::septernion::operator=(const vector& t) inline void Foam::septernion::operator=(const vector& t)
{ {
t_ = t; t_ = t;
r_ = quaternion::I;
} }
inline void Foam::septernion::operator+=(const vector& t) inline void Foam::septernion::operator+=(const vector& t)
@ -118,16 +125,19 @@ inline void Foam::septernion::operator-=(const vector& t)
inline void Foam::septernion::operator=(const quaternion& r) inline void Foam::septernion::operator=(const quaternion& r)
{ {
t_ = Zero;
r_ = r; r_ = r;
} }
inline void Foam::septernion::operator*=(const quaternion& r) inline void Foam::septernion::operator*=(const quaternion& r)
{ {
t_ = r.invTransform(t_);
r_ *= r; r_ *= r;
} }
inline void Foam::septernion::operator/=(const quaternion& r) inline void Foam::septernion::operator/=(const quaternion& r)
{ {
t_ = r.transform(t_);
r_ /= r; r_ /= r;
} }
@ -149,7 +159,7 @@ inline void Foam::septernion::operator/=(const scalar s)
inline Foam::septernion Foam::inv(const septernion& tr) inline Foam::septernion Foam::inv(const septernion& tr)
{ {
return septernion(-tr.r().invTransform(tr.t()), conjugate(tr.r())); return septernion(-tr.r().transform(tr.t()), conjugate(tr.r()));
} }
@ -213,7 +223,7 @@ inline Foam::septernion Foam::operator*
const quaternion& r const quaternion& r
) )
{ {
return septernion(tr.t(), tr.r()*r); return septernion(r.invTransform(tr.t()), tr.r()*r);
} }
@ -223,7 +233,7 @@ inline Foam::septernion Foam::operator/
const quaternion& r const quaternion& r
) )
{ {
return septernion(tr.t(), tr.r()/r); return septernion(r.transform(tr.t()), tr.r()/r);
} }
@ -235,7 +245,7 @@ inline Foam::septernion Foam::operator*
{ {
return septernion return septernion
( (
tr1.t() + tr1.r().transform(tr2.t()), tr2.r().invTransform(tr1.t()) + tr2.t(),
tr1.r().transform(tr2.r()) tr1.r().transform(tr2.r())
); );
} }

View File

@ -178,6 +178,10 @@ public:
// X.v = (E . vw, E . (vl - r^vw)) // X.v = (E . vw, E . (vl - r^vw))
inline spatialVector operator&(const spatialVector& v) const; inline spatialVector operator&(const spatialVector& v) const;
//- Transform position p
// X:p = E . (pl - r)
inline vector transformPoint(const vector& p) const;
//- Transform position p //- Transform position p
// X:p = (E . pw, E . (vl - r)) // X:p = (E . pw, E . (vl - r))
inline spatialVector operator&&(const spatialVector& v) const; inline spatialVector operator&&(const spatialVector& v) const;

View File

@ -128,7 +128,7 @@ inline Foam::spatialTransform::operator spatialTensor() const
inline void Foam::spatialTransform::operator&=(const spatialTransform& X) inline void Foam::spatialTransform::operator&=(const spatialTransform& X)
{ {
E_ &= X.E_; E_ &= X.E_;
r_ = X.r_ + (r_ & X.E_.T()); r_ = X.r_ + (r_ & X.E_);
} }
@ -167,6 +167,15 @@ inline Foam::spatialVector Foam::spatialTransform::operator&&
} }
inline Foam::vector Foam::spatialTransform::transformPoint
(
const vector& p
) const
{
return E_ & (p - r_);
}
inline Foam::spatialTransform::transpose::operator spatialTensor() const inline Foam::spatialTransform::transpose::operator spatialTensor() const
{ {
return spatialTensor return spatialTensor

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -185,7 +185,7 @@ bool Foam::multiSolidBodyMotionFvMesh::update()
const labelList& zonePoints = pointIDs_[i]; const labelList& zonePoints = pointIDs_[i];
UIndirectList<point>(transformedPts, zonePoints) = UIndirectList<point>(transformedPts, zonePoints) =
transform transformPoints
( (
SBMFs_[i].transformation(), SBMFs_[i].transformation(),
pointField(transformedPts, zonePoints) pointField(transformedPts, zonePoints)

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2013 OpenFOAM Foundation \\ / A nd | Copyright (C) 2013-2016 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -63,7 +63,7 @@ solidBodyMotionDisplacementPointPatchVectorField
// Determine current local points and offset // Determine current local points and offset
fixedValuePointPatchVectorField::operator== fixedValuePointPatchVectorField::operator==
( (
transform(SBMFPtr_().transformation(), localPoints0()) transformPoints(SBMFPtr_().transformation(), localPoints0())
-localPoints0() -localPoints0()
); );
} }
@ -86,7 +86,7 @@ solidBodyMotionDisplacementPointPatchVectorField
fixedValuePointPatchVectorField::operator== fixedValuePointPatchVectorField::operator==
( (
transform(SBMFPtr_().transformation(), localPoints0()) transformPoints(SBMFPtr_().transformation(), localPoints0())
-localPoints0() -localPoints0()
); );
} }
@ -117,7 +117,7 @@ solidBodyMotionDisplacementPointPatchVectorField
fixedValuePointPatchVectorField::operator== fixedValuePointPatchVectorField::operator==
( (
transform(SBMFPtr_().transformation(), localPoints0()) transformPoints(SBMFPtr_().transformation(), localPoints0())
-localPoints0() -localPoints0()
); );
} }
@ -160,7 +160,7 @@ void solidBodyMotionDisplacementPointPatchVectorField::updateCoeffs()
// Determine current local points and offset // Determine current local points and offset
fixedValuePointPatchVectorField::operator== fixedValuePointPatchVectorField::operator==
( (
transform(SBMFPtr_().transformation(), localPoints0()) transformPoints(SBMFPtr_().transformation(), localPoints0())
-localPoints0() -localPoints0()
); );

View File

@ -91,9 +91,9 @@ Foam::septernion Foam::solidBodyMotionFunctions::SDA::transformation() const
heaveA_*(sin(wr*time + phh) - sin(phh)) heaveA_*(sin(wr*time + phh) - sin(phh))
); );
quaternion R(quaternion::XYZ, vector(rollA*sin(wr*time + phr), 0, 0)); quaternion R(quaternion::XYZ, vector(rollA*sin(wr*time + phr), 0, 0));
septernion TR(septernion(CofG_ + T)*R*septernion(-CofG_)); septernion TR(septernion(-CofG_ - T)*R*septernion(CofG_));
InfoInFunction << "Time = " << time << " transformation: " << TR << endl; DebugInFunction << "Time = " << time << " transformation: " << TR << endl;
return TR; return TR;
} }

View File

@ -83,9 +83,9 @@ Foam::solidBodyMotionFunctions::axisRotationMotion::transformation() const
scalar magOmega = mag(omega); scalar magOmega = mag(omega);
quaternion R(omega/magOmega, magOmega); quaternion R(omega/magOmega, magOmega);
septernion TR(septernion(origin_)*R*septernion(-origin_)); septernion TR(septernion(-origin_)*R*septernion(origin_));
InfoInFunction << "Time = " << t << " transformation: " << TR << endl; DebugInFunction << "Time = " << t << " transformation: " << TR << endl;
return TR; return TR;
} }

View File

@ -74,9 +74,9 @@ Foam::solidBodyMotionFunctions::linearMotion::transformation() const
const vector displacement = velocity_*t; const vector displacement = velocity_*t;
quaternion R(1); quaternion R(1);
septernion TR(septernion(displacement)*R); septernion TR(septernion(-displacement)*R);
InfoInFunction << "Time = " << t << " transformation: " << TR << endl; DebugInFunction << "Time = " << t << " transformation: " << TR << endl;
return TR; return TR;
} }

View File

@ -77,7 +77,7 @@ Foam::solidBodyMotionFunctions::multiMotion::transformation() const
TR *= SBMFs_[i].transformation(); TR *= SBMFs_[i].transformation();
} }
InfoInFunction << "Time = " << t << " transformation: " << TR << endl; DebugInFunction << "Time = " << t << " transformation: " << TR << endl;
return TR; return TR;
} }

View File

@ -74,9 +74,9 @@ Foam::solidBodyMotionFunctions::oscillatingLinearMotion::transformation() const
const vector displacement = amplitude_*sin(omega_*t); const vector displacement = amplitude_*sin(omega_*t);
quaternion R(1); quaternion R(1);
septernion TR(septernion(displacement)*R); septernion TR(septernion(-displacement)*R);
InfoInFunction << "Time = " << t << " transformation: " << TR << endl; DebugInFunction << "Time = " << t << " transformation: " << TR << endl;
return TR; return TR;
} }

View File

@ -82,9 +82,9 @@ transformation() const
eulerAngles *= pi/180.0; eulerAngles *= pi/180.0;
quaternion R(quaternion::XYZ, eulerAngles); quaternion R(quaternion::XYZ, eulerAngles);
septernion TR(septernion(origin_)*R*septernion(-origin_)); septernion TR(septernion(-origin_)*R*septernion(origin_));
InfoInFunction << "Time = " << t << " transformation: " << TR << endl; DebugInFunction << "Time = " << t << " transformation: " << TR << endl;
return TR; return TR;
} }

View File

@ -78,9 +78,9 @@ Foam::solidBodyMotionFunctions::rotatingMotion::transformation() const
scalar angle = omega_->integrate(0, t); scalar angle = omega_->integrate(0, t);
quaternion R(axis_, angle); quaternion R(axis_, angle);
septernion TR(septernion(origin_)*R*septernion(-origin_)); septernion TR(septernion(-origin_)*R*septernion(origin_));
InfoInFunction << "Time = " << t << " transformation: " << TR << endl; DebugInFunction << "Time = " << t << " transformation: " << TR << endl;
return TR; return TR;
} }

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -29,9 +29,8 @@ License
namespace Foam namespace Foam
{ {
defineTypeNameAndDebug(solidBodyMotionFunction, 0); defineTypeNameAndDebug(solidBodyMotionFunction, 0);
defineRunTimeSelectionTable(solidBodyMotionFunction, dictionary);
defineRunTimeSelectionTable(solidBodyMotionFunction, dictionary);
} }

View File

@ -105,9 +105,9 @@ Foam::solidBodyMotionFunctions::tabulated6DoFMotion::transformation() const
TRV[1] *= pi/180.0; TRV[1] *= pi/180.0;
quaternion R(quaternion::XYZ, TRV[1]); quaternion R(quaternion::XYZ, TRV[1]);
septernion TR(septernion(CofG_ + TRV[0])*R*septernion(-CofG_)); septernion TR(septernion(-CofG_ + -TRV[0])*R*septernion(CofG_));
InfoInFunction << "Time = " << t << " transformation: " << TR << endl; DebugInFunction << "Time = " << t << " transformation: " << TR << endl;
return TR; return TR;
} }

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -193,7 +193,7 @@ bool Foam::solidBodyMotionFvMesh::update()
{ {
fvMesh::movePoints fvMesh::movePoints
( (
transform transformPoints
( (
SBMFPtr_().transformation(), SBMFPtr_().transformation(),
undisplacedPoints_ undisplacedPoints_
@ -205,7 +205,7 @@ bool Foam::solidBodyMotionFvMesh::update()
pointField transformedPts(undisplacedPoints_); pointField transformedPts(undisplacedPoints_);
UIndirectList<point>(transformedPts, pointIDs_) = UIndirectList<point>(transformedPts, pointIDs_) =
transform transformPoints
( (
SBMFPtr_().transformation(), SBMFPtr_().transformation(),
pointField(transformedPts, pointIDs_) pointField(transformedPts, pointIDs_)