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].

This package tries to provide a seamless integration of SIMULTAN in Python. 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 in the type you have defined! Using simple text templates, the type of a SIMULTAN component is assigned to a class in Python. This class then inherits from the imported instance's class using monkey patching.

Best explained with an example:

A component with type 'room' is imported from a project. With the template the class 'EgRoom' is assigned to the type 'Room' in Python. When importing the instance, a new class 'NewRoom' is created, which inherits from EgRoom and the class of the imported object ('SimultanComponent'). Using monkey patching, the EgRoom class is then overwritten with the 'NewRoom' class. Finally, the class 'SimultanComponent' of the imported instance is converted into the new class 'NewRoom' using type-casting.

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.

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 define 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 SIMULTAN-Component if there is a TYPE-Parameter defined. If the TYPE is found, for each item in the content there is a check if a parameter with the name of the content 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, Template, TemplateParser, yaml

# 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:
yaml.dump(templates, 'test.yml')

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.

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:

template_parser = TemplateParser(template_filepath=template_file)
data_model = DataModel(project_path=project_file)
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}')

Extend packages with PySimultan

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.4.tar.gz (29.0 kB view hashes)

Uploaded Source

Built Distribution

PySimultan-0.1.4-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