diff --git a/Framework/DataHandling/inc/MantidDataHandling/SetSample.h b/Framework/DataHandling/inc/MantidDataHandling/SetSample.h
index cafbf459fffb8ffec00a93504c4229b9f4135918..6769bd955b1d072b382907e536ddbc38b0c71856 100644
--- a/Framework/DataHandling/inc/MantidDataHandling/SetSample.h
+++ b/Framework/DataHandling/inc/MantidDataHandling/SetSample.h
@@ -7,6 +7,7 @@
 
 namespace Mantid {
 namespace Geometry {
+class ReferenceFrame;
 class SampleEnvironment;
 }
 namespace DataHandling {
@@ -49,13 +50,18 @@ private:
 
   const Geometry::SampleEnvironment *
   setSampleEnvironment(API::MatrixWorkspace_sptr &workspace,
-                       const Kernel::PropertyManager &args);
+                       const Kernel::PropertyManager_const_sptr &args);
   void setSampleShape(API::MatrixWorkspace_sptr &workspace,
-                      const Kernel::PropertyManager_sptr &args,
+                      const Kernel::PropertyManager_const_sptr &args,
                       const Geometry::SampleEnvironment *sampleEnv);
-  std::string tryCreateXMLFromArgsOnly(const Kernel::PropertyManager_sptr args);
-  std::string createFlatPlateXML(const Kernel::PropertyManager &args) const;
+  std::string
+  tryCreateXMLFromArgsOnly(const Kernel::PropertyManager &args,
+                           const Geometry::ReferenceFrame &refFrame);
+  std::string
+  createFlatPlateXML(const Kernel::PropertyManager &args,
+                     const Geometry::ReferenceFrame &refFrame) const;
   std::string createCylinderLikeXML(const Kernel::PropertyManager &args,
+                                    const Geometry::ReferenceFrame &refFrame,
                                     bool hollow) const;
 
   void runSetSampleShape(API::MatrixWorkspace_sptr &workspace,
diff --git a/Framework/DataHandling/src/SetSample.cpp b/Framework/DataHandling/src/SetSample.cpp
index 3fc2e9c80c9b9c4312c887bae87be1ed96514e20..38e268798773c3250b6504c01d4ab888e363ef86 100644
--- a/Framework/DataHandling/src/SetSample.cpp
+++ b/Framework/DataHandling/src/SetSample.cpp
@@ -3,12 +3,15 @@
 #include "MantidAPI/MatrixWorkspace.h"
 #include "MantidAPI/Sample.h"
 #include "MantidGeometry/Instrument.h"
+#include "MantidGeometry/Instrument/Goniometer.h"
+#include "MantidGeometry/Instrument/ReferenceFrame.h"
 #include "MantidGeometry/Instrument/SampleEnvironmentFactory.h"
 #include "MantidKernel/ConfigService.h"
 #include "MantidKernel/FacilityInfo.h"
 #include "MantidKernel/InstrumentInfo.h"
 #include "MantidKernel/Logger.h"
 #include "MantidKernel/Material.h"
+#include "MantidKernel/Matrix.h"
 #include "MantidKernel/PropertyManager.h"
 #include "MantidKernel/PropertyManagerProperty.h"
 
@@ -19,32 +22,63 @@
 namespace Mantid {
 namespace DataHandling {
 
+using Geometry::Goniometer;
+using Geometry::ReferenceFrame;
 using Geometry::SampleEnvironment;
 using Kernel::Logger;
+using Kernel::PropertyWithValue;
+using Kernel::Quat;
 using Kernel::V3D;
 
 namespace {
+/// Private namespace storing property name strings
+namespace PropertyNames {
+/// Input workspace property name
+const std::string INPUT_WORKSPACE("InputWorkspace");
+/// Geometry property name
+const std::string GEOMETRY("Geometry");
+/// Material property name
+const std::string MATERIAL("Material");
+/// Environment property name
+const std::string ENVIRONMENT("Environment");
+}
+/// Private namespace storing sample environment args
+namespace SEArgs {
+/// Static Name string
+const std::string NAME("Name");
+/// Static Container string
+const std::string CONTAINER("Container");
+}
+/// Provate namespace storing geometry args
+namespace GeometryArgs {
+/// Static Shape string
+const std::string SHAPE("Shape");
+}
 
-/**
- * Retrieve "Axis" property value. The most commmon use case is calling
- * this algorithm from Python where Axis is input as a C long. The
- * definition of long varies across platforms and long v = args.getProperty()
- * is currently unable to cope so we go via the long route to retrieve
- * the value.
- * @param args Dictionary-type containing the argument
- */
-int64_t getAxisIndex(const Kernel::PropertyManager &args) {
-  using Kernel::Property;
-  using Kernel::PropertyWithValue;
-  int64_t axisIdx(1);
-  if (args.existsProperty("Axis")) {
-    Kernel::Property *axisProp = args.getProperty("Axis");
-    axisIdx = static_cast<PropertyWithValue<int64_t> *>(axisProp)->operator()();
-    if (axisIdx < 0 || axisIdx > 2)
-      throw std::invalid_argument(
-          "Geometry.Axis value must be either 0,1,2 (X,Y,Z)");
-  }
-  return axisIdx;
+/// Private namespace storing sample environment args
+namespace ShapeArgs {
+/// Static FlatPlate string
+const std::string FLAT_PLATE("FlatPlate");
+/// Static Cylinder string
+const std::string CYLINDER("Cylinder");
+/// Static HollowCylinder string
+const std::string HOLLOW_CYLINDER("HollowCylinder");
+/// Static CSG string
+const std::string CSG("CSG");
+/// Static Width string
+const std::string WIDTH("Width");
+/// Static Height string
+const std::string HEIGHT("Height");
+/// Static Thick string
+const std::string THICK("Thick");
+/// Static Center string
+const std::string CENTER("Center");
+/// Static Radius string
+const std::string RADIUS("Radius");
+/// Static InnerRadius string
+const std::string INNER_RADIUS("InnerRadius");
+/// Static OuterRadius string
+const std::string OUTER_RADIUS("OuterRadius");
 }
 
 /**
@@ -55,7 +89,7 @@ int64_t getAxisIndex(const Kernel::PropertyManager &args) {
   * @param axis The index of the height-axis of the cylinder
   */
 V3D cylBaseCentre(const std::vector<double> &cylCentre, double height,
-                  int64_t axisIdx) {
+                  unsigned axisIdx) {
   const V3D halfHeight = [&]() {
     switch (axisIdx) {
     case 0:
@@ -76,7 +110,7 @@ V3D cylBaseCentre(const std::vector<double> &cylCentre, double height,
  * @param axisIdx Index 0,1,2 for the axis of a cylinder
  * @return A string containing the axis tag for this index
  */
-std::string axisXML(int64_t axisIdx) {
+std::string axisXML(unsigned axisIdx) {
   switch (axisIdx) {
   case 0:
     return "<axis x=\"1\" y=\"0\" z=\"0\" />";
@@ -109,28 +143,58 @@ const std::string SetSample::summary() const {
 
 /// Validate the inputs against each other @see Algorithm::validateInputs
 std::map<std::string, std::string> SetSample::validateInputs() {
-  using Kernel::PropertyManager_sptr;
+  using Kernel::PropertyManager;
+  using Kernel::PropertyManager_const_sptr;
   std::map<std::string, std::string> errors;
 
+  auto existsAndNotEmptyString =
+      [](const PropertyManager &pm, const std::string &name) {
+        if (pm.existsProperty(name)) {
+          const auto value = pm.getPropertyValue(name);
+          return !value.empty();
+        }
+        return false;
+      };
+
+  auto existsAndNegative =
+      [](const PropertyManager &pm, const std::string &name) {
+        if (pm.existsProperty(name)) {
+          const double value = pm.getProperty(name);
+          if (value < 0.0) {
+            return true;
+          }
+        }
+        return false;
+      };
+
   // Validate Environment
-  PropertyManager_sptr environArgs = getProperty("Environment");
+  const PropertyManager_const_sptr environArgs =
+      getProperty(PropertyNames::ENVIRONMENT);
   if (environArgs) {
-    if (!environArgs->existsProperty("Name")) {
-      errors["Environment"] = "Environment flags must contain a 'Name' entry.";
-    } else {
-      std::string name = environArgs->getPropertyValue("Name");
-      if (name.empty()) {
-        errors["Environment"] = "Environment 'Name' flag is an empty string!";
-      }
+    if (!existsAndNotEmptyString(*environArgs, SEArgs::NAME)) {
+      errors[PropertyNames::ENVIRONMENT] =
+          "Environment flags require a non-empty 'Name' entry.";
     }
 
-    if (!environArgs->existsProperty("Container")) {
-      errors["Environment"] =
-          "Environment flags must contain a 'Container' entry.";
-    } else {
-      std::string name = environArgs->getPropertyValue("Container");
-      if (name.empty()) {
-        errors["Environment"] = "Environment 'Can' flag is an empty string!";
+    if (!existsAndNotEmptyString(*environArgs, SEArgs::CONTAINER)) {
+      errors[PropertyNames::ENVIRONMENT] =
+          "Environment flags require a non-empty 'Container' entry.";
+    }
+  }
+
+  // Validate as much of the shape information as possible
+  const PropertyManager_const_sptr geomArgs =
+      getProperty(PropertyNames::GEOMETRY);
+  if (geomArgs) {
+    if (existsAndNotEmptyString(*geomArgs, GeometryArgs::SHAPE)) {
+      const std::array<const std::string *, 6> positiveValues = {
+          {&ShapeArgs::HEIGHT, &ShapeArgs::WIDTH, &ShapeArgs::THICK,
+           &ShapeArgs::RADIUS, &ShapeArgs::INNER_RADIUS,
+           &ShapeArgs::OUTER_RADIUS}};
+      for (const auto &arg : positiveValues) {
+        if (existsAndNegative(*geomArgs, *arg)) {
+          errors[PropertyNames::GEOMETRY] = *arg + " argument < 0.0";
+        }
       }
     }
   }
@@ -147,18 +211,18 @@ void SetSample::init() {
   using Kernel::PropertyManagerProperty;
 
   // Inputs
-  declareProperty(Kernel::make_unique<WorkspaceProperty<>>("InputWorkspace", "",
-                                                           Direction::InOut),
+  declareProperty(Kernel::make_unique<WorkspaceProperty<>>(
+                      PropertyNames::INPUT_WORKSPACE, "", Direction::InOut),
                   "A workspace whose sample properties will be updated");
   declareProperty(Kernel::make_unique<PropertyManagerProperty>(
-                      "Geometry", Direction::Input),
+                      PropertyNames::GEOMETRY, Direction::Input),
                   "A dictionary of geometry parameters for the sample.");
   declareProperty(Kernel::make_unique<PropertyManagerProperty>(
-                      "Material", Direction::Input),
+                      PropertyNames::MATERIAL, Direction::Input),
                   "A dictionary of material parameters for the sample. See "
                   "SetSampleMaterial for all accepted parameters");
   declareProperty(
-      Kernel::make_unique<PropertyManagerProperty>("Environment",
+      Kernel::make_unique<PropertyManagerProperty>(PropertyNames::ENVIRONMENT,
                                                    Direction::Input),
       "A dictionary of parameters to configure the sample environment");
 }
@@ -170,17 +234,17 @@ void SetSample::exec() {
   using API::MatrixWorkspace_sptr;
   using Kernel::PropertyManager_sptr;
 
-  MatrixWorkspace_sptr workspace = getProperty("InputWorkspace");
-  PropertyManager_sptr environArgs = getProperty("Environment");
-  PropertyManager_sptr geometryArgs = getProperty("Geometry");
-  PropertyManager_sptr materialArgs = getProperty("Material");
+  MatrixWorkspace_sptr workspace = getProperty(PropertyNames::INPUT_WORKSPACE);
+  PropertyManager_sptr environArgs = getProperty(PropertyNames::ENVIRONMENT);
+  PropertyManager_sptr geometryArgs = getProperty(PropertyNames::GEOMETRY);
+  PropertyManager_sptr materialArgs = getProperty(PropertyNames::MATERIAL);
 
   // The order here is important. Se the environment first. If this
   // defines a sample geometry then we can process the Geometry flags
   // combined with this
   const SampleEnvironment *sampleEnviron(nullptr);
   if (environArgs) {
-    sampleEnviron = setSampleEnvironment(workspace, *environArgs);
+    sampleEnviron = setSampleEnvironment(workspace, environArgs);
   }
 
   if (geometryArgs || sampleEnviron) {
@@ -199,15 +263,15 @@ void SetSample::exec() {
  * @param args The dictionary of flags for the environment
  * @return A pointer to the new sample environment
  */
-const Geometry::SampleEnvironment *
-SetSample::setSampleEnvironment(API::MatrixWorkspace_sptr &workspace,
-                                const Kernel::PropertyManager &args) {
+const Geometry::SampleEnvironment *SetSample::setSampleEnvironment(
+    API::MatrixWorkspace_sptr &workspace,
+    const Kernel::PropertyManager_const_sptr &args) {
   using Geometry::SampleEnvironmentSpecFileFinder;
   using Geometry::SampleEnvironmentFactory;
   using Kernel::ConfigService;
 
-  const std::string envName = args.getPropertyValue("Name");
-  const std::string canName = args.getPropertyValue("Container");
+  const std::string envName = args->getPropertyValue(SEArgs::NAME);
+  const std::string canName = args->getPropertyValue(SEArgs::CONTAINER);
   // The specifications need to be qualified by the facility and instrument.
   // Check instrument for name and then lookup facility if facility
   // is unknown then set to default facility & instrument.
@@ -248,7 +312,7 @@ SetSample::setSampleEnvironment(API::MatrixWorkspace_sptr &workspace,
  * @return A string containing the XML definition of the shape
  */
 void SetSample::setSampleShape(API::MatrixWorkspace_sptr &workspace,
-                               const Kernel::PropertyManager_sptr &args,
+                               const Kernel::PropertyManager_const_sptr &args,
                                const Geometry::SampleEnvironment *sampleEnv) {
   using Geometry::Container;
   /* The sample geometry can be specified in two ways:
@@ -258,10 +322,13 @@ void SetSample::setSampleShape(API::MatrixWorkspace_sptr &workspace,
   */
 
   // Try known shapes or CSG first if supplied
-  const auto xml = tryCreateXMLFromArgsOnly(args);
-  if (!xml.empty()) {
-    runSetSampleShape(workspace, xml);
-    return;
+  if (args) {
+    const auto refFrame = workspace->getInstrument()->getReferenceFrame();
+    const auto xml = tryCreateXMLFromArgsOnly(*args, *refFrame);
+    if (!xml.empty()) {
+      runSetSampleShape(workspace, xml);
+      return;
+    }
   }
   // Any arguments in the args dict are assumed to be values that should
   // override the default set by the sampleEnv samplegeometry if it exists
@@ -300,23 +367,26 @@ void SetSample::setSampleShape(API::MatrixWorkspace_sptr &workspace,
 /**
  * Create the required XML for a given shape type plus its arguments
  * @param args A dict of flags defining the shape
+ * @param refFrame Defines the reference frame for the shape
  * @return A string containing the XML if possible or an empty string
  */
 std::string
-SetSample::tryCreateXMLFromArgsOnly(const Kernel::PropertyManager_sptr args) {
+SetSample::tryCreateXMLFromArgsOnly(const Kernel::PropertyManager &args,
+                                    const Geometry::ReferenceFrame &refFrame) {
   std::string result;
-  if (!args || !args->existsProperty("Shape")) {
+  if (!args.existsProperty(GeometryArgs::SHAPE)) {
     return result;
   }
 
-  const auto shape = args->getPropertyValue("Shape");
-  if (shape == "CSG") {
-    result = args->getPropertyValue("Value");
-  } else if (shape == "FlatPlate") {
-    result = createFlatPlateXML(*args);
-  } else if (boost::algorithm::ends_with(shape, "Cylinder")) {
+  const auto shape = args.getPropertyValue(GeometryArgs::SHAPE);
+  if (shape == ShapeArgs::CSG) {
+    result = args.getPropertyValue("Value");
+  } else if (shape == ShapeArgs::FLAT_PLATE) {
+    result = createFlatPlateXML(args, refFrame);
+  } else if (boost::algorithm::ends_with(shape, ShapeArgs::CYLINDER)) {
     result = createCylinderLikeXML(
-        *args, boost::algorithm::starts_with(shape, "Hollow"));
+        args, refFrame,
+        boost::algorithm::equals(shape, ShapeArgs::HOLLOW_CYLINDER));
   } else {
     throw std::invalid_argument(
         "Unknown 'Shape' argument provided in "
@@ -332,61 +402,85 @@ SetSample::tryCreateXMLFromArgsOnly(const Kernel::PropertyManager_sptr args) {
 /**
  * Create the XML required to define a flat plate from the given args
  * @param args A user-supplied dict of args
+ * @param refFrame Defines the reference frame for the shape
  * @return The XML definition string
  */
 std::string
-SetSample::createFlatPlateXML(const Kernel::PropertyManager &args) const {
-  // X
-  double widthInCM = args.getProperty("Width");
-  // Y
-  double heightInCM = args.getProperty("Height");
-  // Z
-  double thickInCM = args.getProperty("Thick");
-  std::vector<double> center = args.getProperty("Center");
-  // convert to metres
-  std::transform(center.begin(), center.end(), center.begin(),
-                 [](double val) { return val *= 0.01; });
-
-  // Half lengths in metres (*0.01*0.5)
+SetSample::createFlatPlateXML(const Kernel::PropertyManager &args,
+                              const Geometry::ReferenceFrame &refFrame) const {
+  // Helper to take 3 coordinates and turn them to a V3D respecting the
+  // current reference frame
+  auto makeV3D = [&refFrame](double x, double y, double z) {
+    V3D v;
+    v[refFrame.pointingHorizontal()] = x;
+    v[refFrame.pointingUp()] = y;
+    v[refFrame.pointingAlongBeam()] = z;
+    return v;
+  };
+  const double widthInCM = args.getProperty(ShapeArgs::WIDTH);
+  const double heightInCM = args.getProperty(ShapeArgs::HEIGHT);
+  const double thickInCM = args.getProperty(ShapeArgs::THICK);
+  // Convert to half-"width" in metres
   const double szX = (widthInCM * 5e-3);
   const double szY = (heightInCM * 5e-3);
   const double szZ = (thickInCM * 5e-3);
+  // Contruct cuboid corners. Define points about origin, rotate and then
+  // translate to final center position
+  auto lfb = makeV3D(szX, -szY, -szZ);
+  auto lft = makeV3D(szX, szY, -szZ);
+  auto lbb = makeV3D(szX, -szY, szZ);
+  auto rfb = makeV3D(-szX, -szY, -szZ);
+  // optional rotation about the center of object
+  if (args.existsProperty("Angle")) {
+    Goniometer gr;
+    const auto upAxis = makeV3D(0, 1, 0);
+    gr.pushAxis("up", upAxis.X(), upAxis.Y(), upAxis.Z(),
+                args.getProperty("Angle"), Geometry::CCW, Geometry::angDegrees);
+    auto &rotation = gr.getR();
+    lfb.rotate(rotation);
+    lft.rotate(rotation);
+    lbb.rotate(rotation);
+    rfb.rotate(rotation);
+  }
+  std::vector<double> center = args.getProperty(ShapeArgs::CENTER);
+  const V3D centrePos(center[0] * 0.01, center[1] * 0.01, center[2] * 0.01);
+  // translate to true center after rotation
+  lfb += centrePos;
+  lft += centrePos;
+  lbb += centrePos;
+  rfb += centrePos;
 
   std::ostringstream xmlShapeStream;
   xmlShapeStream << " <cuboid id=\"sample-shape\"> "
-                 << "<left-front-bottom-point x=\"" << szX + center[0]
-                 << "\" y=\"" << -szY + center[1] << "\" z=\""
-                 << -szZ + center[2] << "\"  /> "
-                 << "<left-front-top-point  x=\"" << szX + center[0]
-                 << "\" y=\"" << szY + center[1] << "\" z=\""
-                 << -szZ + center[2] << "\"  /> "
-                 << "<left-back-bottom-point  x=\"" << szX + center[0]
-                 << "\" y=\"" << -szY + center[1] << "\" z=\""
-                 << szZ + center[2] << "\"  /> "
-                 << "<right-front-bottom-point  x=\"" << -szX + center[0]
-                 << "\" y=\"" << -szY + center[1] << "\" z=\""
-                 << -szZ + center[2] << "\"  /> "
+                 << "<left-front-bottom-point x=\"" << lfb.X() << "\" y=\""
+                 << lfb.Y() << "\" z=\"" << lfb.Z() << "\"  /> "
+                 << "<left-front-top-point  x=\"" << lft.X() << "\" y=\""
+                 << lft.Y() << "\" z=\"" << lft.Z() << "\"  /> "
+                 << "<left-back-bottom-point  x=\"" << lbb.X() << "\" y=\""
+                 << lbb.Y() << "\" z=\"" << lbb.Z() << "\"  /> "
+                 << "<right-front-bottom-point  x=\"" << rfb.X() << "\" y =\""
+                 << rfb.Y() << "\" z=\"" << rfb.Z() << "\"  /> "
                  << "</cuboid>";
-
   return xmlShapeStream.str();
 }
 
 /**
  * Create the XML required to define a cylinder from the given args
  * @param args A user-supplied dict of args
+ * @param refFrame Defines the reference frame for the shape
  * @param hollow True if an annulus is to be created
  * @return The XML definition string
  */
 std::string
 SetSample::createCylinderLikeXML(const Kernel::PropertyManager &args,
+                                 const Geometry::ReferenceFrame &refFrame,
                                  bool hollow) const {
   const std::string tag = hollow ? "hollow-cylinder" : "cylinder";
-  double height = args.getProperty("Height");
-  double innerRadius = hollow ? args.getProperty("InnerRadius") : 0.0;
-  double outerRadius =
-      hollow ? args.getProperty("OuterRadius") : args.getProperty("Radius");
-  std::vector<double> centre = args.getProperty("Center");
-  int64_t axisIdx = getAxisIndex(args);
+  double height = args.getProperty(ShapeArgs::HEIGHT);
+  double innerRadius = hollow ? args.getProperty(ShapeArgs::INNER_RADIUS) : 0.0;
+  double outerRadius = hollow ? args.getProperty(ShapeArgs::OUTER_RADIUS)
+                              : args.getProperty("Radius");
+  std::vector<double> centre = args.getProperty(ShapeArgs::CENTER);
   // convert to metres
   height *= 0.01;
   innerRadius *= 0.01;
@@ -395,6 +489,7 @@ SetSample::createCylinderLikeXML(const Kernel::PropertyManager &args,
                  [](double val) { return val *= 0.01; });
   // XML needs center position of bottom base but user specifies center of
   // cylinder
+  const unsigned axisIdx = static_cast<unsigned>(refFrame.pointingUp());
   const V3D baseCentre = cylBaseCentre(centre, height, axisIdx);
 
   std::ostringstream xmlShapeStream;
@@ -421,7 +516,7 @@ SetSample::createCylinderLikeXML(const Kernel::PropertyManager &args,
 void SetSample::runSetSampleShape(API::MatrixWorkspace_sptr &workspace,
                                   const std::string &xml) {
   auto alg = createChildAlgorithm("CreateSampleShape");
-  alg->setProperty("InputWorkspace", workspace);
+  alg->setProperty(PropertyNames::INPUT_WORKSPACE, workspace);
   alg->setProperty("ShapeXML", xml);
   alg->executeAsChildAlg();
 }
@@ -437,7 +532,7 @@ void SetSample::runChildAlgorithm(const std::string &name,
                                   API::MatrixWorkspace_sptr &workspace,
                                   const Kernel::PropertyManager &args) {
   auto alg = createChildAlgorithm(name);
-  alg->setProperty("InputWorkspace", workspace);
+  alg->setProperty(PropertyNames::INPUT_WORKSPACE, workspace);
   alg->updatePropertyValues(args);
   alg->executeAsChildAlg();
 }
diff --git a/Framework/DataHandling/test/SetSampleTest.h b/Framework/DataHandling/test/SetSampleTest.h
index e90effc98c62d4b76210beb0e04c5ce144ba6161..d15ae0e33896641ab5a1aa6ddc6471cd0ce3b7ea 100644
--- a/Framework/DataHandling/test/SetSampleTest.h
+++ b/Framework/DataHandling/test/SetSampleTest.h
@@ -4,6 +4,7 @@
 #include <cxxtest/TestSuite.h>
 
 #include "MantidDataHandling/SetSample.h"
+#include "MantidGeometry/Instrument/ReferenceFrame.h"
 #include "MantidGeometry/Instrument/SampleEnvironment.h"
 #include "MantidGeometry/Objects/Object.h"
 #include "MantidGeometry/Objects/Rules.h"
@@ -91,8 +92,7 @@ public:
     sampleShape->setID("mysample");
     inputWS->mutableSample().setShape(*sampleShape);
 
-    auto alg = createAlgorithm();
-    alg->setProperty("InputWorkspace", inputWS);
+    auto alg = createAlgorithm(inputWS);
     alg->setProperty("Material", createMaterialProps());
     TS_ASSERT_THROWS_NOTHING(alg->execute());
     TS_ASSERT(alg->isExecuted());
@@ -115,8 +115,7 @@ public:
     sampleShape->setMaterial(alum);
     inputWS->mutableSample().setShape(*sampleShape);
 
-    auto alg = createAlgorithm();
-    alg->setProperty("InputWorkspace", inputWS);
+    auto alg = createAlgorithm(inputWS);
     alg->setProperty("Geometry", createGenericGeometryProps());
     TS_ASSERT_THROWS_NOTHING(alg->execute());
     TS_ASSERT(alg->isExecuted());
@@ -143,8 +142,7 @@ public:
     auto &config = ConfigService::Instance();
     const auto defaultDirs = config.getString("instrumentDefinition.directory");
     config.setString("instrumentDefinition.directory", m_testRoot);
-    auto alg = createAlgorithm();
-    alg->setProperty("InputWorkspace", inputWS);
+    auto alg = createAlgorithm(inputWS);
     alg->setProperty("Environment", createEnvironmentProps());
     TS_ASSERT_THROWS_NOTHING(alg->execute());
     TS_ASSERT(alg->isExecuted());
@@ -175,8 +173,7 @@ public:
     auto &config = ConfigService::Instance();
     const auto defaultDirs = config.getString("instrumentDefinition.directory");
     config.setString("instrumentDefinition.directory", m_testRoot);
-    auto alg = createAlgorithm();
-    alg->setProperty("InputWorkspace", inputWS);
+    auto alg = createAlgorithm(inputWS);
     alg->setProperty("Environment", createEnvironmentProps());
     alg->setProperty("Geometry", createOverrideGeometryProps());
     TS_ASSERT_THROWS_NOTHING(alg->execute());
@@ -201,9 +198,9 @@ public:
   void test_Setting_Geometry_As_FlatPlate() {
     using Mantid::Kernel::V3D;
     auto inputWS = WorkspaceCreationHelper::create2DWorkspaceBinned(1, 1);
+    setTestReferenceFrame(inputWS);
 
-    auto alg = createAlgorithm();
-    alg->setProperty("InputWorkspace", inputWS);
+    auto alg = createAlgorithm(inputWS);
     alg->setProperty("Geometry", createFlatPlateGeometryProps());
     TS_ASSERT_THROWS_NOTHING(alg->execute());
     TS_ASSERT(alg->isExecuted());
@@ -214,16 +211,46 @@ public:
     auto tag = sampleShape.getShapeXML().find("cuboid");
     TS_ASSERT(tag != std::string::npos);
 
-    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0, 0, 0.01)));
+    // Center
+    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0.01, 0, 0)));
+    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0.0105, 0.025, 0.02)));
+    // Origin
     TS_ASSERT_EQUALS(false, sampleShape.isValid(V3D(0, 0, 0.0)));
   }
 
+  void test_Setting_Geometry_As_FlatPlate_With_Rotation() {
+    using Mantid::Kernel::V3D;
+    auto inputWS = WorkspaceCreationHelper::create2DWorkspaceBinned(1, 1);
+    setTestReferenceFrame(inputWS);
+
+    auto alg = createAlgorithm(inputWS);
+    const double angle(45.0);
+    alg->setProperty("Geometry", createFlatPlateGeometryProps(angle));
+    TS_ASSERT_THROWS_NOTHING(alg->execute());
+    TS_ASSERT(alg->isExecuted());
+
+    // New shape
+    const auto &sampleShape = inputWS->sample().getShape();
+    TS_ASSERT(sampleShape.hasValidShape());
+    auto tag = sampleShape.getShapeXML().find("cuboid");
+    TS_ASSERT(tag != std::string::npos);
+
+    // Center should be preserved inside the shape
+    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0.01, 0, 0)));
+    // V3D(0.0005, 0.025, 0.02) rotated by 45 degrees CCW and translated
+    // to center
+    TS_ASSERT_EQUALS(true,
+                     sampleShape.isValid(V3D(-0.00732412, 0.01803122, 0.02)));
+    // End of horizontal axis should now not be inside the object
+    TS_ASSERT_EQUALS(false, sampleShape.isValid(V3D(0, 0.025, 0)));
+  }
+
   void test_Setting_Geometry_As_Cylinder() {
     using Mantid::Kernel::V3D;
     auto inputWS = WorkspaceCreationHelper::create2DWorkspaceBinned(1, 1);
+    setTestReferenceFrame(inputWS);
 
-    auto alg = createAlgorithm();
-    alg->setProperty("InputWorkspace", inputWS);
+    auto alg = createAlgorithm(inputWS);
     alg->setProperty("Geometry", createCylinderGeometryProps());
     TS_ASSERT_THROWS_NOTHING(alg->execute());
     TS_ASSERT(alg->isExecuted());
@@ -234,18 +261,18 @@ public:
     auto tag = sampleShape.getShapeXML().find("cylinder");
     TS_ASSERT(tag != std::string::npos);
 
-    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0, 0.009, 0.015)));
-    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0, -0.009, 0.015)));
-    TS_ASSERT_EQUALS(false, sampleShape.isValid(V3D(0, 0.011, 0.015)));
-    TS_ASSERT_EQUALS(false, sampleShape.isValid(V3D(0, -0.011, 0.015)));
+    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0, 0.049, 0.019)));
+    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0, 0.049, 0.001)));
+    TS_ASSERT_EQUALS(false, sampleShape.isValid(V3D(0, 0.06, 0.021)));
+    TS_ASSERT_EQUALS(false, sampleShape.isValid(V3D(0, 0.06, -0.001)));
   }
 
   void test_Setting_Geometry_As_HollowCylinder() {
     using Mantid::Kernel::V3D;
     auto inputWS = WorkspaceCreationHelper::create2DWorkspaceBinned(1, 1);
+    setTestReferenceFrame(inputWS);
 
-    auto alg = createAlgorithm();
-    alg->setProperty("InputWorkspace", inputWS);
+    auto alg = createAlgorithm(inputWS);
     alg->setProperty("Geometry", createHollowCylinderGeometryProps());
     TS_ASSERT_THROWS_NOTHING(alg->execute());
     TS_ASSERT(alg->isExecuted());
@@ -253,10 +280,10 @@ public:
     // New shape
     const auto &sampleShape = inputWS->sample().getShape();
     TS_ASSERT(sampleShape.hasValidShape());
-    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0, 0.009, 0.045)));
-    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0, -0.009, 0.045)));
-    TS_ASSERT_EQUALS(false, sampleShape.isValid(V3D(0, 0.011, 0.045)));
-    TS_ASSERT_EQUALS(false, sampleShape.isValid(V3D(0, -0.011, 0.045)));
+    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0, 0.035, 0.019)));
+    TS_ASSERT_EQUALS(true, sampleShape.isValid(V3D(0, 0.035, 0.001)));
+    TS_ASSERT_EQUALS(false, sampleShape.isValid(V3D(0, 0.041, 0.021)));
+    TS_ASSERT_EQUALS(false, sampleShape.isValid(V3D(0, 0.041, -0.001)));
   }
 
   //----------------------------------------------------------------------------
@@ -268,8 +295,7 @@ public:
     using StringProperty = Mantid::Kernel::PropertyWithValue<std::string>;
     auto inputWS = WorkspaceCreationHelper::create2DWorkspaceBinned(1, 1);
 
-    auto alg = createAlgorithm();
-    alg->setProperty("InputWorkspace", inputWS);
+    auto alg = createAlgorithm(inputWS);
 
     auto args = boost::make_shared<PropertyManager>();
     args->declareProperty(
@@ -283,8 +309,7 @@ public:
     using StringProperty = Mantid::Kernel::PropertyWithValue<std::string>;
     auto inputWS = WorkspaceCreationHelper::create2DWorkspaceBinned(1, 1);
 
-    auto alg = createAlgorithm();
-    alg->setProperty("InputWorkspace", inputWS);
+    auto alg = createAlgorithm(inputWS);
 
     auto args = boost::make_shared<PropertyManager>();
     args->declareProperty(
@@ -298,8 +323,7 @@ public:
     using StringProperty = Mantid::Kernel::PropertyWithValue<std::string>;
     auto inputWS = WorkspaceCreationHelper::create2DWorkspaceBinned(1, 1);
 
-    auto alg = createAlgorithm();
-    alg->setProperty("InputWorkspace", inputWS);
+    auto alg = createAlgorithm(inputWS);
 
     auto args = boost::make_shared<PropertyManager>();
     args->declareProperty(
@@ -313,18 +337,106 @@ public:
     TS_ASSERT_THROWS(alg->execute(), std::runtime_error);
   }
 
+  void test_Negative_FlatPlate_Dimensions_Give_Validation_Errors() {
+    using Mantid::API::IAlgorithm;
+    using Mantid::Kernel::PropertyManager;
+    using DoubleProperty = Mantid::Kernel::PropertyWithValue<double>;
+    using StringProperty = Mantid::Kernel::PropertyWithValue<std::string>;
+
+    auto alg = createAlgorithm();
+    auto args = boost::make_shared<PropertyManager>();
+    args->declareProperty(
+        Mantid::Kernel::make_unique<StringProperty>("Shape", "FlatPlate"), "");
+    std::array<const std::string, 3> dimensions = {
+        {"Width", "Height", "Thick"}};
+    const std::string geometryProp("Geometry");
+    for (const auto &dim : dimensions) {
+      args->declareProperty(
+          Mantid::Kernel::make_unique<DoubleProperty>(dim, -1.0), "");
+      alg->setProperty(geometryProp, args);
+      TS_ASSERT(validateErrorProduced(*alg, geometryProp));
+      args->removeProperty(dim);
+    }
+  }
+
+  void test_Negative_Cylinder_Dimensions_Give_Validation_Errors() {
+    using Mantid::Kernel::PropertyManager;
+    using DoubleProperty = Mantid::Kernel::PropertyWithValue<double>;
+    using StringProperty = Mantid::Kernel::PropertyWithValue<std::string>;
+
+    auto alg = createAlgorithm();
+    auto args = boost::make_shared<PropertyManager>();
+    args->declareProperty(
+        Mantid::Kernel::make_unique<StringProperty>("Shape", "Cylinder"), "");
+    std::array<const std::string, 2> dimensions = {{"Radius", "Height"}};
+    const std::string geometryProp("Geometry");
+    for (const auto &dim : dimensions) {
+      args->declareProperty(
+          Mantid::Kernel::make_unique<DoubleProperty>(dim, -1.0), "");
+      alg->setProperty(geometryProp, args);
+      TS_ASSERT(validateErrorProduced(*alg, geometryProp));
+      args->removeProperty(dim);
+    }
+  }
+
+  void test_Negative_HollowCylinder_Dimensions_Give_Validation_Errors() {
+    using Mantid::API::IAlgorithm;
+    using Mantid::Kernel::PropertyManager;
+    using DoubleProperty = Mantid::Kernel::PropertyWithValue<double>;
+    using StringProperty = Mantid::Kernel::PropertyWithValue<std::string>;
+
+    auto alg = createAlgorithm();
+    auto args = boost::make_shared<PropertyManager>();
+    args->declareProperty(
+        Mantid::Kernel::make_unique<StringProperty>("Shape", "FlatPlate"), "");
+    std::array<const std::string, 3> dimensions = {
+        {"InnerRadius", "OuterRadius", "Height"}};
+    const std::string geometryProp("Geometry");
+    for (const auto &dim : dimensions) {
+      args->declareProperty(
+          Mantid::Kernel::make_unique<DoubleProperty>(dim, -1.0), "");
+      alg->setProperty(geometryProp, args);
+      TS_ASSERT(validateErrorProduced(*alg, geometryProp));
+      args->removeProperty(dim);
+    }
+  }
+
   //----------------------------------------------------------------------------
   // Non-test methods
   //----------------------------------------------------------------------------
 private:
-  Mantid::API::IAlgorithm_uptr createAlgorithm() {
+  Mantid::API::IAlgorithm_uptr
+  createAlgorithm(const Mantid::API::MatrixWorkspace_sptr &inputWS =
+                      Mantid::API::MatrixWorkspace_sptr()) {
     auto alg = Mantid::Kernel::make_unique<SetSample>();
     alg->setChild(true);
     alg->setRethrows(true);
     alg->initialize();
+    if (inputWS) {
+      alg->setProperty("InputWorkspace", inputWS);
+    }
     return std::move(alg);
   }
 
+  bool validateErrorProduced(Mantid::API::IAlgorithm &alg,
+                             const std::string &name) {
+    const auto errors = alg.validateInputs();
+    if (errors.find(name) != errors.end())
+      return true;
+    else
+      return false;
+  }
+
+  void setTestReferenceFrame(Mantid::API::MatrixWorkspace_sptr workspace) {
+    using Mantid::Geometry::Instrument;
+    using Mantid::Geometry::ReferenceFrame;
+    // Use Z=up,Y=across,X=beam so we test it listens to the reference frame
+    auto inst = boost::make_shared<Instrument>();
+    inst->setReferenceFrame(boost::make_shared<ReferenceFrame>(
+        Mantid::Geometry::Z, Mantid::Geometry::X, Mantid::Geometry::Right, ""));
+    workspace->setInstrument(inst);
+  }
+
   Mantid::Kernel::PropertyManager_sptr createMaterialProps() {
     using Mantid::Kernel::PropertyManager;
     using StringProperty = Mantid::Kernel::PropertyWithValue<std::string>;
@@ -373,7 +485,8 @@ private:
     return props;
   }
 
-  Mantid::Kernel::PropertyManager_sptr createFlatPlateGeometryProps() {
+  Mantid::Kernel::PropertyManager_sptr
+  createFlatPlateGeometryProps(double angle = 0.0) {
     using namespace Mantid::Kernel;
     using DoubleArrayProperty = ArrayProperty<double>;
     using DoubleProperty = PropertyWithValue<double>;
@@ -388,10 +501,13 @@ private:
         Mantid::Kernel::make_unique<DoubleProperty>("Height", 4), "");
     props->declareProperty(
         Mantid::Kernel::make_unique<DoubleProperty>("Thick", 0.1), "");
-    std::vector<double> center{0, 0, 1};
+    std::vector<double> center{1, 0, 0};
     props->declareProperty(
         Mantid::Kernel::make_unique<DoubleArrayProperty>("Center", center), "");
-
+    if (angle != 0.0) {
+      props->declareProperty(
+          Mantid::Kernel::make_unique<DoubleProperty>("Angle", angle), "");
+    }
     return props;
   }
 
@@ -399,7 +515,6 @@ private:
     using namespace Mantid::Kernel;
     using DoubleArrayProperty = ArrayProperty<double>;
     using DoubleProperty = PropertyWithValue<double>;
-    using IntProperty = PropertyWithValue<int64_t>;
     using StringProperty = PropertyWithValue<std::string>;
 
     auto props = boost::make_shared<PropertyManager>();
@@ -412,8 +527,6 @@ private:
     std::vector<double> center{0, 0, 1};
     props->declareProperty(
         Mantid::Kernel::make_unique<DoubleArrayProperty>("Center", center), "");
-    props->declareProperty(Mantid::Kernel::make_unique<IntProperty>("Axis", 1),
-                           "");
 
     return props;
   }
@@ -422,7 +535,6 @@ private:
     using namespace Mantid::Kernel;
     using DoubleArrayProperty = ArrayProperty<double>;
     using DoubleProperty = PropertyWithValue<double>;
-    using IntProperty = PropertyWithValue<int64_t>;
     using StringProperty = PropertyWithValue<std::string>;
 
     auto props = boost::make_shared<PropertyManager>();
@@ -438,8 +550,6 @@ private:
     std::vector<double> center{0, 0, 1};
     props->declareProperty(
         Mantid::Kernel::make_unique<DoubleArrayProperty>("Center", center), "");
-    props->declareProperty(Mantid::Kernel::make_unique<IntProperty>("Axis", 1),
-                           "");
 
     return props;
   }
@@ -458,6 +568,7 @@ private:
       throw std::runtime_error("Expected SurfPoint as top rule");
     }
   }
+
   std::string m_testRoot;
   // Use the TEST_LIVE entry in Facilities
   const std::string m_facilityName = "TEST_LIVE";
diff --git a/docs/source/algorithms/SetSample-v1.rst b/docs/source/algorithms/SetSample-v1.rst
index bb33da877dfd6a1b23466f36517d7045965cd8dd..10ff85bfa9fd1623a96357189ccfe2fc1ef3c58d 100644
--- a/docs/source/algorithms/SetSample-v1.rst
+++ b/docs/source/algorithms/SetSample-v1.rst
@@ -24,7 +24,7 @@ relate to the respective argument.
 Environment
 ###########
 
-Specifies the sample enviorment kit to be used. There are two required keywords:
+Specifies the sample environment kit to be used. There are two required keywords:
 
 - ``Name``: The name of the predefined kit
 - ``Container``: The id of the container within the predefined kit
@@ -71,14 +71,20 @@ For defining the full shape a key called ``Shape`` specifying the desired shape
 expected along with additional keys specifying the values (all values are assumed to
 be in centimeters):
 
-- ``FlatPlate``: Width, Height, Thick, Center
-- ``Cylinder``: Height, Radius, Center, Axis (X=0, Y=1, Z=2)
-- ``HollowCylinder``: Height, InnerRadius, OuterRadius, Center, Axis(X=0, Y=1, Z=2)
+- ``FlatPlate``: Width, Height, Thick, Center, Angle
+- ``Cylinder``: Height, Radius, Center
+- ``HollowCylinder``: Height, InnerRadius, OuterRadius, Center
 - ``CSG``: Value is a string containing any generic shape as detailed in
   :ref:`HowToDefineGeometricShape`
 
 The ``Center`` key is expected to be a list of three values indicating the :python:`[X,Y,Z]`
-position of the center.
+position of the center. The reference frame of the defined instrument is used to
+set the coordinate system for the shape.
+
+The ``Angle`` argument for a flat plate shape is expected to be in degrees and is defined as
+the angle between the positive beam axis and the normal to the face perpendicular to the
+beam axis when it is not rotated, increasing in an anti-clockwise sense. The rotation is
+performed about the vertical axis of the instrument's reference frame.
 
 Material
 ########
@@ -132,11 +138,11 @@ The following example uses a test file called ``CRYO-01.xml`` in the
 
    # A fake host workspace, replace this with your real one.
    ws = CreateSampleWorkspace()
-   # Use geometry from environment but set differnet height for sample
+   # Use geometry from environment but set different height for sample
    SetSample(ws, Environment={'Name': 'CRYO-01', 'Container': '8mm'},
              Geometry={'Shape': 'HollowCylinder', 'Height': 4.0,
                        'InnerRadius': 0.8, 'OuterRadius': 1.0,
-                       'Center': [0.,0.,0.], 'Axis':1},
+                       'Center': [0.,0.,0.]},
              Material={'ChemicalFormula': '(Li7)2-C-H4-N-Cl6'})
 
 .. categories::
diff --git a/docs/source/release/v3.9.0/framework.rst b/docs/source/release/v3.9.0/framework.rst
index bdf9eb17cb1c5af4e4d021986f9094e120e2a997..8b047254ba8f6969736216667015e806806980f3 100644
--- a/docs/source/release/v3.9.0/framework.rst
+++ b/docs/source/release/v3.9.0/framework.rst
@@ -27,6 +27,7 @@ Improved
 - :ref:`MonteCarloAbsorption <algm-MonteCarloAbsorption>`:
    * an `Interpolation` option has been added. Availabile options are: `Linear` & `CSpline`.
    * the method of selecting the scattering point has ben updated to give better agreement with numerical algorithms such as :ref:`CylinderAbsorption <algm-CylinderAbsorption>`.
+- :ref:`SetSample <algm-SetSample>` now accepts an Angle argument for defining a rotated flat plate sample.
 
 Renamed
 #######