Repository: NREL/OpenStudio (language: python)

Issue:
controller outdoorair object
I am using the openstudio package in python. I want to modify the object Controller:OutdoorAir object.

I want to put 'True' or 'Yes' for de field : 'High Humidity Control' and put the name of the zone for the field 'Humidistat Control Zone Name'.

To do this I do the following instructions :

controller_outdoorair = [CA for CA in openstudio.model.getControllerOutdoorAirs(osm)]

for index in range(len(controller_outdoorair)): 
      controller_outdoorair[index].setHighHumidityControl(True)

It works for the field 'High Humidity Control' but It seems that there is no function for the field 'Humidistat Control Zone Name'. The instruction 
controller_outdoorair[index].setHumidistatControlZoneName("Zone name") 
does not work.

I read the documentation here : [https://openstudio-sdk-documentation....](https://openstudio-sdk-documentation.s3.amazonaws.com/cpp/OpenStudio-3.8.0-doc/model/html/classopenstudio_1_1model_1_1_controller_outdoor_air.html)

It sounds that the function to define the zone name does not exist.

Is it normal ?

Regards,
controller outdoorair object
I am using the openstudio package in python. I want to modify the object Controller:OutdoorAir object.

I want to put 'True' or 'Yes' for de field : 'High Humidity Control' and put the name of the zone for the field 'Humidistat Control Zone Name'.

To do this I do the following instructions :

controller_outdoorair = [CA for CA in openstudio.model.getControllerOutdoorAirs(osm)]

for index in range(len(controller_outdoorair)): 
      controller_outdoorair[index].setHighHumidityControl(True)

It works for the field 'High Humidity Control' but It seems that there is no function for the field 'Humidistat Control Zone Name'. The instruction 
controller_outdoorair[index].setHumidistatControlZoneName("Zone name") 
does not work.

I read the documentation here : [https://openstudio-sdk-documentation....](https://openstudio-sdk-documentation.s3.amazonaws.com/cpp/OpenStudio-3.8.0-doc/model/html/classopenstudio_1_1model_1_1_controller_outdoor_air.html)

It sounds that the function to define the zone name does not exist.

Is it normal ?

Regards,


Discussion / hints:
Indeed `setHumidistatControlZone(...)` is commented out and unavailable: https://github.com/NREL/OpenStudio/blob/develop/src/model/ControllerOutdoorAir.hpp#L111.

There is also no forward translation for this field: https://github.com/NREL/OpenStudio/blob/develop/src/energyplus/ForwardTranslator/ForwardTranslateControllerOutdoorAir.cpp#L143-L158.

I need to check whether this omission is intentional or not. The RT seems to suggest that it's intentionally not supported: https://github.com/NREL/OpenStudio/blob/develop/src/energyplus/ReverseTranslator/ReverseTranslateControllerOutdoorAir.cpp#L126-L129.
Indeed `setHumidistatControlZone(...)` is commented out and unavailable: https://github.com/NREL/OpenStudio/blob/develop/src/model/ControllerOutdoorAir.hpp#L111.

There is also no forward translation for this field: https://github.com/NREL/OpenStudio/blob/develop/src/energyplus/ForwardTranslator/ForwardTranslateControllerOutdoorAir.cpp#L143-L158.

I need to check whether this omission is intentional or not. The RT seems to suggest that it's intentionally not supported: https://github.com/NREL/OpenStudio/blob/develop/src/energyplus/ReverseTranslator/ReverseTranslateControllerOutdoorAir.cpp#L126-L129.

Fix patch:
diff --git a/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_8_0_TBD.md b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_8_0_20240508.md
similarity index 80%
rename from developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_8_0_TBD.md
rename to developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_8_0_20240508.md
index 8a70cec5ba0..c55450476ea 100644
--- a/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_8_0_TBD.md
+++ b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_8_0_20240508.md
@@ -1,6 +1,6 @@
 # OpenStudio Version 3.8.0
 
-_Release Notes_ - _TBD_
+_Release Notes_ - 2024-05-08
 
 These release notes describe version 3.8.0 of the OpenStudio SDK developed by the National Renewable Energy Laboratory (NREL), Buildings and Thermal Sciences Center, Commercial Buildings Research Group, Tools Development Section, and associated collaborators. The notes are organized into the following sections:
 
@@ -28,9 +28,9 @@ __**OpenStudio SDK 3.8.0**__
 # Where to Find OpenStudio SDK Documentation
 
 - OpenStudio SDK release documentation, including these release notes, tutorials, and other user documentation, is available at [https://www.openstudio.net/](https://www.openstudio.net/.)
-- C++ API documentation is available at [https://openstudio-sdk-documentation.s3.amazonaws.com/index.html](https://openstudio-sdk-documentation.s3.amazonaws.com/index.html)
-- Measure development documentation is available at [http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/](http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/ )
-- A roadmap for planned features is available at http://nrel.github.io/OpenStudio-user-documentation/getting_started/roadmap/.
+- C++ API documentation is available at [OpenStudio SDK Documentation](https://openstudio-sdk-documentation.s3.amazonaws.com/index.html)
+- Measure development documentation is available at [OpenStudio Measure Writer's Reference Guide](http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/ )
+- A roadmap for planned features is available at [Roadmap](http://nrel.github.io/OpenStudio-user-documentation/getting_started/roadmap/)
 
 # Installation Notes
 
@@ -53,7 +53,7 @@ As usual, you can refer to the **[OpenStudio SDK Compatibility Matrix](https://g
 - Download and install [OpenStudio SDK](https://github.com/NREL/openstudio) and/or any third party tool that embeds the OpenStudio SDK into their software such as [openstudiocoalition/OpenStudioApplication](https://github.com/openstudiocoalition/OpenStudioApplication) depending on your needs. Select components for installation. Note that OpenStudio Application is a standalone app and does not require you to install OpenStudio SDK.
 - A BCL AUth Key is no longer needed to access content on [Building Component Library (BCL)](https://bcl.nrel.gov) BCL is typically accessed through third party OpenStudio applications to provide modelers access to building components and OpenStudio measures; however you an also browse it and download content using the BCL link above.
 
-For help with common installation problems please visit, http://nrel.github.io/OpenStudio-user-documentation/getting_started/getting_started/.
+For help with common installation problems please visit [Getting Started](http://nrel.github.io/OpenStudio-user-documentation/getting_started/getting_started/).
 
 # OpenStudio SDK:
 
@@ -87,11 +87,12 @@ For a list of deprecated and removed methods, please refer to [deprecated_method
    * [#5037](https://github.com/NREL/OpenStudio/issues/5037)
    * [#4918](https://github.com/NREL/OpenStudio/issues/4918)
 
-* [#TBD]() Refactoring and relocating model methods that has been in the openStudio-extension Gem to be in the openstudio-standards Gem or in OpenStudio C++ SDK. A few methods will be moved directly into measures. The result of this is that with OpenStudio 3.8.0 no measures should have to require 'openstudio-extension' within a measure's Ruby code.
 
-* [#TBD]() Update to version 0.6.0 of the openstudio_standards Gem. In addition to some refactoring, this release also included conversion of 90.1 data to formal database.
+* Refactoring and relocating model methods that has been in the openstudio-extensio-gem Gem to be in the openstudio-standards Gem or in OpenStudio C++ SDK. A few methods will be moved directly into measures. The result of this is that with OpenStudio 3.8.0 no measures should have to require 'openstudio-extension' within a measure's Ruby code.
+    * [#5142](https://github.com/NREL/OpenStudio/pull/5142) - Add native support for measure argument methods in OsLib_HelperMethods
 
-* [#TBD]() Enhancements to the Building Component Library (BCL)
+* Enhancements to the Building Component Library (BCL)
+    * [#5126](https://github.com/NREL/OpenStudio/pull/5126) - Add versionModified, repo, org, releaseTag to BCLSearchResult
     * Support for OpenStudio Python Measures
     * Updating Measure Details page to include measure language
     * Link to GitHub Repo from Measure Details page
@@ -111,16 +112,27 @@ For a list of deprecated and removed methods, please refer to [deprecated_method
 
 ## Minor changes and bug fixes
 
-*
-*
-*
-
-## Known Issues / Limitations
-
-*
-*
-*
+Refer to the changelog on the release page at [v3.8.0](https://github.com/NREL/OpenStudio/releases/v3.8.0)
 
 **Full Changelog**: https://github.com/NREL/OpenStudio/compare/v3.7.0...v3.8.0
 
 **New Contributors**:
+
+### OpenStudio Standards v0.6.1
+
+Update the openstudio-standards gem to version [0.6.1](https://github.com/NREL/openstudio-standards/releases/tag/v0.6.1)
+In addition to some refactoring, this release also included conversion of 90.1 data to formal database.
+
+---
+# This YAML header controls the pandoc (via TeX) to PDF settings
+# To convert the markdown to pdf, do `pandoc release_notes.md -o release_notes.pdf`
+title:  'OpenStudio Release Notes - 3.8.0'
+author:
+- National Renewable Energy Laboratory
+colorlinks: true
+linkcolor: blue
+urlcolor: Mahogany
+toccolor: gray
+geometry:
+- margin=1in
+---
diff --git a/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_8_0_20240508.pdf b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_8_0_20240508.pdf
new file mode 100644
index 00000000000..6bde86229b1
Binary files /dev/null and b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_8_0_20240508.pdf differ
diff --git a/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_0_TBD.md b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_0_TBD.md
new file mode 100644
index 00000000000..6cded456f37
--- /dev/null
+++ b/developer/doc/ReleaseNotes/OpenStudio_Release_Notes_3_9_0_TBD.md
@@ -0,0 +1,118 @@
+# OpenStudio Version 3.9.0
+
+_Release Notes_ -  _TDB_
+
+These release notes describe version 3.9.0 of the OpenStudio SDK developed by the National Renewable Energy Laboratory (NREL), Buildings and Thermal Sciences Center, Commercial Buildings Research Group, Tools Development Section, and associated collaborators. The notes are organized into the following sections:
+
+-  Overview
+-  Where to Find OpenStudio Documentation
+-  Installation Notes
+-  OpenStudio SDK: Changelog
+
+# Overview
+
+As of April 2020, development and distribution of the OpenStudioApplication and the SketchUp plugin have transitioned to the OpenStudio Coalition, who is independently managing and distributing the software through its own [openstudiocoalition/OpenStudioApplication](https://github.com/openstudiocoalition/OpenStudioApplication) repository. The OpenStudio SDK is continuing to be actively developed and distributed by NREL and is released two times per year, through a spring and a fall release.
+
+Below is the list of components that is included in this SDK installer:
+
+__**OpenStudio SDK 3.9.0**__
+- EnergyPlus
+- Command Line Interface (CLI)
+- Radiance
+- Ruby API
+- Python API
+- C++ SDK
+
+**Note** that [PAT](https://github.com/NREL/OpenStudio-PAT) is not included in either the SDK or the OpenStudio Coalition's Application installers. You will need to install PAT separately which is distributed on the [OpenStudio-PAT](https://github.com/NREL/OpenStudio-PAT) GitHub page.
+
+# Where to Find OpenStudio SDK Documentation
+
+- OpenStudio SDK release documentation, including these release notes, tutorials, and other user documentation, is available at [https://www.openstudio.net/](https://www.openstudio.net/.)
+- C++ API documentation is available at [OpenStudio SDK Documentation](https://openstudio-sdk-documentation.s3.amazonaws.com/index.html)
+- Measure development documentation is available at [OpenStudio Measure Writer's Reference Guide](http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/ )
+- A roadmap for planned features is available at [Roadmap](http://nrel.github.io/OpenStudio-user-documentation/getting_started/roadmap/)
+
+# Installation Notes
+
+OpenStudio SDK 3.9.0 is supported on:
+
+* 64-bit Windows 7 – 11
+* macOS: 11.6+ x86_64, 12.1+ arm64
+* Ubuntu: 20.04 x86_64, 22.04 x86_64, 22.04 arm64
+* Centos7
+
+OpenStudio SDK 3.9.0 supports [EnergyPlus Release 24.2.0a](https://github.com/NREL/EnergyPlus/releases/tag/24.2.0a), which is bundled with the OpenStudio installer. It is no longer necessary to download and install EnergyPlus separately. Other builds of EnergyPlus are not supported by OpenStudio SDK 3.9.0.
+
+OpenStudio SDK 3.9.0 supports Radiance 5.0.a.12, which is bundled with the OpenStudio installer; users no longer must install Radiance separately, and OpenStudio will use the included Radiance version regardless of any other versions that may be installed on the system. Other builds of Radiance are not supported by OpenStudio SDK 3.9.0.
+
+As usual, you can refer to the **[OpenStudio SDK Compatibility Matrix](https://github.com/NREL/OpenStudio/wiki/OpenStudio-SDK-Version-Compatibility-Matrix)** for more information.
+
+
+## Installation Steps
+
+- Download and install [OpenStudio SDK](https://github.com/NREL/openstudio) and/or any third party tool that embeds the OpenStudio SDK into their software such as [openstudiocoalition/OpenStudioApplication](https://github.com/openstudiocoalition/OpenStudioApplication) depending on your needs. Select components for installation. Note that OpenStudio Application is a standalone app and does not require you to install OpenStudio SDK.
+- A BCL AUth Key is no longer needed to access content on [Building Component Library (BCL)](https://bcl.nrel.gov) BCL is typically accessed through third party OpenStudio applications to provide modelers access to building components and OpenStudio measures; however you an also browse it and download content using the BCL link above.
+
+For help with common installation problems please visit [Getting Started](http://nrel.github.io/OpenStudio-user-documentation/getting_started/getting_started/).
+
+# OpenStudio SDK: Changelog
+
+The 3.9.0 is a **major** release. This update includes several new features, performance improvements, and bug fixes.
+
+## C++ Workflow code
+
+As of OpenStudio SDK 3.7.0 a re-written workflow written in C++ is used by default in place of the Ruby based Workflow Gem that had been used in the past. This enhancement is in support of Python measures being used in an OpenStudio workflow, including mixed language workflows that include both Ruby Measures and Python Measures. If you need to use the older Workflow Gem implementation, you can do that by using the `classic` subcommand after `openstudio`. `classic` will be deprecated in a future version of OpenStudio.
+
+## Python Bindings
+
+As of OpenStudio SDK 3.2.0, Python bindings are officially supported and distributed through Python Package Index (PyPI). To install, users will need to have Python3 installed along with pip and simply run the following command in a terminal window.
+
+`pip install openstudio==3.9.0`
+
+Please see [openstudio on PyPi](https://pypi.org/project/openstudio/) for further instructions on how to install. Users can also visit the test channel at [openstudio on TestPyPi](https://test.pypi.org/project/openstudio/) to install development bindings.
+
+You can also refer to the [OpenStudio SDK Python Binding Version Compatibility Matrix](https://github.com/NREL/OpenStudio/wiki/OpenStudio-SDK-Python-Binding-Version-Compatibility-Matrix) to see the list of supported platforms and python versions.
+
+## New Features, Major Fixes and API-breaking changes
+
+* [#5242](https://github.com/NREL/OpenStudio/pull/5242) - Update to EnergyPlus v24.2.0a
+    * To see the full list of additions and changes, refer to the issue [#5240](https://github.com/NREL/OpenStudio/issues/5240)
+
+* [#5237](https://github.com/NREL/OpenStudio/pull/5237) - Updates to Controller:OutdoorAir
+    * This PR implements the fields `Humidistat Control Zone Name` and `Electronic Enthalpy Limit Curve`
+    * `ControllerOutdoorAir` has two API-breaking changes for `High Humidity Outdoor Air Flow Ratio` and `Control High Indoor Humidity Based on Outdoor Humidity Ratio`. These fields are now-required, so the getters no longer return an optional
+        * `getHighHumidityOutdoorAirFlowRatio` (`boost::optional<double>` to `double`)
+        * `getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio` (`boost::optional<bool>` to `bool`)
+
+## Minor changes and bug fixes
+
+Refer to the changelog on the release page at [v3.9.0](https://github.com/NREL/OpenStudio/releases/v3.9.0)
+
+**Full Changelog**: https://github.com/NREL/OpenStudio/compare/v3.9.0...v3.9.0
+
+**New Contributors**:
+
+### OpenStudio Standards v@STANDARDS_VERSION@
+
+Update the openstudio-standards gem to version [@STANDARDS_VERSION@](https://github.com/NREL/openstudio-standards/releases/tag/v@STANDARDS_VERSION@)
+In addition to some refactoring, this release also included conversion of 90.1 data to formal database.
+
+
+### OpenStudio Server vXXX
+
+
+
+
+---
+# This YAML header controls the pandoc (via TeX) to PDF settings
+# To convert the markdown to pdf, do `pandoc release_notes.md -o release_notes.pdf`
+title:  'OpenStudio Release Notes - 3.9.0'
+author:
+- National Renewable Energy Laboratory
+colorlinks: true
+linkcolor: blue
+urlcolor: Mahogany
+toccolor: gray
+geometry:
+- margin=1in
+---
diff --git a/developer/doc/ReleaseNotes/ReleaseNotes_template.md b/developer/doc/ReleaseNotes/ReleaseNotes_template.md
index 66362ec6f98..e24f1ddbf2f 100644
--- a/developer/doc/ReleaseNotes/ReleaseNotes_template.md
+++ b/developer/doc/ReleaseNotes/ReleaseNotes_template.md
@@ -20,6 +20,7 @@ __**OpenStudio SDK @VERSION@**__
 - Command Line Interface (CLI)
 - Radiance
 - Ruby API
+- Python API
 - C++ SDK
 
 **Note** that [PAT](https://github.com/NREL/OpenStudio-PAT) is not included in either the SDK or the OpenStudio Coalition's Application installers. You will need to install PAT separately which is distributed on the [OpenStudio-PAT](https://github.com/NREL/OpenStudio-PAT) GitHub page.
@@ -27,20 +28,20 @@ __**OpenStudio SDK @VERSION@**__
 # Where to Find OpenStudio SDK Documentation
 
 - OpenStudio SDK release documentation, including these release notes, tutorials, and other user documentation, is available at [https://www.openstudio.net/](https://www.openstudio.net/.)
-- C++ API documentation is available at [https://openstudio-sdk-documentation.s3.amazonaws.com/index.html](https://openstudio-sdk-documentation.s3.amazonaws.com/index.html)
-- Measure development documentation is available at [http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/](http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/ )
-- A roadmap for planned features is available at http://nrel.github.io/OpenStudio-user-documentation/getting_started/roadmap/.
+- C++ API documentation is available at [OpenStudio SDK Documentation](https://openstudio-sdk-documentation.s3.amazonaws.com/index.html)
+- Measure development documentation is available at [OpenStudio Measure Writer's Reference Guide](http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/ )
+- A roadmap for planned features is available at [Roadmap](http://nrel.github.io/OpenStudio-user-documentation/getting_started/roadmap/)
 
 # Installation Notes
 
 OpenStudio SDK @VERSION@ is supported on:
 
 * 64-bit Windows 7 – 11
-* macOS: 10.15+ x86_64, 12.1+ arm64
+* macOS: 11.6+ x86_64, 12.1+ arm64
 * Ubuntu: 20.04 x86_64, 22.04 x86_64, 22.04 arm64
 * Centos7
 
-OpenStudio SDK @VERSION@ supports [EnergyPlus Release 23.1.0](https://github.com/NREL/EnergyPlus/releases/tag/v23.1.0), which is bundled with the OpenStudio installer. It is no longer necessary to download and install EnergyPlus separately. Other builds of EnergyPlus are not supported by OpenStudio SDK @VERSION@.
+OpenStudio SDK @VERSION@ supports [EnergyPlus Release @EP_VERSION@](https://github.com/NREL/EnergyPlus/releases/tag/v@EP_VERSION@), which is bundled with the OpenStudio installer. It is no longer necessary to download and install EnergyPlus separately. Other builds of EnergyPlus are not supported by OpenStudio SDK @VERSION@.
 
 OpenStudio SDK @VERSION@ supports Radiance 5.0.a.12, which is bundled with the OpenStudio installer; users no longer must install Radiance separately, and OpenStudio will use the included Radiance version regardless of any other versions that may be installed on the system. Other builds of Radiance are not supported by OpenStudio SDK @VERSION@.
 
@@ -49,17 +50,18 @@ As usual, you can refer to the **[OpenStudio SDK Compatibility Matrix](https://g
 
 ## Installation Steps
 
-- Download and install [OpenStudio SDK](https://github.com/NREL/openstudio) and/or [openstudiocoalition/OpenStudioApplication](https://github.com/openstudiocoalition/OpenStudioApplication) depending on your needs. Select components for installation. Note that OpenStudio Application is a standalone app and does not require you to install OpenStudio SDK.
-- Setup a Building Component Library (BCL) account to access online building components and measures. View instructions on how to setup your account and configure the key in OpenStudio.
-- The OpenStudio Application SketchUp Plug-in requires SketchUp 2021-2022 (not available for Linux). The OpenStudio Application SketchUp Plug-in does not support older versions of SketchUp. SketchUp must be installed before OpenStudio Application to automatically activate the plugin. If you install SketchUp after OpenStudio Application, simply re-run the OpenStudio Application installer.
+- Download and install [OpenStudio SDK](https://github.com/NREL/openstudio) and/or any third party tool that embeds the OpenStudio SDK into their software such as [openstudiocoalition/OpenStudioApplication](https://github.com/openstudiocoalition/OpenStudioApplication) depending on your needs. Select components for installation. Note that OpenStudio Application is a standalone app and does not require you to install OpenStudio SDK.
+- A BCL AUth Key is no longer needed to access content on [Building Component Library (BCL)](https://bcl.nrel.gov) BCL is typically accessed through third party OpenStudio applications to provide modelers access to building components and OpenStudio measures; however you an also browse it and download content using the BCL link above.
 
-For help with common installation problems please visit, http://nrel.github.io/OpenStudio-user-documentation/getting_started/getting_started/.
+For help with common installation problems please visit [Getting Started](http://nrel.github.io/OpenStudio-user-documentation/getting_started/getting_started/).
 
 # OpenStudio SDK: Changelog
 
-The @VERSION@ is a <minor/major> release. This update includes several new features, performance improvements, and bug fixes.
-You can find the list of Pull Requests that got into this release [here](https://github.com/NREL/OpenStudio/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aclosed+created%3A2023-05-12..2023-10-26+).
+The @VERSION@ is a **<minor/major>** release. This update includes several new features, performance improvements, and bug fixes.
 
+## C++ Workflow code
+
+As of OpenStudio SDK 3.7.0 a re-written workflow written in C++ is used by default in place of the Ruby based Workflow Gem that had been used in the past. This enhancement is in support of Python measures being used in an OpenStudio workflow, including mixed language workflows that include both Ruby Measures and Python Measures. If you need to use the older Workflow Gem implementation, you can do that by using the `classic` subcommand after `openstudio`. `classic` will be deprecated in a future version of OpenStudio.
 
 ## Python Bindings
 
@@ -78,16 +80,33 @@ You can also refer to the [OpenStudio SDK Python Binding Version Compatibility M
 
 ## Minor changes and bug fixes
 
+Refer to the changelog on the release page at [v@VERSION@](https://github.com/NREL/OpenStudio/releases/v@VERSION@)
 
 **Full Changelog**: https://github.com/NREL/OpenStudio/compare/v@VERSION@...v@VERSION@
 
 **New Contributors**:
 
-### OpenStudio Standards vXXX
+### OpenStudio Standards v@STANDARDS_VERSION@
+
+Update the openstudio-standards gem to version [@STANDARDS_VERSION@](https://github.com/NREL/openstudio-standards/releases/tag/v@STANDARDS_VERSION@)
+In addition to some refactoring, this release also included conversion of 90.1 data to formal database.
 
 
 ### OpenStudio Server vXXX
 
 
-## Issue Statistics Since Previous Release
 
+
+---
+# This YAML header controls the pandoc (via TeX) to PDF settings
+# To convert the markdown to pdf, do `pandoc release_notes.md -o release_notes.pdf`
+title:  'OpenStudio Release Notes - @VERSION@'
+author:
+- National Renewable Energy Laboratory
+colorlinks: true
+linkcolor: blue
+urlcolor: Mahogany
+toccolor: gray
+geometry:
+- margin=1in
+---
diff --git a/resources/model/OpenStudio.idd b/resources/model/OpenStudio.idd
index 105e0d2169f..95d26f8ffdc 100644
--- a/resources/model/OpenStudio.idd
+++ b/resources/model/OpenStudio.idd
@@ -22427,7 +22427,7 @@ OS:Controller:OutdoorAir,
        \note This field is only used when the field High Humidity Control = Yes.
        \type real
        \minimum> 0
-       \default 1.0
+       \required-field
   A19, \field Control High Indoor Humidity Based on Outdoor Humidity Ratio
        \note If No is selected, the outdoor air flow rate is modified any time indoor relative
        \note humidity is above the humidistat setpoint. If Yes is selected, the outdoor air
@@ -22435,7 +22435,7 @@ OS:Controller:OutdoorAir,
        \note setpoint and the outdoor humidity ratio is less than the indoor humidity ratio.
        \note This field is only used when the field High Humidity Control = Yes.
        \type choice
-       \default Yes
+       \required-field
        \key Yes
        \key No
   A20, \field Heat Recovery Bypass Control Type
diff --git a/src/energyplus/ForwardTranslator/ForwardTranslateControllerOutdoorAir.cpp b/src/energyplus/ForwardTranslator/ForwardTranslateControllerOutdoorAir.cpp
index 989cd310e08..3b4ebd3b03c 100644
--- a/src/energyplus/ForwardTranslator/ForwardTranslateControllerOutdoorAir.cpp
+++ b/src/energyplus/ForwardTranslator/ForwardTranslateControllerOutdoorAir.cpp
@@ -9,6 +9,9 @@
 #include "../../model/ControllerMechanicalVentilation.hpp"
 #include "../../model/Node.hpp"
 #include "../../model/Schedule.hpp"
+#include "../../model/ThermalZone.hpp"
+#include "../../model/Curve.hpp"
+#include "../../model/ZoneControlHumidistat.hpp"
 #include <utilities/idd/Controller_OutdoorAir_FieldEnums.hxx>
 #include <utilities/idd/IddEnums.hxx>
 
@@ -119,9 +122,14 @@ namespace energyplus {
     } else {
       idfObject.setString(openstudio::Controller_OutdoorAirFields::EconomizerMaximumLimitDewpointTemperature, "");
     }
+
     ///////////////////////////////////////////////////////////////////////////
     // Field: Electronic Enthalpy Limit Curve Name ////////////////////////////
-    idfObject.setString(openstudio::Controller_OutdoorAirFields::ElectronicEnthalpyLimitCurveName, "");
+    if (boost::optional<model::Curve> curve = modelObject.electronicEnthalpyLimitCurve()) {
+      if (boost::optional<IdfObject> _curve = translateAndMapModelObject(curve.get())) {
+        idfObject.setString(Controller_OutdoorAirFields::ElectronicEnthalpyLimitCurveName, _curve->name().get());
+      }
+    }
 
     ///////////////////////////////////////////////////////////////////////////
     // Field: Economizer Minimum Limit DryBulb Temperature ////////////////////
@@ -151,6 +159,17 @@ namespace energyplus {
       }
     }
 
+    // HumidistatControlZoneName
+    if (auto zone_ = modelObject.humidistatControlZone()) {
+      if (boost::optional<ZoneControlHumidistat> humidistat = zone_->zoneControlHumidistat()) {
+        idfObject.setString(openstudio::Controller_OutdoorAirFields::HumidistatControlZoneName, zone_->nameString());
+      } else {
+        LOG(Warn, modelObject.briefDescription() << " has a humidistat control zone " << zone_->nameString()
+                                                 << " without a zone control humidistat; humidistat control zone field will not be translated");
+        idfObject.setString(openstudio::Controller_OutdoorAirFields::HighHumidityControl, "No");
+      }
+    }
+
     // HighHumidityOutdoorAirFlowRatio
     d = modelObject.getHighHumidityOutdoorAirFlowRatio();
     if (d) {
@@ -158,13 +177,10 @@ namespace energyplus {
     }
 
     // ControlHighIndoorHumidityBasedonOutdoorHumidityRatio
-    ob = modelObject.getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio();
-    if (ob) {
-      if (*ob) {
-        idfObject.setString(openstudio::Controller_OutdoorAirFields::ControlHighIndoorHumidityBasedonOutdoorHumidityRatio, "Yes");
-      } else {
-        idfObject.setString(openstudio::Controller_OutdoorAirFields::ControlHighIndoorHumidityBasedonOutdoorHumidityRatio, "No");
-      }
+    if (modelObject.getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio()) {
+      idfObject.setString(openstudio::Controller_OutdoorAirFields::ControlHighIndoorHumidityBasedonOutdoorHumidityRatio, "Yes");
+    } else {
+      idfObject.setString(openstudio::Controller_OutdoorAirFields::ControlHighIndoorHumidityBasedonOutdoorHumidityRatio, "No");
     }
 
     // HeatRecoveryBypassControlType
diff --git a/src/energyplus/ReverseTranslator/ReverseTranslateControllerOutdoorAir.cpp b/src/energyplus/ReverseTranslator/ReverseTranslateControllerOutdoorAir.cpp
index 4fb7d3fe808..3b7450b05ee 100644
--- a/src/energyplus/ReverseTranslator/ReverseTranslateControllerOutdoorAir.cpp
+++ b/src/energyplus/ReverseTranslator/ReverseTranslateControllerOutdoorAir.cpp
@@ -6,6 +6,14 @@
 #include "../ReverseTranslator.hpp"
 #include "../../model/ControllerOutdoorAir.hpp"
 #include "../../model/ControllerOutdoorAir_Impl.hpp"
+#include "../../model/ThermalZone.hpp"
+#include "../../model/ThermalZone_Impl.hpp"
+#include "../../model/Space.hpp"
+#include "../../model/Space_Impl.hpp"
+#include "../../model/Curve.hpp"
+#include "../../model/Curve_Impl.hpp"
+#include "../../model/Schedule.hpp"
+#include "../../model/Schedule_Impl.hpp"
 #include <utilities/idd/Controller_OutdoorAir_FieldEnums.hxx>
 #include "../../utilities/idd/IddEnums.hpp"
 #include <utilities/idd/IddEnums.hxx>
@@ -22,6 +30,9 @@ namespace energyplus {
       return boost::none;
     }
 
+    boost::optional<WorkspaceObject> _wo;
+    boost::optional<ModelObject> _mo;
+
     ControllerOutdoorAir mo(m_model);
 
     boost::optional<std::string> s = workspaceObject.getString(Controller_OutdoorAirFields::Name);
@@ -64,9 +75,14 @@ namespace energyplus {
       mo.setEconomizerMaximumLimitDewpointTemperature(value.get());
     }
 
-    s = workspaceObject.getString(Controller_OutdoorAirFields::ElectronicEnthalpyLimitCurveName);
-    if (s) {
-      LOG(Error, "ControllerOutdoorAir " << workspaceObject.briefDescription() << " references a curve that is not supported");
+    if ((_wo = workspaceObject.getTarget(Controller_OutdoorAirFields::ElectronicEnthalpyLimitCurveName))) {
+      if ((_mo = translateAndMapWorkspaceObject(_wo.get()))) {
+        if (boost::optional<Curve> _curve = _mo->optionalCast<Curve>()) {
+          mo.setElectronicEnthalpyLimitCurve(_curve.get());
+        } else {
+          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Electronic Enthalpy Limit Curve Name'");
+        }
+      }
     }
 
     value = workspaceObject.getDouble(Controller_OutdoorAirFields::EconomizerMinimumLimitDryBulbTemperature);
@@ -84,22 +100,34 @@ namespace energyplus {
       mo.setMinimumLimitType(s.get());
     }
 
-    s = workspaceObject.getString(Controller_OutdoorAirFields::MinimumOutdoorAirScheduleName);
-    if (s) {
-      LOG(Warn,
-          "ControllerOutdoorAir " << workspaceObject.briefDescription() << " references a minimum outdoor air schedule, which is not supported");
+    if ((_wo = workspaceObject.getTarget(Controller_OutdoorAirFields::MinimumOutdoorAirScheduleName))) {
+      if ((_mo = translateAndMapWorkspaceObject(_wo.get()))) {
+        if (boost::optional<Schedule> _schedule = _mo->optionalCast<Schedule>()) {
+          mo.setMinimumOutdoorAirSchedule(_schedule.get());
+        } else {
+          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Minimum Outdoor Air Schedule Name'");
+        }
+      }
     }
 
-    s = workspaceObject.getString(Controller_OutdoorAirFields::MinimumFractionofOutdoorAirScheduleName);
-    if (s) {
-      LOG(Warn, "ControllerOutdoorAir " << workspaceObject.briefDescription()
-                                        << " references a minimum fraction of outdoor air schedule, which is not supported");
+    if ((_wo = workspaceObject.getTarget(Controller_OutdoorAirFields::MinimumFractionofOutdoorAirScheduleName))) {
+      if ((_mo = translateAndMapWorkspaceObject(_wo.get()))) {
+        if (boost::optional<Schedule> _schedule = _mo->optionalCast<Schedule>()) {
+          mo.setMinimumFractionofOutdoorAirSchedule(_schedule.get());
+        } else {
+          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Minimum Fraction of Outdoor Air Schedule Name'");
+        }
+      }
     }
 
-    s = workspaceObject.getString(Controller_OutdoorAirFields::MaximumFractionofOutdoorAirScheduleName);
-    if (s) {
-      LOG(Warn, "ControllerOutdoorAir " << workspaceObject.briefDescription()
-                                        << " references a maximum fraction of outdoor air schedule, which is not supported");
+    if ((_wo = workspaceObject.getTarget(Controller_OutdoorAirFields::MaximumFractionofOutdoorAirScheduleName))) {
+      if ((_mo = translateAndMapWorkspaceObject(_wo.get()))) {
+        if (boost::optional<Schedule> _schedule = _mo->optionalCast<Schedule>()) {
+          mo.setMaximumFractionofOutdoorAirSchedule(_schedule.get());
+        } else {
+          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Maximum Fraction of Outdoor Air Schedule Name'");
+        }
+      }
     }
 
     s = workspaceObject.getString(Controller_OutdoorAirFields::MechanicalVentilationControllerName);
@@ -108,24 +136,27 @@ namespace energyplus {
           "ControllerOutdoorAir " << workspaceObject.briefDescription() << " references a mechanical ventilation controller, which is not supported");
     }
 
-    s = workspaceObject.getString(Controller_OutdoorAirFields::TimeofDayEconomizerControlScheduleName);
-    if (s) {
-      LOG(Warn, "ControllerOutdoorAir " << workspaceObject.briefDescription()
-                                        << " references a time of day economizer control schedule, which is not supported");
-    }
-
-    s = workspaceObject.getString(Controller_OutdoorAirFields::HighHumidityControl);
-    if (s) {
-      if (istringEqual("Yes", s.get())) {
-        mo.setHighHumidityControl(true);
-      } else {
-        mo.setHighHumidityControl(false);
+    if ((_wo = workspaceObject.getTarget(Controller_OutdoorAirFields::TimeofDayEconomizerControlScheduleName))) {
+      if ((_mo = translateAndMapWorkspaceObject(_wo.get()))) {
+        if (boost::optional<Schedule> _schedule = _mo->optionalCast<Schedule>()) {
+          mo.setTimeofDayEconomizerControlSchedule(_schedule.get());
+        } else {
+          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Time of Day Economizer Control Schedule Name'");
+        }
       }
     }
 
-    s = workspaceObject.getString(Controller_OutdoorAirFields::HumidistatControlZoneName);
-    if (s) {
-      LOG(Warn, "ControllerOutdoorAir " << workspaceObject.briefDescription() << " references a high humidity control zone, which is not supported");
+    if ((_wo = workspaceObject.getTarget(Controller_OutdoorAirFields::HumidistatControlZoneName))) {
+      if ((_mo = translateAndMapWorkspaceObject(_wo.get()))) {
+        // Zone is translated, and a Space is returned instead
+        if (boost::optional<Space> space_ = _mo->optionalCast<Space>()) {
+          if (auto z_ = space_->thermalZone()) {
+            mo.setHumidistatControlZone(z_.get());
+          }
+        } else {
+          LOG(Warn, workspaceObject.briefDescription() << " has a wrong type for 'Humidistat Control Zone Name'");
+        }
+      }
     }
 
     value = workspaceObject.getDouble(Controller_OutdoorAirFields::HighHumidityOutdoorAirFlowRatio);
diff --git a/src/energyplus/ReverseTranslator/ReverseTranslateFanSystemModel.cpp b/src/energyplus/ReverseTranslator/ReverseTranslateFanSystemModel.cpp
index 7d6229fbaa1..95f3b3dd148 100644
--- a/src/energyplus/ReverseTranslator/ReverseTranslateFanSystemModel.cpp
+++ b/src/energyplus/ReverseTranslator/ReverseTranslateFanSystemModel.cpp
@@ -133,6 +133,7 @@ namespace energyplus {
         }
       }
     }
+
     // Night Ventilation Mode Pressure Rise: Optional Double
     if (boost::optional<double> _nightVentilationModePressureRise =
           workspaceObject.getDouble(Fan_SystemModelFields::NightVentilationModePressureRise)) {
diff --git a/src/model/AirConditionerVariableRefrigerantFlow.cpp b/src/model/AirConditionerVariableRefrigerantFlow.cpp
index b91756d0af8..10160f2ceb4 100644
--- a/src/model/AirConditionerVariableRefrigerantFlow.cpp
+++ b/src/model/AirConditionerVariableRefrigerantFlow.cpp
@@ -1064,7 +1064,6 @@ namespace model {
 
     bool AirConditionerVariableRefrigerantFlow_Impl::setHeatPumpWasteHeatRecovery(bool heatPumpWasteHeatRecovery) {
       return setBooleanFieldValue(OS_AirConditioner_VariableRefrigerantFlowFields::HeatPumpWasteHeatRecovery, heatPumpWasteHeatRecovery);
-      ;
     }
 
     bool AirConditionerVariableRefrigerantFlow_Impl::setEquivalentPipingLengthusedforPipingCorrectionFactorinCoolingMode(
diff --git a/src/model/ControllerOutdoorAir.cpp b/src/model/ControllerOutdoorAir.cpp
index 284665f9e1c..21affb4dc7f 100644
--- a/src/model/ControllerOutdoorAir.cpp
+++ b/src/model/ControllerOutdoorAir.cpp
@@ -11,12 +11,14 @@
 #include "ControllerMechanicalVentilation_Impl.hpp"
 #include "ScheduleCompact.hpp"
 #include "ScheduleCompact_Impl.hpp"
-#include "CurveQuadratic.hpp"
-#include "CurveQuadratic_Impl.hpp"
+#include "Curve.hpp"
+#include "Curve_Impl.hpp"
 #include "AirflowNetworkOutdoorAirflow.hpp"
 #include "AirflowNetworkOutdoorAirflow_Impl.hpp"
 #include "AirflowNetworkCrack.hpp"
 #include "AirflowNetworkCrack_Impl.hpp"
+#include "ThermalZone.hpp"
+#include "ThermalZone_Impl.hpp"
 
 #include "Model.hpp"
 #include "Model_Impl.hpp"
@@ -25,6 +27,7 @@
 #include <utilities/idd/IddEnums.hxx>
 #include "../utilities/core/Compare.hpp"
 #include "../utilities/core/Assert.hpp"
+#include "../utilities/core/DeprecatedHelpers.hpp"
 
 using openstudio::Handle;
 using openstudio::OptionalHandle;
@@ -86,70 +89,24 @@ namespace model {
     // return any children objects in the hierarchy
     std::vector<ModelObject> ControllerOutdoorAir_Impl::children() const {
       std::vector<ModelObject> result;
-      //result.push_back(this->getElectronicEnthalpyLimitCurve());
-      //result.push_back(this->getMinimumOutdoorAirSchedule());
-      //result.push_back(this->getMinimumFractionOfOutdoorAirSchedule());
-      //result.push_back(this->getMaximumFractionOfOutdoorAirSchedule());
-      //result.push_back(this->getTimeOfDayEconomizerControlSchedule());
       std::vector<AirflowNetworkOutdoorAirflow> myAFNItems =
         getObject<ModelObject>().getModelObjectSources<AirflowNetworkOutdoorAirflow>(AirflowNetworkOutdoorAirflow::iddObjectType());
       result.insert(result.end(), myAFNItems.begin(), myAFNItems.end());
       return result;
     }
 
-    CurveQuadratic ControllerOutdoorAir_Impl::getElectronicEnthalpyLimitCurve() const {
-      try {
-        OptionalWorkspaceObject wo = this->getTarget(openstudio::OS_Controller_OutdoorAirFields::ElectronicEnthalpyLimitCurveName);
-        OptionalCurveQuadratic curveQuadratic = wo->optionalCast<CurveQuadratic>();
-        return *curveQuadratic;
-      } catch (...) {
-        LOG(Error, "Failed to retrieve electronic enthalpy limit curve")
-        throw;
-      }
-    }
-
-    ScheduleCompact ControllerOutdoorAir_Impl::getMinimumOutdoorAirSchedule() const {
-      try {
-        OptionalWorkspaceObject wo = this->getTarget(openstudio::OS_Controller_OutdoorAirFields::MinimumOutdoorAirScheduleName);
-        OptionalScheduleCompact schedule = wo->optionalCast<ScheduleCompact>();
-        return *schedule;
-      } catch (...) {
-        LOG(Error, "Failed to retrieve minimum outdoor air schedule")
-        throw;
-      }
-    }
-
-    ScheduleCompact ControllerOutdoorAir_Impl::getMinimumFractionOfOutdoorAirSchedule() const {
-      try {
-        OptionalWorkspaceObject wo = this->getTarget(openstudio::OS_Controller_OutdoorAirFields::MinimumFractionofOutdoorAirScheduleName);
-        OptionalScheduleCompact schedule = wo->optionalCast<ScheduleCompact>();
-        return *schedule;
-      } catch (...) {
-        LOG(Error, "Failed to retrieve minimum fraciton of outdoor air schedule")
-        throw;
-      }
+    boost::optional<Curve> ControllerOutdoorAir_Impl::electronicEnthalpyLimitCurve() const {
+      return getObject<ModelObject>().getModelObjectTarget<Curve>(OS_Controller_OutdoorAirFields::ElectronicEnthalpyLimitCurveName);
     }
 
-    ScheduleCompact ControllerOutdoorAir_Impl::getMaximumFractionOfOutdoorAirSchedule() const {
-      try {
-        OptionalWorkspaceObject wo = this->getTarget(openstudio::OS_Controller_OutdoorAirFields::MaximumFractionofOutdoorAirScheduleName);
-        OptionalScheduleCompact schedule = wo->optionalCast<ScheduleCompact>();
-        return *schedule;
-      } catch (...) {
-        LOG(Error, "Failed to retrieve maximum fraction of outdoor air schedule")
-        throw;
-      }
+    bool ControllerOutdoorAir_Impl::setElectronicEnthalpyLimitCurve(const Curve& curve) {
+      bool result = setPointer(OS_Controller_OutdoorAirFields::ElectronicEnthalpyLimitCurveName, curve.handle());
+      return result;
     }
 
-    ScheduleCompact ControllerOutdoorAir_Impl::getTimeOfDayEconomizerControlSchedule() const {
-      try {
-        OptionalWorkspaceObject wo = this->getTarget(openstudio::OS_Controller_OutdoorAirFields::TimeofDayEconomizerControlScheduleName);
-        OptionalScheduleCompact schedule = wo->optionalCast<ScheduleCompact>();
-        return *schedule;
-      } catch (...) {
-        LOG(Error, "Failed to retrieve time of day economizer control schedule")
-        throw;
-      }
+    void ControllerOutdoorAir_Impl::resetElectronicEnthalpyLimitCurve() {
+      bool result = setString(OS_Controller_OutdoorAirFields::ElectronicEnthalpyLimitCurveName, "");
+      OS_ASSERT(result);
     }
 
     OptionalAirLoopHVACOutdoorAirSystem ControllerOutdoorAir_Impl::airLoopHVACOutdoorAirSystem() const {
@@ -224,7 +181,6 @@ namespace model {
 
     bool ControllerOutdoorAir_Impl::setEconomizerControlType(const std::string& value) {
       return setString(openstudio::OS_Controller_OutdoorAirFields::EconomizerControlType, value);
-      ;
     }
 
     std::string ControllerOutdoorAir_Impl::getEconomizerControlActionType() const {
@@ -233,7 +189,6 @@ namespace model {
 
     bool ControllerOutdoorAir_Impl::setEconomizerControlActionType(const std::string& value) {
       return setString(openstudio::OS_Controller_OutdoorAirFields::EconomizerControlActionType, value);
-      ;
     }
 
     //get needs to return a boost optional double since "" is a valid input
@@ -303,7 +258,6 @@ namespace model {
 
     bool ControllerOutdoorAir_Impl::setLockoutType(const std::string& value) {
       return setString(openstudio::OS_Controller_OutdoorAirFields::LockoutType, value);
-      ;
     }
 
     std::string ControllerOutdoorAir_Impl::getMinimumLimitType() const {
@@ -312,60 +266,45 @@ namespace model {
 
     bool ControllerOutdoorAir_Impl::setMinimumLimitType(const std::string& value) {
       return setString(openstudio::OS_Controller_OutdoorAirFields::MinimumLimitType, value);
-      ;
     }
 
     boost::optional<bool> ControllerOutdoorAir_Impl::getHighHumidityControl() const {
-      boost::optional<bool> retVal;
+      return getBooleanFieldValue(OS_Controller_OutdoorAirFields::HighHumidityControl);
+    }
 
-      if (OptionalString s = getString(OS_Controller_OutdoorAirFields::HighHumidityControl)) {
-        if (istringEqual(s.get(), "yes")) {
-          retVal = true;
-        } else {
-          retVal = false;
-        }
-      }
+    boost::optional<ThermalZone> ControllerOutdoorAir_Impl::humidistatControlZone() const {
+      return getObject<ModelObject>().getModelObjectTarget<ThermalZone>(OS_Controller_OutdoorAirFields::HumidistatControlZoneName);
+    }
 
-      return retVal;
+    bool ControllerOutdoorAir_Impl::setHumidistatControlZone(const ThermalZone& thermalZone) {
+      bool result = setPointer(OS_Controller_OutdoorAirFields::HumidistatControlZoneName, thermalZone.handle());
+      result = result && setString(OS_Controller_OutdoorAirFields::HighHumidityControl, "Yes");
+      return result;
     }
 
-    bool ControllerOutdoorAir_Impl::setHighHumidityControl(bool val) {
-      if (val) {
-        return setString(OS_Controller_OutdoorAirFields::HighHumidityControl, "Yes");
-      } else {
-        return setString(OS_Controller_OutdoorAirFields::HighHumidityControl, "No");
-      }
+    void ControllerOutdoorAir_Impl::resetHumidistatControlZone() {
+      bool result = setString(OS_Controller_OutdoorAirFields::HumidistatControlZoneName, "");
+      result = result && setString(OS_Controller_OutdoorAirFields::HighHumidityControl, "No");
+      OS_ASSERT(result);
     }
 
-    OptionalDouble ControllerOutdoorAir_Impl::getHighHumidityOutdoorAirFlowRatio() const {
-      return getDouble(openstudio::OS_Controller_OutdoorAirFields::HighHumidityOutdoorAirFlowRatio);
+    double ControllerOutdoorAir_Impl::getHighHumidityOutdoorAirFlowRatio() const {
+      boost::optional<double> value = getDouble(openstudio::OS_Controller_OutdoorAirFields::HighHumidityOutdoorAirFlowRatio, true);
+      OS_ASSERT(value);
+      return value.get();
     }
+
     bool ControllerOutdoorAir_Impl::setHighHumidityOutdoorAirFlowRatio(double v) {
-      return setDouble(openstudio::OS_Controller_OutdoorAirFields::HighHumidityOutdoorAirFlowRatio, v);
-      ;
+      bool result = setDouble(openstudio::OS_Controller_OutdoorAirFields::HighHumidityOutdoorAirFlowRatio, v);
+      return result;
     }
 
-    boost::optional<bool> ControllerOutdoorAir_Impl::getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio() const {
-      boost::optional<bool> retVal;
-      OptionalString s = getString(OS_Controller_OutdoorAirFields::ControlHighIndoorHumidityBasedonOutdoorHumidityRatio);
-      if (!s) {
-        return retVal;
-      }
-      std::string temp = *s;
-      boost::to_lower(temp);
-      if (temp == "no") {
-        retVal = false;
-      } else {
-        retVal = true;
-      }
-      return retVal;
+    bool ControllerOutdoorAir_Impl::getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio() const {
+      return getBooleanFieldValue(OS_Controller_OutdoorAirFields::ControlHighIndoorHumidityBasedonOutdoorHumidityRatio);
     }
+
     bool ControllerOutdoorAir_Impl::setControlHighIndoorHumidityBasedOnOutdoorHumidityRatio(bool v) {
-      if (v) {
-        return setString(OS_Controller_OutdoorAirFields::ControlHighIndoorHumidityBasedonOutdoorHumidityRatio, "No");
-      } else {
-        return setString(OS_Controller_OutdoorAirFields::ControlHighIndoorHumidityBasedonOutdoorHumidityRatio, "Yes");
-      }
+      return setBooleanFieldValue(OS_Controller_OutdoorAirFields::ControlHighIndoorHumidityBasedonOutdoorHumidityRatio, v);
     }
 
     OptionalString ControllerOutdoorAir_Impl::getHeatRecoveryBypassControlType() const {
@@ -599,10 +538,9 @@ namespace model {
     setString(OS_Controller_OutdoorAirFields::MaximumFractionofOutdoorAirScheduleName, "");
     setString(OS_Controller_OutdoorAirFields::ControllerMechanicalVentilation, "");
     setString(OS_Controller_OutdoorAirFields::TimeofDayEconomizerControlScheduleName, "");
-    setHighHumidityControl(false);
-    setString(OS_Controller_OutdoorAirFields::HumidistatControlZoneName, "");
-    setString(OS_Controller_OutdoorAirFields::HighHumidityOutdoorAirFlowRatio, "");
-    setString(OS_Controller_OutdoorAirFields::ControlHighIndoorHumidityBasedonOutdoorHumidityRatio, "");
+    setString(OS_Controller_OutdoorAirFields::HighHumidityControl, "No");
+    setHighHumidityOutdoorAirFlowRatio(1.0);
+    setControlHighIndoorHumidityBasedOnOutdoorHumidityRatio(true);
     setHeatRecoveryBypassControlType("BypassWhenWithinEconomizerLimits");
     setEconomizerOperationStaging("InterlockedWithMechanicalCooling");
 
@@ -612,24 +550,16 @@ namespace model {
 
   ControllerOutdoorAir::ControllerOutdoorAir(std::shared_ptr<detail::ControllerOutdoorAir_Impl> impl) : ParentObject(std::move(impl)) {}
 
-  CurveQuadratic ControllerOutdoorAir::getElectronicEnthalpyLimitCurve() const {
-    return getImpl<detail::ControllerOutdoorAir_Impl>()->getElectronicEnthalpyLimitCurve();
+  boost::optional<Curve> ControllerOutdoorAir::electronicEnthalpyLimitCurve() const {
+    return getImpl<detail::ControllerOutdoorAir_Impl>()->electronicEnthalpyLimitCurve();
   }
 
-  ScheduleCompact ControllerOutdoorAir::getMinimumOutdoorAirSchedule() const {
-    return getImpl<detail::ControllerOutdoorAir_Impl>()->getMinimumOutdoorAirSchedule();
+  bool ControllerOutdoorAir::setElectronicEnthalpyLimitCurve(const Curve& curve) {
+    return getImpl<detail::ControllerOutdoorAir_Impl>()->setElectronicEnthalpyLimitCurve(curve);
   }
 
-  ScheduleCompact ControllerOutdoorAir::getMinimumFractionOfOutdoorAirSchedule() const {
-    return getImpl<detail::ControllerOutdoorAir_Impl>()->getMinimumFractionOfOutdoorAirSchedule();
-  }
-
-  ScheduleCompact ControllerOutdoorAir::getMaximumFractionOfOutdoorAirSchedule() const {
-    return getImpl<detail::ControllerOutdoorAir_Impl>()->getMaximumFractionOfOutdoorAirSchedule();
-  }
-
-  ScheduleCompact ControllerOutdoorAir::getTimeOfDayEconomizerControlSchedule() const {
-    return getImpl<detail::ControllerOutdoorAir_Impl>()->getTimeOfDayEconomizerControlSchedule();
+  void ControllerOutdoorAir::resetElectronicEnthalpyLimitCurve() {
+    getImpl<detail::ControllerOutdoorAir_Impl>()->resetElectronicEnthalpyLimitCurve();
   }
 
   boost::optional<AirLoopHVACOutdoorAirSystem> ControllerOutdoorAir::airLoopHVACOutdoorAirSystem() const {
@@ -725,10 +655,23 @@ namespace model {
   }
 
   bool ControllerOutdoorAir::setHighHumidityControl(bool val) {
-    return getImpl<detail::ControllerOutdoorAir_Impl>()->setHighHumidityControl(val);
+    DEPRECATED_AT_MSG(3, 8, 0, "Use setHumidistatControlZone instead.");
+    return false;
+  }
+
+  boost::optional<ThermalZone> ControllerOutdoorAir::humidistatControlZone() const {
+    return getImpl<detail::ControllerOutdoorAir_Impl>()->humidistatControlZone();
+  }
+
+  bool ControllerOutdoorAir::setHumidistatControlZone(const ThermalZone& thermalZone) {
+    return getImpl<detail::ControllerOutdoorAir_Impl>()->setHumidistatControlZone(thermalZone);
+  }
+
+  void ControllerOutdoorAir::resetHumidistatControlZone() {
+    getImpl<detail::ControllerOutdoorAir_Impl>()->resetHumidistatControlZone();
   }
 
-  boost::optional<double> ControllerOutdoorAir::getHighHumidityOutdoorAirFlowRatio() const {
+  double ControllerOutdoorAir::getHighHumidityOutdoorAirFlowRatio() const {
     return getImpl<detail::ControllerOutdoorAir_Impl>()->getHighHumidityOutdoorAirFlowRatio();
   }
 
@@ -736,7 +679,7 @@ namespace model {
     return getImpl<detail::ControllerOutdoorAir_Impl>()->setHighHumidityOutdoorAirFlowRatio(v);
   }
 
-  boost::optional<bool> ControllerOutdoorAir::getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio() const {
+  bool ControllerOutdoorAir::getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio() const {
     return getImpl<detail::ControllerOutdoorAir_Impl>()->getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio();
   }
 
diff --git a/src/model/ControllerOutdoorAir.hpp b/src/model/ControllerOutdoorAir.hpp
index 125b29b2dcb..aaf95cc0cb5 100644
--- a/src/model/ControllerOutdoorAir.hpp
+++ b/src/model/ControllerOutdoorAir.hpp
@@ -8,6 +8,7 @@
 
 #include "ModelAPI.hpp"
 #include "ParentObject.hpp"
+#include "../utilities/core/Deprecated.hpp"
 
 namespace openstudio {
 
@@ -19,12 +20,13 @@ namespace model {
 
   }  // namespace detail
 
-  class CurveQuadratic;
+  class Curve;
   class ScheduleCompact;
   class ControllerMechanicalVentilation;
   class AirLoopHVACOutdoorAirSystem;
   class AirflowNetworkOutdoorAirflow;
   class AirflowNetworkCrack;
+  class ThermalZone;
 
   class MODEL_API ControllerOutdoorAir : public ParentObject
   {
@@ -90,8 +92,9 @@ namespace model {
     bool setEconomizerMaximumLimitDewpointTemperature(double value);
     void resetEconomizerMaximumLimitDewpointTemperature();
 
-    //QuadraticCurve getElectronicEnthalpyLimitCurve() const;
-    //bool setElectronicEnthalpyLimitCurve(QuadraticCurve c);
+    boost::optional<Curve> electronicEnthalpyLimitCurve() const;
+    bool setElectronicEnthalpyLimitCurve(const Curve& curve);
+    void resetElectronicEnthalpyLimitCurve();
 
     //get needs to return a boost optional double since "" is a valid input
     boost::optional<double> getEconomizerMinimumLimitDryBulbTemperature() const;
@@ -105,15 +108,16 @@ namespace model {
     bool setMinimumLimitType(const std::string& value);
 
     boost::optional<bool> getHighHumidityControl() const;
-    bool setHighHumidityControl(bool val);
+    OS_DEPRECATED(3, 8, 0) bool setHighHumidityControl(bool val);
 
-    //Zone getHumidistatControlZone() const;
-    //bool setHumidistatControlZone(Zone z)
+    boost::optional<ThermalZone> humidistatControlZone() const;
+    bool setHumidistatControlZone(const ThermalZone& thermalZone);
+    void resetHumidistatControlZone();
 
-    boost::optional<double> getHighHumidityOutdoorAirFlowRatio() const;
+    double getHighHumidityOutdoorAirFlowRatio() const;
     bool setHighHumidityOutdoorAirFlowRatio(double v);
 
-    boost::optional<bool> getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio() const;
+    bool getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio() const;
     bool setControlHighIndoorHumidityBasedOnOutdoorHumidityRatio(bool v);
 
     boost::optional<std::string> getHeatRecoveryBypassControlType() const;
@@ -147,18 +151,6 @@ namespace model {
     explicit ControllerOutdoorAir(std::shared_ptr<detail::ControllerOutdoorAir_Impl> impl);
 
    private:
-    CurveQuadratic getElectronicEnthalpyLimitCurve() const;
-
-    ScheduleCompact getMinimumOutdoorAirSchedule() const;
-
-    ScheduleCompact getMinimumFractionOfOutdoorAirSchedule() const;
-
-    ScheduleCompact getMaximumFractionOfOutdoorAirSchedule() const;
-
-    //Controller:MechanicalVentilation getMechanicalVentilationController() const;
-
-    ScheduleCompact getTimeOfDayEconomizerControlSchedule() const;
-
     ControllerOutdoorAir(const Handle& handle, const Model& model);
 
     REGISTER_LOGGER("openstudio.model.ControllerOutdoorAir");
diff --git a/src/model/ControllerOutdoorAir_Impl.hpp b/src/model/ControllerOutdoorAir_Impl.hpp
index c496e4095d7..04a4b4b0f7d 100644
--- a/src/model/ControllerOutdoorAir_Impl.hpp
+++ b/src/model/ControllerOutdoorAir_Impl.hpp
@@ -11,11 +11,12 @@
 namespace openstudio {
 namespace model {
 
-  class CurveQuadratic;
+  class Curve;
   class ScheduleCompact;
   class AirLoopHVACOutdoorAirSystem;
   class ControllerMechanicalVentilation;
   class AirflowNetworkOutdoorAirflow;
+  class ThermalZone;
 
   namespace detail {
 
@@ -67,8 +68,9 @@ namespace model {
       boost::optional<double> getEconomizerMaximumLimitDewpointTemperature() const;
       bool setEconomizerMaximumLimitDewpointTemperature(boost::optional<double> value);
 
-      //QuadraticCurve getElectronicEnthalpyLimitCurve() const;
-      //bool setElectronicEnthalpyLimitCurve(QuadraticCurve c);
+      boost::optional<Curve> electronicEnthalpyLimitCurve() const;
+      bool setElectronicEnthalpyLimitCurve(const Curve& curve);
+      void resetElectronicEnthalpyLimitCurve();
 
       //get needs to return a boost optional double since "" is a valid input
       boost::optional<double> getEconomizerMinimumLimitDryBulbTemperature() const;
@@ -81,15 +83,15 @@ namespace model {
       bool setMinimumLimitType(const std::string& value);
 
       boost::optional<bool> getHighHumidityControl() const;
-      bool setHighHumidityControl(bool val);
 
-      //Zone getHumidistatControlZone() const;
-      //bool setHumidistatControlZone(Zone z)
+      boost::optional<ThermalZone> humidistatControlZone() const;
+      bool setHumidistatControlZone(const ThermalZone& thermalZone);
+      void resetHumidistatControlZone();
 
-      OptionalDouble getHighHumidityOutdoorAirFlowRatio() const;
+      double getHighHumidityOutdoorAirFlowRatio() const;
       bool setHighHumidityOutdoorAirFlowRatio(double v);
 
-      boost::optional<bool> getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio() const;
+      bool getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio() const;
       bool setControlHighIndoorHumidityBasedOnOutdoorHumidityRatio(bool v);
 
       OptionalString getHeatRecoveryBypassControlType() const;
@@ -98,16 +100,6 @@ namespace model {
       std::string economizerOperationStaging() const;
       bool setEconomizerOperationStaging(const std::string& v);
 
-      CurveQuadratic getElectronicEnthalpyLimitCurve() const;
-
-      ScheduleCompact getMinimumOutdoorAirSchedule() const;
-
-      ScheduleCompact getMinimumFractionOfOutdoorAirSchedule() const;
-
-      ScheduleCompact getMaximumFractionOfOutdoorAirSchedule() const;
-
-      ScheduleCompact getTimeOfDayEconomizerControlSchedule() const;
-
       boost::optional<AirLoopHVACOutdoorAirSystem> airLoopHVACOutdoorAirSystem() const;
 
       boost::optional<double> minimumOutdoorAirFlowRate() const;
diff --git a/src/osversion/VersionTranslator.cpp b/src/osversion/VersionTranslator.cpp
index b4c7b138e15..97bb0906262 100644
--- a/src/osversion/VersionTranslator.cpp
+++ b/src/osversion/VersionTranslator.cpp
@@ -144,7 +144,8 @@ namespace osversion {
     m_updateMethods[VersionString("3.6.0")] = &VersionTranslator::update_3_5_1_to_3_6_0;
     m_updateMethods[VersionString("3.7.0")] = &VersionTranslator::update_3_6_1_to_3_7_0;
     m_updateMethods[VersionString("3.8.0")] = &VersionTranslator::update_3_7_0_to_3_8_0;
-    // m_updateMethods[VersionString("3.8.0")] = &VersionTranslator::defaultUpdate;
+    m_updateMethods[VersionString("3.9.0")] = &VersionTranslator::update_3_8_0_to_3_9_0;
+    // m_updateMethods[VersionString("3.9.0")] = &VersionTranslator::defaultUpdate;
 
     // List of previous versions that may be updated to this one.
     //   - To increment the translator, add an entry for the version just released (branched for
@@ -181,9 +182,9 @@ namespace osversion {
       VersionString("2.7.1"),  VersionString("2.7.2"),  VersionString("2.8.0"),  VersionString("2.8.1"),  VersionString("2.9.0"),
       VersionString("2.9.1"),  VersionString("3.0.0"),  VersionString("3.0.1"),  VersionString("3.1.0"),  VersionString("3.2.0"),
       VersionString("3.2.1"),  VersionString("3.3.0"),  VersionString("3.4.0"),  VersionString("3.5.0"),  VersionString("3.5.1"),
-      VersionString("3.6.0"),  VersionString("3.6.1"),  VersionString("3.7.0"),
+      VersionString("3.6.0"),  VersionString("3.6.1"),  VersionString("3.7.0"),  VersionString("3.8.0"),
       // Note: do **not** include the **current** version in m_startVersions, stop at the previous release
-      //VersionString("3.8.0"),
+      //VersionString("3.9.0"),
     };
   }
 
@@ -9196,5 +9197,52 @@ namespace osversion {
 
   }  // end update_3_7_0_to_3_8_0
 
+  std::string VersionTranslator::update_3_8_0_to_3_9_0(const IdfFile& idf_3_8_0, const IddFileAndFactoryWrapper& idd_3_9_0) {
+    std::stringstream ss;
+    boost::optional<std::string> value;
+
+    ss << idf_3_8_0.header() << '\n' << '\n';
+    IdfFile targetIdf(idd_3_9_0.iddFile());
+    ss << targetIdf.versionObject().get();
+
+    for (const IdfObject& object : idf_3_8_0.objects()) {
+      auto iddname = object.iddObject().name();
+
+      if (iddname == "OS:Controller:OutdoorAir") {
+
+        // 2 Fields have been made required from 3.8.0 to 3.9.0:
+        // ----------------------------------------------
+        // * High Humidity Outdoor Air Flow Ratio * 24
+        // * Control High Indoor Humidity Based on Outdoor Humidity Ratio * 25
+
+        auto iddObject = idd_3_9_0.getObject(iddname);
+        IdfObject newObject(iddObject.get());
+
+        for (size_t i = 0; i < object.numFields(); ++i) {
+          if ((value = object.getString(i))) {
+            newObject.setString(i, value.get());
+          }
+        }
+
+        if (newObject.isEmpty(24)) {
+          newObject.setDouble(24, 1.0);
+        }
+        if (newObject.isEmpty(25)) {
+          newObject.setString(25, "Yes");
+        }
+
+        ss << newObject;
+        m_refactored.emplace_back(std::move(object), std::move(newObject));
+
+        // No-op
+      } else {
+        ss << object;
+      }
+    }
+
+    return ss.str();
+
+  }  // end update_3_8_0_to_3_9_0
+
 }  // namespace osversion
 }  // namespace openstudio
diff --git a/src/osversion/VersionTranslator.hpp b/src/osversion/VersionTranslator.hpp
index b5a93555c6f..4600c2faa75 100644
--- a/src/osversion/VersionTranslator.hpp
+++ b/src/osversion/VersionTranslator.hpp
@@ -236,6 +236,7 @@ namespace osversion {
     std::string update_3_5_1_to_3_6_0(const IdfFile& idf_3_5_1, const IddFileAndFactoryWrapper& idd_3_6_0);
     std::string update_3_6_1_to_3_7_0(const IdfFile& idf_3_6_1, const IddFileAndFactoryWrapper& idd_3_7_0);
     std::string update_3_7_0_to_3_8_0(const IdfFile& idf_3_7_0, const IddFileAndFactoryWrapper& idd_3_8_0);
+    std::string update_3_8_0_to_3_9_0(const IdfFile& idf_3_8_0, const IddFileAndFactoryWrapper& idd_3_9_0);
 
     IdfObject updateUrlField_0_7_1_to_0_7_2(const IdfObject& object, unsigned index);
 


Test patch:
diff --git a/src/model/test/ControllerOutdoorAir_GTest.cpp b/src/model/test/ControllerOutdoorAir_GTest.cpp
index 6062f9af7a6..dcb8b47e80f 100644
--- a/src/model/test/ControllerOutdoorAir_GTest.cpp
+++ b/src/model/test/ControllerOutdoorAir_GTest.cpp
@@ -15,6 +15,8 @@
 #include "../AirflowNetworkCrack_Impl.hpp"
 #include "../AirflowNetworkReferenceCrackConditions.hpp"
 #include "../AirflowNetworkReferenceCrackConditions_Impl.hpp"
+#include "../ThermalZone.hpp"
+#include "../CurveQuadratic.hpp"
 
 #include <utilities/idd/OS_Controller_OutdoorAir_FieldEnums.hxx>
 #include <utilities/idd/IddEnums.hxx>
@@ -76,4 +78,37 @@ TEST_F(ModelFixture, ControllerOutdoorAir_GettersSetters) {
   // Economizer Operation Staging
   EXPECT_TRUE(controller.setEconomizerOperationStaging("EconomizerFirst"));
   EXPECT_EQ("EconomizerFirst", controller.economizerOperationStaging());
+
+  // Electronic Enthalpy Limit Curve
+  EXPECT_FALSE(controller.electronicEnthalpyLimitCurve());
+  CurveQuadratic electronicEnthalpyLimitCurve(model);
+  EXPECT_TRUE(controller.setElectronicEnthalpyLimitCurve(electronicEnthalpyLimitCurve));
+  ASSERT_TRUE(controller.electronicEnthalpyLimitCurve());
+  EXPECT_EQ(electronicEnthalpyLimitCurve, controller.electronicEnthalpyLimitCurve().get());
+  controller.resetElectronicEnthalpyLimitCurve();
+  EXPECT_FALSE(controller.electronicEnthalpyLimitCurve());
+
+  // Humidistat Control Zone
+  ASSERT_TRUE(controller.getHighHumidityControl());
+  EXPECT_FALSE(controller.getHighHumidityControl().get());
+  ThermalZone humidistatControlZone(model);
+  EXPECT_TRUE(controller.setHumidistatControlZone(humidistatControlZone));
+  ASSERT_TRUE(controller.humidistatControlZone());
+  EXPECT_EQ(humidistatControlZone, controller.humidistatControlZone().get());
+  ASSERT_TRUE(controller.getHighHumidityControl());
+  EXPECT_TRUE(controller.getHighHumidityControl().get());
+  controller.resetHumidistatControlZone();
+  EXPECT_FALSE(controller.humidistatControlZone());
+  ASSERT_TRUE(controller.getHighHumidityControl());
+  EXPECT_FALSE(controller.getHighHumidityControl().get());
+
+  // High Humidity Outdoor Air Flow Ratio
+  EXPECT_EQ(1.0, controller.getHighHumidityOutdoorAirFlowRatio());
+  EXPECT_TRUE(controller.setHighHumidityOutdoorAirFlowRatio(2.0));
+  EXPECT_EQ(2.0, controller.getHighHumidityOutdoorAirFlowRatio());
+
+  // Control High Indoor Humidity Based on Outdoor Humidity Ratio
+  EXPECT_TRUE(controller.getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio());
+  EXPECT_TRUE(controller.setControlHighIndoorHumidityBasedOnOutdoorHumidityRatio(false));
+  EXPECT_FALSE(controller.getControlHighIndoorHumidityBasedOnOutdoorHumidityRatio());
 }
diff --git a/src/osversion/test/3_9_0/test_vt_ControllerOutdoorAir.osm b/src/osversion/test/3_9_0/test_vt_ControllerOutdoorAir.osm
new file mode 100644
index 00000000000..9884e2629ce
--- /dev/null
+++ b/src/osversion/test/3_9_0/test_vt_ControllerOutdoorAir.osm
@@ -0,0 +1,56 @@
+
+OS:Version,
+  {ff5269f8-4354-477b-aa90-d656733b4909}, !- Handle
+  3.8.0;                                  !- Version Identifier
+
+OS:Controller:OutdoorAir,
+  {2b51de85-c6f1-42e2-be76-53aabac1dd57}, !- Handle
+  Controller Outdoor Air 1,               !- Name
+  ,                                       !- Relief Air Outlet Node Name
+  ,                                       !- Return Air Node Name
+  ,                                       !- Mixed Air Node Name
+  ,                                       !- Actuator Node Name
+  0,                                      !- Minimum Outdoor Air Flow Rate {m3/s}
+  Autosize,                               !- Maximum Outdoor Air Flow Rate {m3/s}
+  NoEconomizer,                           !- Economizer Control Type
+  ModulateFlow,                           !- Economizer Control Action Type
+  28,                                     !- Economizer Maximum Limit Dry-Bulb Temperature {C}
+  64000,                                  !- Economizer Maximum Limit Enthalpy {J/kg}
+  ,                                       !- Economizer Maximum Limit Dewpoint Temperature {C}
+  ,                                       !- Electronic Enthalpy Limit Curve Name
+  -100,                                   !- Economizer Minimum Limit Dry-Bulb Temperature {C}
+  NoLockout,                              !- Lockout Type
+  FixedMinimum,                           !- Minimum Limit Type
+  ,                                       !- Minimum Outdoor Air Schedule Name
+  ,                                       !- Minimum Fraction of Outdoor Air Schedule Name
+  ,                                       !- Maximum Fraction of Outdoor Air Schedule Name
+  {1a10e873-d7f2-412b-96c4-81731937086b}, !- Controller Mechanical Ventilation
+  ,                                       !- Time of Day Economizer Control Schedule Name
+  No,                                     !- High Humidity Control
+  ,                                       !- Humidistat Control Zone Name
+  ,                                       !- High Humidity Outdoor Air Flow Ratio
+  ,                                       !- Control High Indoor Humidity Based on Outdoor Humidity Ratio
+  BypassWhenWithinEconomizerLimits,       !- Heat Recovery Bypass Control Type
+  InterlockedWithMechanicalCooling;       !- Economizer Operation Staging
+
+OS:Controller:MechanicalVentilation,
+  {1a10e873-d7f2-412b-96c4-81731937086b}, !- Handle
+  Controller Mechanical Ventilation 1,    !- Name
+  {68a1d80e-6ef4-49d0-ad28-8dbf399ebd36}, !- Availability Schedule
+  ,                                       !- Demand Controlled Ventilation
+  ;                                       !- System Outdoor Air Method
+
+OS:Schedule:Constant,
+  {68a1d80e-6ef4-49d0-ad28-8dbf399ebd36}, !- Handle
+  Always On Discrete,                     !- Name
+  {bc7824f2-a62d-4a3b-914b-8a1b3eb143da}, !- Schedule Type Limits Name
+  1;                                      !- Value
+
+OS:ScheduleTypeLimits,
+  {bc7824f2-a62d-4a3b-914b-8a1b3eb143da}, !- Handle
+  OnOff,                                  !- Name
+  0,                                      !- Lower Limit Value
+  1,                                      !- Upper Limit Value
+  Discrete,                               !- Numeric Type
+  Availability;                           !- Unit Type
+
diff --git a/src/osversion/test/3_9_0/test_vt_ControllerOutdoorAir.rb b/src/osversion/test/3_9_0/test_vt_ControllerOutdoorAir.rb
new file mode 100644
index 00000000000..8b50085a61a
--- /dev/null
+++ b/src/osversion/test/3_9_0/test_vt_ControllerOutdoorAir.rb
@@ -0,0 +1,9 @@
+#require '/usr/local/openstudio-3.8.0/Ruby/openstudio'
+
+include OpenStudio::Model
+
+m = Model.new
+
+controller = ControllerOutdoorAir.new(m)
+
+m.save('test_vt_ControllerOutdoorAir.osm', true)
diff --git a/src/osversion/test/VersionTranslator_GTest.cpp b/src/osversion/test/VersionTranslator_GTest.cpp
index d7d81842494..41b14e429b0 100644
--- a/src/osversion/test/VersionTranslator_GTest.cpp
+++ b/src/osversion/test/VersionTranslator_GTest.cpp
@@ -3938,8 +3938,8 @@ TEST_F(OSVersionFixture, update_3_6_1_to_3_7_0_ControllerOutdoorAir) {
   EXPECT_TRUE(coa.isEmpty(21));                                            // Time of Day Economizer Control Schedule Name
   EXPECT_EQ("No", coa.getString(22).get());                                // High Humidity Control
   EXPECT_TRUE(coa.isEmpty(23));                                            // Humidistat Control Zone Name
-  EXPECT_TRUE(coa.isEmpty(24));                                            // High Humidity Outdoor Air Flow Ratio
-  EXPECT_TRUE(coa.isEmpty(25));                                            // Control High Indoor Humidity Based on Outdoor Humidity Ratio
+  EXPECT_EQ(1.0, coa.getDouble(24).get());                                 // High Humidity Outdoor Air Flow Ratio
+  EXPECT_EQ("Yes", coa.getString(25).get());                               // Control High Indoor Humidity Based on Outdoor Humidity Ratio
   EXPECT_EQ("BypassWhenWithinEconomizerLimits", coa.getString(26).get());  // Heat Recovery Bypass Control Type
   EXPECT_EQ("InterlockedWithMechanicalCooling", coa.getString(27).get());  // Economizer Operation Staging
 }
@@ -4207,3 +4207,45 @@ TEST_F(OSVersionFixture, update_3_7_0_to_3_8_0_ScheduleDay) {
   EXPECT_EQ(0, sch_day.getInt(5).get());                    // Minute 1
   EXPECT_EQ(0, sch_day.getDouble(6).get());                 // Value Until Time 1
 }
+
+TEST_F(OSVersionFixture, update_3_8_0_to_3_9_0_ControllerOutdoorAir) {
+  openstudio::path path = resourcesPath() / toPath("osversion/3_9_0/test_vt_ControllerOutdoorAir.osm");
+  osversion::VersionTranslator vt;
+  boost::optional<model::Model> model = vt.loadModel(path);
+  ASSERT_TRUE(model) << "Failed to load " << path;
+
+  openstudio::path outPath = resourcesPath() / toPath("osversion/3_9_0/test_vt_ControllerOutdoorAir_updated.osm");
+  model->save(outPath, true);
+
+  std::vector<WorkspaceObject> controllers = model->getObjectsByType("OS:Controller:OutdoorAir");
+  ASSERT_EQ(1u, controllers.size());
+  WorkspaceObject controller = controllers[0];
+
+  EXPECT_EQ("Controller Outdoor Air 1", controller.getString(1).get());           // Name
+  EXPECT_TRUE(controller.isEmpty(2));                                             // Relief Air Outlet Node Name
+  EXPECT_TRUE(controller.isEmpty(3));                                             // Return Air Node Name
+  EXPECT_TRUE(controller.isEmpty(4));                                             // Mixed Air Node Name
+  EXPECT_TRUE(controller.isEmpty(5));                                             // Actuator Node Name
+  EXPECT_EQ(0.0, controller.getDouble(6).get());                                  // Minimum Outdoor Air Flow Rate {m3/s}
+  EXPECT_EQ("Autosize", controller.getString(7).get());                           // Maximum Outdoor Air Flow Rate {m3/s}
+  EXPECT_EQ("NoEconomizer", controller.getString(8).get());                       // Economizer Control Type
+  EXPECT_EQ("ModulateFlow", controller.getString(9).get());                       // Economizer Control Action Type
+  EXPECT_EQ(28.0, controller.getDouble(10).get());                                // Economizer Maximum Limit Dry-Bulb Temperature {C}
+  EXPECT_EQ(64000.0, controller.getDouble(11).get());                             // Economizer Maximum Limit Enthalpy {J/kg}
+  EXPECT_TRUE(controller.isEmpty(12));                                            // Economizer Maximum Limit Dewpoint Temperature {C}
+  EXPECT_TRUE(controller.isEmpty(13));                                            // Electronic Enthalpy Limit Curve Name
+  EXPECT_EQ(-100.0, controller.getDouble(14).get());                              // Economizer Minimum Limit Dry-Bulb Temperature {C}
+  EXPECT_EQ("NoLockout", controller.getString(15).get());                         // Lockout Type
+  EXPECT_EQ("FixedMinimum", controller.getString(16).get());                      // Minimum Limit Type
+  EXPECT_TRUE(controller.isEmpty(17));                                            // Minimum Outdoor Air Schedule Name
+  EXPECT_TRUE(controller.isEmpty(18));                                            // Minimum Fraction of Outdoor Air Schedule Name
+  EXPECT_TRUE(controller.isEmpty(19));                                            // Maximum Fraction of Outdoor Air Schedule Name
+  ASSERT_TRUE(controller.getTarget(20));                                          // Controller Mechanical Ventilation
+  EXPECT_TRUE(controller.isEmpty(21));                                            // Time of Day Economizer Control Schedule Name
+  EXPECT_EQ("No", controller.getString(22).get());                                // High Humidity Control
+  EXPECT_TRUE(controller.isEmpty(23));                                            // Humidistat Control Zone Name
+  EXPECT_EQ(1.0, controller.getDouble(24).get());                                 // High Humidity Outdoor Air Flow Ratio
+  EXPECT_EQ("Yes", controller.getString(25).get());                               // Control High Indoor Humidity Based on Outdoor Humidity Ratio
+  EXPECT_EQ("BypassWhenWithinEconomizerLimits", controller.getString(26).get());  // Heat Recovery Bypass Control Type
+  EXPECT_EQ("InterlockedWithMechanicalCooling", controller.getString(27).get());  // Economizer Operation Staging
+}