Condor Design¶
Overall goals and API¶
Condor is a new mathematical modeling framework for Python, developed at NASA’s Ames Research Center. Initial development began in April 2023 to address model implementation challenges for aircraft synthesis and robust orbital trajectory design. Condor emphasizes modern approaches from the scientific python community, and leverages many open-source software packages to expedite development and ensure robust and efficient run-time.
The goal is for Condor to help evaluate numerical models and then get out of the way. One key aspect to achieve this goal was to create an API that looked as much like the mathematical description as possible with as little distraction from programming cruft as possible. For example, Sellar [sellar] introduces an arbitrary system of algebraic equations to represent coupling in multi-disciplinary analysis,
should be writable as
y1 == x[0] ** 2 + x[1] + x[2] - 0.2 * y2
y2 == y1**0.5 + x[0] + x[1]
Of course, in both the mathematic and programmatic description, the source of each
symbol must be defined. In an engineering memo, we might say “where
import condor as co
class Coupling(co.AlgebraicSystem):
x = parameter(shape=3)
y1 = variable(initializer=1.)
y2 = variable(initializer=1.)
residual(y1 == x[0] ** 2 + x[1] + x[2] - 0.2 * y2)
residual(y2 == y1**0.5 + x[0] + x[1])
which can be evaluated by instantiating the model with numerical values for hte parameter, which binds the result from the iterative solver to the named element and field attributes on the instance,
coupling = Coupling([5., 2., 1]) # evaluate the model numerically
print(coupling.y1, coupling.y2) # individual elements are bound numerically
print(coupling.variable) # fields are bound as a dataclass
This pythonic datastructure allows Condor to be integrated into larger analysis workflows with as little Condor-specific coding as possible.
Condor uses metaprogramming to to turn the class declaration mechanism into a blackboard-like environment to achieve the desired API. This approach helps us see these mathematical models as datastructures that can then be transformed as needed to automate the process that is typically performed manually for defining and evaluating mathematical models in engineering analysis,

Architecture¶
We followed modern pythonic best-practices and patterns to settle on a multi-layered architecture like the Model-View-Controller paradigm in web development. The three key components of the architecture are:
The model layer, which provides an API for users to write their model. Condor models are ultimately a data structure which represents the represents the user’s mathematical intent for the model.
The backend layer provides a consistent interface to a third party Computational Engine, a symbolic-computational library which provides symbolic representation of elements and operations with awareness for basic differential calculus. The goal for the backend is provide a thin wrapper with a consistent interface so the computational engine implementation could be swapped out. Currently, we ship with CasADi as the only engine, although we hope to demonstrate a backend module for an alternate backend in the future.
The implementation layer is the glue code that operates on the model data structure, using the backend to form the numerical functions needed to call the third-party solvers which implement the nuemrical algorithms of interest. The implementation layer then calls the solver and binds the results to the model instance.

The Model Layer¶
Each user model is declared as a subclass of a Model Template, a class
with a
ModelType
metaclass, which defines the fields from which elements are drawn to
define the model. Condor currently ships with 5 model templates:
built-in template |
fields |
||
---|---|---|---|
input |
internal |
output |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Models can be used recursively, building up more sophisticated models by embedding models within another. However, system encapsolation is enforced so only elements from input and output fields are accessible after the model has been defined. For example, we may wish to optimize Sellar’s algebraic system of equations. Mathematically, we can define the optimization as
where
from condor import operators as ops
class Sellar(co.OptimizationProblem):
x = variable(shape=3, lower_bound=0, upper_bound=10)
coupling = Coupling(x)
y1, y2 = coupling
objective = x[2]**2 + x[1] + y1 + ops.exp(-y2)
constraint(y1 > 3.16)
constraint(24. > y2)
As with the system of algebraic equations, we can numerically solve this optimization
problem by providing an initial value for the variables and instantiating the model.
The resulting object will have a dot-able data structure with the bound results,
including the embedded Coupling
model:
Sellar.set_initial(x=[5,2,1])
sellar = Sellar()
print()
print("objective value:", sellar.objective) # scalar value
print(sellar.constraint) # field
print(sellar.coupling.y1) # embedded-model element
The built-in model types provide a useful library to build small or one-off modeling capabilities. We also ensured that there were good mechanisms for customizing models and creating new models to address repeat and sophisticated modeling tasks.
Fields and Elements¶
Fields contain the elements. Can be
Metaprogramming class declaration¶
Ionel provides a nice overview of Python3 process for class declaration and object instantiation. Relevant for us is the following call-order. For class declaration,
Metaclass.__prepare__()
creates a class dictionary at the entry of theclass
declaration.Each assignment within the class declaration uses the
__setitem__()
of the class dictionaryMetaclass.__new__()
is passed the (filled) class dictionary and creates the class viatype.__call__()
. Note thatMetaclass.__init__()
is also called after this but is not as useful because theclass
is already fully constructed by this point; the__init__
can only be used to organize post-processing.
Is there any shared flow between a Template and Model? Yes, show Template first then Model.
ModelTemplate
declaration¶
Annotated flow of how a ModelTemplate
is created
Model
declaration¶
Annotated flow of how a Model
is created –
Calling and binding¶
the __class__
is __call__()
ed which calls the __new__()
which creates
the self
object which is then passed to __init__()
.
The condor.Model.__init__()
parses the positional and keyword arguments to bind
the values for the input field(s). Then the
The implementation layer¶
The implementation layer is responsible for using the backend to create the numerical functions needed to evaluate model and call any solvers as needed.
The embedded Options
class inside a model provides a name-space.
Attributes without a leading underscore are placed into a dict
for
the keyword arguments to the implementation’s construct()
method. Special behavior for _implementation
and …
Solver options can be passed from throught he Options attribute. Ultimately it is the implementations job to parse the Options, but except where different solvers for the same model-type conflict the intention is to make the argument manipulation at the implementation layer as thin as possible.
the Options
can be considered model inputs that make sense to have a default. They
are also intended to be inputs that don’t define the mathematical meaning of the model.
The backend¶
The backend layer provides a common interface to potential
“computational engine” libraries. Currently, we support only the
CasADi engine. Condor uses a “shim” so that the capability needed by the computational
engine can be accessed from the same import within the library. For each engine, a
backends
module must be provided to adapt the engine to a common API.
Using Condor for a “tool” or library¶
Useful engineering analysis tools can be built as a Python library simply by
constructing the desired model witht he contrib
models. Since the Model is
defined by constructing a class
, Python class variable scoping prevents the dynamic
definition of models inside a factory function. To get around this, a “configuration” pattern
was defined with a dynamic_link()
helper. The Systems Analysis Office at NASA’s Ames
Research Center has used this approach to build an aircraft synthesis and analysis tool using
Condor.
More recently, the metaprogramming back-bone of Condor was refactored to facilitate
the customization of symbolic processing to facilitate the creation of custom
ModelTemplate
s. To create a new type of analysis tool, we now recommend leveraging
this capability. A design process might include:
Identify the data required to specify the analysis, and identify the
Field
(or create a customField
) that would be appropriate for holding that dataIdentify (or create) what solver and implementation is needed, including a mapping from the new type of Model to the an existing model or solver.
Implement a
process_placeholder()
for processing the models data so the implementation can call the solver.
References
Sellar, R., Batill, S., and Renaud, J., “Response Surface Based, Concurrent Subspace Optimization for Multidisciplinary System Design,” 1996. https://doi.org/10.2514/6.1996-714