Skip to content

Instantly share code, notes, and snippets.

@jmarrec
Created February 27, 2020 10:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmarrec/e1294ba5be61a061b500337d96dd2417 to your computer and use it in GitHub Desktop.
Save jmarrec/e1294ba5be61a061b500337d96dd2417 to your computer and use it in GitHub Desktop.
Create_UniqueModelObject_Clone_tests.ipynb
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "import re",
"execution_count": null,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "# Find UniqueModelObjects\n\nBecause UniqueModelObjects aren't a specific class, we first need to find which one are supposed to be Unique"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Grep on SWIG_UNIQUEMODELOBJECT"
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "s = !/bin/grep \"SWIG_UNIQUE\" *.i",
"execution_count": null,
"outputs": []
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "re_swig = re.compile(r'.*.i:\\s*SWIG_UNIQUEMODELOBJECT\\((.*)\\);')",
"execution_count": null,
"outputs": []
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "uniqueClasses = [m.groups()[0] for x in s if (m := re_swig.match(x))]",
"execution_count": null,
"outputs": []
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "# 'Model_Common_Include.i:%define SWIG_UNIQUEMODELOBJECT(_name)', is throw-off\nlen(s), len(uniqueClasses)",
"execution_count": null,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Using IDD property `\\unique-object` (using CLI)"
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "unique_objs1 = !$os_build_rel/Products/openstudio -e \"factory = OpenStudio::IddFileAndFactoryWrapper.new('OpenStudio'.to_IddFileType); factory.objects.select{|o| o.properties.unique}.each{|o| puts o.name.to_s}\"\n\n# Actually, there's alread a method to do that\nunique_objs = !$os_build_rel/Products/openstudio -e \"factory = OpenStudio::IddFileAndFactoryWrapper.new('OpenStudio'.to_IddFileType); factory.uniqueObjects.each{|o| puts o.name.to_s}\"",
"execution_count": null,
"outputs": []
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "len(unique_objs1), len(unique_objs)",
"execution_count": null,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Differences between the two methods"
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "cli_uniqueClasses = [x.replace('OS:', '').replace(':', '') for x in unique_objs]",
"execution_count": null,
"outputs": []
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "set(cli_uniqueClasses) - set(uniqueClasses)",
"execution_count": null,
"outputs": []
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "set(uniqueClasses) - set(cli_uniqueClasses)",
"execution_count": null,
"outputs": []
},
{
"metadata": {},
"cell_type": "markdown",
"source": "# Find their base class to see if we could implement a \"UniqueModelObject\" class"
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "re_base_class = re.compile(r'class\\s+(?:MODEL_API )?(?P<ClassName>.*?)\\s+:\\s+public\\s+(?P<BaseClass>.*?)\\s*{')",
"execution_count": null,
"outputs": []
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "result = []\nfor obj in unique_objs:\n className = obj.replace('OS:', '').replace(':', '')\n try:\n with open(f\"{className}.hpp\", 'r') as f:\n content = f.read()\n re_base_class = re.compile(r'class\\s+(?:MODEL_API )?(?P<ClassName>' + className + ')\\s+:\\s+public\\s+(?P<BaseClass>.*?)\\s*{')\n if (m := re_base_class.search(content)):\n d = m.groupdict()\n d['IddObjectType'] = obj\n print(d)\n result.append(d)\n except FileNotFoundError:\n print(f\"File not found for {obj}\")\n \n# Sort it, for import in correct order\nresult.sort(key=lambda d: d['ClassName'])",
"execution_count": null,
"outputs": []
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "from collections import Counter\nCounter([d['BaseClass'] for d in result])",
"execution_count": null,
"outputs": []
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "list(filter(lambda d: d['BaseClass']=='ParentObject', result))",
"execution_count": null,
"outputs": []
},
{
"metadata": {},
"cell_type": "raw",
"source": "# With pandas: much nicer, including table representation, but not a STL package\nimport pandas as pd\ndf = pd.DataFrame(result).sort_values(by='BaseClass')\ndf['BaseClass'].value_counts()"
},
{
"metadata": {},
"cell_type": "raw",
"source": "df[df['BaseClass'] == 'ParentObject']"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "# Generate a GTest for cloning Unique ModelObjects"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "**In the end I only care about the C++ GTest program, but I'm going to use the same program in ruby that I'll run with the CLI so I can test ahead of time whether the GTest would pass or not**"
},
{
"metadata": {
"scrolled": true,
"trusted": false
},
"cell_type": "code",
"source": "# C++ Test program\ntest_program = \"\"\"\nTEST_F(ModelFixture, {className}_UniqueModelObject_Clone)\n{{\n // create a model to use\n Model model;\n\n // Get the Unique ModelObject\n EXPECT_FALSE(model.getOptionalUniqueModelObject<{className}>());\n {className} {camelClassName} = model.getUniqueModelObject<{className}>();\n EXPECT_TRUE(model.getOptionalUniqueModelObject<{className}>());\n // We use a comment to see if cloning to another model works\n {camelClassName}.setComment(\"Custom Object\");\n\n // clone it into the same model\n {className} {camelClassName}Clone = {camelClassName}.clone(model).cast<{className}>();\n // UniqueModelObject: should be the same as the original\n EXPECT_EQ({camelClassName}, {camelClassName}Clone);\n EXPECT_EQ(\"! Custom Object\", {camelClassName}Clone.comment());\n\n // clone it into a different model\n Model model2;\n EXPECT_FALSE(model2.getOptionalUniqueModelObject<{className}>());\n {className} {camelClassName}Clone2 = {camelClassName}.clone(model2).cast<{className}>();\n EXPECT_TRUE(model2.getOptionalUniqueModelObject<{className}>());\n EXPECT_EQ(\"! Custom Object\", {camelClassName}Clone2.comment());\n}}\n\"\"\"\n\n# Ruby Test program\nruby_test_program = \"\"\"include OpenStudio::Model\n\nmodel = Model.new\n{camelClassName} = model.get{className}\n{camelClassName}.setComment('Custom Object')\n\n{camelClassName}Clone = {camelClassName}.clone(model)\nraise if model.numObjectsOfType('{iddObjectType}'.to_IddObjectType) != 1\nraise if {camelClassName}Clone.comment() != '! Custom Object'\n\nmodel2 = Model.new\nraise if model2.numObjectsOfType('{iddObjectType}'.to_IddObjectType) != 0\n{camelClassName}Clone2 = {camelClassName}.clone(model2)\nraise if model2.numObjectsOfType('{iddObjectType}'.to_IddObjectType) != 1\nraise if {camelClassName}Clone2.comment() != '! Custom Object'\n\"\"\"",
"execution_count": null,
"outputs": []
},
{
"metadata": {},
"cell_type": "raw",
"source": "# Define variable if need to test\niddObjType = 'OS:ExternalInterface'\ncN = iddObjType.replace('OS:', '').replace(':', '')\nccN = cN[0].lower() + cN[1:]"
},
{
"metadata": {},
"cell_type": "raw",
"source": "# test C++ program\nprint(test_program.format(className=cN, \n camelClassName=ccN))"
},
{
"metadata": {},
"cell_type": "raw",
"source": "# test Ruby program\nruby_prog = ruby_test_program.format(className=cN,\n camelClassName=ccN,\n iddObjectType=iddObjType)\n\n \n# one_liner = \"; \".join(ruby_prog.splitlines())\n# print(one_liner)\n\n# Could not make that work, so I'll use a file\n# !$os_build_rel/Products/openstudio -e '\"{one_liner}\"'\n\nprint(ruby_prog)\nwith open('temp.rb', 'w') as f:\n f.write(ruby_prog)\n \ncmd_result = !$os_build_rel/Products/openstudio temp.rb\nprint(cmd_result)"
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "header_start = \"\"\"/***********************************************************************************************************************\n* OpenStudio(R), Copyright (c) 2008-2019, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved.\n*\n* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the\n* following conditions are met:\n*\n* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following\n* disclaimer.\n*\n* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following\n* disclaimer in the documentation and/or other materials provided with the distribution.\n*\n* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products\n* derived from this software without specific prior written permission from the respective party.\n*\n* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works\n* may not use the \"OpenStudio\" trademark, \"OS\", \"os\", or any other confusingly similar designation without specific prior\n* written permission from Alliance for Sustainable Energy, LLC.\n*\n* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED\n* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\n* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n***********************************************************************************************************************/\n\n#include <gtest/gtest.h>\n#include \"ModelFixture.hpp\"\n\n#include \"../Model.hpp\"\n#include \"../Model_Impl.hpp\"\n\"\"\"\n\n\nheader_className = '''#include \"../{className}.hpp\"\n#include \"../{className}_Impl.hpp\"\n'''\n\nheader_end = \"\"\"\nusing namespace openstudio;\nusing namespace openstudio::model;\n\n\"\"\"",
"execution_count": null,
"outputs": []
},
{
"metadata": {
"trusted": false
},
"cell_type": "code",
"source": "# I define it here, so that on subsequent runs of the below cell it's already populated to avoid re-rerunning ruby\nprint_at_end = []\nalreadyCheckedInRuby = False",
"execution_count": null,
"outputs": []
},
{
"metadata": {
"scrolled": false,
"trusted": false
},
"cell_type": "code",
"source": "with open('test/UniqueModelObject_GTest.cpp', 'w') as gtest:\n gtest.write(header_start)\n\n for d in result:\n gtest.write(header_className.format(className = d['ClassName']))\n \n gtest.write(header_end)\n\n\n for d in result:\n className = d['ClassName']\n iddObjectType = d['IddObjectType']\n camelClassName = className[0].lower() + className[1:]\n\n # If the list is not already populated, we test via ruby\n if not alreadyCheckedInRuby:\n ruby_prog = ruby_test_program.format(className=className, \n camelClassName=camelClassName,\n iddObjectType=iddObjectType)\n with open('temp.rb', 'w') as f:\n f.write(ruby_prog)\n\n cmd_result = !$os_build_rel/Products/openstudio temp.rb\n # If ruby test failed, we'll print the C++ GTest at end\n if cmd_result:\n print(cmd_result)\n print_at_end.append(d)\n continue\n # If already populated, and found to not work, skip\n elif d in print_at_end:\n print(f\"Skipping {className}, already found to not work\")\n continue\n\n gtest_f = test_program.format(className=className, camelClassName=camelClassName)\n\n gtest.write(gtest_f)\n \n for d in print_at_end:\n className = d['ClassName']\n iddObjectType = d['IddObjectType']\n camelClassName = className[0].lower() + className[1:]\n \n gtest_f = test_program.format(className=className, camelClassName=camelClassName)\n \n gtest.write(\"\\n// TODO: Not working for {className}\".format(className=className))\n gtest.write(\"\\n// \".join(gtest_f.splitlines()))\n gtest.write(\"\\n\")\n \nalreadyCheckedInRuby = True",
"execution_count": null,
"outputs": []
}
],
"metadata": {
"kernelspec": {
"name": "python3",
"display_name": "Python 3",
"language": "python"
},
"language_info": {
"name": "python",
"version": "3.8.0",
"mimetype": "text/x-python",
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"pygments_lexer": "ipython3",
"nbconvert_exporter": "python",
"file_extension": ".py"
},
"toc": {
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"base_numbering": 1,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
},
"gist": {
"id": "",
"data": {
"description": "Create_UniqueModelObject_Clone_tests.ipynb",
"public": true
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}
@jmarrec
Copy link
Author

jmarrec commented Feb 27, 2020

To be placed in OpenStudio/src/model/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment