Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Developer] Adjust alfalfa_utility #5259

Merged
merged 4 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions python/engine/test/PythonEngine_GTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,28 +145,28 @@ TEST_F(PythonEngineFixture, AlfalfaMeasure) {

ASSERT_EQ(measurePtr->name(), "Alfalfa Measure");

std::string workflow_json = "\
{\
\"seed\": \"../seed.osm\",\
\"weather_file\": \"../weather.epw\",\
\"steps\": [\
{\
\"arguments\": {},\
\"description\": \"The method attempts to build an alfalfa json in the measure\",\
\"measure_dir_name\": \"AlfalfaMeasure\",\
\"modeler_description\": \"The method attempts to build an alfalfa json in the measure\",\
\"name\": \"AlfalfaMeasure\"\
}\
]\
}";


std::string workflow_json = R"json(
{
"seed": "../seed.osm",
"weather_file": "../weather.epw",
"steps": [
{
"arguments": {},
"description": "The method attempts to build an alfalfa json in the measure",
"measure_dir_name": "AlfalfaMeasure",
"modeler_description": "The method attempts to build an alfalfa json in the measure",
"name": "AlfalfaMeasure"
}
]
}
)json";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, what is the json(...)json a directive to?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing, it's an optional arbitrary string, at most 16 characters long. I like to use it, when this is a json thing I put json, when it's a sql query I put sql... etc.

(technically speaking you could configure your editor to pick that up and apply a different syntax highlighting based on it)

https://en.cppreference.com/w/cpp/language/string_literal

 R"d-char-seq (optional)(r-char-seq (optional))d-char-seq (optional)"

d-char-seq - A sequence of one or more d-char s, at most 16 characters long
d-char - A character from the basic character set, except parentheses, backslash and spaces


openstudio::model::Model model;
openstudio::WorkflowJSON workflow = *openstudio::WorkflowJSON::load(workflow_json);
openstudio::measure::OSRunner runner(workflow);
EXPECT_TRUE(runner.alfalfa().getPoints().empty());

openstudio::measure::OSArgumentMap arguments;
measurePtr->run(model, runner, arguments);

EXPECT_EQ(5, runner.alfalfa().getPoints().size());
}
40 changes: 20 additions & 20 deletions src/utilities/alfalfa/AlfalfaActuator.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "AlfalfaActuator.hpp"
#include "utilities/idd/IddObject.hpp"
#include "utilities/idd/IddEnums.hpp"
#include "../idd/IddObject.hpp"
#include "../idd/IddEnums.hpp"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the convention is to use relative paths for project imports? I was opting for absolute as it doesn't break everything when you move files around.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the way all other files are as far as I can tell. It's debatable whether it's a good idea, but I'd prefer consistency.

(Bit of context: We don't move things often (if at all), and I am not afraid of spending 15 minutes to write a python script with a regex or two to fixup the paths if we need to move things)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that sounds good, currently there a question on the other PR from kyle as to whether alfalfa should be moved out of utilities to be on the same level. Ideally I think I'd wait for that before switching everything back to relative, but as you noted it's not that big of a lift to fix.


#include <utilities/idd/OS_EnergyManagementSystem_Actuator_FieldEnums.hxx>
#include <utilities/idd/EnergyManagementSystem_Actuator_FieldEnums.hxx>
Expand All @@ -12,11 +12,11 @@ namespace openstudio {
namespace alfalfa {
AlfalfaActuator::AlfalfaActuator(const std::string& component_name, const std::string& component_type, const std::string& control_type)
: AlfalfaComponent("Actuator", Capability::Input | Capability::Output) {
if (component_name.size() == 0) {
if (component_name.empty()) {
throw std::runtime_error("Error creating AlfalfaActuator: component_name must be non-empty");
} else if (component_type.size() == 0) {
} else if (component_type.empty()) {
throw std::runtime_error("Error creating AlfalfaActuator: component_type must be non-empty");
} else if (control_type.size() == 0) {
} else if (control_type.empty()) {
throw std::runtime_error("Error creating AlfalfaActuator: control_type must be non-empty");
}
parameters["component_name"] = component_name;
Expand All @@ -27,35 +27,35 @@ namespace alfalfa {
AlfalfaActuator::AlfalfaActuator(const IdfObject& actuator) : AlfalfaComponent("Actuator", Capability::Input | Capability::Output) {
IddObjectType idd_type = actuator.iddObject().type();

boost::optional<std::string> component_name;
boost::optional<std::string> component_type;
boost::optional<std::string> control_type;
std::string component_name;
std::string component_type;
std::string control_type;

if (idd_type == IddObjectType::OS_EnergyManagementSystem_Actuator) {
component_name = actuator.getString(OS_EnergyManagementSystem_ActuatorFields::ActuatedComponentName);
component_type = actuator.getString(OS_EnergyManagementSystem_ActuatorFields::ActuatedComponentType);
control_type = actuator.getString(OS_EnergyManagementSystem_ActuatorFields::ActuatedComponentControlType);
component_name = actuator.getString(OS_EnergyManagementSystem_ActuatorFields::ActuatedComponentName).value_or("");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't know about this pattern, seems like a good improvement.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

component_type = actuator.getString(OS_EnergyManagementSystem_ActuatorFields::ActuatedComponentType).value_or("");
control_type = actuator.getString(OS_EnergyManagementSystem_ActuatorFields::ActuatedComponentControlType).value_or("");
} else if (idd_type == IddObjectType::EnergyManagementSystem_Actuator) {
component_name = actuator.getString(EnergyManagementSystem_ActuatorFields::ActuatedComponentUniqueName);
component_type = actuator.getString(EnergyManagementSystem_ActuatorFields::ActuatedComponentType);
control_type = actuator.getString(EnergyManagementSystem_ActuatorFields::ActuatedComponentControlType);
component_name = actuator.getString(EnergyManagementSystem_ActuatorFields::ActuatedComponentUniqueName).value_or("");
component_type = actuator.getString(EnergyManagementSystem_ActuatorFields::ActuatedComponentType).value_or("");
control_type = actuator.getString(EnergyManagementSystem_ActuatorFields::ActuatedComponentControlType).value_or("");
} else {
throw std::runtime_error(fmt::format("Error creating AlfalfaActuator: {} is not a supported object type", idd_type.valueDescription()));
}

if (!component_name.is_initialized() || component_name.get().size() == 0) {
if (component_name.empty()) {
throw std::runtime_error("Error creating AlfalfaActuator: Object is missing a component name");
}
if (!component_type.is_initialized() || component_type.get().size() == 0) {
if (component_type.empty()) {
throw std::runtime_error("Error creating AlfalfaActuator: Object is missing a component type");
}
if (!control_type.is_initialized() || control_type.get().size() == 0) {
if (control_type.empty()) {
throw std::runtime_error("Error creating AlfalfaActuator: Object is missing a control type");
}

parameters["component_name"] = component_name.get();
parameters["component_type"] = component_type.get();
parameters["control_type"] = control_type.get();
parameters["component_name"] = component_name;
parameters["component_type"] = component_type;
parameters["control_type"] = control_type;
}
} // namespace alfalfa
} // namespace openstudio
3 changes: 1 addition & 2 deletions src/utilities/alfalfa/AlfalfaActuator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
#define ALFALFA_COMPONENT_ACTUATOR_HPP

#include "AlfalfaComponent.hpp"
#include "utilities/idf/IdfObject.hpp"
#include "utilities/core/Logger.hpp"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger was included everywhere but not used. Because you throw std::runtime_error.

Maybe you should use a proper Logger and use LOG_AND_THROW, one of the two.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean by "use a proper Logger and use LOG_AND_THROW, one of the two" do you mean or? Can you provide an example of somewhere in the OS code where the pattern you'd like me to use is?

#include "../idf/IdfObject.hpp"
#include "../UtilitiesAPI.hpp"

namespace openstudio {
Expand Down
5 changes: 2 additions & 3 deletions src/utilities/alfalfa/AlfalfaComponent.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#include "AlfalfaComponent.hpp"
#include <iostream>

namespace openstudio {
namespace alfalfa {

AlfalfaComponent::AlfalfaComponent(const std::string& type, const Capability capabilities) : type(type), capabilities(capabilities) {}
AlfalfaComponent::AlfalfaComponent(const std::string& type, Capability capabilities) : type(type), capabilities(capabilities) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is fair, since it's just a uint under the hood

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it's a POD type [1] that'll fit in a register. I tried adding a clang-tidy check for that in LLVM (and someone else did independently) but it never got in.

[1] This may be an unsigned int, and it is on GCC for eg (cf https://gcc.godbolt.org/z/47z5z3bP8), but it's actually implementation defined per the cpp standards.


bool AlfalfaComponent::canInput() const {
return capabilities & Capability::Input;
Expand All @@ -15,7 +14,7 @@ namespace alfalfa {
}

bool AlfalfaComponent::operator==(const AlfalfaComponent& rhs) const {
return type == rhs.type && capabilities == rhs.capabilities && parameters == rhs.parameters;
return (type == rhs.type) && (capabilities == rhs.capabilities) && (parameters == rhs.parameters);
}

} // namespace alfalfa
Expand Down
2 changes: 1 addition & 1 deletion src/utilities/alfalfa/AlfalfaComponent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace alfalfa {
class UTILITIES_API AlfalfaComponent
{
public:
AlfalfaComponent(const std::string& type, const Capability capabilities);
AlfalfaComponent(const std::string& type, Capability capabilities);

/**
* Get if the component can be used as an Input to the model
Expand Down
1 change: 0 additions & 1 deletion src/utilities/alfalfa/AlfalfaConstant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#define ALFALFA_COMPONENT_CONSTANT_HPP

#include "AlfalfaComponent.hpp"
#include "utilities/core/Logger.hpp"

#include "../UtilitiesAPI.hpp"

Expand Down
8 changes: 4 additions & 4 deletions src/utilities/alfalfa/AlfalfaGlobalVariable.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "AlfalfaGlobalVariable.hpp"

#include "utilities/idd/IddObject.hpp"
#include "utilities/idd/IddEnums.hpp"
#include "../idd/IddObject.hpp"
#include "../idd/IddEnums.hpp"

#include <utilities/idd/EnergyManagementSystem_GlobalVariable_FieldEnums.hxx>
#include <utilities/idd/OS_EnergyManagementSystem_GlobalVariable_FieldEnums.hxx>
Expand All @@ -13,7 +13,7 @@ namespace openstudio {
namespace alfalfa {
AlfalfaGlobalVariable::AlfalfaGlobalVariable(const std::string& variable_name)
: AlfalfaComponent("GlobalVariable", Capability::Output | Capability::Input) {
if (variable_name.size() == 0) {
if (variable_name.empty()) {
throw std::runtime_error("Error creating AlfalfaGlobalVariable: variable_name must be non-empty");
}
parameters["variable_name"] = variable_name;
Expand All @@ -32,7 +32,7 @@ namespace alfalfa {
throw std::runtime_error(fmt::format("Error creating AlfalfaGlobalVariable: {} is not a supported object type", idd_type.valueDescription()));
}

if (!variable_name.is_initialized() || variable_name.get().size() == 0) {
if (!variable_name.is_initialized() || variable_name.get().empty()) {
throw std::runtime_error("Error creating AlfalfaGlobalVariable: Object is missing a variable name");
}

Expand Down
3 changes: 1 addition & 2 deletions src/utilities/alfalfa/AlfalfaGlobalVariable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
#define ALFALFA_COMPONENT_GLOBALVARIABLE_HPP

#include "AlfalfaComponent.hpp"
#include "utilities/idf/IdfObject.hpp"
#include "utilities/core/Logger.hpp"
#include "../idf/IdfObject.hpp"

#include "../UtilitiesAPI.hpp"

Expand Down
8 changes: 4 additions & 4 deletions src/utilities/alfalfa/AlfalfaMeter.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "AlfalfaMeter.hpp"

#include "utilities/idd/IddObject.hpp"
#include "utilities/idd/IddEnums.hpp"
#include "../idd/IddObject.hpp"
#include "../idd/IddEnums.hpp"

#include <utilities/idd/OS_Output_Meter_FieldEnums.hxx>
#include <utilities/idd/Output_Meter_FieldEnums.hxx>
Expand All @@ -12,7 +12,7 @@
namespace openstudio {
namespace alfalfa {
AlfalfaMeter::AlfalfaMeter(const std::string& meter_name) : AlfalfaComponent("Meter", Capability::Output) {
if (meter_name.size() == 0) {
if (meter_name.empty()) {
throw std::runtime_error("Error creating AlfalfaMeter: meter_name must be non-empty");
}
parameters["meter_name"] = meter_name;
Expand All @@ -29,7 +29,7 @@ namespace alfalfa {
throw std::runtime_error(fmt::format("Error creating AlfalfaMeter: {} is not a supported object type", idd_type.valueDescription()));
}

if (!meter_name.is_initialized() || meter_name.get().size() == 0) {
if (!meter_name.is_initialized() || meter_name.get().empty()) {
throw std::runtime_error("Error creating AlfalfaMeter: Object is missing a meter name");
}

Expand Down
3 changes: 1 addition & 2 deletions src/utilities/alfalfa/AlfalfaMeter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
#define ALFALFA_COMPONENT_METER_HPP

#include "AlfalfaComponent.hpp"
#include "utilities/idf/IdfObject.hpp"
#include "utilities/core/Logger.hpp"
#include "../idf/IdfObject.hpp"

#include "../UtilitiesAPI.hpp"

Expand Down
12 changes: 6 additions & 6 deletions src/utilities/alfalfa/AlfalfaOutputVariable.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include "AlfalfaOutputVariable.hpp"

#include "utilities/idd/IddObject.hpp"
#include "utilities/idd/IddEnums.hpp"
#include "../idd/IddObject.hpp"
#include "../idd/IddEnums.hpp"

#include <utilities/idd/OS_EnergyManagementSystem_OutputVariable_FieldEnums.hxx>
#include <utilities/idd/OS_Output_Variable_FieldEnums.hxx>
Expand All @@ -15,9 +15,9 @@ namespace openstudio {
namespace alfalfa {
AlfalfaOutputVariable::AlfalfaOutputVariable(const std::string& variable_key, const std::string& variable_name)
: AlfalfaComponent("OutputVariable", Capability::Output) {
if (variable_key.size() == 0) {
if (variable_key.empty()) {
throw std::runtime_error("Error creating AlfalfaOutputVariable: variable_key must be non-empty");
} else if (variable_name.size() == 0) {
} else if (variable_name.empty()) {
throw std::runtime_error("Error creating AlfalfaOutputVariable: variable_name must be non-empty");
} else if (variable_key == "*") {
throw std::runtime_error("Error creating AlfalfaOutputVariable: Key must not be wildcard '*'");
Expand Down Expand Up @@ -50,10 +50,10 @@ namespace alfalfa {
throw std::runtime_error(fmt::format("Error creating AlfalfaOutputVariable: {} is not a supported object type", idd_type.valueDescription()));
}

if (!variable_key.is_initialized() || variable_key.get().size() == 0) {
if (!variable_key.is_initialized() || variable_key.get().empty()) {
throw std::runtime_error("Error creating AlfalfaOutputVariable: Object is missing a variable key");
}
if (!variable_name.is_initialized() || variable_name.get().size() == 0) {
if (!variable_name.is_initialized() || variable_name.get().empty()) {
throw std::runtime_error("Error creating AlfalfaOutputVariable: Object is missing a variable name");
}
if (variable_key.get() == "*") {
Expand Down
3 changes: 1 addition & 2 deletions src/utilities/alfalfa/AlfalfaOutputVariable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
#define ALFALFA_COMPONENT_OUTPUTVARIABLE_HPP

#include "AlfalfaComponent.hpp"
#include "utilities/idf/IdfObject.hpp"
#include "utilities/core/Logger.hpp"
#include "../idf/IdfObject.hpp"

#include "../UtilitiesAPI.hpp"

Expand Down
44 changes: 25 additions & 19 deletions src/utilities/alfalfa/AlfalfaPoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@
#include <boost/regex.hpp>
#include <fmt/format.h>

#include <memory>
#include <string_view>

namespace openstudio {
namespace alfalfa {

static constexpr std::string_view ID_VALID_CHARS_MSG = "IDs can only contain letters, numbers, and the following special characters _-[]():";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to move this out of the class.

Let me make sure I'm understanding this correctly. The purpose of using a constexpr std::string_view is that this gets populated in memory and returns a pointer at compile time so that each time the string is accessed it doesn't need to build a new one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a global static constexpr variable. I could omit the static, and it'd still be static because it's declared at global scope and it's constexpr, but this is more explicit.

And so it initializes it in static storage (the .rodata section of the binary). The string_view is materialized in the binary.

see this short example: https://gcc.godbolt.org/z/o7veTMETP

and if you want to learn more about constexpr, I suggest C++ weekly, the start of this episode maybe: https://www.youtube.com/watch?v=IDQ0ng8RIqs


namespace detail {
AlfalfaPoint_Impl::AlfalfaPoint_Impl() {}
AlfalfaPoint_Impl::AlfalfaPoint_Impl() = default;
} // namespace detail

AlfalfaPoint::AlfalfaPoint() : m_impl(std::shared_ptr<detail::AlfalfaPoint_Impl>(new detail::AlfalfaPoint_Impl())) {}
AlfalfaPoint::AlfalfaPoint() : m_impl(std::make_shared<detail::AlfalfaPoint_Impl>()) {}

AlfalfaPoint::AlfalfaPoint(const std::string& display_name) : m_impl(std::shared_ptr<detail::AlfalfaPoint_Impl>(new detail::AlfalfaPoint_Impl())) {
AlfalfaPoint::AlfalfaPoint(const std::string& display_name) : m_impl(std::make_shared<detail::AlfalfaPoint_Impl>()) {
setDisplayName(display_name);
}

Expand Down Expand Up @@ -65,7 +71,7 @@ namespace alfalfa {
if (isValidId(id)) {
m_impl->m_id = id;
} else {
throw std::runtime_error(ID_VALID_CHARS_MSG);
throw std::runtime_error(ID_VALID_CHARS_MSG.data());
}
}

Expand All @@ -74,7 +80,7 @@ namespace alfalfa {
}

void AlfalfaPoint::setDisplayName(const std::string& display_name) {
if (display_name.size() == 0) {
if (display_name.empty()) {
throw std::runtime_error("Display name must have non-zero length");
}
m_impl->m_display_name = display_name;
Expand All @@ -97,36 +103,36 @@ namespace alfalfa {
Json::Value AlfalfaPoint::toJSON() const {
Json::Value point;

if (id().is_initialized()) {
point["id"] = id().get();
if (auto id_ = id()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh cool, I didn't realize you can do assignment in the if

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

boost::optional has an operator bool(), which is going to be used here.

FYI, C++17 added init-statement to if. So you could do something like

if (auto x = func(); x.condition()) {

https://en.cppreference.com/w/cpp/language/if

point["id"] = *id_;
} else {
throw std::runtime_error("Point requires a valid ID for export. " + ID_VALID_CHARS_MSG);
throw std::runtime_error(fmt::format("Point requires a valid ID for export. {}", ID_VALID_CHARS_MSG));
}
point["name"] = displayName();
if (input().is_initialized()) {
point["input"]["type"] = input().get().type;
point["input"]["parameters"] = input().get().parameters;
if (auto input_ = input()) {
point["input"]["type"] = input_->type;
point["input"]["parameters"] = input_->parameters;
}

if (output().is_initialized()) {
point["output"]["type"] = output().get().type;
point["output"]["parameters"] = output().get().parameters;
if (auto output_ = output()) {
point["output"]["type"] = output_->type;
point["output"]["parameters"] = output_->parameters;
}

if (units().is_initialized()) {
point["units"] = units().get();
if (auto units_ = units()) {
point["units"] = *units_;
}

point["optional"] = optional();

return point;
}

bool AlfalfaPoint::isValidId(const std::string& id) const {
return boost::regex_match(id, boost::regex("^[A-Za-z0-9_\\-\\[\\]:()]*$")) && id.size() > 0;
bool AlfalfaPoint::isValidId(const std::string& id) {
return !id.empty() && boost::regex_match(id, boost::regex(R"(^[A-Za-z0-9_\-\[\]:()]*$)"));
}

std::string AlfalfaPoint::toIdString(const std::string& str) const {
std::string AlfalfaPoint::toIdString(const std::string& str) {
return boost::regex_replace(str, boost::regex(" "), "_");
}

Expand Down
Loading