Skip to main content

Python Package to import and work with the SIMULTAN Data model

Project description

PySimultan

Python Package to import and work with the SIMULTAN Data model [SIMULTAN REPORT].

What is it?

This package tries to provide a simple and seamless integration of SIMULTAN in python.

With only three lines of code and a template you can import a SIMULTAN-Project:

from PySimultan import DataModel, TemplateParser

template_parser = TemplateParser(template_filepath='my_template.yml')
data_model = DataModel(project_path='my_project.simultan')
typed_data = data_model.get_typed_data(template_parser=template_parser)

You can also integrate SIMULTAN in your existing Project with PySimultan and a few lines of code:

See readme_test_5:

from PySimultan import DataModel, TemplateParser


class MyWindow(object):

    def __init__(self, *args, **kwargs):

        pass

    def open(self):
        print(f'Window {self.Name} with area {self.Area} m² opened')


template_parser = TemplateParser()
template_parser.bases['Window'] = MyWindow
template_parser.create_template_classes()
data_model = DataModel(project_path='test.simultan')
typed_data = data_model.get_typed_data(template_parser=template_parser)

# assuming the fist object in the data-model is of the TYPE 'Window'
typed_data[0].open()

Installation

PySimultan can be installed via pip. To install PySimultan run:

# PyPI
pip install PySimultan

Background:

As SIMULTAN is written in C#, the package uses Python.NET to import the Simultan libraries. With Python.NET all functions of the dlls are preserved, and you are able to develop in python without any changes (except for cases that are .NET specific).

To make life easier for developers, we have implemented a typing process that imports components as instances of the type you have defined! Using simple text templates, the 'TYPE' of a SIMULTAN component is assigned to a class in Python.

Best explained with an example:

A component with the 'TYPE' parameter 'Room' is imported from a project. With the template a python class Room is assigned to the 'TYPE': 'Room'. The new class Room is created when parsing the template. It inherits from:

  • a default class which wraps the imported object (like SimultanObject)
  • a class created from the template, adding properties for the content.
  • a user defined class defined in the templates 'inherit_from' entry

This way SIMULTAN can be integrated into existing packages with minimal adjustments!

Features:

  • fast: use of lazy loading, caching and parallelization
  • variable search depth for references
  • minimal dependencies
  • easy to integrate

To Do:

  • Only read access. Write and save has not been tested yet.
  • Calculations not implemented
  • Networks not implemented

Usage

Data model Typing

Since SIMULTAN is a dynamic data model, the data must first be typed. To do this, the TYPE parameter is created in a component to be typed and a corresponding value is set.

For example, the user creates a component 'Living Room 1 Floor', which he can identify and assign as a room on the first floor based on the name. However, for an application it is not clear which data corresponds to what. Therefore, the data must first be typed. For typing, the parameter 'TYPE' is created in a component and a corresponding value is set.

In this example the TYPE of the component is 'Room' and every application which imports the project can identify it as of the type 'Room'.

In Python readme_test_1:

from PySimultan import DataModel
data_model = DataModel(project_path='example1.simultan')
print(data_model.data.Items)
print(f'Component type: {data_model.data.Items[0].ContainedParameters.Items[1].TextValue}')

prints a list for the components and the type of the first component:

[<ParameterStructure.Component.Component object at 0x00000157F77AA7C0>]
Component type: Room

Now we can import and determine the type of the component

TemplateParser

We can determine the type of our component, but to do this with a lots of components and sub-components is getting annoying fast. This is where we can use the TemplateParser.

The TemplateParser is a class which scans the data model and creates instances of types, defined in a template.

We want to load the following components in python:

Therefore, we have to create a template which defines how our class should look like. Let's look at this template-file template_example3.yml:

- !Template
  template_name: Room
  template_id: '1'
  inherits_from:
  content: [Internal heat gains, Volume, Room Name, Furniture]
  documentation: 'Room: defines a Room or a Zone'
  units: {Internal heat gains: W, Volume: , Room Name: '-', Furniture: '-'}
  types: {Internal heat gains: float, Volume: float, Room Name: str}
  slots: {Furniture: Element_00}
- !Template
  template_name: Table
  template_id: '2'
  inherits_from:
  content: [Number Persons]
  documentation: 'Table: defines a table with number of persons'
  units: {Number Persons: '-'}
  types: {Number Persons: float}
  slots: {}

In this yaml-file is one 'Room'-template which defines how components of the TYPE 'Room' should be handled. There is a entry called content which defines which content for this type is expected. The content in the SIMULTAN data model can be a Parameter, a sub-component or a linked component. In this example four items in the SIMULTAN-component are expected: 'Internal heat gains', 'Volume', 'Room Name' and 'Furniture'.

What the Template parser does is that it checks in the SIMULTAN-Component if there is a TYPE-Parameter defined. If the TYPE is found, there is a check for each item in the content if a parameter with the name of the content is defined.

For example the first item of the content is 'Internal heat gains'. If a parameter with this name is found in the component, the property of the generated python-component will return the value of the SIMULTAN-parameter 'Internal heat gains'.

If no parameter is found, PySimultan checks if there is a slot defined for the content. Sub-components or linked components in the SIMULTAN data model have a slot and a slot-extension. With this slot- /extension PySimultan can determine which component or sub-component is the value of the content.

In this example for the content 'Furniture' the slot 'Element_00' is defined. If no Parameter 'Furniture' is found, PySimultan checks if there is a entry with the slot 'Element' and the slot-extension '0' in the component and return this instance.

To load the 'Room' component run readme_test_3:

from PySimultan import DataModel, TemplateParser

# use pkg_resources to load package example files
with pkg_resources.path(readme_examples, 'template_example3.yml') as r_path:
    template_file = str(r_path)

# create and write the templates
create_template(template_file)

# create the template parser
template_parser = TemplateParser(template_filepath=template_file)

# load the SIMULTAN project
data_model = DataModel(project_path=project_file)

# create the typed datamodel form the loaded SIMULTAN project and the template_parser
typed_data = data_model.get_typed_data(template_parser=template_parser, create_all=False)

If we inspect typed_data we can see that there is a instance of the type 'Room' with the attributes we defined in the template. The attribute 'Furniture' is the linked component with the Slot 'Element' and the slot-extension 0.

We can easily access all content which was defined in the template with python .dot synatx:

print(typed_data[0].Furniture)
print(typed_data[0].Volume)

# Properties with blanks: use getattr
print(getattr(typed_data[0], 'Internal heat gains'))

Templates

File Description

Template-files are yaml files which define one or more templates. A template has the following keywords:

  • template_name: The name of the template; [str]; This name is also used for the 'TYPE' value of a SIMULTAN-component and the class name in python: Example: 'TestTemplate'

  • template_id: ID of the template; [int]; There is currently no functionality for the ID of the template; Example: 1

  • inherits_from: parent class of the template; [-]; Template from which this template inherits. Can be another Template or the name of another template. Example: see Table and ModifiedTable template

  • content: List of the content names; [-]; this list defines which attributes the python class for this template has. The values of these attributes are either the value of the parameter with the name of the SIMULTAN-component or the value of the defined slot (see slots); Example: [Internal heat gains, Volume, Room Name, Furniture]

  • documentation: Documentation of the template; [str]; Documentation for other developers and users;Example:

  • units: Units of the content; [dict]; dictionary with the name of the content as key and unit as value. No further functionality. Example: {Internal heat gains: W, Volume: m³, Room Name: '-', Furniture: '-'}

  • types: Type of the content; [dict]; The type defines if the current_value or the text_value of a SIMULTAN-component is returned. If the type is str the text_value is returned, otherwise the current_value, the sub-component or the refence is returned; Example: {Internal heat gains: float, Volume: float, Room Name: str}

  • slots: slots of the content; [dict]; if no parameter with the content name is found, the reference or the sub-component with the here defined slot is returned; Example: {Furniture: Element_00}

  • synonyms: synonyme names of the python attributes; [dict]; in python class: the content is accessed through the synonym instead of the content name

Template Example

- !Template
  template_name: Room
  template_id: '1'
  inherits_from:
  content: [Internal heat gains, Volume, Room Name, Furniture]
  documentation: 'Room: defines a Room or a Zone'
  units: {Internal heat gains: W, Volume: , Room Name: '-', Furniture: '-'}
  types: {Internal heat gains: float, Volume: float, Room Name: str}
  slots: {Furniture: Element_00}
- &id001 !Template
  template_name: Table
  template_id: '2'
  inherits_from:
  content: [Number Persons]
  documentation: 'Table: defines a table with number of persons'
  units: {Number Persons: '-'}
  types: {Number Persons: float}
  slots: {}
- !Template
  template_name: ModifiedTable
  template_id: '3'
  inherits_from: *id001
  content:
  documentation: See Table
  units:
  types:
  slots: {}
- !Template
  template_name: MaterialList
  template_id: '4'
  inherits_from: List
  content: []
  documentation: List with Materials. This list inherits from the build-in type List
  units: {}
  types: {}
  slots: {}
- !Template
  template_name: MyFace
  template_id: '101'
  inherits_from: Geometric Area
  content: []
  documentation: Face. This type inherits from the build-in type Geometric Area
  units: {}
  types: {}
  slots: {}

Template generation in python

With PySimultan template-files can be generated in an easy way. Templates can be created as instances of PySimultan.Template and dumped to a file:

from PySimultan import Template, yaml

# create several templates:
face_template = Template(template_name='MyFace',
                         template_id='1',
                         content=[],
                         inherits_from='Geometric Area',
                         documentation='',
                         units={},
                         types={},
                         slots={}
                         )

building_template = Template(template_name='Building',
                             template_id='2',
                             content=['FloorList'],
                             documentation='',
                             units={},
                             types={},
                             slots={'FloorList': 'Liste_00'},
                             inherits_from='SmartCampusBuilding'
                             )

floor_list_template = Template(template_name='FloorList',
                               template_id='3',
                               content=[],
                               inherits_from='List',
                               documentation='List with Floors',
                               units={},
                               types={},
                               slots={}
                               )

# create a list of the templates to write to a file:
templates = [face_template, building_template, floor_list_template]

# write the templates to a file:
with open(r'test.yml',
          mode='w',
          encoding="utf-8") as f_obj:
    yaml.dump(templates, f_obj)

Geometry

In SIMULTAN geometry is handled in a different way. The geometry is seperated from the data-model and there can be multiple geometry files and modles. The geometry-model is also loaded separately from the data-model.

When a project with a geometry model is loaded, there are automatically typed_geo_models generated. These geometry models are python objects with python objects of vertices, edges, faces ...

These python instances are also used in the typed data model. These instances inherit from

Default Types

There are default types for components of these slots (or TYPE):

  • List slot: 'Liste' ('List'); returns a list in python. A list instead a component with subcomponents is easier to handle. Additionaly the items in the list are sorted by their slot extension

  • ValueField slot: 'ValueField'; returns a pandas dataframe for the value-field

  • BuildInFace slot: 'Geometrische_Flächen' ('Geometric Area'); returns a instance of the class BuildInFace, has the link to GeometricFace

  • BuildInVolume slot: 'Geometrische_Volumina'; returns a instance of the class BuildInVolume, has the link to GeometricVolume

  • BuildInZone type: 'BuildInZone'; returns a instance of the class BuildInZone (a zone can consist of multiple volumes)

Geometry default Types:

  • GeometricLayer
  • GeometricVertex
  • GeometricEdge
  • GeometricEdgeLoop
  • GeometricFace
  • GeometricVolume

Tips and tricks:

get all instances of a type

The TemplateParser keeps track of all classes and instances. So it's easy to find all instances of a certain type in the data-model. TemplateParser.template_classes returns a dictionary with all generated python classes. These classes have an attribute cls_instances which returns a list with all instances of this type. ValueField, List and all classes which inherit from these do not have cls_instances.

After loading the data-model:

from PySimultan import TemplateParser, DataModel
template_parser = TemplateParser(template_filepath='my_template.yml')
data_model = DataModel(project_path='my_project.simultan')
typed_data = data_model.get_typed_data(template_parser=template_parser, create_all=True)

for cls in template_parser.template_classes.values():
    if hasattr(cls, 'cls_instances'):
        print(f'{cls.__name__}: {cls.cls_instances}')

Integrate PySimultan in your package

PySimultan tries to make the integration of the SIMULTAN data model as simple as possible. The integration is done by using user-defined classes to inherit from when the template classes are created.

All generated classes must inherit from a PySimultan base class which wraps the imported SIMULTAN-component. This class tracks created instances and wraps the SIMULTAN-component.

There are two methods to use your own implementation for the classes of the imported components.

Use TemplateParser's bases and geo_bases

The TemplateParser has the two dictionaries bases and geo_bases from which the created template-classes inherit if the 'inherit_from' entry in a Template is not empty or a 'default type' is detected. To use your own implementation of a class you just have to create or overwrite a entry in the bases or geo_bases with the template name as key and your class as value:

Example readme_test_5:

from PySimultan import DataModel, TemplateParser


class MyWindow(object):

    def __init__(self, *args, **kwargs):

        self.another_attribute = kwargs.get('another_attribute', None)

    def open(self):
        print(f'Window {self.Name} with area {self.Area} m² opened')


template_parser = TemplateParser(template_filepath=template_file)
template_parser.bases['Window'] = MyWindow
template_parser.create_template_classes()
data_model = DataModel(project_path=project_file)
typed_data = data_model.get_typed_data(template_parser=template_parser)

typed_data[0].open()

Limitations:

  • the __init__ method must be implemented with *args and **kwargs as arguments
  • in the __init__ method, the attributes defined in content must not be initialized or skipped. Background: For the defined content in the template are properties created who's getters and setters access the SIMULTAN-component. Therefore, the attributes must not be initialized. A workaround is to inherit directly from SimultanObject and overwrite the __init__-method.

Use monkey patching

More infos

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

PySimultan-0.1.13.tar.gz (30.3 kB view hashes)

Uploaded Source

Built Distribution

PySimultan-0.1.13-py3-none-any.whl (21.3 MB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page