The discipline, a key concept¶
How is a discipline defined?¶
What is a discipline?¶
A discipline is a set of calculations that:
produces a dictionary of arrays as outputs
from a dictionary of arrays as inputs
using either a Python function, or equations or an external software, or a workflow engine.
How is a discipline implemented in GEMSEO?¶
Programmatically speaking, disciplines are implemented in GEMSEO through the MDODiscipline
class.
They are defined by three elements:
the
MDODiscipline.input_grammar
attribute: the set of rules that defines valid input data,the
MDODiscipline.output_grammar
attribute: the set of rules that defines valid output data,the
MDODiscipline._run()
method: the method to compute the output data from the input data.
Grammar¶
The input and output specifications are defined in a grammar,
through the MDODiscipline.input_grammar
and MDODiscipline.output_grammar
attributes,
which can be either a SimpleGrammar
or a JSONGrammar
(default grammar), or your own which
derives from the BaseGrammar
class.
Note
The grammar is a very powerful and key concept. There are multiple ways of creating grammars in GEMSEO. The preferred one for integrating simulation processes is the use of a JSON schema, but is not detailed here for the sake of simplicity. For more explanations about grammars, see Interfacing simulation software.
Warning
All the inputs and outputs names of the disciplines in a scenario shall be consistent.
GEMSEO assumes that the data are tagged by their names with a global convention in the whole process.
What two disciplines call “X” shall be the same “X”. The coupling variables for instance, are detected thanks to these conventions.
Inheritance¶
The disciplines are all subclasses of MDODiscipline
, from which they must inherit.
To be used, if your MDODiscipline
of interest does not exist, you must:
define a class inheriting from
MDODiscipline
,define the input and output grammars in the constructor,
implement the
MDODiscipline._run()
method which defines the way in which the output set values are obtained from the input set values.
Note
Typically, when we deal with an interfaced software,
the MDODiscipline._run()
method gets the inputs from the
input grammar, calls a software, and writes the outputs to the output grammar.
Note
The JSON grammars are automatically detected when they are in the same
folder as your subclass module and named "CLASSNAME_input.json"
and "CLASSNAME_output.json"
and the auto_detect_grammar_files
option is True
.
What are the API functions in GEMSEO?¶
Once a sub-class of MDODiscipline
is defined, an instance of this discipline can be created from the create_discipline()
API function.
Furthermore, many disciplines inheriting from MDODiscipline
are already implemented in GEMSEO.
Use the get_available_disciplines()
API function to discover them:
from gemseo import get_available_disciplines
get_available_disciplines()
which results in:
['RosenMF', 'SobieskiAerodynamics', 'DOEScenario', 'MDOScenario', 'SobieskiMission', 'SobieskiBaseWrapper', 'Sellar1', 'Sellar2', 'MDOChain', 'SobieskiStructure', 'Structure', 'SobieskiPropulsion', 'Scenario', 'AnalyticDiscipline', 'MDOScenarioAdapter', 'SellarSystem', 'ScalableFittedDiscipline', 'Aerodynamics', 'Mission', 'PropaneComb1', 'PropaneComb2', 'PropaneComb3', 'PropaneReaction', 'MDOParallelChain']
Note
These available MDODiscipline
can be classified into different categories:
classes implementing scenario, a key concept in GEMSEO:
Scenario
andDOEScenario
,MDOScenario
,classes implementing MDO problem disciplines:
Sobieski’s SSBJ problem:
SobieskiAerodynamics
,SobieskiMission
,SobieskiBaseWrapper
,SobieskiStructure
andSobieskiPropulsion
,Sellar problem:
Sellar1
,Sellar2
andSellarSystem
,Aerostructure problem:
Structure
,Aerodynamics
andMission
,Propane problem:
PropaneComb1
,PropaneComb2
,PropaneComb3
andPropaneReaction
,
classes implementing special disciplines:
MDOParallelChain
,MDOChain
,ScalableDiscipline
andMDOScenarioAdapter
.classes implementing optimization discipline:
RosenMF
.
How to instantiate an existing MDODiscipline
?¶
We can easily instantiate an internal discipline by means of the create_discipline()
, e.g.:
from gemseo import create_discipline
sellar_system = create_discipline('SellarSystem')
We can easily instantiate multiple built-in disciplines by means of the create_discipline()
method,
using a list of discipline names rather than a single discipline name, e.g.:
from gemseo import create_discipline
disciplines = create_discipline(['Sellar1', 'Sellar2', 'SellarSystem'])
In this case, disciplines
is a list of MDODiscipline
,
where the first one is an instance of Sellar1
,
the second one is an instance of Sellar2
and
the third one is an instance of SellarSystem
.
Note
If the constructor of a discipline has specific arguments,
these arguments can be passed into a dict
to the create_discipline()
method,
e.g.:
from gemseo import create_discipline
discipline = create_discipline('MyDisciplineWithArguments', **kwargs)
where kwargs = {'arg1_key': arg1_val, 'arg1_key': arg1_val, ...}
.
Note
We can easily instantiate an external discipline by means of the create_discipline()
(see Extend GEMSEO features):
from gemseo import create_discipline
discipline = create_discipline('MyExternalDiscipline')
How to set the cache policy?¶
We can set the cache policy of a discipline by means of the MDODiscipline.set_cache_policy()
method,
either using the default cache strategy, e.g.:
sellar_system.set_cache_policy(cache_type=sellar_system.CacheType.SIMPLE)
or the HDF5 cache strategy with the discipline name as node name (here SellarSystem
), e.g.:
sellar_system.set_cache_policy(cache_type=sellar_system.CacheType.HDF5, cache_hdf_file='cached_data.hdf5')
or the HDF5 cache strategy with a user-defined name as node name (here node
), e.g.:
sellar_system.set_cache_policy(cache_type=sellar_system.CacheType.HDF5, cache_hdf_file='cached_data.hdf5', cache_hdf_node_path='node')
Note
Click here. to get more information about caching strategies.
Note
The MDODiscipline.set_cache_policy()
method takes an additional argument, named cache_tolerance
,
which represents the tolerance for the approximate cache maximal relative norm difference to consider that two input arrays are equal.
By default, cache_tolerance
is equal to zero. We can get its value by means of the MDODiscipline.cache_tol
getter
and change its value by means of the MDODiscipline.cache_tol
setter.
How to execute an MDODiscipline
?¶
We can execute an MDODiscipline
,
either with its default input values, e.g.:
sellar_system.execute()
which results in:
{'obj': array([ 1.36787944+0.j]), 'y_2': array([ 1.+0.j]), 'y_1': array([ 1.+0.j]), 'c_1': array([ 2.16+0.j]), 'c_2': array([-23.+0.j]), 'x_shared': array([ 1.+0.j, 0.+0.j]), 'x_local': array([ 0.+0.j])}
or with user-defined values, defined into a dict
indexed by input data names with NumPy array values, e.g.:
import numpy as np
input_data = {'y_1': array([ 2.]), 'x_shared': array([ 1., 0.]), 'y_2': array([ 1.]), 'x_local': array([ 0.])}
sellar_system.execute(input_data)
which results in:
{'obj': array([ 4.36787944+0.j]), 'y_2': array([ 1.]), 'y_1': array([ 2.]), 'c_1': array([-0.84+0.j]), 'c_2': array([-23.+0.j]), 'x_shared': array([ 1., 0.]), 'x_local': array([ 0.])}
How to get information about an instantiated MDODiscipline
?¶
5.a. How to get input and output data names?¶
We can get the input and output data names by means of the MDODiscipline.get_input_data_names()
and MDODiscipline.get_output_data_names()
methods, e.g.:
print(sellar_system.get_input_data_names(), sellar_system.get_output_data_names())
which results in:
['y_1', 'x_shared', 'y_2', 'x_local'] ['c_1', 'c_2', 'obj']
5.b. How to check the validity of input or output data?¶
We can check the validity of a dict
of input data (resp. output data) by means of the MDODiscipline.check_input_data()
(resp. MDODiscipline.check_output_data()
) methods, e.g.:
sellar_system.check_input_data(sellar_system.default_inputs)
does not raise any error while:
sellar_system.check_input_data({'a': array([1.]), 'b': array([1., -6.2])})
raises the error:
gemseo.core.grammar.InvalidDataException: Invalid input data for: SellarSystem
How to get the default input values?¶
We can get the default input data by means of the MDODiscipline.default_inputs
attribute, e.g.:
print(sellar_system.default_inputs)
which results in:
{'y_0': array([ 1.+0.j]), 'x_shared': array([ 1.+0.j, 0.+0.j]), 'y_1': array([ 1.+0.j]), 'x_local': array([ 0.+0.j])}
How to get input and output data values?¶
All input or output data values as a list of arrays¶
Once the discipline has been executed, we can get all the input data values (resp. output data values) of the last execution by means of the MDODiscipline.get_all_inputs()
method (resp. MDODiscipline.get_all_outputs()
method), e.g.
sellar_system.execute()
sellar_system.get_all_inputs()
sellar_system.get_all_outputs()
which results in:
[array([ 1.+0.j]), array([ 1.+0.j, 0.+0.j]), array([ 1.+0.j]), array([ 0.+0.j])]
[array([ 2.16+0.j]), array([-23.+0.j]), array([ 1.36787944+0.j])]
The list
returned by sellar_system.get_all_inputs()
(resp. sellar_system.get_all_outputs()
) is sorted according to the order in sellar_system.get_input_data_names()
(resp. sellar_system.get_output_data_names()
).
All input or output data values as a large array¶
This list
of NumPy arrays can be converted into a large NumPy array by means of MDODiscipline.get_inputs_asarray()
method (resp. MDODiscipline.get_outputs_asarray()
), e.g.
sellar_system.execute()
sellar_system.get_inputs_asarray()
sellar_system.get_outputs_asarray()
which results in:
array([ 1.+0.j, 1.+0.j, 0.+0.j, 1.+0.j, 0.+0.j])
array([ 2.16000000+0.j, -23.00000000+0.j, 1.36787944+0.j])
All input or output data values as a dictionary¶
The same result can be obtained with a dict
format by means of the MDODiscipline.get_input_data()
and MDODiscipline.get_output_data()
methods:
sellar_system.execute()
sellar_system.get_input_data()
sellar_system.get_output_data()
which results in:
{'x_local': array([ 0.+0.j]), 'x_shared': array([ 1.+0.j, 0.+0.j]), 'y_1': array([ 1.+0.j]), 'y_0': array([ 1.+0.j])}
{'c_1': array([ 2.16+0.j]), 'c_2': array([-23.+0.j]), 'obj': array([ 1.36787944+0.j])}
Some input or output data values as a list¶
We can also get the data value for a given variable name or a given list
of variable names by means of the MDODiscipline.get_inputs_by_name()
or MDODiscipline.get_outputs_by_name()
method.
How to get any local data value?¶
Once the discipline has been executed, we can get the value of any variable or list
of variables (inputs, outputs and others)
stored in the MDODiscipline.local_data
attribute
by means of the MDODiscipline.get_local_data_by_name()
method, e.g.
sellar_system.execute()
sellar_system.get_local_data_by_name('obj')
sellar_system.get_local_data_by_name(['obj', 'x_shared'])
which results in:
array([ 1.36787944+0.j])
[array([ 1.36787944+0.j]), array([ 1.+0.j, 0.+0.j])]
How to store data in the MDODiscipline.local_data
attribute?¶
We can store data in the MDODiscipline.local_data
attribute
by means of the MDODiscipline.store_local_data()
method
whose arguments are the names of the variables to store. We can store either data for variables
from input or output grammars, or data for other variables, e.g.:
print(sellar_system.local_data)
{'obj': array([ 1.36787944+0.j]), 'y_2': array([ 1.+0.j]), 'y_1': array([ 1.+0.j]), 'c_1': array([ 2.16+0.j]), 'c_2': array([-23.+0.j]), 'x_shared': array([ 1.+0.j, 0.+0.j]), 'x_local': array([ 0.+0.j])}
sellar_system.store_local_data(**{'obj': array([1.]), 'new_variable': 'value'})
{'obj': array([ 1.]), 'new_variable': 'value', 'y_2': array([ 1.+0.j]), 'y_1': array([ 1.+0.j]), 'c_1': array([ 2.16+0.j]), 'c_2': array([-23.+0.j]), 'x_shared': array([ 1.+0.j, 0.+0.j]), 'x_local': array([ 0.+0.j])}