Settings Attribute Machinery#
Each object has a settings
attribute which is used to store relevant and useful data. This is particularly useful for the algorithms where things like solver tolerance can be set. This notebook will given an overview of this attribute and its features and behavior.
import openpnm as op
Let’s start by creating a StokesFlow
algorithm, which has plenty of settings so is good to demonstrate:
pn = op.network.Demo(shape=[4, 4, 1])
w = op.phase.Water(network=pn)
w.add_model_collection(op.models.collections.physics.basic)
flow = op.algorithms.StokesFlow(network=pn, phase=w)
First lets print the settings
attribute:
print(flow.settings)
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
Settings Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
uuid 3d5421d2-13f1-4ad1-b632-8e7000d9d5bf
default_domain domain_1
cache True
conductance throat.hydraulic_conductance
phase phase_01
quantity pore.pressure
variable_props TypedSet()
f_rtol 1e-06
newton_maxiter 5000
relaxation_factor 1.0
x_rtol 1e-06
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
settings
is a custom class that behaves somewhat like Python’s dataclass
but is actually useful (sorry Python). Let’s start by exploring the settings on flow
(without talking about their meaning).
Datatype is enforced#
Once a data type is written to an attribute, all subsequent values must be of the same type.
flow.settings.f_rtol = 2.0
print(flow.settings.f_rtol)
2.0
try:
flow.settings.f_rtol = 'two'
except Exception as e:
print(e)
Attribute 'f_rtol' can only accept values of type <class 'float'>, but the recieved value was of type <class 'str'>
Settings can be access as attributes or dict keys#
This feature just makes it easy to access things programmatically using flow.settings[key]
rather then getattr(flow.settings, key)
, while ensuring the settings
attributes can still be easily viewed using tab-completion.
print(flow.settings['f_rtol'])
flow.settings['f_rtol'] = 3.0
print(flow.settings.f_rtol)
2.0
3.0
Namespace is clean#
When developing this feature we considered many relevant packages like traits
, attrs
, pydantic
, etc, but they all had a major drawback: The namespace the class was cluttered with many methods relevant to using the package. We wanted the settings
attribute to contain only settings:
for item in dir(flow.settings):
if not item.startswith('_'):
print(item)
cache
conductance
default_domain
f_rtol
newton_maxiter
phase
quantity
relaxation_factor
uuid
variable_props
x_rtol
Collections also enforce types#
As shown above, once a setting is given a certain value, all future values must have the same datatype. We also implemented several “typed” collections, like TypedList
and TypedSet
, which insist that all elements must have the same type:
print(flow.settings.variable_props)
TypedSet()
flow.settings.variable_props.add('pore.pressure')
print(flow.settings.variable_props)
TypedSet({'pore.pressure'})
try:
flow.settings.variable_props.add(0.0)
except Exception as e:
print(e)
This list cannot accept values of type <class 'float'>
Note that the first entry into the TypedSet
defines the type of all subsequent entries. The reason for this type enforcement is basically to prevent users from writing a value to a setting that OpenPNM does not expect. OpenPNM fetches these values from various places in the code and uses them, so they must be usable.
Settings are attached before init#
An instance of the Settings
class is attached to the settings
attribute of every object even prior to initialization. This is done by overloading the __new__
method of the Base2
class, from which every OpenPNM object descends.
new = op.core.Base2()
print(new.settings)
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
Settings Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
uuid f7a96033-dba8-47b9-acb9-2b40307769d8
default_domain domain_1
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
During this step the uuid
is generated, to ensure all objects have a unique value here (This is useful if an object is saved and reloaded for instance).