Let's explore the details of how the configuration file works!
The configuration file edited by the user (~/cea.config
) is only the tip of the iceberg, resting on a foundation of the default configuration file default.config
file and the class :pycea.config.Configuration
, which reads in the default configuration file as well as the user configuration file and makes those parameters available to the scripts. Each script is provided with an instance of :pycea.config.Configuration
called config
.
Each parameter is defined in a section. Each parameter has a type, which specifies the range of values allowed for that parameter and also how to read and write them to the configuration file.
Access to parameters through the config
variable happens by section. Since all section names and parameter names in the configuration file follow the kebab-case naming convention, and these are not valid python identifiers, a translation is made to the snake_case naming convention: All hyphens (-
) are replaced by underscores (_
).
The syntax is simple:
"config." + [section] + "." + parameter
The section name is optional for the section general
, so config.general.scenario
refers to the same parameter as config.scenario
. Note that these parameters can also be set:
config.scenario = r'C:\hoenggerberg\baseline'
If you want to persist these changes to disk, you need to explicitly save them with :pycea.config.Configuration.save
.
Note
It is a bad idea to have multiple instances of :pycea.config.Configuration
, as if one part of a script changes a parameter, this will not be reflected in the other instances. Each CEA script accepts a config
argument to it's main
function and should only use that.
config
graph [splines=ortho, nodesep=0.1, rankdir="TD"] node [shape="record", fontname="Arial", fontsize="8"] Configuration -> Section [label=" 0.."]; Section -> Parameter [label=" 0.."];
Configuration [label="{Configurationapply_command_line_args()lsave()l}"]; Parameter [label="{Parameterinitialize()lget()lset()lencode()ldecode()l}"]; Section [label="{Section__getattr__()l__setattr()__l}"];
- {
rank="same"; Configuration; Section; Parameter;
}
An instance of :pycea.config.Configuration
is created with an optional config_file
parameter that specifies the configuration file to load as the user configuration file. This defaults to ~/cea.config
. This file is parsed as a :pyConfigParser.SafeConfigParser
, using the default configuration as a backup for the values and stored in the attribute user_config
. Another :pyConfigParser.SafeConfigParser
is created for the default configuration and stored in the attribute default_config
.
Next, the default_config
is used to create a dictionary of :py:class`cea.config.Section` objects and each section is populated with a dictionary of :pycea.config.Parameter
instances. The default configuration file lists not only each parameter, but additional keys for each parameter as well. Example:
[general]
scenario = C:\reference-case-open\baseline
scenario.type = PathParameter
scenario.help = Path to the scenario to run
Using this information, the parameter general:scenario
is assigned a default value of C:\reference-case-open\baseline
, is represented by a subtype of :pycea.config.Parameter
called :py:class:`cea.config.PathParameter and has a help text "Path to the scenario to run" - which is stored in the help
attribute of the parameter object.
Some subclasses of :pycea.config.Parameter
have additional configuration, like the `cea.config.ChoiceParameter`:
[data-helper]
region = CH
region.type = ChoiceParameter
region.choices = CH SIN custom
region.help = The region to use for the databases (either CH or SIN) - set to "custom" if you want to edit them
When the config
instance is creating the parameters, each parameter object is given a chance to initialize itself with a call to :pycea.config.Parameter.initialize(parser)
with parser
set to the default_config
. Subclasses of Parameter
can override this method to read this additional configuration.
When a script does something like config.general.weather
, the config.sections
dictionary is checked for the section named general
and the parameters
dictionary in that section is checked for a parameter named weather
. The :pycea.config.Parameter.get
method is called on that parameter and the result of this call is returned.
Based on the default configuration file, this is defined as:
[general]
weather = Zug-inducity_1990_2010_TMY
weather.type = WeatherPathParameter
weather.help = either a full path to a weather file or the name of one of the weather files shipped with the CEA
So the parameter is of type :pycea.config.WeatherPathParameter
.
Inside the :pycea.config.Parameter.get
method, a call is made to :pycea.config.Parameter.decode
, passing in the value read from the user configuration file. Subclasses of Parameter
specify how to encode and decode values to the configuration file. The semantics are:
decode
takes a string from a configuration file (or from the command line) and returns a typed value (e.g. abool
if the parameter type is :pycea.config.BooleanParameter
).encode
takes a typed value (e.g. a boolean value) and encodes it to a string that can be stored in the configuration file.
In the case of :pycea.config.WeatherPathParameter
, decode
will ensure that the path to the weather file exists and, if just the name of a weather file in the CEA weather file database is returned, resolves that to the full path to that file. Hence, on my system, the value of config.weather
is C:\Users\darthoma\Documents\GitHub\CityEnergyAnalyst\cea\databases\weather\Zurich.epw
.
The mechanism for saving a value to the config file works similarly: :pycea.config.Parameter.set
is called, which in turn calls :pycea.config.Parameter.encode
- subclasses can override this to provide type specific behaviour.
Steps:
- subclass :py
cea.config.Parameter
- optional: override
initialize
to settings - optional: override
encode
to format the parameter value as a string - optional: override
decode
to read the parameter value from a string
Check the existing parameter types for ideas!