diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..27bcee3 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,4 @@ +template: | + ## What’s Changed + + $CHANGES diff --git a/.gitignore b/.gitignore index 5a80814..5664773 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,7 @@ target/ .idea/ /docs/reference/ + +test.dck + +test_deck.txt diff --git a/README.md b/README.md index 5081b25..a7460f8 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ pip install trnsystor ## Usage -Since TRNSYS 18, type proformas can be exported to XML schemas. *trnsystor* builds on -this easy to read data structure to easily create TrnsysModels using the most popular -scripting language in the data science community: +Since TRNSYS 18, type proformas can be exported to XML schemas. *trnsystor* builds on this +easy to read data structure to easily create TrnsysModels using the most popular scripting +language in the data science community: [Python](https://www.economist.com/graphic-detail/2018/07/26/python-is-becoming-the-worlds-most-popular-coding-language). From the xml file of a type proforma, simply create a TrnsysModel object by invoking the @@ -91,9 +91,9 @@ pipe1.connect_to(pipe2, mapping={0:0, 1:1}) ## Equations In the TRNSYS studio, equations are components holding a list of user-defined expressions. -In trnsystor a similar approach has been taken: the `Equation` class handles the -creation of equations and the [EquationCollection` class handles the block of equations. -Here's an example: +In trnsystor a similar approach has been taken: the `Equation` class handles the creation +of equations and the [EquationCollection` class handles the block of equations. Here's an +example: First, create a series of Equation by invoking the [from_expression` constructor. This allows you to input the equation as a string. @@ -149,8 +149,8 @@ and saving it to file. ### Simulation Cards The Simulation Cards is a chuck of code that informs TRNSYS of various simulation controls -such as start time end time and time-step. trnsystor implements many of those -*Statements* with a series of Statement objects. +such as start time end time and time-step. trnsystor implements many of those *Statements* +with a series of Statement objects. For instance, to create simulation cards using default values, simply call the `all()` constructor: @@ -171,3 +171,20 @@ LIMITS 25 10 25 ! Max iterations Max warnings Trace limit EQSOLVER 0 ! EQUATION SOLVER statement ``` +### Selecting elements of components + +Inputs, Outputs, Parameters, Derivatives, SpecialCards and ExternalFiles can be accessed +via their attribute in any TrnsysModel component. They are accessed via their position as +for in a list. It is also possible to `slice` the collection to retrieved more than one +element. In this case a list is returned: + +```python +>>> from trnsystor.trnsysmodel import TrnsysModel +>>> pipe = TrnsysModel.from_xml("tests/input_files/Type951.xml") +>>> pipe.inputs[0:2] # getting the first 2 inputs +[Inlet Fluid Temperature - Pipe 1; units=C; value=15.0 °C +The temperature of the fluid flowing into the first buried horizontal pipe., Inlet Fluid Flowrate - Pipe 1; units=(kg)/(hr); value=0.0 kg/hr +The flowrate of fluid into the first buried horizontal pipe.] + +``` + diff --git a/docs/conf.py b/docs/conf.py index 3e00848..13ca0d5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,4 @@ +"""Docs module.""" # Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full @@ -13,6 +14,8 @@ import os import sys +from trnsystor import __version__ + sys.path.insert(0, os.path.abspath(".")) sys.path.insert(0, os.path.abspath("trnsystor")) @@ -23,9 +26,8 @@ author = "Samuel Letellier-Duchesne" # The full version, including alpha/beta/rc tags -import trnsystor -version = release = trnsystor.__version__ +version = release = __version__ # -- General configuration --------------------------------------------------- diff --git a/setup.cfg b/setup.cfg index 255a528..8cc3adb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,4 +7,11 @@ description-file = README.md max-line-length = 88 extend-ignore = # See https://github.com/PyCQA/pycodestyle/issues/373 - E203, \ No newline at end of file + E203, E501 +docstring-convention=google +exclude = + .git, + __pycache__, + docs/conf.py, + tests + setup.py \ No newline at end of file diff --git a/setup.py b/setup.py index ff4db06..1f9c6b8 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,11 @@ +"""Setup/install the package.""" # Always prefer setuptools over distutils import codecs import os import re from os import path -from setuptools import setup, find_packages +from setuptools import find_packages, setup here = os.getcwd() diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..bb4fdfd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Testing module.""" diff --git a/tests/input_files/Type25c.bmp b/tests/input_files/Type25c.bmp new file mode 100644 index 0000000..ad079bd Binary files /dev/null and b/tests/input_files/Type25c.bmp differ diff --git a/tests/input_files/Type25c.xml b/tests/input_files/Type25c.xml new file mode 100644 index 0000000..2ca0109 --- /dev/null +++ b/tests/input_files/Type25c.xml @@ -0,0 +1,248 @@ + + + Printer - No units printed to output file + See the list of contributors in the main manual + Solar Energy Laboratory, University of Wisconsin-Madison + SEL and TESS + TRNSYS v7.5 + February 2016 + 4 + 32 + C:\software\pyTrnsysType\tests\input_files\Type25c.bmp + 25 + 9999 + +
The printer component is used to output (or print) selected system variables at specified (even) intervals + of time. In this mode, unit descriptors (kJ/hr, degC, W, etc.) are NOT printed to the output file with each + column heading. Output can be printed in even time intervals starting relative to the simulation start time or + can be printed in absolute time. If relative printing is chosen with a one hour print interval and the + simulation starts at time 0.5, values will be printed at times 0.5, 1.5, 2.5, etc. If absolute printing is + selected, for the same simulation, values will be printed at times 0.5, 1.0, 2.0, 3.0, etc. Type25 is also able + to print simulation information as a header to the output file (name of input file, and time of simulation run). + It is further able to append new data to an existing file or can be set to overwrite the existing file. +
+ + + 1 + Input to be printed + input + any + any + string + -Inf + +Inf + [ ; ] + label + SN + Input to be printed + + + 2 + Printing interval + parameter + Time + hr + real + -12 + +Inf + [ ; ] + STEP + SN + The time interval at which printing is to occur. If the time interval is less than zero, then + the print interval will be measured in the absolute value of this parameter expressed in months. + Examples: + 1: print every hour + -1: print every month + The default value (STEP) is a TRNSYS parameter equal to the simulation time step + + + + 3 + Start time + parameter + Time + hr + real + 0 + +Inf + [ ; ] + START + SN + The time of the year in hours at which printing is to start. + The default value (START) is a TRNSYS parameter equal to the simulation start time + + + + 4 + Stop time + parameter + Time + hr + real + 0 + +Inf + [ ; ] + STOP + SN + The time of the year in hours at which printing is to stop. + The default value (STOP) is a TRNSYS parameter equal to the simulation stop time + + + + 5 + Logical unit + parameter + Dimensionless + - + integer + 30 + 999 + [ ; ] + 30 + SN + This parameter sets the Fortran Logical Unit (File reference number) of the output file. It is + used internally by TRNSYS to refer to the file. This parameter will automatically be assigned to a + unique value by the TRNSYS Studio + + + + 6 + Units printing mode + parameter + Dimensionless + - + integer + 0 + 0 + [ ; ] + 0 + SN + This parameter is set to 0, so no units are printed to the output file + + + 7 + Relative or absolute start time + parameter + Dimensionless + - + integer + 0 + 1 + [ ; ] + 0 + SN + This parameter controls whether the print intervals are relative or absolute + 0: print at time intervals relative to the simulation start time + 1: print at absolute time intervals + For example, if the simulation start time is 0.5, the simulation time step is 0.25 and the printing time + step is 1: + If this parameter is set to 0, printing will occur at 0.5, 1.5, 2.5, etc. + If this parameter is set to 1, printing will occur at 1, 2, 3, etc. + + + + 8 + Overwrite or Append + parameter + Dimensionless + - + integer + -1 + 1 + [ ; ] + -1 + SN + This parameter decides whether the file is appended to or overwritten: + -1: Overwrite the output file + 1: Append to the output file + + + + 9 + Print header + parameter + Dimensionless + - + integer + -1 + 1 + [ ; ] + -1 + SN + This parameters decides whether or not a header with input file information will be printed to + the output file or not + -1: Do not print header + 1: Print header + + + + 10 + Delimiter + parameter + Dimensionless + - + integer + 0 + 2 + [ ; ] + 0 + SN + This parameter controls the delimiter used in the output file: + 0: use tabs to delimit columns + 1: use spaces to delimit columns + 2: use commas to delimit columns + + + + 11 + Print labels + parameter + Dimensionless + - + integer + -1 + 1 + [ ; ] + 1 + SN + This parameter decides whether or not labels (variable descriptors) should be printed as column + headers: + -1: Do not print descriptors + 1: Print descriptors + + + + + + input + 1 + 1 + + + input + 1 + 1 + + How many variables are to be printed by this component? + 1 + 100 + + + 1 + 50 + + + + + Output file for printed results + ***.out + + ***.out + + Logical unit + no + + + df /c + .\SourceCode\Types\Type25.f90 +
diff --git a/tests/test_utils.py b/tests/test_utils.py index c892e76..6d4fd93 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,7 @@ +"""Test utils module.""" import pytest -from trnsystor import redistribute_vertices +from trnsystor.utils import redistribute_vertices class TestRedistributeVertices: @@ -30,7 +31,7 @@ def test_redistribute_vertices(self, line): assert newline.length == line.length def test_redistribute_vertices_wrongtype(self, ring): - """Tests unsupported geometry""" + """Tests unsupported geometry.""" with pytest.raises(TypeError): assert redistribute_vertices(ring, 10) diff --git a/tests/test_xml.py b/tests/test_xml.py index 93a5c1b..3da2bf3 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -1,3 +1,4 @@ +"""Test module.""" import os import sys from tempfile import NamedTemporaryFile, TemporaryFile @@ -13,7 +14,7 @@ @pytest.fixture(scope="class") def fan_type(): - """Fixture to create a TrnsysModel from xml""" + """Fixture to create a TrnsysModel from xml.""" from trnsystor.trnsysmodel import TrnsysModel fan1 = TrnsysModel.from_xml(Path("tests/input_files/Type146.xml")) @@ -22,7 +23,7 @@ def fan_type(): @pytest.fixture(scope="class") def pipe_type(): - """Fixture to create a TrnsysModel from xml. Also tests using a Path""" + """Fixture to create a TrnsysModel from xml. Also tests using a Path.""" from trnsystor.trnsysmodel import TrnsysModel pipe = TrnsysModel.from_xml("tests/input_files/Type951.xml") @@ -56,8 +57,10 @@ def weather_type(): class TestTrnsysModel: @patch("builtins.input", return_value="y") def test_chiller_type(self, input): - """Fixture to create a TrnsysModel from xml from an xml that contains - unknown tags. Should prompt user. Passes when input == 'y'""" + """Fixture to create a TrnsysModel from xml that contains unknown tags. + + Should prompt user. Passes when input == 'y' + """ from trnsystor.trnsysmodel import TrnsysModel fan1 = TrnsysModel.from_xml("tests/input_files/Type107-simplified.xml") @@ -79,9 +82,10 @@ def test_cycles(self, pipe_type): assert actual == n_nodes def test_cycles_2(self, pipe_type): - """changing number of fluid nodes from 100 to 20 should create 20 outputs - for pipe 2 and 20 outputs for pipe 1""" + """changing number of fluid nodes from 100 to 20 should create 20 outputs. + for pipe 2 and 20 outputs for pipe 1 + """ outputs = pipe_type.outputs mylist = list(outputs.keys()) sub_1 = "Average_Fluid_Temperature_Pipe_1_" @@ -106,7 +110,7 @@ def test_cycles_2(self, pipe_type): assert n_nodes == expected_2 def test_cycles_issue14(self): - """https://github.com/samuelduchesne/trnsystor/issues/14""" + """https://github.com/samuelduchesne/trnsystor/issues/14.""" from trnsystor.trnsysmodel import TrnsysModel valve = TrnsysModel.from_xml("tests/input_files/Type647.xml") @@ -143,21 +147,21 @@ def test_cancel_missing_tag(self, tank_type): with NamedTemporaryFile("w", delete=False) as tmp: tmp.write(str(soup)) tmp.close() - tank = TrnsysModel.from_xml(tmp.name) + TrnsysModel.from_xml(tmp.name) os.unlink(tmp.name) def test_out_of_bounds(self, pipe_type): - """should trigger ValueError because out of bounds""" + """Trigger ValueError because out of bounds.""" with pytest.raises(ValueError): pipe_type.parameters["Number_of_Radial_Soil_Nodes"] = 21 def test_get_attr(self, fan_type): - """Test getter for class TypeVariable""" + """Test getter for class TypeVariable.""" in_air_temp = fan_type.inputs["Inlet_Air_Temperature"] assert in_air_temp def test_set_attr(self, fan_type): - """Test setter for class TypeVariable""" + """Test setter for class TypeVariable.""" new_value = 12 attr_name = "Inlet_Air_Temperature" fan_type.inputs[attr_name] = new_value @@ -166,8 +170,10 @@ def test_set_attr(self, fan_type): assert fan_type.inputs[attr_name].value == Q_.__class__(new_value, Q_.units) def test_set_attr_quantity(self, fan_type): - """Test setter for class TypeVariable with type _Quantity. This tests - setting a value with different but equivalent units""" + """Test setter for class TypeVariable with type _Quantity. + + This tests setting a value with different but equivalent units. + """ attr_name = "Rated_Volumetric_Flow_Rate" new_value = fan_type.parameters[attr_name].value.to("m^3/s") * 10 fan_type.parameters[attr_name] = new_value @@ -184,12 +190,12 @@ def test_set_initial_input_values(self, fan_type): assert fan_type.initial_input_values[attr_name] def test_get_attr_derivative(self, tank_type): - """Test setter for class Derivative""" + """Test setter for class Derivative.""" attr_name = "Initial_temperature_of_node_1" assert tank_type.derivatives[attr_name].value.m == 50.0 def test_set_attr_derivative(self, tank_type): - """Test setter for class Derivative""" + """Test setter for class Derivative.""" attr_name = "Initial_temperature_of_node_1" tank_type.derivatives[attr_name] = 60 assert tank_type.derivatives[attr_name].value.m == 60.0 @@ -200,7 +206,7 @@ def test_get_attr_specialCards(self, plotter: TrnsysModel): print(plotter.special_cards) def test_set_attr_cycle_parameters(self, pipe_type): - """Test setter for class TypeVariable""" + """Test setter for class TypeVariable.""" attr_name = "Radial_Distance_of_Node_1" new_value = 0.05 pipe_type.parameters[attr_name] = new_value @@ -211,11 +217,11 @@ def test_set_attr_cycle_parameters(self, pipe_type): ) def test_to_deck(self, fan_type): - """test to Input File representation of a TrnsysModel""" + """Test to Input File representation of a TrnsysModel.""" print(fan_type._to_deck()) def test_initial_input_values_to_deck(self, fan_type): - """test to Input File representation of a TrnsysModel""" + """Test to Input File representation of a TrnsysModel.""" print(fan_type.initial_input_values._to_deck()) def test_set_attr_cycle_question(self, tank_type): @@ -237,13 +243,13 @@ def test_set_attr_cycle_question_2(self, tank_type): ) def test_trnsysmodel_repr(self, tank_type): - """test the __repr__ for :class:`TrnsysModel`""" + """Test the __repr__ for :class:`TrnsysModel`.""" assert ( repr(tank_type)[3:] == "Type4: Storage Tank; Fixed Inlets, Uniform Losses" ) def test_typecycle_repr(self, tank_type): - """test the __repr__ for :class:`TypeCycle`""" + """Test the __repr__ for :class:`TypeCycle`.""" assert repr(tank_type._meta.cycles[0]) == "output 1 to 13" def test_collections_repr(self, tank_type): @@ -285,7 +291,7 @@ def test_TypeVariable_repr(self, tank_type): break def test_set_wrong_type(self, fan_type): - """try to assign a complexe number should raise a TypeError""" + """Try to assign a complex number should raise a TypeError.""" with pytest.raises(TypeError): fan_type.parameters["Rated_Volumetric_Flow_Rate"] = 2 + 3j @@ -296,7 +302,7 @@ def test_set_wrong_type(self, fan_type): "integer", "real", pytest.param( - "complexe", marks=pytest.mark.xfail(raises=NotImplementedError) + "complex", marks=pytest.mark.xfail(raises=NotImplementedError) ), ], ) @@ -382,7 +388,7 @@ def test_get_external_file(self, weather_type): ) def test_set_external_file(self, weather_type): - """Test setting a different path for external files""" + """Test setting a different path for external files.""" from path import Path # test set Path behavior @@ -408,13 +414,12 @@ def test_set_position(self, fan_type): assert fan_type.studio.position == Point(50, 40) def test_set_link_style_to_itself_error(self, fan_type): - """Setting a link style on an non-existent connection should raise KeyError""" + """Setting a link style on an non-existent connection should raise KeyError.""" fan_type.invalidate_connections() with pytest.raises(KeyError): fan_type.set_link_style(fan_type) def test_set_link_style_to_itself(self, fan_type): - """""" fan_type.connect_to(fan_type, mapping={0: 0}) fan_type.set_link_style(fan_type) @@ -475,7 +480,7 @@ def test_statement_class(self): from trnsystor.statement.statement import Statement statement = Statement() - assert print(statement) == None + assert print(statement) is None def test_version_statement(self): from trnsystor.statement.version import Version @@ -513,7 +518,7 @@ def test_width_statement(self): width_statement = Width(80) assert width_statement._to_deck() == "WIDTH 80" with pytest.raises(ValueError): - wrong_statement = Width(1000) + Width(1000) def test_nocheck_statement(self, fan_type): from trnsystor.statement.nocheck import NoCheck @@ -539,7 +544,7 @@ def test_nolist_statement(self): @pytest.mark.parametrize("classmethod", ["all", "debug_template", "basic_template"]) def test_control_cards(self, classmethod): - """Call different class methods on ControlCards""" + """Call different class methods on ControlCards.""" from trnsystor.controlcards import ControlCards cc = getattr(ControlCards, classmethod)() @@ -644,12 +649,9 @@ def test_symbolic_expression_2(self, tank_type, fan_type): print(eq) def test_malformed_symbolic_expression(self, tank_type, fan_type): - """passed only two args while expression asks for 3""" - from trnsystor.statement.constant import Constant - + """Pass only two args while expression asks for 3.""" name = "var_a" exp = "log(a) + b / 12 - c" - c_ = Constant.from_expression("start=0") from trnsystor.statement.equation import Equation start = Equation("start", 0) @@ -660,6 +662,7 @@ def test_empty_equationcollection(self): from trnsystor.collections.equation import EquationCollection eq = EquationCollection() + assert eq is not None def test_equation_collection(self, equation_block): from trnsystor.collections.equation import EquationCollection @@ -675,10 +678,10 @@ def test_equation_collection(self, equation_block): # An equal sign needs to be included in an expression with pytest.raises(ValueError): - equa1 = Equation.from_expression("TdbAmb : [011,001]") + Equation.from_expression("TdbAmb : [011,001]") def test_update_equation_collection(self, equation_block): - """test different .update() recipes""" + """Test different .update() recipes.""" from trnsystor.collections.equation import EquationCollection from trnsystor.statement.equation import Equation @@ -721,7 +724,7 @@ def test_equation_with_typevariable(self, fan_type): ) def test_two_unnamed_equationcollection(self, fan_type): - """make sure objects with same name=None can be created""" + """Make sure objects with same name=None can be created.""" from trnsystor.collections.equation import EquationCollection from trnsystor.statement.equation import Equation @@ -742,7 +745,7 @@ def test_constant_collection(self, constant_block): print(constant_block) def test_update_constant_collection(self, constant_block): - """test different .update() recipes""" + """Test different .update() recipes.""" from trnsystor.collections.constant import ConstantCollection from trnsystor.statement.constant import Constant @@ -885,7 +888,7 @@ def test_save(self, pvt_deck): class TestComponent: def test_unique_hash(self, fan_type): - """copying a component should change its hash""" + """Copying a component should change its hash.""" fan_type_2 = fan_type.copy() assert hash(fan_type) != hash(fan_type_2) @@ -953,7 +956,7 @@ def test_type951(self, deck, tmp_path): class TestStudioCanvas: - """TODO: complete tests for Canvas LinkStyles and path positioning""" + """TODO: complete tests for Canvas LinkStyles and path positioning.""" @pytest.mark.skip("known bug when copying type brakes cycles.") def test_shortest_path(self, tank_type: TrnsysModel, pipe_type: TrnsysModel): diff --git a/trnsystor/__init__.py b/trnsystor/__init__.py index cd8a8cb..d3e449b 100644 --- a/trnsystor/__init__.py +++ b/trnsystor/__init__.py @@ -1,12 +1,15 @@ -# Version of the package -__version__ = "1.3.1" +"""Trnsystor Module.""" -# warn if a newer version of archetypal is available from outdated import warn_if_outdated +from .controlcards import ControlCards +from .deck import Deck +from .trnsysmodel import TrnsysModel + +# Version of the package +__version__ = "1.3.2" + +# warn if a newer version of archetypal is available warn_if_outdated("trnsystor", __version__) -from .utils import * -from .trnsysmodel import TrnsysModel -from .deck import Deck -from .controlcards import ControlCards +__all__ = ["ControlCards", "Deck", "TrnsysModel", "__version__"] diff --git a/trnsystor/anchorpoint.py b/trnsystor/anchorpoint.py index 265d205..cac80f9 100644 --- a/trnsystor/anchorpoint.py +++ b/trnsystor/anchorpoint.py @@ -1,6 +1,4 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""AnchorPoint module.""" import itertools @@ -8,10 +6,11 @@ class AnchorPoint(object): - """Handles the anchor point. There are 6 anchor points around a component""" + """Handles the anchor point. There are 6 anchor points around a component.""" def __init__(self, model, offset=20, height=40, width=40): - """ + """Initialize object. + Args: model (Component): The Component. offset (float): The offset to give the anchor points from the center @@ -28,8 +27,9 @@ def studio_anchor(self, other, loc): """Return the studio anchor based on a location. Args: - other: TrnsysModel - loc (2-tuple): + other (TrnsysModel): The other TrnsysModel used to find the anchor of self. + loc (2-tuple): A 2-tuple of location, eg.: ("best", "best") or + of :attr:`anchor_ids`. """ if "best" not in loc: return loc @@ -41,10 +41,7 @@ def studio_anchor(self, other, loc): return u_loc, v_loc def find_best_anchors(self, other): - """ - Args: - other: - """ + """Find best anchor points to connect self and other.""" dist = {} for u in self.anchor_points.values(): for v in other.anchor_points.values(): @@ -58,15 +55,18 @@ def find_best_anchors(self, other): @property def anchor_points(self): + """Return dict of anchor points str->tuple.""" return self.get_octo_pts_dict(self.offset) @property def reverse_anchor_points(self): + """Return dict of anchor points tuple->str.""" pts = self.get_octo_pts_dict(self.offset) return {(pt.x, pt.y): key for key, pt in pts.items()} @property def studio_anchor_mapping(self): + """Return dict of anchor mapping str->tuple.""" from shapely.affinity import translate p_ = {} @@ -85,6 +85,7 @@ def studio_anchor_mapping(self): @property def studio_anchor_reverse_mapping(self): + """Return dict of anchor mapping tuple->str.""" return { (0, 0): "top-left", (20, 0): "top-center", @@ -97,13 +98,15 @@ def studio_anchor_reverse_mapping(self): } def get_octo_pts_dict(self, offset=10): - """Define 8-anchor :class:`Point` around the :class:`TrnsysModel` in - cartesian space and return a named-dict with human readable meaning. + """Define 8-anchor :class:`Point` around the :class:`TrnsysModel`. + + In cartesian space and returns a named-dict with human readable meaning. These points are equally dispersed at the four corners and 4 edges of - the center, at distance = :attr:`offset` + the center, at distance = :attr:`offset`. - See :func:`~trnsysmodel.TrnsysType.set_link_style` or - :class:`trnsysmodel.LinkStyle` for more details. + See Also: + - :meth:`trnsystor.component.Component.set_link_style` + - :class:`trnsystor.linkstyle.LinkStyle` Args: offset (float): The offset around the center point of :attr:`self`. @@ -131,4 +134,5 @@ def get_octo_pts_dict(self, offset=10): @property def centroid(self): + """Return centroid of self.""" return self.model.studio.position diff --git a/trnsystor/canvas.py b/trnsystor/canvas.py index 1cf6924..19e6cee 100644 --- a/trnsystor/canvas.py +++ b/trnsystor/canvas.py @@ -1,25 +1,35 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""StudioCanvas module.""" import networkx as nx -from shapely.geometry import box, LineString +from shapely.geometry import LineString, box class StudioCanvas: - # TODO: Document class + """StudioCanvas class. + + Handles geometric positioning of Components on a grid. + """ + def __init__(self, width=2000, height=1000): - self._grid_valid = True - self._grid = None + """Initialize object. + + Args: + width (int): The width of the grid in points. + height (int): The height of the grid in points. + """ self.width = width self.height = height + self._grid_valid = True + self._grid = None + @property def bbox(self): - """The :class:`shapely.geometry.geo.box` representation of the studio canvas""" + """Return a bounding box rectangle for the canvas.""" return box(0, 0, self.width, self.height) @property def grid_is_valid(self): + """Return True if grid is valid.""" if self._grid_valid: return True else: @@ -27,7 +37,7 @@ def grid_is_valid(self): @property def grid(self): - """The two-dimensional grid graph of the studio canvas""" + """Return the two-dimensional grid graph of the studio canvas.""" if self.grid_is_valid and self._grid is not None: return self._grid else: @@ -35,6 +45,7 @@ def grid(self): return self._grid def invalidate_grid(self): + """Invalidate grid.""" self._grid_valid = False def resize_canvas(self, width, height): @@ -51,7 +62,7 @@ def resize_canvas(self, width, height): self.invalidate_grid() def shortest_path(self, u, v, donotcross=True): - """ + """Return shortest path between u and v on the :attr:`grid`. Args: u (Point): The *from* Point geometry. diff --git a/trnsystor/collections/__init__.py b/trnsystor/collections/__init__.py index 5807536..8db6426 100644 --- a/trnsystor/collections/__init__.py +++ b/trnsystor/collections/__init__.py @@ -1,6 +1,4 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Collections Module.""" __all__ = [ "ComponentCollection", diff --git a/trnsystor/collections/components.py b/trnsystor/collections/components.py index d67dff8..5ebe587 100644 --- a/trnsystor/collections/components.py +++ b/trnsystor/collections/components.py @@ -1,13 +1,15 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""ComponentCollection module.""" import collections class ComponentCollection(collections.UserList): - """A class that handles collections of components, eg.; TrnsysModels, - EquationCollections and ConstantCollections + """A class that handles collections of components. + + Supported members: + - :class:`TrnsysModels` + - :class:`EquationCollections` + - :class:`ConstantCollections` Get a component from a ComponentCollection using either the component's unit numer or its full name. @@ -25,8 +27,15 @@ class ComponentCollection(collections.UserList): @property def iloc(self): + """Access a component by its :attr:`unit_number`.""" return dict({item.unit_number: item for item in self.data}) @property def loc(self): + """Access a components by its identify (self). + + Examples: + >>> cc = ComponentCollection([tank_type]) + >>> assert cc.loc[tank_type] == cc.iloc[tank_type.unit_number] + """ return dict({item: item for item in self.data}) diff --git a/trnsystor/collections/constant.py b/trnsystor/collections/constant.py index c032299..4f9c11a 100644 --- a/trnsystor/collections/constant.py +++ b/trnsystor/collections/constant.py @@ -1,20 +1,15 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""ConstantCollection module.""" import collections import tabulate -from trnsystor.component import Component -from trnsystor.name import Name +from trnsystor.component import Component from trnsystor.statement import Constant -from trnsystor.studio import StudioHeader class ConstantCollection(Component, collections.UserDict): - """A class that behaves like a dict and that collects one or more - :class:`Constants`. + """A class that behaves like a dict and collects one or more :class:`Constants`. You can pass a dict of Equation or you can pass a list of Equation. In the latter, the :attr:`Equation.name` attribute will be used as a key. @@ -41,32 +36,36 @@ def __init__(self, mutable=None, name=None, **kwargs): super(ConstantCollection, self).__init__(_dict, meta=None, name=name, **kwargs) def __getitem__(self, key): - """ - Args: - key: - """ + """Get item.""" if isinstance(key, int): value = list(self.data.values())[key] + elif isinstance(key, slice): + value = list(self.data.values()).__getitem__(key) else: value = super().__getitem__(key) return value def __setitem__(self, key, value): + """Set item.""" # optional processing here value.model = self super().__setitem__(key, value) def __repr__(self): + """Return Deck representation of self.""" return self._to_deck() def __hash__(self): + """Return hash(self).""" return self.unit_number def __eq__(self, other): + """Return self == other.""" return hash(self) == hash(other) def update(self, E=None, **F): - """D.update([E, ]**F). Update D from a dict/list/iterable E and F. + """Update D from a dict/list/iterable E and F. + If E is present and has a .keys() method, then does: for k in E: D[ k] = E[k] If E is present and lacks a .keys() method, then does: for cts.name, @@ -106,15 +105,21 @@ def update(self, E=None, **F): super(ConstantCollection, self).update(_e) @property - def size(self): + def size(self) -> int: + """Return len(self).""" return len(self) @property - def unit_number(self): + def unit_number(self) -> int: + """Return the unit_number of self. Negative by design. + + Hint: + Only :class:`TrnsysModel` objects have a positive unit_number. + """ return self._unit * -1 def _to_deck(self): - """To deck representation + """Return deck representation of self. Examples:: @@ -133,9 +138,9 @@ def _to_deck(self): return str(header_comment) + str(head) + str(core) def _get_inputs(self): - """inputs getter. Sorts by order number each time it is called""" + """Sort by order number each time it is called.""" return self def _get_outputs(self): - """outputs getter. Since self is already a dict, return self.""" + """Return outputs. Since self is already a dict, return self.""" return self diff --git a/trnsystor/collections/cycle.py b/trnsystor/collections/cycle.py index cd80ffc..84eb7c2 100644 --- a/trnsystor/collections/cycle.py +++ b/trnsystor/collections/cycle.py @@ -1,18 +1,12 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""CycleCollection module.""" import collections class CycleCollection(collections.UserList): - """Collection of :class:`trnsystor.typecycle.TypeCycle`""" + """Collection of :class:`trnsystor.typecycle.TypeCycle`.""" def __getitem__(self, key): - """Get item by key. - - Args: - key: - """ + """Get item by key.""" value = super().__getitem__(key) return value diff --git a/trnsystor/collections/derivatives.py b/trnsystor/collections/derivatives.py index 20a68ef..dbb86f7 100644 --- a/trnsystor/collections/derivatives.py +++ b/trnsystor/collections/derivatives.py @@ -1,6 +1,4 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""DerivativesCollection module.""" import tabulate @@ -9,13 +7,10 @@ class DerivativesCollection(VariableCollection): - """Subclass of :class:`VariableCollection` specific to Derivatives""" - - def __init__(self): - super().__init__() - pass + """Subclass of :class:`VariableCollection` specific to Derivatives.""" def __repr__(self): + """Return repr(self).""" num_inputs = "{} Inputs:\n".format(self.size) inputs = "\n".join( ['"{}": {:~P}'.format(key, value.value) for key, value in self.data.items()] @@ -23,8 +18,7 @@ def __repr__(self): return num_inputs + inputs def _to_deck(self): - """Returns the string representation for the Input File (.dck)""" - + """Return deck representation of self.""" if self.size == 0: # Don't need to print empty inputs return "" diff --git a/trnsystor/collections/equation.py b/trnsystor/collections/equation.py index 80314b3..4745240 100644 --- a/trnsystor/collections/equation.py +++ b/trnsystor/collections/equation.py @@ -1,20 +1,15 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""EquationCollection module.""" import collections import tabulate -from trnsystor.component import Component -from trnsystor.name import Name +from trnsystor.component import Component from trnsystor.statement import Equation -from trnsystor.studio import StudioHeader class EquationCollection(Component, collections.UserDict): - """A class that behaves like a dict and that collects one or more - :class:`Equations`. + """Behaves like a dict and collects one or more :class:`Equations`. This class behaves a little bit like the equation component in the TRNSYS Studio, meaning that you can list equation in a block, give it a name, etc. @@ -53,29 +48,32 @@ def __init__(self, mutable=None, name=None, **kwargs): super(EquationCollection, self).__init__(_dict, meta=None, name=name, **kwargs) def __getitem__(self, key): - """ - Args: - key: - """ + """Get item.""" if isinstance(key, int): value = list(self.data.values())[key] + elif isinstance(key, slice): + value = list(self.data.values()).__getitem__(key) else: value = super().__getitem__(key) return value def __hash__(self): + """Return hash(self).""" return self.unit_number def __repr__(self): + """Return Deck representation of self.""" return self._to_deck() def __setitem__(self, key, value): + """Set item.""" # optional processing here value.model = self super().__setitem__(key, value) def update(self, E=None, **F): - """D.update([E, ]**F). Update D from a dict/list/iterable E and F. + """Update D from a dict/list/iterable E and F. + If E is present and has a .keys() method, then does: for k in E: D[ k] = E[k] If E is present and lacks a .keys() method, then does: for eq.name, @@ -117,22 +115,26 @@ def update(self, E=None, **F): _e.update(_f) super(EquationCollection, self).update(_e) - def setdefault(self, key, value=None): - if key not in self: - self[key] = value - return self[key] - @property def size(self): + """Return len(self).""" return len(self) @property def unit_number(self): + """Return the unit_number of self. Negative by design. + + Hint: + Only :class:`TrnsysModel` objects have a positive unit_number. + """ return self._unit * -1 @property def unit_name(self): - """This type does not have a unit_name. Return component name""" + """Return ``name`` of self. + + This type does not have a unit_name. + """ return self.name @property @@ -141,7 +143,7 @@ def model(self): return self.__class__.__name__ def _to_deck(self): - """To deck representation + """Return deck representation of self. Examples:: @@ -160,19 +162,14 @@ def _to_deck(self): return str(header_comment) + str(head) + str(core) def _get_inputs(self): - """inputs getter. Sorts by order number each time it is called""" + """Sort by order number each time it is called.""" return self def _get_outputs(self): - """outputs getter. Since self is already a dict, return self.""" + """Return outputs. Since self is already a dict, return self.""" return self def _get_ordered_filtered_types(self, classe_, store): - """ - Args: - classe_: - store: - """ return collections.OrderedDict( (attr, self._meta[store][attr]) for attr in sorted( @@ -182,11 +179,6 @@ def _get_ordered_filtered_types(self, classe_, store): ) def _get_filtered_types(self, classe_, store): - """ - Args: - classe_: - store: - """ return filter( lambda kv: isinstance(self._meta[store][kv], classe_), self._meta[store] ) diff --git a/trnsystor/collections/externalfile.py b/trnsystor/collections/externalfile.py index d930eeb..7635f66 100644 --- a/trnsystor/collections/externalfile.py +++ b/trnsystor/collections/externalfile.py @@ -1,36 +1,29 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""ExternalFileCollection module.""" import collections import tabulate from path import Path -from trnsystor import standerdized_name from trnsystor.externalfile import ExternalFile +from trnsystor.utils import standardize_name class ExternalFileCollection(collections.UserDict): - """A collection of :class:`ExternalFile` objects""" + """A collection of :class:`ExternalFile` objects.""" def __getitem__(self, key): - """ - Args: - key: - """ + """Get item.""" if isinstance(key, int): value = list(self.data.values())[key] + elif isinstance(key, slice): + value = list(self.data.values()).__getitem__(key) else: value = super().__getitem__(key) return value def __setitem__(self, key, value): - """ - Args: - key: - value: - """ + """Set item.""" if isinstance(value, ExternalFile): """if a ExternalFile is given, simply set it""" super().__setitem__(key, value) @@ -45,25 +38,26 @@ def __setitem__(self, key, value): ) def __str__(self): + """Return deck representation of self.""" return self._to_deck() @classmethod def from_dict(cls, dictionary): - """Construct an :class:`~ExternalFileCollection` from a dict of - :class:`~ExternalFile` objects with the object's id as a key. + """Construct from a dict of :class:`~ExternalFile` objects. + + The object's ``id`` is used as the key. Args: dictionary (dict): The dict of {key: :class:`~ExternalFile`} """ item = cls() for key in dictionary: - # self.parameters[key] = ex_file.logical_unit - named_key = standerdized_name(dictionary[key].question) + named_key = standardize_name(dictionary[key].question) item.__setitem__(named_key, dictionary[key]) return item def _to_deck(self): - """Returns the string representation for the external files (.dck)""" + """Return deck representation of self.""" if self: head = "*** External files\n" v_ = ( diff --git a/trnsystor/collections/initialinputvalues.py b/trnsystor/collections/initialinputvalues.py index 6957a94..aa3e322 100644 --- a/trnsystor/collections/initialinputvalues.py +++ b/trnsystor/collections/initialinputvalues.py @@ -1,26 +1,29 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""InitialInputValuesCollection module.""" import tabulate from pint.quantity import _Quantity from trnsystor.collections.variable import VariableCollection -from trnsystor.statement import Equation, Constant +from trnsystor.statement import Constant, Equation from trnsystor.typevariable import TypeVariable from trnsystor.utils import _parse_value class InitialInputValuesCollection(VariableCollection): - """Subclass of :class:`VariableCollection` specific to Initial Input - Values - """ + """Subclass of :class:`VariableCollection` specific to Initial Input Values. - def __init__(self): - super().__init__() - pass + Hint: + Iterating over `InitialInputValuesCollection` will not pass Inputs that + considered ``questions``. For example, Type15 (printer) has a question for + the number of variables to be printed by the component. This question can be + accessed with `.inputs[ + "How_many_variables_are_to_be_printed_by_this_component_"]` to + modify the number of values. But when iterating over the initial inputs, + the question will not be returned in the iterator; only regular inputs will. + """ def __repr__(self): + """Return repr(self).""" num_inputs = "{} Initial Input Values:\n".format(self.size) value: TypeVariable inputs = "\n".join( @@ -31,20 +34,22 @@ def __repr__(self): ) return num_inputs + inputs + def __iter__(self): + """Iterate over inputs except questions.""" + return iter({k: v for k, v in self.data.items() if not v._is_question}) + def __getitem__(self, key): - """ - Args: - key: - """ + """Get item.""" if isinstance(key, int): - type_variable = list(self.values())[key] + value = list(self.values())[key] + elif isinstance(key, slice): + value = list(self.data.values()).__getitem__(key) else: - type_variable = super(VariableCollection, self).__getitem__(key) - return type_variable + value = super(VariableCollection, self).__getitem__(key) + return value def __setitem__(self, key, value): """Set item.""" - if isinstance(value, TypeVariable): """if a TypeVariable is given, simply set it""" super().__setitem__(key, value) @@ -65,7 +70,7 @@ def __setitem__(self, key, value): ) def _to_deck(self): - """Returns the string representation for the Initial Input Values""" + """Return deck representation of self.""" if self.size == 0: # Don't need to print empty inputs return "" diff --git a/trnsystor/collections/input.py b/trnsystor/collections/input.py index 2383bcf..d10dfc9 100644 --- a/trnsystor/collections/input.py +++ b/trnsystor/collections/input.py @@ -1,31 +1,48 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""InputCollection module.""" import tabulate from trnsystor.collections.variable import VariableCollection -from trnsystor.statement import Equation, Constant +from trnsystor.statement import Constant, Equation from trnsystor.typevariable import TypeVariable class InputCollection(VariableCollection): - """Subclass of :class:`VariableCollection` specific to Inputs""" + """Subclass of :class:`VariableCollection` specific to Inputs. - def __init__(self): - super().__init__() - pass + Hint: + Iterating over `InputCollection` will not pass Inputs that considered + ``questions``. For example, Type15 (printer) has a question for the number + of variables to be printed by the component. This question can be accessed + with `.inputs["How_many_variables_are_to_be_printed_by_this_component_"]` to + modify the number of values. But when iterating over the inputs, the question + will not be returned in the iterator; only regular inputs will. + """ def __repr__(self): + """Return Deck representation of self.""" num_inputs = "{} Inputs:\n".format(self.size) - inputs = "\n".join( - ['"{}": {:~P}'.format(key, value.value) for key, value in self.data.items()] - ) + try: + inputs = "\n".join( + [ + '"{}": {:~P}'.format(key, value.value) + for key, value in self.data.items() + ] + ) + except ValueError: # Invalid format specifier + inputs = "\n".join( + [ + '"{}": {}'.format(key, value.value) + for key, value in self.data.items() + ] + ) return num_inputs + inputs - def _to_deck(self): - """Returns the string representation for the Input File (.dck)""" + def __iter__(self): + """Iterate over inputs except questions.""" + return iter({k: v for k, v in self.data.items() if not v._is_question}) + def _to_deck(self): + """Return deck representation of self.""" if self.size == 0: # Don't need to print empty inputs return "" @@ -89,3 +106,8 @@ def _to_deck(self): ) core = tabulate.tabulate(_ins, tablefmt="plain", numalign="left") return str(head) + core + "\n" + + @property + def size(self): + """Return the number of inputs excluding questions.""" + return len([i for i in self]) diff --git a/trnsystor/collections/output.py b/trnsystor/collections/output.py index 3007218..712f879 100644 --- a/trnsystor/collections/output.py +++ b/trnsystor/collections/output.py @@ -1,18 +1,12 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""OutputCollection module.""" from trnsystor.collections.variable import VariableCollection class OutputCollection(VariableCollection): - """Subclass of :class:`VariableCollection` specific to Outputs""" - - def __init__(self): - super().__init__() - pass + """Subclass of :class:`VariableCollection` specific to Outputs.""" def __repr__(self): + """Return repr(self).""" num_inputs = "{} Outputs:\n".format(self.size) inputs = "\n".join( ['"{}": {:~P}'.format(key, value.value) for key, value in self.data.items()] diff --git a/trnsystor/collections/parameter.py b/trnsystor/collections/parameter.py index 0e7f498..29a3ffc 100644 --- a/trnsystor/collections/parameter.py +++ b/trnsystor/collections/parameter.py @@ -1,7 +1,4 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""Parameter module.""" import tabulate from pint.quantity import _Quantity @@ -11,13 +8,10 @@ class ParameterCollection(VariableCollection): - """Subclass of :class:`VariableCollection` specific to Parameters""" - - def __init__(self): - super().__init__() - pass + """Subclass of :class:`VariableCollection` specific to Parameters.""" def __repr__(self): + """Return repr(self).""" num_inputs = "{} Parameters:\n".format(self.size) inputs = "\n".join( ['"{}": {:~P}'.format(key, value.value) for key, value in self.data.items()] @@ -25,8 +19,7 @@ def __repr__(self): return num_inputs + inputs def _to_deck(self): - """Returns the string representation for the Input File (.dck)""" - + """Return deck representation of self.""" head = "PARAMETERS {}\n".format(self.size) # loop through parameters and print the (value, name) tuples. v_ = [] @@ -59,5 +52,5 @@ def _to_deck(self): @property def size(self): - """The number of parameters""" + """Return the number of inputs.""" return len([p for p in self if not self[p]._is_question]) diff --git a/trnsystor/collections/specialcards.py b/trnsystor/collections/specialcards.py index e61d94a..7269258 100644 --- a/trnsystor/collections/specialcards.py +++ b/trnsystor/collections/specialcards.py @@ -1,6 +1,4 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""SpecialCardsCollection module.""" import collections import tabulate @@ -12,7 +10,7 @@ class SpecialCardsCollection(collections.UserList): """Subclass of :class:`VariableCollection` specific to Derivatives.""" def __repr__(self): - """Return repr.""" + """Return repr(self).""" return self._to_deck() def __str__(self): @@ -20,8 +18,7 @@ def __str__(self): return self._to_deck() def _to_deck(self): - """Return the deck representation for the SpecialCards (.dck).""" - + """Return deck representation of self.""" if self.size == 0: # Don't need to print empty inputs return "" diff --git a/trnsystor/collections/variable.py b/trnsystor/collections/variable.py index a8bcd8b..1a2b9b6 100644 --- a/trnsystor/collections/variable.py +++ b/trnsystor/collections/variable.py @@ -1,20 +1,17 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""VariableCollection module.""" import collections from pint.quantity import _Quantity -from trnsystor import standerdized_name from trnsystor.statement import Constant, Equation from trnsystor.typevariable import TypeVariable -from trnsystor.utils import _parse_value +from trnsystor.utils import _parse_value, standardize_name class VariableCollection(collections.UserDict): - """A collection of :class:`VariableType` as a dict. Handles getting and - setting variable values. + """A collection of :class:`VariableType` as a dict. + + Handles getting and setting variable values. """ def __getattr__(self, key): @@ -29,6 +26,8 @@ def __getitem__(self, key): """Get item.""" if isinstance(key, int): value = list(self.data.values())[key] + elif isinstance(key, slice): + value = list(self.data.values()).__getitem__(key) else: value = super(VariableCollection, self).__getitem__(key) return value @@ -42,7 +41,6 @@ def __setattr__(self, key, value): def __setitem__(self, key, value): """Set item.""" - if isinstance(value, TypeVariable): """if a TypeVariable is given, simply set it""" super().__setitem__(key, value) @@ -67,22 +65,23 @@ def __str__(self): return self._to_deck() def _to_deck(self): + """Return deck representation of self.""" pass @classmethod def from_dict(cls, dictionary): - """ - Args: - dictionary: + """Return VariableCollection from dict. + + Sets also the class attribute using ``named_key``. """ item = cls() for key in dictionary: - named_key = standerdized_name(dictionary[key].name) + named_key = standardize_name(dictionary[key].name) item.__setitem__(named_key, dictionary[key]) setattr(item, named_key, dictionary[key]) return item @property def size(self): - """The number of parameters""" + """The number of variable in the collection.""" return len(self) diff --git a/trnsystor/component.py b/trnsystor/component.py index 7d36f3e..964a2ad 100644 --- a/trnsystor/component.py +++ b/trnsystor/component.py @@ -1,12 +1,9 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Component module.""" import itertools from abc import ABCMeta, abstractmethod import networkx as nx from bs4 import Tag -from networkx import MultiDiGraph from shapely.geometry import Point from trnsystor.canvas import StudioCanvas @@ -16,16 +13,19 @@ class Component(metaclass=ABCMeta): - """Base class for Trnsys elements that interact with the Studio. + """Component class. + + Base class for Trnsys elements that interact with the Studio. :class:`TrnsysModel`, :class:`ConstantCollection` and - :class:`EquationCollection` implement this class.""" + :class:`EquationCollection` implement this class. + """ INIT_UNIT_NUMBER = itertools.count(start=1) STUDIO_CANVAS = StudioCanvas() UNIT_GRAPH = nx.MultiDiGraph() def __init__(self, *args, **kwargs): - """Initialize a Component with the following parameters: + """Initialize class. Args: name (str): Name of the component. @@ -39,31 +39,37 @@ def __init__(self, *args, **kwargs): self.UNIT_GRAPH.add_node(self) def __del__(self): + """Delete self.""" if self in self.UNIT_GRAPH: self.UNIT_GRAPH.remove_node(self) def __hash__(self): + """Return hash(self).""" return self.unit_number def __eq__(self, other): + """Return self == other.""" if isinstance(other, self.__class__): return self.unit_number == other.unit_number else: return self.unit_number == other def copy(self): + """Return copy of self.""" pass @property def link_styles(self): + """Return :class:`LinkStyles` of self.""" return [ data["LinkStyle"] for u, v, key, data in self.UNIT_GRAPH.edges(keys=True, data=True) ] def set_canvas_position(self, pt, trnsys_coords=False): - """Set position of self in the canvas. Use cartesian coordinates: origin - 0,0 is at bottom-left. + """Set position of self in the canvas. + + Use cartesian coordinates: origin 0,0 is at bottom-left. Hint: The Studio Canvas origin corresponds to the top-left of the canvas. @@ -81,7 +87,8 @@ def set_canvas_position(self, pt, trnsys_coords=False): Args: pt (Point or 2-tuple): The Point geometry or a tuple of (x, y) coordinates. - trnsys_coords: + trnsys_coords (bool): Set to True of ``pt`` is given in Trnsys Studio + coordinates: origin 0,0 is at top-left. """ if not isinstance(pt, Point): pt = Point(*pt) @@ -96,23 +103,19 @@ def set_canvas_position(self, pt, trnsys_coords=False): ) def set_component_layer(self, layers): - """Change the component's layer. Pass a list to change multiple layers - - Args: - layers (str or list): - """ + """Change the layer of self. Pass a list to change multiple layers.""" if isinstance(layers, str): layers = [layers] self.studio.layer = layers @property - def unit_number(self): - """int: Returns the model's unit number (unique)""" + def unit_number(self) -> int: + """Return the model's unit number (unique).""" return self._unit @property - def type_number(self): - """int: Returns the model's type number, eg.: 104 for Type104""" + def type_number(self) -> int: + """Return the model's type number, eg.: ``104`` for Type104.""" return int( self._meta.type if not isinstance(self._meta.type, Tag) @@ -120,13 +123,13 @@ def type_number(self): ) @property - def unit_name(self): - """str: Returns the model's unit name, eg.: 'Type104'""" + def unit_name(self) -> str: + """Return the model's unit name, eg.: 'Type104'.""" return "Type{}".format(self.type_number) @property - def model(self): - """str: The path of this model's proforma""" + def model(self) -> str: + """Return the path of this model's proforma.""" try: model = self._meta.model except AttributeError: @@ -151,16 +154,12 @@ def centroid(self): @abstractmethod def _get_inputs(self): - """inputs getter. Sorts by order number and resolves cycles each time it - is called - """ + """Sorts by order number and resolves cycles each time it is called.""" pass @abstractmethod def _get_outputs(self): - """outputs getter. Sorts by order number and resolves cycles each time - it is called - """ + """Sorts by order number and resolves cycles each time it is called.""" pass def set_link_style( @@ -282,22 +281,24 @@ def connect_to(self, other, mapping=None, link_style_kwargs=None): @property def successors(self): - """Other objects to which this TypeVariable is connected. successors""" + """Other objects to which this TypeVariable is connected. Successors.""" return self.UNIT_GRAPH.successors(self) @property def is_connected(self): """Whether or not this Component is connected to another TypeVariable. - Connected to or connected by.""" + + Connected to or connected by. + """ return any([len(list(self.predecessors)) > 0, len(list(self.successors)) > 0]) @property def predecessors(self): - """Other objects from which this TypeVariable is connected. Predecessors""" + """Other objects from which this TypeVariable is connected. Predecessors.""" return self.UNIT_GRAPH.predecessors(self) def invalidate_connections(self): - """iterate over successors and predecessors and remove the edges. + """Iterate over successors and predecessors and remove the edges. Todo: restore paths in self.studio_canvas.grid """ @@ -306,7 +307,6 @@ def invalidate_connections(self): edges.append((self, nbr)) for nbr in self.UNIT_GRAPH.predecessors(self): edges.append((nbr, self)) - has_edge = True while edges: edge = edges.pop() self.UNIT_GRAPH.remove_edge(*edge) @@ -314,4 +314,5 @@ def invalidate_connections(self): edges.append(edge) def _to_deck(self): - pass \ No newline at end of file + """Return deck representation of self.""" + pass diff --git a/trnsystor/controlcards.py b/trnsystor/controlcards.py index 16052e1..436c6be 100644 --- a/trnsystor/controlcards.py +++ b/trnsystor/controlcards.py @@ -1,32 +1,31 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""ControlCards module.""" import tabulate -from trnsystor.component import Component +from trnsystor.component import Component from trnsystor.statement import ( + DFQ, End, - Version, - Simulation, - Tolerances, + EqSolver, Limits, + List, + Map, NaNCheck, + NoCheck, + NoList, OverwriteCheck, + Simulation, + Solver, TimeReport, - DFQ, + Tolerances, + Version, Width, - NoCheck, - EqSolver, - Solver, - NoList, - List, - Map, ) class ControlCards(object): - """The :class:`ControlCards` is a container for all the TRNSYS Simulation + """ControlCards class. + + The :class:`ControlCards` is a container for all the TRNSYS Simulation Control Statements and Listing Control Statements. It implements the :func:`_to_deck` method which pretty-prints the statements with their docstrings. @@ -50,8 +49,9 @@ def __init__( list=None, map=None, ): - """Each simulation must have SIMULATION and END statements. The other - simulation control statements are optional. Default values are assumed + """Insure that each simulation has a SIMULATION and END statements. + + The other simulation control statements are optional. Default values are assumed for TOLERANCES, LIMITS, SOLVER, EQSOLVER and DFQ if they are not present Args: @@ -91,7 +91,7 @@ def __init__( :class:`NoCheck` for more details. eqsolver (EqSolver, optional): The Equation Solving Method Statement. The order in which blocks of EQUATIONS are solved is - controlled by the EQSOLVER statement. See :class:`EqSolver` for + controlled by the EQSOLVER Statement. See :class:`EqSolver` for more details. solver (Solver, optional): The SOLVER Statement. Select the computational scheme. See :class:`Solver` for more details. @@ -141,10 +141,15 @@ def __repr__(self): @classmethod def all(cls): - """Returns a SimulationCard with all available Statements initialized - or with their default values. This class method is not recommended since - many of the Statements are a time consuming process and should be used - as a debugging tool. + """Return a SimulationCard with all available Statements. + + If not initialized, default values are used. This class method is not + recommended since many of the Statements are a time consuming process and + should be used as a debugging tool. + + See Also: + - :meth:`basic_template` + - :meth:`debug_template` """ return cls( Version(), @@ -166,7 +171,7 @@ def all(cls): @classmethod def debug_template(cls): - """Returns a SimulationCard with useful debugging Statements.""" + """Return a SimulationCard with useful debugging Statements.""" return cls( version=Version(), simulation=Simulation(), @@ -177,12 +182,13 @@ def debug_template(cls): @classmethod def basic_template(cls): - """Returns a SimulationCard with only the required Statements""" + """Return a SimulationCard with only the required Statements.""" return cls(version=Version(), simulation=Simulation()) def _to_deck(self): - """Creates a string representation. If the :attr:`doc` is specified, a - small description is printed in comments + """Return deck representation of self. + + If the :attr:`doc` is specified, a small description is printed in comments. """ version = str(self.version) + "\n" head = "*** Control Cards\n" @@ -200,8 +206,5 @@ def _to_deck(self): return version + head + statements def set_statement(self, statement): - """ - Args: - statement: - """ + """Set `statement`.""" self.__setattr__(statement.__class__.__name__.lower(), statement) diff --git a/trnsystor/deck.py b/trnsystor/deck.py index cb5c6a1..c67c50b 100644 --- a/trnsystor/deck.py +++ b/trnsystor/deck.py @@ -1,6 +1,4 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Deck module.""" import datetime import itertools @@ -12,44 +10,42 @@ from pandas import to_datetime from pandas.io.common import _get_filepath_or_buffer, get_handle from path import Path -from trnsystor.component import Component -from shapely.geometry import Point, LineString +from shapely.geometry import LineString, Point -from trnsystor import TrnsysModel, get_rgb_from_int from trnsystor.anchorpoint import AnchorPoint from trnsystor.collections.components import ComponentCollection from trnsystor.collections.constant import ConstantCollection from trnsystor.collections.equation import EquationCollection +from trnsystor.component import Component from trnsystor.controlcards import ControlCards from trnsystor.linkstyle import _studio_to_linestyle from trnsystor.name import Name from trnsystor.statement import ( - End, - Version, + DFQ, Constant, - Simulation, - Tolerances, + End, + EqSolver, + Equation, Limits, - DFQ, - Width, List, - Solver, NaNCheck, OverwriteCheck, + Simulation, + Solver, TimeReport, - EqSolver, - Equation, -) -from trnsystor.trnsysmodel import ( - MetaData, + Tolerances, + Version, + Width, ) +from trnsystor.trnsysmodel import MetaData, TrnsysModel +from trnsystor.utils import get_rgb_from_int class DeckFormatter: - """Class for handling the formatting of deck files""" + """Class for handling the formatting of deck files.""" def __init__(self, obj, path_or_buf, encoding=None, mode="w"): - + """Initialize object.""" if path_or_buf is None: path_or_buf = StringIO() io_args = _get_filepath_or_buffer( @@ -88,7 +84,9 @@ def save(self): class Deck(object): - """The Deck class holds :class:`TrnsysModel` objects, the + """Deck class. + + The Deck class holds :class:`TrnsysModel` objects, the :class:`ControlCards` and specifies the name of the project. This class handles reading from a file (see :func:`read_file`) and printing to a file (see :func:`save`). @@ -104,7 +102,7 @@ def __init__( canvas_width=1200, canvas_height=1000, ): - """Initialize a Deck object with parameters: + """Initialize a Deck object. Args: name (str): The name of the project. @@ -149,7 +147,7 @@ def __init__( @classmethod def read_file(cls, file, author=None, date_created=None, proforma_root=None): - """Returns a Deck from a file + """Returns a Deck from a file. Args: file (str or Path): Either the absolute or relative path to the file to be @@ -187,19 +185,12 @@ def read_file(cls, file, author=None, date_created=None, proforma_root=None): return dck def __str__(self): + """Return deck representation of self.""" return self._to_string() @property def graph(self): - import networkx as nx - - G = nx.MultiDiGraph() - for component in self.models: - G = nx.compose(G, component.UNIT_GRAPH) - return G - - @property - def graph(self): + """Return the :class:`MultiDiGraph` of self.""" import networkx as nx G = nx.MultiDiGraph() @@ -219,9 +210,7 @@ def graph(self): return G def check_deck_integrity(self): - """Checks if Deck definition passes a few obvious rules""" - - # Check if external file assignments are all unique. + """Checks if Deck definition passes a few obvious rules.""" from collections import Counter ext_files = [] @@ -232,14 +221,13 @@ def check_deck_integrity(self): if file: ext_files.append(file.value) if sum(1 for i in Counter(ext_files).values() if i > 1): - lg.warn( + lg.warning( "Some ExternalFile paths have duplicated names. Please make sure all " "ASSIGNED paths are unique unless this is desired." ) def update_models(self, amodel): - """Update the Deck.models attribute with a :class:`TrnsysModel` or a - list of :class:`TrnsysModel`. + """Update the :attr:`models` attribute with a :class:`TrnsysModel` (or list). Args: amodel (Component or list of Component): @@ -260,10 +248,7 @@ def update_models(self, amodel): self.models.append(amodel) def remove_models(self, amodel): - """ - Args: - amodel: - """ + """Remove `amodel` from self.models.""" if isinstance(amodel, Component): amodel = [amodel] for amodel in amodel: @@ -275,21 +260,20 @@ def remove_models(self, amodel): break def to_file(self, path_or_buf, encoding=None, mode="w"): - """Saves the Deck object to file + """Save the Deck object to file. Examples: - >>> from trnsystor.deck import Deck >>> deck = Deck("Unnamed") >>> deck.to_file("my_project.dck",None,"w") Args: - encoding: - mode: path_or_buf (Union[str, Path, IO[AnyStr]]): str or file handle, default None File path or object, if None is provided the result is returned as a string. If a file object is passed it should be opened with `newline=''`, disabling universal newlines. + encoding (str or None): Encoding to use. + mode (str): Mode to open path_or_buf with. """ self.check_deck_integrity() @@ -337,14 +321,6 @@ def _to_string(self): @classmethod def _parse_logic(cls, cc, dck, dcklines, line, proforma_root): - """ - Args: - cc: - dck: - dcklines: - line: - proforma_root: - """ if proforma_root is None: proforma_root = Path.getcwd() else: @@ -552,7 +528,9 @@ def distance(a, b): linewidth, path, ) - except: + except KeyError: + # "Trying to set a LinkStyle on a non-existent connection. + # Make sure to connect [26]Type2: yFill using '.connect_to()'" pass if key == "model": _mod = match.group("model").strip() @@ -560,7 +538,7 @@ def distance(a, b): tmf_basename = tmf.basename() try: meta = MetaData.from_xml(tmf) - except: + except FileNotFoundError: # replace extension with ".xml" and retry xml_basename = tmf_basename.stripext() + ".xml" xmls = proforma_root.glob("*.xml") @@ -626,8 +604,7 @@ def set_typevariable(dck, i, model, tvar, key): getattr(model, key)[i] = tvar def _parse_line(self, line): - """Do a regex search against all defined regexes and return the key and - match result of the first matching regex + """Search against all defined regexes and return (key, match). Args: line (str): the line string to parse. @@ -635,7 +612,6 @@ def _parse_line(self, line): Returns: 2-tuple: the key and the match. """ - for key, rx in self._setup_re().items(): match = rx.search(line) if match: @@ -644,10 +620,11 @@ def _parse_line(self, line): return None, None def _setup_re(self): - """set up regular expressions. use https://regexper.com to visualise - these if required - """ + """Set up regular expressions. + Hint: + Use https://regexper.com to visualise these if required. + """ rx_dict = { "version": re.compile( r"(?i)(?P^version)(?P.*?)(?=(?:!|\\n|$))" diff --git a/trnsystor/externalfile.py b/trnsystor/externalfile.py index 25e096c..769fa70 100644 --- a/trnsystor/externalfile.py +++ b/trnsystor/externalfile.py @@ -1,6 +1,4 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""ExternalFile module.""" import itertools from bs4 import Tag @@ -8,19 +6,32 @@ class ExternalFile(object): - """ExternalFile class.""" + """ExternalFile class. + + The External File Specification allows the user to associate a TRNSYS parameter ( + typically a logical unit) with an external file through the use of the TRNSYS + ASSIGN, FILES, or DESIGNATE statements. This feature allows the author to + describe a question that will be asked in the assembly window. If the ASSIGN + statement is used, a parameter has to be associated with this external file to + define its FORTRAN logical unit number. For example, "Which file contains the + meteorological information?” When the input file is generated, it will contain a + TRNSYS ASSIGN statement with the answer to the question and the value of the + associated parameter. If the user would instead rather use the DESIGNATE input + file keyword for their component (please see more information in the TRNEdit + manual), the “Designate” checkbox will have to be clicked. + """ logic_unit = itertools.count(start=30) _logic_unit = itertools.count(start=30) def __init__(self, question, default, answers, parameter, designate): - """Initilize object from arguments. + """Initialize object from arguments. Args: - question (str): - default (str): - answers (list of str): - parameter (str): + question (str): Question to ask. + default (str): Default answer. + answers (list of str): List of possible answers. + parameter (str): The parameter associated with the external file. designate (bool): If True, the external files are assigned to logical unit numbers from within the TRNSYS input file. Files that are assigned to a logical unit number using a DESIGNATE @@ -38,7 +49,8 @@ def __init__(self, question, default, answers, parameter, designate): @classmethod def from_tag(cls, tag): - """ + """Create ExternalFile from Tag. + Args: tag (Tag): The XML tag with its attributes and contents. """ diff --git a/trnsystor/linkstyle.py b/trnsystor/linkstyle.py index 911cc88..74d6986 100644 --- a/trnsystor/linkstyle.py +++ b/trnsystor/linkstyle.py @@ -1,15 +1,15 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""LinkStyle module.""" import numpy as np from matplotlib.colors import colorConverter from shapely.geometry import LineString from trnsystor.anchorpoint import AnchorPoint -from trnsystor.utils import redistribute_vertices, get_int_from_rgb +from trnsystor.utils import get_int_from_rgb, redistribute_vertices class LinkStyle(object): + """LinkStyle class.""" + def __init__( self, u, @@ -21,7 +21,8 @@ def __init__( path=None, autopath=True, ): - """ + """Initialize class. + Args: u (Component): from Model. v (Component): to Model. @@ -45,6 +46,7 @@ def __init__( linewidth (float): The link line width in points. path (LineString or MultiLineString): The path the link should follow. + autopath (bool): If True, find best path. """ self.u = u self.v = v @@ -57,6 +59,7 @@ def __init__( @property def path(self): + """Return the path of self.""" if self._path is None: u_anchor_name, v_anchor_name = self.anchor_ids _u = AnchorPoint(self.u).anchor_points[u_anchor_name] @@ -74,6 +77,7 @@ def path(self, value): @property def anchor_ids(self): + """Return studio anchor ids.""" if isinstance(self.loc, tuple): loc_u, loc_v = self.loc else: @@ -82,14 +86,11 @@ def anchor_ids(self): return AnchorPoint(self.u).studio_anchor(self.v, (loc_u, loc_v)) def __repr__(self): + """Return Deck representation of self.""" return self._to_deck() def set_color(self, color): - """Set the color of the line. - - Args: - color (color): - """ + """Set the color of the line.""" self._color = color def get_color(self): @@ -129,7 +130,11 @@ def get_linewidth(self): return self._linewidth def _to_deck(self): - """0:20:40:20:1:0:0:0:1:513,441:471,441:471,430:447,430""" + """Return deck representation of self. + + Examples: + 0:20:40:20:1:0:0:0:1:513,441:471,441:471,430:447,430 + """ u_anchor_name, v_anchor_name = self.anchor_ids anchors = ( ":".join( @@ -171,10 +176,6 @@ def _to_deck(self): def _linestyle_to_studio(ls): - """ - Args: - ls: - """ linestyle_dict = { "-": 0, "solid": 0, @@ -192,10 +193,6 @@ def _linestyle_to_studio(ls): def _studio_to_linestyle(ls): - """ - Args: - ls: - """ linestyle_dict = {0: "-", 1: "--", 2: ":", 3: "-.", 4: "-.."} _ls = linestyle_dict.get(ls) return _ls diff --git a/trnsystor/name.py b/trnsystor/name.py index 5c9301d..7b4e4c2 100644 --- a/trnsystor/name.py +++ b/trnsystor/name.py @@ -1,29 +1,27 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Name module.""" class Name(object): - """Handles the attribution of user defined names for :class:`TrnsysModel`, + """Name class. + + Handles the attribution of user defined names for :class:`TrnsysModel`, :class:`EquationCollection` and more. """ existing = [] # a list to store the created names def __init__(self, name=None): - """Pick a name. Will increment the name if already used - - Args: - name: - """ + """Pick a name. Will increment the name if already used.""" self.name = self.create_unique(name) def create_unique(self, name): - """Check if name has already been used. If so, try to increment until - not used + """Return unique name. + + Checks if ``name`` has already been used. If so, try to increment until not + used. Args: - name: + name (str): The name to render unique. """ if not name: return None @@ -38,7 +36,9 @@ def create_unique(self, name): return the_name def __repr__(self): + """Return str(self).""" return str(self) def __str__(self): + """Return str(self).""" return str(self.name) diff --git a/trnsystor/run.py b/trnsystor/run.py index 6a561f5..89da0e8 100644 --- a/trnsystor/run.py +++ b/trnsystor/run.py @@ -1,6 +1,4 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Run module.""" class Run: @@ -12,4 +10,5 @@ class Run: """ def __init__(self): + """Initialize object.""" pass diff --git a/trnsystor/specialcard.py b/trnsystor/specialcard.py index 2e3e6a6..271e67f 100644 --- a/trnsystor/specialcard.py +++ b/trnsystor/specialcard.py @@ -1,12 +1,11 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""SpecialCard module.""" from bs4 import Tag class SpecialCard(object): - """Once the user has defined the Parameters, Inputs, Outputs, and Derivatives, + """SpecialCard class. + + Once the user has defined the Parameters, Inputs, Outputs, and Derivatives, some components require addition description statements (or Cards). An example of a model that requires a special card is the TYPE 65 Online plotter which must specify the titles for each axis and for the plot. These special cards can be @@ -15,6 +14,7 @@ class SpecialCard(object): """ def __init__(self, name=None, question=None, default=None, answers=None): + """Initialize object.""" if answers is None: answers = [] self.name = name @@ -24,7 +24,8 @@ def __init__(self, name=None, question=None, default=None, answers=None): @classmethod def from_tag(cls, tag): - """ + """Create SpecialCard from Tag. + Args: tag (Tag): The XML tag with its attributes and contents. """ diff --git a/trnsystor/statement/__init__.py b/trnsystor/statement/__init__.py index 93fd1de..8e352f4 100644 --- a/trnsystor/statement/__init__.py +++ b/trnsystor/statement/__init__.py @@ -1,6 +1,4 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Statement module.""" __all__ = [ "Constant", diff --git a/trnsystor/statement/constant.py b/trnsystor/statement/constant.py index 4e26d84..7c44749 100644 --- a/trnsystor/statement/constant.py +++ b/trnsystor/statement/constant.py @@ -1,14 +1,13 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""Constant module.""" import itertools from trnsystor.statement.statement import Statement class Constant(Statement): - """The CONSTANTS statement is useful when simulating a number of systems + """CONSTANTS Statement. + + The CONSTANTS statement is useful when simulating a number of systems with identical component configurations but with different parameter values, initial input values, or initial values of time dependent variables. """ @@ -17,7 +16,8 @@ class Constant(Statement): instances = {} def __init__(self, name=None, equals_to=None, doc=None): - """ + """Initialize object. + Args: name (str): The left hand side of the equation. equals_to (str, TypeVariable): The right hand side of the equation. @@ -27,7 +27,7 @@ def __init__(self, name=None, equals_to=None, doc=None): super().__init__() try: c_ = Constant.instances[name] - except: + except KeyError: self._n = next(self._new_id) self.name = name self.equals_to = equals_to @@ -42,9 +42,10 @@ def __init__(self, name=None, equals_to=None, doc=None): @classmethod def from_expression(cls, expression, doc=None): - """Create a Constant from a string expression. Anything before the equal - sign ("=") will become the Constant's name and anything after will - become the equality statement. + """Create a Constant from a string expression. + + Anything before the equal sign ("=") will become the Constant's name and + anything after will become the equality statement. Hint: The simple expressions are processed much as FORTRAN arithmetic @@ -68,8 +69,9 @@ def from_expression(cls, expression, doc=None): @property def constant_number(self): - """The equation number. Unique""" + """The equation number (unique).""" return self._n def _to_deck(self): + """Return deck representation of self.""" return self.equals_to diff --git a/trnsystor/statement/dfq.py b/trnsystor/statement/dfq.py index 2e62fad..3fc77af 100644 --- a/trnsystor/statement/dfq.py +++ b/trnsystor/statement/dfq.py @@ -1,19 +1,18 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""DFQ Statement.""" from trnsystor.statement.statement import Statement class DFQ(Statement): - """The optional DFQ card allows the user to select one of three algorithms + """DFQ Statement. + + The optional DFQ card allows the user to select one of three algorithms built into TRNSYS to numerically solve differential equations (see Manual 08-Programmer’s Guide for additional information about solution of differential equations). """ def __init__(self, k=1): - """Initialize the Differential Equation Solving Method Statement + """Initialize the Differential Equation Solving Method Statement. Args: k (int, optional): an integer between 1 and 3. If a DFQ card is not @@ -33,4 +32,5 @@ def __init__(self, k=1): self.doc = "TRNSYS numerical integration solver method" def _to_deck(self): + """Return deck representation of self.""" return str("DFQ {}".format(self.k)) diff --git a/trnsystor/statement/end.py b/trnsystor/statement/end.py index d9d2601..0748114 100644 --- a/trnsystor/statement/end.py +++ b/trnsystor/statement/end.py @@ -1,19 +1,20 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""End Statement.""" from trnsystor.statement.statement import Statement class End(Statement): - """The END statement must be the last line of a TRNSYS input file. It + """END Statement. + + The END statement must be the last line of a TRNSYS input file. It signals the TRNSYS processor that no more control statements follow and that the simulation may begin. """ def __init__(self): + """Initialize object.""" super().__init__() self.doc = "The END Statement" def _to_deck(self): + """Return deck representation of self.""" return "END" diff --git a/trnsystor/statement/eqsolver.py b/trnsystor/statement/eqsolver.py index 0bd12bd..9fba629 100644 --- a/trnsystor/statement/eqsolver.py +++ b/trnsystor/statement/eqsolver.py @@ -1,19 +1,20 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""EqSolver Statement.""" from trnsystor.statement.statement import Statement class EqSolver(Statement): - """With the release of TRNSYS 16, new methods for solving blocks of + """EqSolver Statement. + + With the release of TRNSYS 16, new methods for solving blocks of EQUATIONS statements were added. For additional information on EQUATIONS statements, please refer to section 6.3.9. The order in which blocks of - EQUATIONS are solved is controlled by the EQSOLVER statement. + EQUATIONS are solved is controlled by the EQSOLVER Statement. """ def __init__(self, n=0): - """Hint: + """Initialize object. + + Hint: :attr:`n` can have any of the following values: 1. n=0 (default if no value is provided) if a component output or @@ -39,4 +40,5 @@ def __init__(self, n=0): self.doc = "EQUATION SOLVER statement" def _to_deck(self): + """Return deck representation of self.""" return "EQSOLVER {}".format(self.n) diff --git a/trnsystor/statement/equation.py b/trnsystor/statement/equation.py index f37cb86..fb09f11 100644 --- a/trnsystor/statement/equation.py +++ b/trnsystor/statement/equation.py @@ -1,16 +1,18 @@ +"""Equation module.""" import itertools -from trnsystor.component import Component -from sympy import Symbol, Expr +from sympy import Expr, Symbol -from trnsystor import TypeVariableSymbol, print_my_latex from trnsystor.statement.constant import Constant from trnsystor.statement.statement import Statement from trnsystor.typevariable import TypeVariable +from trnsystor.utils import TypeVariableSymbol, print_my_latex class Equation(Statement, TypeVariable): - """The EQUATIONS statement allows variables to be defined as algebraic + """EQUATION Statement. + + The EQUATIONS statement allows variables to be defined as algebraic functions of constants, previously defined variables, and outputs from TRNSYS components. These variables can then be used in place of numbers in the TRNSYS input file to represent inputs to components; numerical values of @@ -29,12 +31,14 @@ class Equation(Statement, TypeVariable): _new_id = itertools.count(start=1) def __init__(self, name=None, equals_to=None, doc=None, model=None): - """ + """Initialize object. + Args: name (str): The left hand side of the equation. equals_to (str, TypeVariable): The right hand side of the equation. doc (str, optional): A small description optionally printed in the deck file. + model (Component): The TrnsysModel this Equation belongs to. """ super().__init__() self._n = next(self._new_id) @@ -44,16 +48,19 @@ def __init__(self, name=None, equals_to=None, doc=None, model=None): self.model = model # the TrnsysModel this Equation belongs to. def __repr__(self): + """Return repr(self).""" return " = ".join([self.name, self._to_deck()]) def __str__(self): - return self.__repr__() + """Return repr(self).""" + return repr(self) @classmethod def from_expression(cls, expression, doc=None): - """Create an equation from a string expression. Anything before the - equal sign ("=") will become a Constant and anything after will become - the equality statement. + """Create an equation from a string expression. + + Anything before the equal sign ("=") will become a Constant and anything + after will become the equality statement. Example: Create a simple expression like so: @@ -75,7 +82,9 @@ def from_expression(cls, expression, doc=None): @classmethod def from_symbolic_expression(cls, name, exp, *args, doc=None): - """Crate an equation with a combination of a generic expression (with + """Create an equation from symbolic expression. + + Crate an equation with a combination of a generic expression (with placeholder variables) and a list of arguments. The underlying engine will use Sympy and symbolic variables. You can use a mixture of :class:`TypeVariable` and :class:`Equation`, :class:`Constant` as @@ -164,20 +173,22 @@ def from_symbolic_expression(cls, name, exp, *args, doc=None): @property def eq_number(self): - """The equation number. Unique""" + """Return the equation number (unique).""" return self._n @property def idx(self): - """The 0-based index of the Equation""" + """Return the 0-based index of the Equation.""" ns = {e: i for i, e in enumerate(self.model)} return ns[self.name] @property def unit_number(self): + """Return the unit number of the EquationCollection self belongs to.""" return self.model.unit_number def _to_deck(self): + """Return deck representation of self.""" if isinstance(self.equals_to, TypeVariable): return "[{unit_number}, {output_id}]".format( unit_number=self.equals_to.model.unit_number, diff --git a/trnsystor/statement/limites.py b/trnsystor/statement/limites.py index 64a0def..ff9a9f9 100644 --- a/trnsystor/statement/limites.py +++ b/trnsystor/statement/limites.py @@ -1,19 +1,19 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""Limits Statement.""" from trnsystor.statement.statement import Statement class Limits(Statement): - """The LIMITS statement is an optional control statement used to set limits + """LIMITS Statement. + + The LIMITS statement is an optional control statement used to set limits on the number of iterations that will be performed by TRNSYS during a time step before it is determined that the differential equations and/or algebraic equations are not converging. """ def __init__(self, m=25, n=10, p=None): - """ + """Initialize object. + Args: m (int): is the maximum number of iterations which can be performed during a time-step before a WARNING message is printed out. @@ -31,6 +31,10 @@ def __init__(self, m=25, n=10, p=None): self.doc = "Max iterations\tMax warnings\tTrace limit" def _to_deck(self): - """TOLERANCES 0.001 0.001""" + """Return deck representation of self. + + Examples: + TOLERANCES 0.001 0.001 + """ head = "LIMITS {} {} {}".format(self.m, self.n, self.p) return str(head) diff --git a/trnsystor/statement/list.py b/trnsystor/statement/list.py index 9f945be..e76af79 100644 --- a/trnsystor/statement/list.py +++ b/trnsystor/statement/list.py @@ -1,23 +1,24 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""List Statement.""" from trnsystor.statement.statement import Statement class List(Statement): - """The LIST statement is used to turn on the TRNSYS processor listing after - it has been turned off by a NOLIST statement. + """LIST Statement. + + The LIST statement is used to turn on the TRNSYS processor listing after + it has been turned off by a NOLIST Statement. """ def __init__(self, activate=False): - """Hint: + """Initialize object. + + Hint: The listing is assumed to be on at the beginning of a TRNSYS input file. As many LIST cards as desired may appear in a TRNSYS input file and may be located anywhere in the input file. Args: - activate (bool): + activate (bool): Print to deck if True. """ super().__init__() self.activate = activate diff --git a/trnsystor/statement/map.py b/trnsystor/statement/map.py index 36029a6..65b47a5 100644 --- a/trnsystor/statement/map.py +++ b/trnsystor/statement/map.py @@ -1,18 +1,17 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""Map Statement.""" from trnsystor.statement.statement import Statement class Map(Statement): - """The MAP statement is an optional control statement that is used to obtain + """MAP Statement. + + The MAP statement is an optional control statement that is used to obtain a component output map listing which is particularly useful in debugging component interconnections. """ def __init__(self, activate=True): - """Setting active to True will add the MAP statement + """Setting active to True will add the MAP Statement. Args: activate (bool): Setting active to True will add the MAP statement @@ -22,4 +21,5 @@ def __init__(self, activate=True): self.doc = "MAP statement" def _to_deck(self): + """Return deck representation of self.""" return "MAP" if self.active else "" diff --git a/trnsystor/statement/nancheck.py b/trnsystor/statement/nancheck.py index 8a577cc..6e25363 100644 --- a/trnsystor/statement/nancheck.py +++ b/trnsystor/statement/nancheck.py @@ -1,12 +1,12 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""NaNCheck Statement.""" from trnsystor.statement.statement import Statement class NaNCheck(Statement): - """One problem that has plagued TRNSYS simulation debuggers is that in + """NaNCheck Statement. + + One problem that has plagued TRNSYS simulation debuggers is that in Fortran, the “Not a Number” (NaN) condition can be passed along through numerous subroutines without being flagged as an error. For example, a division by zero results in a variable being set to NaN. This NaN can then @@ -37,4 +37,5 @@ def __init__(self, n=0): self.doc = "The NAN_CHECK Statement" def _to_deck(self): + """Return deck representation of self.""" return "NAN_CHECK {}".format(self.n) diff --git a/trnsystor/statement/nocheck.py b/trnsystor/statement/nocheck.py index e0a5378..aca826a 100644 --- a/trnsystor/statement/nocheck.py +++ b/trnsystor/statement/nocheck.py @@ -1,19 +1,20 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""NoCheck Statement.""" from trnsystor.statement.statement import Statement class NoCheck(Statement): - """TRNSYS allows up to 20 different INPUTS to be removed from the list of + """NoCheck Statement. + + TRNSYS allows up to 20 different INPUTS to be removed from the list of INPUTS to be checked for convergence (see Section 1.9). """ def __init__(self, inputs=None): - """ + """Initialize object. + Args: - inputs (list of Input): + inputs (list of Input): The list of Inputs. """ super().__init__() if not inputs: @@ -26,6 +27,7 @@ def __init__(self, inputs=None): self.doc = "CHECK Statement" def _to_deck(self): + """Return deck representation of self.""" head = "NOCHECK {}\n".format(len(self.inputs)) core = "\t".join( [ diff --git a/trnsystor/statement/nolist.py b/trnsystor/statement/nolist.py index 810b474..c34f505 100644 --- a/trnsystor/statement/nolist.py +++ b/trnsystor/statement/nolist.py @@ -1,17 +1,18 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""NoList Statement.""" from trnsystor.statement.statement import Statement class NoList(Statement): - """The NOLIST statement is used to turn off the listing of the TRNSYS input + """NOLIST Statement. + + The NOLIST statement is used to turn off the listing of the TRNSYS input file. """ def __init__(self, active=True): - """ + """Initialize object. + Args: active (bool): Setting activate to True will add the NOLIST statement """ @@ -20,4 +21,5 @@ def __init__(self, active=True): self.doc = "NOLIST statement" def _to_deck(self): + """Return deck representation of self.""" return "NOLIST" if self.active else "" diff --git a/trnsystor/statement/overwritecheck.py b/trnsystor/statement/overwritecheck.py index 531a950..aa49195 100644 --- a/trnsystor/statement/overwritecheck.py +++ b/trnsystor/statement/overwritecheck.py @@ -1,12 +1,12 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""OverwriteCheck Statement.""" from trnsystor.statement.statement import Statement class OverwriteCheck(Statement): - """A common error in non standard and user written TRNSYS Type routines is + """OverwriteCheck Statement. + + A common error in non standard and user written TRNSYS Type routines is to reserve too little space in the global output array. By default, each Type is accorded 20 spots in the global TRNSYS output array. However, there is no way to prevent the Type from then writing in (for example) the 21st @@ -33,4 +33,5 @@ def __init__(self, n=0): self.doc = "The OVERWRITE_CHECK Statement" def _to_deck(self): + """Return deck representation of self.""" return "OVERWRITE_CHECK {}".format(self.n) diff --git a/trnsystor/statement/simulation.py b/trnsystor/statement/simulation.py index e3b0047..272ecf2 100644 --- a/trnsystor/statement/simulation.py +++ b/trnsystor/statement/simulation.py @@ -1,19 +1,19 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Simulation Statement.""" from trnsystor.statement.statement import Statement class Simulation(Statement): - """The SIMULATION statement is required for all simulations, and must be - placed in the TRNSYS input file prior to the first UNIT-TYPE statement. The + """SIMULATION Statement. + + The SIMULATION statement is required for all simulations, and must be + placed in the TRNSYS input file prior to the first UNIT-TYPE Statement. The simulation statement determines the starting and stopping times of the simulation as well as the time step to be used. """ def __init__(self, start=0, stop=8760, step=1): - """Initialize the Simulation statement + """Initialize the Simulation Statement. Attention: With TRNSYS 16 and beyond, the starting time is now specified as the @@ -32,5 +32,8 @@ def __init__(self, start=0, stop=8760, step=1): self.doc = "Start time\tEnd time\tTime step" def _to_deck(self): - """SIMULATION to tf Δt""" + """Return deck representation of self. + + SIMULATION to tf Δt + """ return "SIMULATION {} {} {}".format(self.start, self.stop, self.step) diff --git a/trnsystor/statement/solver.py b/trnsystor/statement/solver.py index 64a6f9f..fd29754 100644 --- a/trnsystor/statement/solver.py +++ b/trnsystor/statement/solver.py @@ -1,19 +1,20 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Solver Statement.""" from trnsystor.statement.statement import Statement class Solver(Statement): - """A SOLVER command has been added to TRNSYS to select the computational + """SOLVER Statement. + + A SOLVER command has been added to TRNSYS to select the computational scheme. The optional SOLVER card allows the user to select one of two algorithms built into TRNSYS to numerically solve the system of algebraic and differential equations. """ def __init__(self, k=0, rf_min=1, rf_max=1): - """ + """Initialize object. + Args: k (int): the solution algorithm. rf_min (float): the minimum relaxation factor. @@ -37,6 +38,7 @@ def __init__(self, k=0, rf_min=1, rf_max=1): ) def _to_deck(self): + """Return deck representation of self.""" return ( "SOLVER {} {} {}".format(self.k, self.rf_min, self.rf_max) if self.k == 0 diff --git a/trnsystor/statement/statement.py b/trnsystor/statement/statement.py index 9f90d68..0f32a60 100644 --- a/trnsystor/statement/statement.py +++ b/trnsystor/statement/statement.py @@ -1,19 +1,22 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Statement module.""" class Statement(object): - """This is the base class for many of the TRNSYS Simulation Control and + """Statement class. + + This is the base class for many of the TRNSYS Simulation Control and Listing Control Statements. It implements common methods such as the repr() method. """ def __init__(self): + """Initialize object.""" self.doc = "" def __repr__(self): + """Return deck representation of self.""" return self._to_deck() def _to_deck(self): + """Return deck representation of self.""" return "" diff --git a/trnsystor/statement/timereport.py b/trnsystor/statement/timereport.py index 6e1e71e..6f9354c 100644 --- a/trnsystor/statement/timereport.py +++ b/trnsystor/statement/timereport.py @@ -1,12 +1,12 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""TimeReport Statement.""" from trnsystor.statement.statement import Statement class TimeReport(Statement): - """The statement TIME_REPORT turns on or off the internal calculation of the + """TIME_REPORT Statement. + + The statement TIME_REPORT turns on or off the internal calculation of the time spent on each unit. If this feature is desired, the listing file will contain this information at the end of the file. """ @@ -23,4 +23,5 @@ def __init__(self, n=0): self.doc = "The TIME_REPORT Statement" def _to_deck(self): + """Return deck representation of self.""" return "TIME_REPORT {n}".format(n=self.n) diff --git a/trnsystor/statement/tolerances.py b/trnsystor/statement/tolerances.py index 80fb56f..8166122 100644 --- a/trnsystor/statement/tolerances.py +++ b/trnsystor/statement/tolerances.py @@ -1,17 +1,18 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Tolerances Statement.""" from trnsystor.statement.statement import Statement class Tolerances(Statement): - """The TOLERANCES statement is an optional control statement used to specify + """TOLERANCES Statement. + + The TOLERANCES statement is an optional control statement used to specify the error tolerances to be used during a TRNSYS simulation. """ def __init__(self, epsilon_d=0.01, epsilon_a=0.01): - """ + """Initialize object. + Args: epsilon_d: is a relative (and -epsilon_d is an absolute) error tolerance controlling the integration error. @@ -25,6 +26,10 @@ def __init__(self, epsilon_d=0.01, epsilon_a=0.01): self.doc = "Integration\tConvergence" def _to_deck(self): - """TOLERANCES 0.001 0.001""" + """Return deck representation of self. + + Examples: + TOLERANCES 0.001 0.001 + """ head = "TOLERANCES {} {}".format(self.epsilon_d, self.epsilon_a) return str(head) diff --git a/trnsystor/statement/version.py b/trnsystor/statement/version.py index cdec6ff..d031acc 100644 --- a/trnsystor/statement/version.py +++ b/trnsystor/statement/version.py @@ -1,19 +1,19 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Version Statement.""" from trnsystor.statement.statement import Statement class Version(Statement): - """Added with TRNSYS version 15. The idea of the command is that by labeling + """VERSION Statement. + + Added with TRNSYS version 15. The idea of the command is that by labeling decks with the TRNSYS version number that they were created under, it is easy to keep TRNSYS backwards compatible. The version number is saved by the TRNSYS kernel and can be acted upon. """ def __init__(self, v=(18, 0)): - """Initialize the Version statement + """Initialize the Version Statement. Args: v (tuple): A tuple of (major, minor) eg. 18.0 :> (18, 0) @@ -24,11 +24,9 @@ def __init__(self, v=(18, 0)): @classmethod def from_string(cls, string): - """ - Args: - string: - """ + """Create Version statement from str version number. eg. 18.0.""" return cls(tuple(map(int, string.split(".")))) def _to_deck(self): + """Return deck representation of self.""" return "VERSION {}".format(".".join(map(str, self.v))) diff --git a/trnsystor/statement/width.py b/trnsystor/statement/width.py index d717b38..48c5e32 100644 --- a/trnsystor/statement/width.py +++ b/trnsystor/statement/width.py @@ -1,12 +1,12 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""Width Statement.""" from trnsystor.statement.statement import Statement class Width(Statement): - """The WIDTH statement is an optional control statement is used to set the + """WIDTH Statement. + + The WIDTH statement is an optional control statement is used to set the number of characters to be allowed on a line of TRNSYS output. Note: @@ -25,14 +25,11 @@ def __init__(self, n=120): self.doc = "The number of printed characters per line" def _to_deck(self): + """Return deck representation of self.""" return str("WIDTH {}".format(self.k)) @staticmethod def _check_range(n): - """ - Args: - n: - """ if n >= 72 and n <= 132: return n else: diff --git a/trnsystor/studio.py b/trnsystor/studio.py index f222719..e6ea767 100644 --- a/trnsystor/studio.py +++ b/trnsystor/studio.py @@ -1,16 +1,17 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""StudioHeader module.""" from shapely.geometry import Point class StudioHeader(object): - """Each TrnsysModel has a StudioHeader which handles the studio comments + """StudioHeader class. + + Each TrnsysModel has a StudioHeader which handles the studio comments such as position, UNIT_NAME, model, POSITION, LAYER, LINK_STYLE """ def __init__(self, unit_name, model, position, layer=None): - """ + """Initialize object. + Args: unit_name (str): The unit_name, eg.: "Type104". model (Path): The path of the tmf/xml file. @@ -28,31 +29,18 @@ def __init__(self, unit_name, model, position, layer=None): self.link_styles = {} def __str__(self): + """Return deck representation of self.""" return self._to_deck() @classmethod def from_component(cls, model): - """ - Args: - model (Component): - """ + """Create object from :class:`TrnsysModel`.""" position = Point(50, 50) layer = ["Main"] return cls(model.name, model.model, position, layer) def _to_deck(self): - """ - Examples: - >>> - *$UNIT_NAME Boulder, CO - *$MODEL .\Weather Data Reading and Processing\Standard - Format\TMY2\Type15-2.tmf - *$POSITION 69 182 - *$LAYER Main # - - Returns: - (str): The string representation of the StudioHeader. - """ + """Return deck representation of self.""" unit_name = "*$UNIT_NAME {}".format(self.unit_name) model = "*$MODEL {}".format(self.model.expand()) position = "*$POSITION {} {}".format(self.position.x, self.position.y) diff --git a/trnsystor/trnsysmodel.py b/trnsystor/trnsysmodel.py index 3654762..b9d9c57 100644 --- a/trnsystor/trnsysmodel.py +++ b/trnsystor/trnsysmodel.py @@ -1,3 +1,4 @@ +"""TrnsysModel module.""" import collections import copy import itertools @@ -17,16 +18,10 @@ from trnsystor.collections.specialcards import SpecialCardsCollection from trnsystor.component import Component from trnsystor.externalfile import ExternalFile +from trnsystor.specialcard import SpecialCard from trnsystor.studio import StudioHeader from trnsystor.typecycle import TypeCycle -from trnsystor.typevariable import ( - Derivative, - Input, - Output, - Parameter, - TypeVariable, -) -from trnsystor.specialcard import SpecialCard +from trnsystor.typevariable import Derivative, Input, Output, Parameter, TypeVariable class MetaData(object): @@ -100,11 +95,11 @@ def __init__( cycles (list, optional): List of TypeCycle. source (Path): Path of the source code. externalFiles (trnsystor.external_file.ExternalFileCollection): A - class handling - ExternalFiles for this object. + class handling ExternalFiles for this object. compileCommand (str): Command used to recompile this type. model (Path): Path of the xml or tmf file. - **kwargs: + specialCards (list of SpecialCards): List of SpecialCards. + **kwargs: Other keyword arguments passed to the constructor. """ self.compileCommand = compileCommand self.object = object @@ -146,11 +141,6 @@ def from_tag(cls, tag, **kwargs): for child in tag.children if isinstance(child, Tag) } - xml_args = { - child.name: child.prettify() - for child in tag.children - if isinstance(child, Tag) - } meta_args.update(kwargs) return cls(**{attr: meta_args[attr] for attr in meta_args}) @@ -175,21 +165,12 @@ def check_extra_tags(self, kwargs): raise NotImplementedError() def __getitem__(self, item): - """eg.: self[item] :param item: - - Args: - item: - """ + """Get item. self[item].""" return getattr(self, item) @classmethod def from_xml(cls, xml, **kwargs): - """Initialize MetaData from xml file. - - Args: - xml: - **kwargs: - """ + """Initialize MetaData from xml file.""" xml_file = Path(xml) with open(xml_file) as xml: soup = BeautifulSoup(xml, "xml") @@ -201,10 +182,13 @@ def from_xml(cls, xml, **kwargs): class TrnsysModel(Component): + """TrnsysModel class.""" + def __init__(self, meta, name): - """Main Class for holding TRNSYS components. Alone, this __init__ method - does not do much. See the :func:`from_xml` class method for the official - constructor of this class. + """Initialize object. + + Alone, this __init__ method does not do much. See the :func:`from_xml` class + method for the official constructor of this class. Args: meta (MetaData): A class containing the model's metadata. @@ -213,11 +197,11 @@ def __init__(self, meta, name): super().__init__(name=name, meta=meta) def __str__(self): - """Return Deck representation of self.""" - return f"[{self.unit_number}]Type{self.type_number}: {self.name}" # self._to_deck() + """Return repr(self).""" + return f"[{self.unit_number}]Type{self.type_number}: {self.name}" def __repr__(self): - """str: The String representation of this object.""" + """Return repr(self).""" return f"[{self.unit_number}]Type{self.type_number}: {self.name}" @classmethod @@ -252,6 +236,9 @@ def from_xml(cls, xml, **kwargs): def copy(self, invalidate_connections=True): """Copy object. + The new object has a new unit_number. + The new object is translated by 50 pts to the right on the canvas. + Args: invalidate_connections (bool): If True, connections to other models will be reset. @@ -268,44 +255,46 @@ def copy(self, invalidate_connections=True): return new @property - def derivatives(self): - """VariableCollection: returns the model's derivatives""" + def derivatives(self) -> DerivativesCollection: + """Return derivatives of self.""" return self._get_derivatives() @property - def special_cards(self): - """VariableCollection: returns the model's special cards""" + def special_cards(self) -> SpecialCardsCollection: + """Return special cards of self.""" return self._get_special_cards() @property - def initial_input_values(self): - """VariableCollection: returns the model's initial input values.""" + def initial_input_values(self) -> InitialInputValuesCollection: + """Return initial input values of self.""" return self._get_initial_input_values() @property - def parameters(self): - """ParameterCollection: returns the model's parameters.""" + def parameters(self) -> ParameterCollection: + """Return parameters of self.""" return self._get_parameters() @property - def external_files(self): - """ExternalFileCollection: returns the model's external files""" + def external_files(self) -> ExternalFileCollection: + """Return external files of self.""" return self._get_external_files() @property - def anchor_points(self): - """dict: Returns the 8-AnchorPoints as a dict with the anchor point - location ('top-left', etc.) as a key. + def anchor_points(self) -> dict: + """Return the 8-AnchorPoints as a dict. + + The anchor point location ('top-left', etc.) is the key. """ return AnchorPoint(self).anchor_points @property def reverse_anchor_points(self): + """Reverse anchor points.""" return AnchorPoint(self).reverse_anchor_points @classmethod def _from_tag(cls, tag, **kwargs): - """Class method to create a :class:`TrnsysModel` from a tag + """Class method to create a :class:`TrnsysModel` from a tag. Args: tag (Tag): The XML tag with its attributes and contents. @@ -363,16 +352,16 @@ def _from_tag(cls, tag, **kwargs): return model def _get_initial_input_values(self): - """initial input values getter""" + """Get initial input values.""" try: self._resolve_cycles("input", Input) input_dict = self._get_ordered_filtered_types(Input, "variables") # filter out cyclebases input_dict = { - k: v for k, v in input_dict.items() if v._iscyclebase == False + k: v for k, v in input_dict.items() if v._iscyclebase is False } return InitialInputValuesCollection.from_dict(input_dict) - except: + except TypeError: return InitialInputValuesCollection() def _get_inputs(self): @@ -385,43 +374,39 @@ def _get_inputs(self): input_dict = self._get_ordered_filtered_types(Input, "variables") # filter out cyclebases input_dict = { - k: v for k, v in input_dict.items() if v._iscyclebase == False + k: v for k, v in input_dict.items() if v._iscyclebase is False } return InputCollection.from_dict(input_dict) - except: + except TypeError: return InputCollection() def _get_outputs(self): - """outputs getter. Sorts by order number and resolves cycles each time - it is called - """ + """Sorts by order number and resolves cycles each time it is called.""" # output_dict = self._get_ordered_filtered_types(Output) try: self._resolve_cycles("output", Output) output_dict = self._get_ordered_filtered_types(Output, "variables") # filter out cyclebases output_dict = { - k: v for k, v in output_dict.items() if v._iscyclebase == False + k: v for k, v in output_dict.items() if v._iscyclebase is False } return OutputCollection.from_dict(output_dict) except TypeError: return OutputCollection() def _get_parameters(self): - """parameters getter. Sorts by order number and resolves cycles each - time it is called - """ + """Sorts by order number and resolves cycles each time it is called.""" self._resolve_cycles("parameter", Parameter) param_dict = self._get_ordered_filtered_types(Parameter, "variables") # filter out cyclebases - param_dict = {k: v for k, v in param_dict.items() if v._iscyclebase == False} + param_dict = {k: v for k, v in param_dict.items() if v._iscyclebase is False} return ParameterCollection.from_dict(param_dict) def _get_derivatives(self): self._resolve_cycles("derivative", Derivative) deriv_dict = self._get_ordered_filtered_types(Derivative, "variables") # filter out cyclebases - deriv_dict = {k: v for k, v in deriv_dict.items() if v._iscyclebase == False} + deriv_dict = {k: v for k, v in deriv_dict.items() if v._iscyclebase is False} return DerivativesCollection.from_dict(deriv_dict) def _get_special_cards(self): @@ -445,8 +430,9 @@ def _get_external_files(self): return ExternalFileCollection() # return empty collection def _get_ordered_filtered_types(self, class_name, store): - """Returns an ordered dict of :class:`TypeVariable` filtered by - *class_name* and ordered by their order number attribute. + """Return an ordered dict of :class:`TypeVariable`. + + Filtered by *class_name* and ordered by their order number attribute. Args: class_name: Name of TypeVariable to filer: Choices are :class:`Input`, @@ -463,7 +449,7 @@ def _get_ordered_filtered_types(self, class_name, store): ) def _get_filtered_types(self, class_name, store): - """Returns a filter of TypeVariables from the self._meta[store] by *class_name* + """Return a filter of TypeVariables from the self._meta[store] by *class_name*. Args: class_name: Name of TypeVariable to filer: Choices are :class:`Input`, @@ -481,10 +467,6 @@ def _resolve_cycles(self, type_, class_): Proformas can contain parameters, inputs and outputs that have a variable number of entries. This will deal with their creation each time the linked parameters are changed. - - Args: - type_: - class_: """ output_dict = self._get_ordered_filtered_types(class_, "variables") cycles = { @@ -593,7 +575,7 @@ def _resolve_cycles(self, type_, class_): self._meta.variables.update({id(item): item}) def _to_deck(self): - """print the Input File (.dck) representation of this TrnsysModel""" + """Return deck representation of self.""" unit_type = f"UNIT {self.unit_number} TYPE {self.type_number} {self.name}\n" studio = self.studio params = self.parameters @@ -615,10 +597,7 @@ def _to_deck(self): ) def update_meta(self, new_meta): - """ - Args: - new_meta: - """ + """Update self with new :class:`MetaData`.""" for attr in self._meta.__dict__: if hasattr(new_meta, attr): setattr(self._meta, attr, getattr(new_meta, attr)) @@ -654,6 +633,7 @@ def update_meta(self, new_meta): ) def plot(self): + """Plot the model.""" import matplotlib.pyplot as plt G = nx.DiGraph() @@ -677,18 +657,3 @@ def plot(self): ) plt.show() return ax - - -class Derivatives: - # Todo: Implement Derivatives - pass - - -class Trace: - # Todo: Implement Trace - pass - - -class Format: - # Todo: Implement Format - pass diff --git a/trnsystor/typecycle.py b/trnsystor/typecycle.py index eb120cf..c5f849f 100644 --- a/trnsystor/typecycle.py +++ b/trnsystor/typecycle.py @@ -1,14 +1,13 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - +"""TypeCycle module.""" import collections import itertools from bs4 import Tag -class TypeCycle(object): +class TypeCycle: + """TypeCycle class.""" + def __init__( self, role=None, @@ -19,22 +18,8 @@ def __init__( maxSize=None, paramName=None, question=None, - **kwargs, ): - """ - Args: - role (str): The role of the TypeCycle. "parameter", "input", - "output" - firstRow: - lastRow: - cycles: - minSize: - maxSize: - paramName: - question: - **kwargs: - """ - super().__init__() + """Initialize object.""" self.role = role self.firstRow = firstRow self.lastRow = lastRow @@ -46,7 +31,8 @@ def __init__( @classmethod def from_tag(cls, tag): - """ + """Create TypeCycle from Tag. + Args: tag (Tag): The XML tag with its attributes and contents. """ @@ -63,15 +49,17 @@ def from_tag(cls, tag): return cls(**dict_) def __repr__(self): + """Return repr(self).""" return self.role + " {} to {}".format(self.firstRow, self.lastRow) @property def default(self): + """Return the default value of self.""" return int(self.minSize) @property def idxs(self): - """0-based index of the TypeVariable(s) concerned with this cycle""" + """0-based index of the TypeVariable(s) concerned with this cycle.""" return ( list( itertools.chain( @@ -87,6 +75,7 @@ def idxs(self): @property def is_question(self): + """Return True if self is a question.""" return ( any(cycle.question is not None for cycle in self.cycles) if self.cycles diff --git a/trnsystor/typevariable.py b/trnsystor/typevariable.py index c9c9f6c..c60e9bc 100644 --- a/trnsystor/typevariable.py +++ b/trnsystor/typevariable.py @@ -1,19 +1,17 @@ -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -# Copyright (c) 2019 - 2021. Samuel Letellier-Duchesne and trnsystor contributors + -# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +"""TypeVariable module.""" import collections import copy import re from bs4 import Tag -from trnsystor import parse_type, standerdized_name from trnsystor.linkstyle import LinkStyle -from trnsystor.utils import _parse_value +from trnsystor.utils import _parse_value, parse_type, standardize_name class TypeVariable(object): - """ + """TypeVariable class. + :class:`TypeVariable` is the main object class that handles storage of TRNSYS component parameters, inputs, outputs and derivatives. Parameters, Inputs, Outputs and Derivatives are all subclasses of TypeVariable. @@ -41,11 +39,11 @@ def __init__( definition=None, model=None, ): - """Initialize a TypeVariable with the following attributes: + """Initialize a TypeVariable with the following attributes. Args: val (int, float, _Quantity): The actual value hold by this object. - order (str): + order (str): The order of the variable. name (str): This name will be seen by the user in the connections window and all other variable information windows. role (str): The role of the variables such as input, output, etc. @@ -75,7 +73,7 @@ def __init__( the inputs and derivatives and suppressed for the outputs. symbol (str): The symbol of the unit (not used). definition (str): A short description of the variable. - model: + model (TrnsysModel): the TrnsysModel this TypeVariable belongs to. """ super().__init__() self._is_question = False @@ -110,18 +108,17 @@ def from_tag(cls, tag, model=None): Args: tag (Tag): The XML tag with its attributes and contents. - model (TrnsysModel): + model (TrnsysModel): The model. """ role = tag.find("role").text val = tag.find("default").text try: val = float(val) - except: # todo: find type of error - # val is a string + except ValueError: + # could not convert string to float. if val == "STEP": val = 1 - # Todo: figure out better logic when default value - # is 'STEP + # Todo: figure out better logic when default value is 'STEP' elif val == "START": val = 1 elif val == "STOP": @@ -143,18 +140,23 @@ def from_tag(cls, tag, model=None): ) def __float__(self): + """Return magnitude of self.""" return self.value.m def __int__(self): + """Return int(self).""" return int(self.value.m) def __mul__(self, other): + """Return self * other.""" return float(self) * other def __add__(self, other): + """Return self + other.""" return float(self) + other def __sub__(self, other): + """Return self - other.""" return float(self) - other def _parse_types(self): @@ -168,7 +170,7 @@ def _parse_types(self): self.__setattr__(attr, int(value)) def copy(self): - """TypeVariable: Make a copy of :attr:`self`""" + """TypeVariable: Make a copy of :attr:`self`.""" new_self = copy.copy(self) return new_self @@ -176,7 +178,8 @@ def copy(self): def is_connected(self): """Whether or not this TypeVariable is connected to another TypeVariable. - Checks if self is in any keys""" + Checks if self is in any keys + """ return self.predecessor is not None @property @@ -202,10 +205,10 @@ def predecessor(self): @property def idx(self): - """The 0-based index of the TypeVariable""" + """Get the 0-based variable index of self.""" ordered_dict = collections.OrderedDict( ( - standerdized_name(self.model._meta.variables[attr].name), + standardize_name(self.model._meta.variables[attr].name), [self.model._meta.variables[attr], 0], ) for attr in sorted( @@ -213,7 +216,7 @@ def idx(self): lambda kv: isinstance( self.model._meta.variables[kv], self.__class__ ) - and self.model._meta.variables[kv]._iscyclebase == False, + and self.model._meta.variables[kv]._iscyclebase is False, self.model._meta.variables, ), key=lambda key: self.model._meta.variables[key].order, @@ -225,10 +228,11 @@ def idx(self): value[1] = i i += 1 - return ordered_dict[standerdized_name(self.name)][1] + return ordered_dict[standardize_name(self.name)][1] @property def one_based_idx(self): + """Get the 1-based variable index of self such as it appears in Trnsys.""" return self.idx + 1 def connect_to(self, other, link_style_kwargs=None): @@ -271,8 +275,12 @@ def connect_to(self, other, link_style_kwargs=None): ) def __repr__(self): + """Return repr(self).""" try: - return f"{self.name}; units={self.unit}; value={self.value:~P}\n{self.definition}" + return ( + f"{self.name}; units={self.unit}; " + f"value={self.value:~P}\n{self.definition}" + ) except Exception: return ( f"{self.name}; units={self.unit}; value={self.value}\n" @@ -281,71 +289,52 @@ def __repr__(self): class Parameter(TypeVariable): - """A subclass of :class:`TypeVariable` specific to parameters""" + """A subclass of :class:`TypeVariable` specific to parameters.""" def __init__(self, val, **kwargs): - """A subclass of :class:`TypeVariable` specific to parameters. - - Args: - val: - **kwargs: - """ + """A subclass of :class:`TypeVariable` specific to parameters.""" super().__init__(val, **kwargs) self._parse_types() class Input(TypeVariable): - """A subclass of :class:`TypeVariable` specific to inputs""" + """A subclass of :class:`TypeVariable` specific to inputs.""" def __init__(self, val, **kwargs): - """A subclass of :class:`TypeVariable` specific to inputs. - - Args: - val: - **kwargs: - """ + """A subclass of :class:`TypeVariable` specific to inputs.""" super().__init__(val, **kwargs) self._parse_types() class InitialInputValue(TypeVariable): - """A subclass of :class:`TypeVariable` specific to Initial Input Values""" + """A subclass of :class:`TypeVariable` specific to Initial Input Values.""" def __init__(self, val, **kwargs): - """A subclass of :class:`TypeVariable` specific to inputs. - - Args: - val: - **kwargs: - """ + """A subclass of :class:`TypeVariable` specific to inputs.""" super().__init__(val, **kwargs) self._parse_types() class Output(TypeVariable): - """A subclass of :class:`TypeVariable` specific to outputs""" + """A subclass of :class:`TypeVariable` specific to outputs.""" def __init__(self, val, **kwargs): - """A subclass of :class:`TypeVariable` specific to outputs. - - Args: - val: - **kwargs: - """ + """A subclass of :class:`TypeVariable` specific to outputs.""" super().__init__(val, **kwargs) self._parse_types() @property - def is_connected(self): + def is_connected(self) -> bool: + """Return True of self has any successor.""" return len(self.successors) > 0 @property def successors(self): - """Other TypeVariables to which this TypeVariable is connected. Successors""" + """Other TypeVariables to which this TypeVariable is connected. Successors.""" successors = [] for suc in self.model.UNIT_GRAPH.successors(self.model): for key in self.model.UNIT_GRAPH[self.model][suc]: @@ -356,18 +345,15 @@ def successors(self): class Derivative(TypeVariable): - """the DERIVATIVES for a given :class:`TrnsysModel` specify initial values, + """Derivatives class. + + the DERIVATIVES for a given :class:`TrnsysModel` specify initial values, such as the initial temperatures of various nodes in a thermal storage tank or the initial zone temperatures in a multi zone building. """ def __init__(self, val, **kwargs): - """A subclass of :class:`TypeVariable` specific to derivatives. - - Args: - val: - **kwargs: - """ + """A subclass of :class:`TypeVariable` specific to derivatives.""" super().__init__(val, **kwargs) self._parse_types() diff --git a/trnsystor/utils.py b/trnsystor/utils.py index 171884d..fcaeb20 100644 --- a/trnsystor/utils.py +++ b/trnsystor/utils.py @@ -1,17 +1,20 @@ +"""Utils module.""" import math import re from pint import UnitRegistry from pint.quantity import _Quantity from shapely.geometry import LineString -from sympy import Symbol, Expr, cacheit +from sympy import Expr, Symbol, cacheit from sympy.core.assumptions import StdFactKB from sympy.core.logic import fuzzy_bool +from sympy.printing import StrPrinter def affine_transform(geom, matrix=None): - """Apply affine transformation to geometry. By, default, flip geometry along - the x axis. + """Apply affine transformation to geometry. + + By, default, flip geometry along the x axis. Hint: visit affine_matrix_ for other affine transformation matrices. @@ -33,8 +36,9 @@ def affine_transform(geom, matrix=None): def get_rgb_from_int(rgb_int): - """Simple utility to convert an rgb int color to its red, green and blue - colors. Values are used ranging from 0 to 255 for each of the components. + """Convert an rgb int color to its red, green and blue colors. + + Values are used ranging from 0 to 255 for each of the components. Important: Unlike Java, the TRNSYS Studio will want an integer where bits 0-7 are @@ -59,8 +63,9 @@ def get_rgb_from_int(rgb_int): def get_int_from_rgb(rgb): - """Simple utility to convert an RBG color to its TRNSYS Studio compatible - int color. Values are used ranging from 0 to 255 for each of the components. + """Convert an RBG color to its TRNSYS Studio compatible int color. + + Values are used ranging from 0 to 255 for each of the components. Important: Unlike Java, the TRNSYS Studio will want an integer where bits 0-7 are @@ -85,10 +90,7 @@ def get_int_from_rgb(rgb): def resolve_type(args): - """ - Args: - args: - """ + """Return float for :class:`_Quantity` or number.""" if isinstance(args, _Quantity): return args.m else: @@ -96,14 +98,6 @@ def resolve_type(args): def _parse_value(value, _type, unit, bounds=(-math.inf, math.inf), name=None): - """ - Args: - value: - _type: - unit: - bounds: - name: - """ if not name: name = "" _type = parse_type(_type) @@ -111,7 +105,8 @@ def _parse_value(value, _type, unit, bounds=(-math.inf, math.inf), name=None): try: f = _type(value) - except: + except ValueError: + # invalid literal for int() with base 10: '+Inf' if value == "STEP": value = 1 # Todo: figure out better logic when default value is 'STEP' @@ -136,10 +131,7 @@ def _parse_value(value, _type, unit, bounds=(-math.inf, math.inf), name=None): def parse_type(_type): - """ - Args: - _type (type or str): - """ + """Parse type str as builtin type.""" if isinstance(_type, type): return _type elif _type == "integer": @@ -152,16 +144,15 @@ def parse_type(_type): raise NotImplementedError() -def standerdized_name(name): - """ - Args: - name: - """ +def standardize_name(name): + """Replace invalid characters with underscores.""" return re.sub("[^0-9a-zA-Z]+", "_", name) def parse_unit(unit): - """Units defined in the xml proformas follow a convention that is not quite + """Return supported unit. + + Units defined in the xml proformas follow a convention that is not quite compatible with `Pint` . This method will catch known discrepancies. Args: @@ -194,11 +185,14 @@ def parse_unit(unit): def redistribute_vertices(geom, distance): - """from https://stackoverflow.com/a/35025274 + """Redistribute vertices by a certain distance. + + Hint: + https://stackoverflow.com/a/35025274 Args: - geom: - distance: + geom (LineString): The geometry. + distance (float): The distance used to redistribute vertices. """ if geom.length == 0: return geom @@ -218,49 +212,44 @@ def redistribute_vertices(geom, distance): ureg = UnitRegistry() -from sympy.printing import StrPrinter - class DeckFilePrinter(StrPrinter): - """Print derivative of a function of symbols in deck file form. This will - override the :func:`sympy.printing.str.StrPrinter#_print_Symbol` method to + """Print derivative of a function of symbols in deck file form. + + This will override the :func:`sympy.printing.str.StrPrinter#_print_Symbol` method to print the TypeVariable's unit_number and output number. """ def _print_Symbol(self, expr): - """print the TypeVariable's unit_number and output number. :param expr: - - Args: - expr (TypeVariable or Equation): - """ + """Print the TypeVariable's unit_number and output number.""" try: return "[{}, {}]".format( expr.model.model.unit_number, expr.model.one_based_idx ) - except: + except AttributeError: + # 'Symbol' object has no attribute 'model' return expr.name def print_my_latex(expr): - """Most of the printers define their own wrappers for print(). These - wrappers usually take printer settings. Our printer does not have any - settings. + """Most of the printers define their own wrappers for print(). - Args: - expr: + These wrappers usually take printer settings. Our printer does not have any + settings. """ return DeckFilePrinter().doprint(expr) class TypeVariableSymbol(Symbol): - """This is a subclass of the sympy Symbol class. It is a bit of a hack, so - hopefully nothing bad will happen. + """This is a subclass of the sympy Symbol class. + + It is a bit of a hack, so hopefully nothing bad will happen. """ def __new__(cls, type_variable, **assumptions): - """TypeVariableSymbol are identified by TypeVariable and assumptions: + """:class:`TypeVariableSymbol` are identified by TypeVariable and assumptions. - >>> from trnsystor import TypeVariableSymbol + >>> from trnsystor.utils import TypeVariableSymbol >>> TypeVariableSymbol("x") == TypeVariableSymbol("x") True >>> TypeVariableSymbol("x", real=True) == TypeVariableSymbol("x", @@ -276,11 +265,7 @@ def __new__(cls, type_variable, **assumptions): return TypeVariableSymbol.__xnew_cached_(cls, type_variable, **assumptions) def __new_stage2__(cls, model, **assumptions): - """ - Args: - model: - **assumptions: - """ + """Return new stage.""" obj = Expr.__new__(cls) obj.name = model.name obj.model = model