WSF
WsfMultiresolutionWrapperMetaModelImpl.hpp
Go to the documentation of this file.
1// ****************************************************************************
2// CUI
3//
4// The Advanced Framework for Simulation, Integration, and Modeling (AFSIM)
5//
6// Copyright (C) 2021 Stellar Science; U.S. Government has Unlimited Rights.
7//
8// The use, dissemination or disclosure of data in this file is subject to
9// limitation or restriction. See accompanying README and LICENSE for details.
10// ****************************************************************************
11
12#include <algorithm>
13#include <iterator>
14#include <type_traits>
15
17#include "UtInput.hpp"
18#include "UtInputBlock.hpp"
19#include "UtLog.hpp"
20#include "UtMemory.hpp"
21#include "WsfComponentRoles.hpp"
23#include "WsfPlatform.hpp"
24#include "WsfScenario.hpp"
25
26template<typename DerivedComponent>
31
32template<typename DerivedComponent>
37
38template<typename DerivedComponent>
40{
41 const std::string command(aInput.GetCommand());
42 if (command == "model")
43 {
44 UtInputBlock inputBlock(aInput);
45 return ProcessModel(inputBlock, ProcessModelName(inputBlock));
46 }
47 else if (command == "add" || command == "edit")
48 {
49 // Ensure next command is model, then process
50 std::string nextCommand;
51 aInput.ReadCommand(nextCommand);
52 if (nextCommand != "model")
53 {
54 throw UtInput::BadValue(aInput, std::string("Unexpected command ") + nextCommand + ". Expected model");
55 }
56 UtInputBlock inputBlock(aInput);
57 return ProcessModel(inputBlock, ProcessModelName(inputBlock, command == "edit"));
58 }
59 else if (command == "common")
60 {
61 return ProcessCommonParameters(aInput);
62 }
63 else
64 {
66 }
67}
68
69template<typename DerivedComponent>
70bool WsfMultiresolutionWrapperMetaModel<DerivedComponent>::ProcessModel(
71 UtInputBlock& aInputBlock,
72 const typename WsfMultiresolutionWrapperMetaModel<DerivedComponent>::Index aModelIndex)
73{
74 namespace wm = wsf::multiresolution;
75 assert(aModelIndex < mComponentModels.size());
76 ModelWithFidelity& model = mComponentModels[aModelIndex];
77 std::string nextCommand;
78 while (aInputBlock.ReadCommand(nextCommand))
79 {
80 if (nextCommand == "fidelity_range")
81 {
82 model.mFidelityRange = wm::ProcessFidelityRange(aInputBlock.GetInput());
83 AffirmDisjointWithFidelityRanges(aInputBlock.GetInput(), model);
84 }
85 else if (nextCommand == wm::BaseComponentName<DerivedComponent>())
86 {
87 model.mComponent = ProcessComponent(aInputBlock.GetInput(), std::move(model.mComponent));
88 }
89 else if (nextCommand == "default")
90 {
91 mDefaultComponentIndex = aModelIndex;
92 }
93 else
94 {
95 throw UtInput::BadValue(aInputBlock.GetInput(),
96 std::string("Unexpected command ") + nextCommand +
97 ". Expected either fidelity_range, default, or " +
98 std::string(wm::BaseComponentName<DerivedComponent>()));
99 }
100 }
101 FinalizeModelProcessing(aInputBlock.GetInput(), model);
102 return true;
103}
104
105template<typename DerivedComponent>
106std::unique_ptr<DerivedComponent>
107WsfMultiresolutionWrapperMetaModel<DerivedComponent>::ProcessComponent(UtInput& aInput,
108 std::unique_ptr<DerivedComponent> aExistingComponent)
109{
110 UtInputBlock componentBlock(aInput);
111 std::unique_ptr<DerivedComponent> component =
112 aExistingComponent ? std::move(aExistingComponent) : ProcessComponentType(aInput);
113 assert(component); // ProcessComponentType throws if it can't read the type
114 bool processedInput = true;
115 std::string nextCommand;
116 while (componentBlock.ReadCommand(nextCommand) && processedInput)
117 {
118 processedInput &= component->ProcessInput(aInput);
119 }
120 if (!processedInput)
121 {
122 throw UtInput::BadValue(aInput, "Model couldn't process its input. Model type: " + component->GetType());
123 }
124 return component;
125}
126
127template<typename DerivedComponent>
128typename WsfMultiresolutionWrapperMetaModel<DerivedComponent>::Index
129WsfMultiresolutionWrapperMetaModel<DerivedComponent>::ProcessModelName(UtInputBlock& aInputBlock,
130 const ut::optional<bool> aExpectExists)
131{
132 std::string modelName;
133 aInputBlock.GetInput().ReadValue(modelName);
134 ut::optional<Index> modelIndex = ModelIndexWithName(modelName);
135 // Check errors
136 if (aExpectExists)
137 {
138 if (modelIndex.has_value() && !aExpectExists.value())
139 {
140 throw UtInput::BadValue(aInputBlock.GetInput(), "Expected no existing model with name " + modelName);
141 }
142 else if (!modelIndex.has_value() && aExpectExists.value())
143 {
144 throw UtInput::BadValue(aInputBlock.GetInput(), "Expected existing model with name " + modelName);
145 }
146 }
147 // Insert new model if necessary
148 if (!modelIndex.has_value())
149 {
150 mComponentModels.emplace_back();
151 mComponentModels.back().mName = std::move(modelName);
152 modelIndex = mComponentModels.size() - 1;
153 }
154 return *modelIndex;
155}
156
157template<typename DerivedComponent>
158std::unique_ptr<DerivedComponent> WsfMultiresolutionWrapperMetaModel<DerivedComponent>::ProcessComponentType(UtInput& aInput)
159{
160 std::string baseType;
161 aInput.ReadValue(baseType);
162 WsfObjectTypeList<DerivedComponent>& componentTypeList =
164 auto component = std::unique_ptr<DerivedComponent>(componentTypeList.Clone(baseType));
165 if (!component)
166 {
167 // A failed call to Clone invokes deferred loading, so a second call should be successful if the type exists.
168 component.reset(componentTypeList.Clone(baseType));
169 }
170 if (!component)
171 {
172 throw UtInput::BadValue(aInput, "Could not load type " + baseType);
173 }
174 return component;
175}
176
177template<typename DerivedComponent>
178bool WsfMultiresolutionWrapperMetaModel<DerivedComponent>::ProcessCommonParameters(UtInput& aInput)
179{
180 auto commonInputLocation = aInput.StoreLocation();
181 bool inputProcessed = true;
182 if (mComponentModels.empty())
183 {
184 throw UtInput::BadValue(aInput, "Expected common input block after definition of one or more models.");
185 }
186 for (auto& component : mComponentModels)
187 {
188 aInput.RestoreLocation(commonInputLocation);
189 UtInputBlock inputBlock(aInput);
190 std::string nextCommand;
191 while (inputBlock.ReadCommand(nextCommand))
192 {
193 inputProcessed &= component.mComponent->ProcessInput(aInput);
194 }
195 }
196 return inputProcessed;
197}
198
199template<typename DerivedComponent>
200void WsfMultiresolutionWrapperMetaModel<DerivedComponent>::FinalizeModelProcessing(
201 UtInput& aInput,
202 const typename WsfMultiresolutionWrapperMetaModel<DerivedComponent>::ModelWithFidelity& aModelWithFidelity)
203{
204 if (aModelWithFidelity.mComponent)
205 {
206 // If this isn't directly read, check that the default doesn't overlap.
207 AffirmDisjointWithFidelityRanges(aInput, aModelWithFidelity);
208 }
209 else
210 {
211 // This should be unreachable due to other error checking, but this serves as a final check that a valid
212 // model exists.
213 throw UtInput::BadValue(aInput,
214 "Expected a model specification of type " +
216 }
217}
218
219template<typename DerivedComponent>
220void WsfMultiresolutionWrapperMetaModel<DerivedComponent>::AffirmDisjointWithFidelityRanges(
221 UtInput& aInput,
222 const typename WsfMultiresolutionWrapperMetaModel<DerivedComponent>::ModelWithFidelity& aModelWithFidelity) const
223{
224 const auto result = std::find_if(mComponentModels.cbegin(),
225 mComponentModels.cend(),
226 [&aModelWithFidelity](const ModelWithFidelity& model)
227 {
228 return aModelWithFidelity.mComponent.get() != model.mComponent.get() &&
229 aModelWithFidelity.mFidelityRange.Overlaps(model.mFidelityRange);
230 });
231 if (result != mComponentModels.end())
232 {
233 throw UtInput::BadValue(aInput,
234 "Expected no overlap between any models' fidelity_range. Found overlap between " +
235 aModelWithFidelity.mName + " and " + result->mName);
236 }
237}
238
239template<typename DerivedComponent>
240ut::optional<typename WsfMultiresolutionWrapperMetaModel<DerivedComponent>::Index>
241WsfMultiresolutionWrapperMetaModel<DerivedComponent>::ModelIndexForFidelity(const double aFidelity) const
242{
243 assert(!mComponentModels.empty());
244 assert(aFidelity >= 0.0);
245 assert(aFidelity <= 1.0);
246 const auto result =
247 std::find_if(mComponentModels.cbegin(),
248 mComponentModels.cend(),
249 [aFidelity](const ModelWithFidelity& model) { return model.mFidelityRange.Contains(aFidelity); });
250 if (result != mComponentModels.cend())
251 {
252 return std::distance(mComponentModels.cbegin(), result);
253 }
254 ut::log::warning() << "No matching model found in " << wsf::multiresolution::CommandName<DerivedComponent>()
255 << " on platform " << this->GetComponentParent()->GetName() << ". Using default model.";
256 if (!mDefaultComponentIndex)
257 {
258 ut::log::error() << "No default model in " << wsf::multiresolution::CommandName<DerivedComponent>()
259 << " on platform " << this->GetComponentParent()->GetName();
260 }
261 return mDefaultComponentIndex;
262}
263
264template<typename DerivedComponent>
265typename ut::optional<typename WsfMultiresolutionWrapperMetaModel<DerivedComponent>::Index>
266WsfMultiresolutionWrapperMetaModel<DerivedComponent>::ModelIndexWithName(const std::string& aTypeName) const
267{
268 const auto result = std::find_if(mComponentModels.cbegin(),
269 mComponentModels.cend(),
270 [&aTypeName](const ModelWithFidelity& model) { return model.mName == aTypeName; });
271 if (result != mComponentModels.cend())
272 {
273 return std::distance(mComponentModels.cbegin(), result);
274 }
275 else
276 {
277 return ut::nullopt;
278 }
279}
280
281template<typename DerivedComponent>
282std::unique_ptr<DerivedComponent> WsfMultiresolutionWrapperMetaModel<DerivedComponent>::GetModel() const
283{
284 const ut::optional<Index> componentIndex = ModelIndexForFidelity(this->GetFidelity());
285 if (componentIndex.has_value())
286 {
287 return std::unique_ptr<DerivedComponent>{mComponentModels[*componentIndex].mComponent->Clone()};
288 }
289 else
290 {
291 return nullptr;
292 }
293}
Definition WsfComponent.hpp:39
bool ProcessInput(UtInput &aInput) override
Definition WsfMultiresolutionPlatformComponentImpl.hpp:159
virtual std::unique_ptr< DerivedComponent > GetModel() const =0
bool ProcessInput(UtInput &aInput) override
Definition WsfMultiresolutionWrapperMetaModelImpl.hpp:39
WsfComponent * CloneComponent() const override
Definition WsfMultiresolutionWrapperMetaModelImpl.hpp:33
WsfObject * Clone() const override
Definition WsfMultiresolutionWrapperMetaModelImpl.hpp:27
Definition WsfObjectTypeList.hpp:42
T * Clone(WsfStringId aId) const override
Definition WsfObjectTypeList.hpp:118
WsfObject()
This is the constructor for the WsfObject class.
Definition WsfObject.cpp:88
Definition ComponentNameHelper.cpp:17
constexpr const char * BaseComponentName()
Definition ComponentNameHelper.hpp:103
std::string CommandName()
This name, associated with DerivedComponent, is for use in the multiresolution framework....
Definition ComponentNameHelper.hpp:74
WsfObjectTypeList< DerivedComponent > & GetObjectTypeList(UtInput &aInput)
Definition WsfMultiresolutionPlatformComponent.hpp:85
Copyrights Multiple, All Rights Reserved