Mantid
Loading...
Searching...
No Matches
LoadSampleEnvironment.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
10#ifdef ENABLE_LIB3MF
12#endif
18
22#include "MantidAPI/Sample.h"
23
30
31#include <Poco/Path.h>
32#include <boost/algorithm/string.hpp>
33#include <fstream>
34
35namespace Mantid::DataHandling {
36
37namespace {
38double DegreesToRadians(double angle) { return angle * M_PI / 180; }
39} // namespace
40
41// Register the algorithm into the algorithm factory
42DECLARE_ALGORITHM(LoadSampleEnvironment)
43
44using namespace Kernel;
45using namespace API;
46using namespace Geometry;
47
49 auto wsValidator = std::make_shared<InstrumentValidator>();
50 // input workspace
51 declareProperty(std::make_unique<WorkspaceProperty<>>("InputWorkspace", "", Direction::Input, wsValidator),
52 "The name of the workspace containing the instrument to add "
53 "the Environment");
54
55 // Environment file
56 const std::vector<std::string> extensions{".stl", ".3mf"};
57 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Load, extensions),
58 "The path name of the file containing the Environment");
59
60 // scale to use for stl
61 declareProperty("Scale", "cm", "The scale of the stl: m, cm, or mm");
62
63 // Output workspace
64 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
65 "The name of the workspace that will contain the loaded "
66 "Environment of the sample");
67
68 // Environment Name
69 declareProperty("EnvironmentName", "Environment");
70
71 // New Can or Add
72 declareProperty("Add", false);
73
74 // Rotation angles
75 declareProperty("XDegrees", 0.0, "The degrees to rotate on the x axis by");
76 declareProperty("YDegrees", 0.0, "The degrees to rotate on the y axis by");
77 declareProperty("ZDegrees", 0.0, "The degrees to rotate on the z axis by");
78
79 // Vector to translate mesh
80 declareProperty(std::make_unique<ArrayProperty<double>>("TranslationVector", "0,0,0"),
81 "Vector by which to translate the loaded environment");
82
83 declareProperty("SetMaterial", false);
84
85 // properties for SetMaterial
86
87 declareProperty("ChemicalFormula", "", "The chemical formula, see examples in documentation");
88
89 declareProperty("AtomicNumber", 0, "The atomic number");
90 declareProperty("MassNumber", 0, "Mass number if ion (use 0 for default mass sensity)");
91 auto mustBePositive = std::make_shared<BoundedValidator<double>>();
92 mustBePositive->setLower(0.0);
93 declareProperty("SampleNumberDensity", EMPTY_DBL(), mustBePositive,
94 "This number density of the sample in number of "
95 "atoms per cubic angstrom will be used instead of "
96 "calculated");
97 declareProperty("ZParameter", EMPTY_DBL(), mustBePositive, "Number of formula units in unit cell");
98 declareProperty("UnitCellVolume", EMPTY_DBL(), mustBePositive,
99 "Unit cell volume in Angstoms^3. Will be calculated from the "
100 "OrientedLattice if not supplied.");
101 declareProperty("CoherentXSection", EMPTY_DBL(), mustBePositive,
102 "Optional: This coherent cross-section for the sample "
103 "material in barns will be used instead of tabulated");
104 declareProperty("IncoherentXSection", EMPTY_DBL(), mustBePositive,
105 "Optional: This incoherent cross-section for the sample "
106 "material in barns will be used instead of tabulated");
107 declareProperty("AttenuationXSection", EMPTY_DBL(), mustBePositive,
108 "Optional: This absorption cross-section for the sample "
109 "material in barns will be used instead of tabulated");
110 declareProperty("ScatteringXSection", EMPTY_DBL(), mustBePositive,
111 "Optional: This total scattering cross-section (coherent + "
112 "incoherent) for the sample material in barns will be used "
113 "instead of tabulated");
114 const std::vector<std::string> attExtensions{".DAT"};
115 declareProperty(std::make_unique<FileProperty>("AttenuationProfile", "", FileProperty::OptionalLoad, attExtensions),
116 "The path name of the file containing the attenuation profile");
117 declareProperty("SampleMassDensity", EMPTY_DBL(), mustBePositive,
118 "Measured mass density in g/cubic cm of the sample "
119 "to be used to calculate the number density.");
120 const std::vector<std::string> units({"Atoms", "Formula Units"});
121 declareProperty("NumberDensityUnit", units.front(), std::make_shared<StringListValidator>(units),
122 "Choose which units SampleNumberDensity referes to.");
123
124 // Perform Group Associations.
125 std::string formulaGrp("By Formula or Atomic Number");
126 setPropertyGroup("ChemicalFormula", formulaGrp);
127 setPropertyGroup("AtomicNumber", formulaGrp);
128 setPropertyGroup("MassNumber", formulaGrp);
129 setPropertySettings("ChemicalFormula", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
130 setPropertySettings("AtomicNumber", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
131 setPropertySettings("MassNumber", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
132
133 std::string densityGrp("Sample Density");
134 setPropertyGroup("SampleNumberDensity", densityGrp);
135 setPropertyGroup("NumberDensityUnit", densityGrp);
136 setPropertyGroup("ZParameter", densityGrp);
137 setPropertyGroup("UnitCellVolume", densityGrp);
138 setPropertyGroup("SampleMassDensity", densityGrp);
139 setPropertySettings("SampleNumberDensity", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
140 setPropertySettings("ZParameter", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
141 setPropertySettings("UnitCellVolume", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
142 setPropertySettings("SampleMassDensity", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
143 setPropertySettings("NumberDensityUnit",
144 std::make_unique<EnabledWhenProperty>("SampleNumberDensity", IS_NOT_DEFAULT));
145
146 std::string specificValuesGrp("Override Cross Section Values");
147 setPropertyGroup("CoherentXSection", specificValuesGrp);
148 setPropertyGroup("IncoherentXSection", specificValuesGrp);
149 setPropertyGroup("AttenuationXSection", specificValuesGrp);
150 setPropertyGroup("ScatteringXSection", specificValuesGrp);
151 setPropertyGroup("AttenuationProfile", specificValuesGrp);
152 setPropertySettings("CoherentXSection", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
153 setPropertySettings("IncoherentXSection", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
154 setPropertySettings("AttenuationXSection", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
155 setPropertySettings("ScatteringXSection", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
156 setPropertySettings("AttenuationProfile", std::make_unique<EnabledWhenProperty>("SetMaterial", IS_NOT_DEFAULT));
157}
158
159std::map<std::string, std::string> LoadSampleEnvironment::validateInputs() {
160 std::map<std::string, std::string> result;
161 if (getProperty("SetMaterial")) {
163 params.chemicalSymbol = getPropertyValue("ChemicalFormula");
164 params.atomicNumber = getProperty("AtomicNumber");
165 params.massNumber = getProperty("MassNumber");
166 params.numberDensity = getProperty("SampleNumberDensity");
167 params.zParameter = getProperty("ZParameter");
168 params.unitCellVolume = getProperty("UnitCellVolume");
169 params.massDensity = getProperty("SampleMassDensity");
170 result = ReadMaterial::validateInputs(params);
171 }
172 return result;
173}
174
184void LoadSampleEnvironment::loadEnvironmentFromSTL(const std::string &filename, Sample &sample, const bool add,
185 std::string debugString) {
186 std::unique_ptr<SampleEnvironment> environment = nullptr;
187 std::shared_ptr<MeshObject> environmentMesh = nullptr;
188
189 std::unique_ptr<LoadAsciiStl> asciiStlReader = nullptr;
190 std::unique_ptr<LoadBinaryStl> binaryStlReader = nullptr;
191 const std::string scaleProperty = getPropertyValue("Scale");
192 const ScaleUnits scaleType = getScaleTypeFromStr(scaleProperty);
193
194 bool isBinary;
195 if (LoadBinaryStl::isBinarySTL(filename)) {
196 isBinary = true;
197 } else if (LoadAsciiStl::isAsciiSTL(filename)) {
198 isBinary = false;
199 } else {
200 throw Exception::ParseError("Could not read file, did not match either STL Format", filename, 0);
201 }
202 std::unique_ptr<LoadStl> reader = nullptr;
203
204 if (getProperty("SetMaterial")) {
206 params.chemicalSymbol = getPropertyValue("ChemicalFormula");
207 params.atomicNumber = getProperty("AtomicNumber");
208 params.massNumber = getProperty("MassNumber");
209 params.numberDensity = getProperty("SampleNumberDensity");
210 params.zParameter = getProperty("ZParameter");
211 params.unitCellVolume = getProperty("UnitCellVolume");
212 params.massDensity = getProperty("SampleMassDensity");
213 params.coherentXSection = getProperty("CoherentXSection");
214 params.incoherentXSection = getProperty("IncoherentXSection");
215 params.attenuationXSection = getProperty("AttenuationXSection");
216 params.scatteringXSection = getProperty("ScatteringXSection");
217 params.attenuationProfileFileName = getPropertyValue("AttenuationProfile");
218 const std::string numberDensityUnit = getProperty("NumberDensityUnit");
219 if (numberDensityUnit == "Atoms") {
221 } else {
223 }
224 if (isBinary) {
225 reader = std::make_unique<LoadBinaryStl>(filename, scaleType, params);
226 } else {
227 reader = std::make_unique<LoadAsciiStl>(filename, scaleType, params);
228 }
229 } else {
230 if (isBinary) {
231 reader = std::make_unique<LoadBinaryStl>(filename, scaleType);
232 } else {
233 reader = std::make_unique<LoadAsciiStl>(filename, scaleType);
234 }
235 }
236
237 environmentMesh = reader->readShape();
238
239 const double xRotation = DegreesToRadians(getProperty("xDegrees"));
240 const double yRotation = DegreesToRadians(getProperty("yDegrees"));
241 const double zRotation = DegreesToRadians(getProperty("zDegrees"));
242 environmentMesh = reader->rotate(environmentMesh, xRotation, yRotation, zRotation);
243 const std::vector<double> translationVector = getProperty("TranslationVector");
244 environmentMesh = reader->translate(environmentMesh, translationVector);
245
246 std::string name = getProperty("EnvironmentName");
247 if (add) {
248 environment = std::make_unique<SampleEnvironment>(sample.getEnvironment());
249 environment->add(environmentMesh);
250 } else {
251 auto can = std::make_shared<Container>(environmentMesh);
252 environment = std::make_unique<SampleEnvironment>(name, can);
253 }
254
255 debugString += "Environment has: " + std::to_string(environment->nelements()) + " elements.";
256 g_log.debug() << debugString;
257
258 // Put Environment into sample.
259 sample.setEnvironment(std::move(environment));
260
261 auto translatedVertices = environmentMesh->getVertices();
262 if (g_log.is(Logger::Priority::PRIO_DEBUG)) {
263 int i = 0;
264 for (double vertex : translatedVertices) {
265 i++;
266 g_log.debug(std::to_string(vertex));
267 if (i % 3 == 0) {
268 g_log.debug("\n");
269 }
270 }
271 }
272}
273
285 [[maybe_unused]] const std::string &filename,
286 [[maybe_unused]] Sample &sample, [[maybe_unused]] const bool add,
287 [[maybe_unused]] std::string &debugString) {
288#ifdef ENABLE_LIB3MF
289 std::unique_ptr<Geometry::SampleEnvironment> environment = nullptr;
290 Mantid3MFFileIO MeshLoader;
291 MeshLoader.LoadFile(filename);
292 boost::shared_ptr<MeshObject> environmentMesh = nullptr;
293 std::string name = getProperty("EnvironmentName");
294 std::vector<std::shared_ptr<Geometry::MeshObject>> environmentMeshes;
295 std::shared_ptr<Geometry::MeshObject> sampleMesh;
296
297 MeshLoader.readMeshObjects(environmentMeshes, sampleMesh);
298
299 if (sampleMesh) {
300 sampleMesh->rotate(inputWS->run().getGoniometer().getR());
301 sample.setShape(sampleMesh);
302 }
303
304 for (auto environmentMesh : environmentMeshes) {
305 if (!environment) {
306 if (add) {
307 environment = std::make_unique<SampleEnvironment>(sample.getEnvironment());
308 environment->add(environmentMesh);
309 } else {
310 auto can = std::make_shared<Container>(environmentMesh);
311 environment = std::make_unique<SampleEnvironment>(name, can);
312 }
313 } else {
314 environment->add(environmentMesh);
315 }
316
317 debugString += "Environment has: " + std::to_string(environment->nelements()) + " elements.";
318 }
319
320 // Put Environment into sample.
321 sample.setEnvironment(std::move(environment));
322#else
323 throw std::runtime_error("3MF format not supported on this platform");
324#endif
325}
326
328
329 MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
330 MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
331
332 if (inputWS != outputWS) {
333 outputWS = inputWS->clone();
334 }
335
336 const std::string filename = getProperty("Filename");
337 const std::ifstream file(filename.c_str());
338 if (!file) {
339 g_log.error("Unable to open file: " + filename);
340 throw Exception::FileError("Unable to open file: ", filename);
341 }
342
343 const bool add = getProperty("Add");
344 std::string debugString;
345 Sample &sample = outputWS->mutableSample();
346
347 std::string fileExt = Poco::Path(filename).getExtension();
348
349 std::transform(fileExt.begin(), fileExt.end(), fileExt.begin(), toupper);
350
351 if (fileExt == "STL") {
352 loadEnvironmentFromSTL(filename, sample, add, debugString);
353 } else if (fileExt == "3MF") {
354 loadEnvironmentFrom3MF(inputWS, filename, sample, add, debugString);
355 } else {
356 throw "Invalid file extension";
357 }
358
359 // get the material name and number density for debug
360 const auto outMaterial = outputWS->sample().getEnvironment().getContainer().material();
361 debugString += "\n"
362 "Environment Material: " +
363 outMaterial.name();
364 debugString += "\n"
365 "Environment Material Number Density: " +
366 std::to_string(outMaterial.numberDensity());
367 // Set output workspace
368 setProperty("OutputWorkspace", outputWS);
369 g_log.debug(debugString);
370}
371
372} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
Kernel::Logger & g_log
Definition: Algorithm.h:451
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
Definition: FileProperty.h:53
@ Load
allowed here which will be passed to the algorithm
Definition: FileProperty.h:52
This class stores information about the sample used in particular run.
Definition: Sample.h:33
void setEnvironment(std::shared_ptr< Geometry::SampleEnvironment > env)
Set the environment used to contain the sample.
Definition: Sample.cpp:148
const Geometry::SampleEnvironment & getEnvironment() const
Get a reference to the sample's environment.
Definition: Sample.cpp:136
A property class for workspaces.
static bool isAsciiSTL(const std::string &filename)
static bool isBinarySTL(const std::string &filename)
void exec() override
Virtual method - must be overridden by concrete algorithm.
void loadEnvironmentFrom3MF(const API::MatrixWorkspace_const_sptr &inputWS, const std::string &filename, API::Sample &sample, const bool add, std::string &debugString)
Load a sample environment definition from a .3mf file.
void init() override
Virtual method - must be overridden by concrete algorithm.
const std::string name() const override
Algorithm's name for identification overriding a virtual method.
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
void loadEnvironmentFromSTL(const std::string &filename, API::Sample &sample, const bool add, std::string debugString)
Load a sample environment definition from a .stl file.
Class to load and save .3mf files .3mf format is a 3D manufacturing format for storing mesh descripti...
void readMeshObjects(std::vector< MeshObject_sptr > &meshObjects, MeshObject_sptr &sample)
Read a set of mesh objects from the in memory lib3mf model object.
void LoadFile(std::string filename)
Load 3MF format file.
static ValidationErrors validateInputs(const MaterialParameters &params)
Validate the parameters to build the material from, this returns any errors in the inputs.
Support for a property that holds an array of values.
Definition: ArrayProperty.h:28
Records the filename and the description of failure.
Definition: Exception.h:98
Records the filename, the description of failure and the line on which it happened.
Definition: Exception.h:115
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings > settings)
void setPropertyGroup(const std::string &name, const std::string &group)
Set the group for a given property.
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
bool is(int level) const
Returns true if at least the given log level is set.
Definition: Logger.cpp:146
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
ScaleUnits getScaleTypeFromStr(const std::string &scaleProperty)
Definition: MeshFileIO.h:73
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition: EmptyValues.h:43
std::string to_string(const wide_integer< Bits, Signed > &n)
This struct contains the parameters for constructing a material, and gives them a default value for e...
Definition: ReadMaterial.h:32
double numberDensity
The sample number density to set, defaults to EMPTY_DBL()
Definition: ReadMaterial.h:40
double coherentXSection
The coherent scattering cross section to set, defaults to EMPTY_DBL()
Definition: ReadMaterial.h:56
int massNumber
The mass number to set, defaults to 0.
Definition: ReadMaterial.h:38
double attenuationXSection
The absorption cross section to set, defaults to EMPTY_DBL()
Definition: ReadMaterial.h:60
Kernel::MaterialBuilder::NumberDensityUnit numberDensityUnit
A flag indicating the unit of sampleNumberDensity.
Definition: ReadMaterial.h:68
double massDensity
The sample mass density to set, defaults to EMPTY_DBL()
Definition: ReadMaterial.h:50
double scatteringXSection
The total scattering cross section to set, defaults to EMPTY_DBL()
Definition: ReadMaterial.h:62
double incoherentXSection
The incoherent scattering cross section to set, defaults to EMPTY_DBL()
Definition: ReadMaterial.h:58
double zParameter
The zParameter to set, defaults to EMPTY_DBL()
Definition: ReadMaterial.h:46
std::string chemicalSymbol
The chemical formula to set, defaults to the empty string.
Definition: ReadMaterial.h:34
double unitCellVolume
The unit cell volume to set, defaults to EMPTY_DBL()
Definition: ReadMaterial.h:48
int atomicNumber
The atomic number to set, defaults to 0.
Definition: ReadMaterial.h:36
std::string attenuationProfileFileName
The name or path of a file containing an attenuation profile.
Definition: ReadMaterial.h:64
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54