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

v24.1.0-IOFreeze: HeatExchangerAirToAirSensibleAndLatent #5099

Merged
merged 17 commits into from
Mar 7, 2024

Conversation

joseph-robertson
Copy link
Collaborator

@joseph-robertson joseph-robertson commented Feb 26, 2024

Pull request overview

Remove:

  • Sensible Effectiveness at 75% Heating Air Flow
  • Latent Effectiveness at 75% Heating Air Flow
  • Sensible Effectiveness at 75% Cooling Air Flow
  • LatentEffectiveness at 75% Cooling Air Flow

Add:

  • Sensible Effectiveness of Heating Air Flow Curve Name
  • Latent Effectiveness of Heating Air Flow Curve Name
  • Sensible Effectiveness of Cooling Air Flow Curve Name
  • Latent Effectiveness of Cooling Air Flow Curve Name

Questions:

  • Do we want to support deprecated 75 methods? If so, seems like it would get tricky. They would need to operate on TableLookup objects, checking for existence, creating if not in existence, etc. I think even the 100 methods would need to modify the TableLookup objects...

Pull Request Author

  • Model API Changes / Additions
  • Any new or modified fields have been implemented in the EnergyPlus ForwardTranslator (and ReverseTranslator as appropriate)
  • Model API methods are tested (in src/model/test)
  • EnergyPlus ForwardTranslator Tests (in src/energyplus/Test)
  • If a new object or method, added a test in NREL/OpenStudio-resources: Add Link
  • If needed, added VersionTranslation rules for the objects (src/osversion/VersionTranslator.cpp)
  • Verified that C# bindings built fine on Windows, partial classes used as needed, etc.
  • All new and existing tests passes
  • If methods have been deprecated, update rest of code to use the new methods

Labels:

  • If change to an IDD file, add the label IDDChange
  • If breaking existing API, add the label APIChange
  • If deemed ready, add label Pull Request - Ready for CI so that CI builds your PR

Review Checklist

This will not be exhaustively relevant to every PR.

  • Perform a Code Review on GitHub
  • Code Style, strip trailing whitespace, etc.
  • All related changes have been implemented: model changes, model tests, FT changes, FT tests, VersionTranslation, OS App
  • Labeling is ok
  • If defect, verify by running develop branch and reproducing defect, then running PR and reproducing fix
  • If feature, test running new feature, try creative ways to break it
  • CI status: all green or justified

@joseph-robertson joseph-robertson added Pull Request - Ready for CI This pull request if finalized and is ready for continuous integration verification prior to merge. IDDChange labels Feb 26, 2024
@joseph-robertson joseph-robertson self-assigned this Feb 26, 2024
@joseph-robertson joseph-robertson mentioned this pull request Feb 26, 2024
13 tasks
@joseph-robertson joseph-robertson marked this pull request as ready for review March 5, 2024 15:25
Copy link
Collaborator

@jmarrec jmarrec left a comment

Choose a reason for hiding this comment

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

I was tempted to say we shouldn't even try to support backward compat here (just log a message saying the method does nothing now and you should call xxxCurve() method), but it seems some amount of backward compat is needed due to the SDD stuff.

I think I'm going to sleep on it, I'll try to think overnight about the amount of work that'd be necessary to properly support it.

Comment on lines 9054 to 9063
m_new.push_back(varList);
ss << varList;

m_new.push_back(var);
ss << var;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
m_new.push_back(varList);
ss << varList;
m_new.push_back(var);
ss << var;
ss << varList;
m_new.emplace_back(std::move(varList));
ss << var;
m_new.emplace_back(std::move(var));

Comment on lines 9033 to 9039
m_refactored.push_back(RefactoredObjectData(object, newObject));
ss << newObject;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
m_refactored.push_back(RefactoredObjectData(object, newObject));
ss << newObject;
ss << newObject;
m_refactored.emplace_back(object, std::move(newObject));

bool HeatExchangerAirToAirSensibleAndLatent_Impl::setSensibleEffectivenessat100CoolingAirFlow(double sensibleEffectivenessat100CoolingAirFlow) {
// FIXME: wouldn't this value need to get set on the tablelookup if it exists?
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think so. The 100% value is set, and then the Curve will express the relationship of the Effectiveness as a function of the cooling air flow. This is the same thing that's used for a eg for a Chiller: you set the COP at rated conditions, and the curve modifies based on it.

I guess remove all these FIXME

Comment on lines 543 to 594
TableIndependentVariable var(model);
var.setInterpolationMethod("Linear");
var.setExtrapolationMethod("Linear");
var.setMinimumValue(0.0);
var.setMaximumValue(10.0);
var.setUnitType("Dimensionless");
var.addValue(0.75);
var.addValue(1.0);

TableLookup s75heating(model);
s75heating.setNormalizationMethod("DivisorOnly");
s75heating.setNormalizationDivisor(0.76);
s75heating.setMinimumOutput(0.0);
s75heating.setMaximumOutput(10.0);
s75heating.setOutputUnitType("Dimensionless");
s75heating.addOutputValue(0.81);
s75heating.addOutputValue(0.76);
s75heating.addIndependentVariable(var);
setSensibleEffectivenessofHeatingAirFlowCurve(s75heating);

TableLookup l75heating(model);
l75heating.setNormalizationMethod("DivisorOnly");
l75heating.setNormalizationDivisor(0.68);
l75heating.setMinimumOutput(0.0);
l75heating.setMaximumOutput(10.0);
l75heating.setOutputUnitType("Dimensionless");
l75heating.addOutputValue(0.73);
l75heating.addOutputValue(0.68);
l75heating.addIndependentVariable(var);
setLatentEffectivenessofHeatingAirFlowCurve(l75heating);

TableLookup s75cooling(model);
s75cooling.setNormalizationMethod("DivisorOnly");
s75cooling.setNormalizationDivisor(0.76);
s75cooling.setMinimumOutput(0.0);
s75cooling.setMaximumOutput(10.0);
s75cooling.setOutputUnitType("Dimensionless");
s75cooling.addOutputValue(0.81);
s75cooling.addOutputValue(0.76);
s75cooling.addIndependentVariable(var);
setSensibleEffectivenessofCoolingAirFlowCurve(s75cooling);

TableLookup l75cooling(model);
l75cooling.setNormalizationMethod("DivisorOnly");
l75cooling.setNormalizationDivisor(0.68);
l75cooling.setMinimumOutput(0.0);
l75cooling.setMaximumOutput(10.0);
l75cooling.setOutputUnitType("Dimensionless");
l75cooling.addOutputValue(0.73);
l75cooling.addOutputValue(0.68);
l75cooling.addIndependentVariable(var);
setLatentEffectivenessofCoolingAirFlowCurve(l75cooling);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok, so I get why this is here: to maintain the same effectiveness at different airflows as we had before.

  • We could also not do this, similar to what E+ would do if you create this object in the IDF Editor
    • Leave them empty so the effectiveness is linear.
    • You could extract that bit of code into a public method, something like "applyDefaultEffectivenessCurves" or something for convenience, so that the user has the ability to use the former defaults with a single method call.
    • VT (already done here) + modification of the OS resources ruby tests would ensure we don't show EUI deviations.
  • If we want these curves to be set, then please name the Table / Vars

Comment on lines 807 to 920
// DEPRECATED
double HeatExchangerAirToAirSensibleAndLatent::sensibleEffectivenessat75HeatingAirFlow() const {
DEPRECATED_AT_MSG(3, 8, 0, "As of EnergyPlus 24.1.0, this property ...");
if (boost::optional<TableLookup> sensibleEffectivenessofHeatingAirFlowCurve_ =
sensibleEffectivenessofHeatingAirFlowCurve()->optionalCast<TableLookup>()) {
return sensibleEffectivenessofHeatingAirFlowCurve_->outputValues()[0];
} else {
return getImpl<detail::HeatExchangerAirToAirSensibleAndLatent_Impl>()->sensibleEffectivenessat100HeatingAirFlow();
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

  • You have no proof that the outputValues()[0] corresponds to X=0.75. If I created that TableLookup manually, this may very well NOT be the case.

  • You MUST check if the sensibleEffectivenessofHeatingAirFlowCurve() is initialized before you deference it or it'll throw if not. Your testing didn't catch it because the curves are optional BUT your constructor is initializing them.

  • If I have set something other than a TableLookup (meaning a regular Curve:XXX object), you're also returning the 100 airflow without warning.

Copy link
Collaborator

Choose a reason for hiding this comment

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

All the getters would work fine if we had TableLookup_Impl::evaluate, but we don't.

@@ -744,5 +804,196 @@ namespace model {
return getImpl<detail::HeatExchangerAirToAirSensibleAndLatent_Impl>()->autosizedNominalSupplyAirFlowRate();
}

// DEPRECATED
double HeatExchangerAirToAirSensibleAndLatent::sensibleEffectivenessat75HeatingAirFlow() const {
DEPRECATED_AT_MSG(3, 8, 0, "As of EnergyPlus 24.1.0, this property ...");
Copy link
Collaborator

Choose a reason for hiding this comment

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

TODO: update the message.

DEPRECATED_AT_MSG(3, 8, 0, "Use sensibleEffectivenessofHeatingAirFlowCurve instead.");

Comment on lines 857 to 880
} else if (!sensibleEffectivenessofHeatingAirFlowCurve()) {
double sensibleEffectivenessat100HeatingAirFlow =
getImpl<detail::HeatExchangerAirToAirSensibleAndLatent_Impl>()->sensibleEffectivenessat100HeatingAirFlow();

TableIndependentVariable var(model());
var.setInterpolationMethod("Linear");
var.setExtrapolationMethod("Linear");
var.setMinimumValue(0.0);
var.setMaximumValue(10.0);
var.setUnitType("Dimensionless");
var.addValue(0.75);
var.addValue(1.0);

TableLookup tableLookup(model());
tableLookup.setNormalizationMethod("DivisorOnly");
tableLookup.setNormalizationDivisor(sensibleEffectivenessat100HeatingAirFlow);
tableLookup.setMinimumOutput(0.0);
tableLookup.setMaximumOutput(10.0);
tableLookup.setOutputUnitType("Dimensionless");
tableLookup.addOutputValue(sensibleEffectivenessat75HeatingAirFlow);
tableLookup.addOutputValue(sensibleEffectivenessat100HeatingAirFlow);
tableLookup.addIndependentVariable(var);

return setSensibleEffectivenessofHeatingAirFlowCurve(tableLookup);
Copy link
Collaborator

Choose a reason for hiding this comment

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

No ambiguity in this case: if the curve isnt there, we can make a table lookup indeed.

Comment on lines 850 to 856
if (boost::optional<TableLookup> sensibleEffectivenessofHeatingAirFlowCurve_ =
sensibleEffectivenessofHeatingAirFlowCurve()->optionalCast<TableLookup>()) {
double sensibleEffectivenessat100HeatingAirFlow =
getImpl<detail::HeatExchangerAirToAirSensibleAndLatent_Impl>()->sensibleEffectivenessat100HeatingAirFlow();
sensibleEffectivenessofHeatingAirFlowCurve_->setOutputValues(
{sensibleEffectivenessat75HeatingAirFlow, sensibleEffectivenessat100HeatingAirFlow});
return setSensibleEffectivenessofHeatingAirFlowCurve(sensibleEffectivenessofHeatingAirFlowCurve_.get());
Copy link
Collaborator

Choose a reason for hiding this comment

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

same problems: can't dereference the curve object if not initialized, and the setting of the output values isn't good since you're not also modifying the independent variable.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Side note, but related to a potential final conclusion on whether we should try to maintain backward compatibilty: The TableLookup interface is crap from a user experience perspective. The former TableMultiVariableLookup was better in that sense since you could do addPoint(x, y) and it'd detect if the x value was already there, and you could call boost::optional<double> yValue(const std::vector<double>& xValues)

I think we should re-add that capability...

Comment on lines 2722 to 2728
if (boost::optional<model::Curve> curve_ = hx.sensibleEffectivenessofHeatingAirFlowCurve()) {
if (boost::optional<model::TableLookup> sensibleEffectivenessofHeatingAirFlowCurve_ = curve_->optionalCast<TableLookup>()) {
double sensibleEffectivenessat100HeatingAirFlow = hx.sensibleEffectivenessat100HeatingAirFlow();
sensibleEffectivenessofHeatingAirFlowCurve_->setOutputValues({_htgSensEff75.get(), sensibleEffectivenessat100HeatingAirFlow});
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok, so it seems we do want to support some amount of backward compatibility in setting the 75% effectivness due to this MapHVAC stuff.

I don't think the current implementation here is good though. You're creating the object, so you're in control. You should create the curve instead of checking whether it exists.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the opposite way (OSM > SDD) is missing too.

@jmarrec
Copy link
Collaborator

jmarrec commented Mar 5, 2024

Options:

  • Remove backward compatibility altogether. This would require the SDD stuff to be updated... Is that an option @kbenne ?
  • Implement backward compatibility manually. This requires a bit of code to do it right, I'm guessing I can do it in about 4 hours.
  • Implement TableLookup_Impl::evaluate: this would ideally require using https://github.com/bigladder/btwxt like E+ does, but the packaging is problematic. It has submodules dependencies (google test, fmt and bigladder's courier) and therefore creating a conan recipe is quite problematic. Using CMAke's ExternalPackageAdd isn't really helping either since some of these dependencies we also do have and we don't want to mix up deps versions.

@ci-commercialbuildings
Copy link
Collaborator

ci-commercialbuildings commented Mar 7, 2024

@jmarrec
Copy link
Collaborator

jmarrec commented Mar 7, 2024

@joseph-robertson I made some changes, this is good enough to go into the main IO freeze branch

@jmarrec jmarrec merged commit ebce271 into v24.1.0-IOFreeze Mar 7, 2024
0 of 4 checks passed
@mdahlhausen
Copy link
Collaborator

@joseph-robertson @jmarrec we may need to modify the work done in this PR. Some models made in OpenStudio are hitting a failure in EnergyPlus with the default curve set: NREL/EnergyPlus#10464

@mdahlhausen
Copy link
Collaborator

update - issue is the default table from OpenStudio has a divide by zero. Need to change the OpenStudio default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
IDDChange Pull Request - Ready for CI This pull request if finalized and is ready for continuous integration verification prior to merge.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants