
Security News
Meet Socket at Black Hat Europe and BSides London 2025
Socket is heading to London! Stop by our booth or schedule a meeting to see what we've been working on.
The primary goal of CoSApp is to help technical departments in the design of complex systems. To do so, the framework allows the simulation of various systems representing the different parts of the final product in a common environment. The benefit is the ability for each subsystem team to carry out design study with a direct feedback at product level.
The main features are:
Couple your simulation models with CoSApp to get immediate impact on main product variables and iterate to converge on a better design.
All systems can share design parameters associated with an acceptable range. You can take advantage of those limited degrees of freedom without fear of breaking your collaborators' work.
CoSApp solvers can be combined into versatile, customized workflows that fit specific simulation intents.
Have a look at the introduction, containing many tutorials!
This code is the property of Safran SA. It uses code coming from various open-source projects (see LICENSE file).
If you use CoSApp, please cite us!
Lac et al. (2024), CoSApp: a Python library to create, simulate and design complex systems, Journal of Open Source Software 9(94), 6292.
BibTeX entry:
@article{Lac.etal:joss2024,
author={Étienne Lac and Guy {De Spiegeleer} and Adrien Delsalle and Frédéric Collonval and Duc-Trung Lê and Mathias Malandain},
title={CoSApp: a Python library to create, simulate and design complex systems},
journal={Journal of Open Source Software},
year={2024},
volume={9},
number={94},
pages={6292},
doi={10.21105/joss.06292},
publisher={The Open Journal}
}
Run a Jupyter Lab instance with binder to try out CoSApp features through examples.
Deprecation of methods NonLinearSolver.extend and RunSingleCase.extend, replaced by new methods add_problem (MR #430).
Declaring integer-valued numpy arrays no longer raises a warning (MR #428).
Harmonize solver option "history" between NonLinearSolver and Optimizer (MR #432).
Optimizer option "monitored" is now deprecated, in favour of "history".
Moreover, option "history" can now be set at recorder creation:
solver = system.add_solver(NonLinearSolver("solver"))
solver.add_recorder(DataFrameRecorder(...), history=True)
numpy arrays in mathematical algorithms (MR #433).cosapp.systems.batch_run and support class BatchRunner to obtain the response surface of a system over a set of test points, with multiprocessing support (MRs #423, #424).System.make_surrogate (MR #422).data for recorders (MR #421).System.get_variable to obtain a variable object (containing unit, description and so on) from its contextual name in a system (MR #417).BasePort.get_details, replaced by methods get_variable and variable_dict (MR #420).System.problem in the scope of method setup_run (MR #419).System.call_setup_run, to make sure that the system tree is setup before drivers are (MR #418).system_update_signal for all time drivers, emitted at each time step (MR #406).MonteCarlo (MR #409).EventError in module cosapp.multimode (MR #407).TwoPointCubicPolynomial (MR #404).CrankNicolson (MRs #395, #397).state_io functions (MR #398).The parallelization of certain drivers was implemented by Adrien Delsalle and Gaétan Laurens (MRs #330, #332, #334, #335, #339, #341, #345, #348, #353, #357, #365, #374, #390):
LinearDoE and MonteCarlo;NonLinearSolver (computation of the Jacobian matrix in parallel).This major feature relies on a robust serialization of systems, drivers and recorders, using either pickle or json.
The serialization of all drivers, in particular, allows for the parallel execution of the drivers listed above, even when they contain sub-drivers.
In the example below, using the Ballistics system from the advanced time driver tutorial, we compute the initial velocity leading to a targetted end point, after an imposed flight time of two seconds.
This design case implies a RungeKutta time driver, embedded into a NonLinearSolver driver.
For the latter, we request a parallel computation of the Jacobian matrix by forward finite differences, using three workers.
from cosapp.drivers import NonLinearSolver, RungeKutta
from cosapp.core.numerics.solve import FfdJacobianEvaluation
from cosapp.utils.execution import ExecutionPolicy, ExecutionType
# Set test case
point = Ballistics("point")
ncpus = 3 # compute the Jacobian matrix using 3 workers
solver = point.add_driver(
NonLinearSolver(
"solver",
jac=FfdJacobianEvaluation(
execution_policy=ExecutionPolicy(ncpus, ExecutionType.MULTI_PROCESSING)
),
)
)
driver = solver.add_child(RungeKutta(time_interval=(0, 2), dt=0.1, order=3))
# Set design problem: compute initial velocity that leads to a target point
solver.add_unknown("v0").add_equation("x == [10, 0, 0]")
# Define a time simulation scenario
driver.set_scenario(
init={"x": [0, 0, 0], "v": "v0"},
values={"point.mass": 1.5, "point.k": 0.9},
)
# Set initial guess & solve
point.v0 = np.ones(3)
point.run_drivers()
CrankNicolson (MRs #377).
This driver essentially works like a nonlinear solver, solving for both transient variables and intrinsic unknowns of the system of interest.
Therefore, it is highly recommended for dynamic systems containing algebraic loops or intrinsic constraints, instead of nesting a nonlinear solver inside an explicit time driver.numpy.where and logspace to the scope of evaluable expressions, used in equations and boundary conditions, e.g. (MR #382).EvalString serialization (MR #383).MonteCarlo with a time subdriver (MRs #378, #392).numpy 2, with retrocompatibility with version 1 (MRs #309, #387).pytest 8.3 was restored (MR #388).list[str] or dict[str, float] (instead of typing.List and typing.Dict), introduced in Python 3.9.ExplicitTimeDriver.set_scenario (MR #363).PeriodicEvent.tick (MR #366).Improve the performance of unknowns and residues (MRs #350, #352, by Gaétan Laurens). In particular, this version introduces the possiblity to declare as unknown any settable attribute of an input object (that is an object contained in an input port), which until now was only possible for input port variables. Example:
from cosapp.base import System
from cosapp.drivers import NonLinearSolver
from dataclasses import dataclass
@dataclass
class Foo:
a: float
x: float
class SomeSystem(System):
def setup(self):
self.add_inward("foo", Foo(a=1.0, x=1.0))
self.add_outward("y", 0.0)
def compute(self):
self.y = self.foo.a - self.foo.x**3
system = SomeSystem("system")
solver = system.add_driver(NonLinearSolver("solver"))
solver.add_unknown("foo.x") # attribute `x` can now be manipulated by the solver
solver.add_equation("y == 0")
system.foo.a = -2.0
system.run_drivers()
import pytest, math
assert system.foo.x == pytest.approx(math.cbrt(system.foo.a))
Fix bug in TwoPointCubicInterpolator with multidimensional arrays (MR #355).
Allow filtered events in merged events (MR #356):
system.event.trigger = Event.merge(
event_a,
event_b.filter("x > 0"),
)
Introduction of periodic events (MR #360):
from cosapp.multimode import PeriodicTrigger
system.event_a.trigger = PeriodicTrigger(period=2.3)
system.event_b.trigger = PeriodicTrigger(period=0.25, t0=0.33)
asv_runner in main README file (MR #358).NonLinearSolver, to facilitate convergence path analysis (MR #312).FixedPointSolver, solving algebraic loops by fixed-point iterations (MRs #315 & #322).Driver.available_options, returning the list of options available for a particular driver (MR #316).System.pull_design_method, to promote sub-system design methods at parent level easily (MR #314).System.init_mode called before each time simulation, for mode initialization (MR #347).FixedPointSolver in the driver tutorial (MR #346).swap_system (MR #342).A JupyterLite image including CoSApp is now available in the main README file (MR #300).
Fix a bug with primary event initialization (MR #293).
Improved API for partial connections: name mappings can now be given as lists mixing variable names and dictionaries, which is convenient when most variable names are identical, and only a few differ (MRs #294, #295 & #306). Example:
from cosapp.base import System
class SomeSystem(System):
def setup(self):
foo = self.add_child(Foo('foo'), pulling=['a', 'b', {'c': 'c_foo'}])
bar = self.add_child(Bar('bar'))
self.connect(foo, bar, ['x', 'y', {'z': 'v'}])
New utility function cosapp.tools.views.show_tree displaying the hierarchical tree of a system similar to a folder tree in a filesystem (MRs #296-#298).
Residue.variables providing the names of the variables involved in the residue (MR #299).numpy v1, until full migration to v2 (MR #310).self.problem.clear() during transitions, for instance, no longer affects time-dependent unknowns such as transients.RunSingleCase (MR #279).pytest version due to a bug in version 8.1 (MR #280).sphinx < 7.3 in the documentation building environment, owing to an incompatibility with sphinx-mdinclude (MR #289). This is a temporary patch until root cause is fixed.NonLinearSolver for systems with rates (MR #268).Publication of an article on CoSApp in the Journal of Open-Source Software, referenced to version 0.15.4 (MR #271).
NonLinearSolver (MR #261).System (MR #259).pythonfmu < 0.6.3 in dependency list, to prevent a crash during tests (MR #262). Temporary fix until root cause is identified.VisJs rendering of sub-systems (MR #252).EvalString (MR #253).TypeError when passing pulling argument as a tuple in System.add_child (MR #256).MathematicalProblem objects now indicates the number of unknowns and equations, for a better readability (MR #249).execution_index in System.add_child and Driver.add_child can now take a negative value, with a behaviour following that of list.insert (MR #250).ExplicitTimeRecorder.event_data, when no recorder is set (MR #245).NonLinearSolver with NumPy array residues (MR #248).RunSingleCase subdriver runner in NonLinearSolver drivers (MR #239).swap_system to replace on the fly a subsystem by another System instance (MR #238).Improved performance, through a revised clean/dirty mechanism (MR #215).
Possibility to add a contextual description to sub-systems and ports of a system, as well as sub-drivers (MR #216). This feature is useful for automatic documentation tools, and has been included in the Markdown representation of systems (also used in function cosapp.tools.display_doc). Example:
from cosapp.base import System
from my_module import FlowPort
class MySystem(System):
def setup(self):
self.add_input(FlowPort, "fl_in1", desc="Primary inlet flow port")
self.add_input(FlowPort, "fl_in2", desc="Secondary inlet flow port")
self.add_output(FlowPort, "fl_out")
New hook function _parse_module_config, returning pre-defined settings for cosapp.tools.parse_module (MR #218). This allows module maintainers to simply call
from cosapp.tools import parse_module
import my_module
parse_module(my_module)
instead of, e.g.,
parse_module(
my_module,
ctor_config={
"ComplexSystem1": [
dict(n=1, foo=0.5),
dict(n=2, foo=0.1),
],
"ComplexSystem2": [
dict(xi=0.0, __alias__="ComplexSystem2_a"),
dict(xi=1.0, __alias__="ComplexSystem2_b"),
],
},
excludes=["Foo*", "*Bar?"],
)
provided my_module._parse_module_config() returns a dictionary specifying the values of ctor_config, excludes, etc.
Make SolverResults a dataclass, for easier handling of NonLinearSolver.results, e.g. (MR #220).
Expose attribute problem in system setup (MR #221). Previously, problem was only exposed in method System.transition.
NonLinearSolver log message (MR #214).cosapp.tools.parse_module (MR #219).cosapp.tools.display_doc for classes with setup arguments (MR #208).Note: extensive use of self-documenting f-strings (introduced in Python 3.8) has made tutorials incompatible with Python 3.7.
parse_module in cosapp.tools collecting all system and port classes within a Python module. This parser, generating a JSON file containing the description of all CoSApp symbols, is primarily meant to be used for constructional GUI applications, developed separately (MRs #192 & #209).The code is now tested for Python 3.8, 3.9 and 3.10. Support of Python 3.7 is thus officially dropped, although no version-specific Python code was introduced in this version of CoSApp.
Module connectors moved from cosapp.core to cosapp.ports (MR #189).
New "direct" (with no unit conversion) connector classes PlainConnector, CopyConnector and DeepCopyConnector, in cosapp.ports.connectors (MR #188).
New method MathematicalProblem.is_empty(), equivalent to shape == (0, 0) (MR #186).
Improved VisJs graph rendering, by limiting node size for long system names (MR #184).
New utility functions get_state and set_state in cosapp.utils, for quick system data recovery (MR #193):
from cosapp.utils import get_state, set_state
s = SomeSystem('s')
# ... many design steps, say
# Save state in local object
designed = get_state(s)
s.drivers.clear()
s.add_driver(SomeDriver('driver'))
try:
s.run_drivers()
except:
# Recover previous state
set_state(s, designed)
Functions radians, degrees and arctan2/atan2 have been added to the scope of EvalString objects, and can therefore be used in equations, e.g. (MR #178).
Recorders can now record constant properties (MR #181).
Deprecation of System.get_unsolved_problem in favour of new method assembled_problem (MR #174).
Inner off-design problem of systems is now exposed as attribute problem, but only within the transition method (MR #174). This allows users to add or remove off-design constraints during event-driven transitions, while keeping this property inaccessible the rest of the time.
from cosapp.base import System
from math import sin, cos
class SomeSystem(System):
def setup(self):
self.add_inward('x', 0.0)
self.add_inward('y', 0.0)
self.add_outward('z', 0.0)
a = self.add_event('event_a', trigger='x > y')
b = self.add_event('event_b', trigger='x < y')
def compute(self):
self.z = cos(self.x) * sin(self.y)
def transition(self):
offdesign = self.problem
if self.event_a.present:
offdesign.clear()
offdesign.add_equation('z == 0.5').add_unknown('x')
if self.event_b.present:
offdesign.clear()
None variables (MR #172).System.add_child when a pulling error is raised (MR #197).System.new_problem to facilitate the creation of dynamic design methods, e.g. (MR #162).NonLinearSolver (MRs #148 and #152).Optimizer.set_objective is deprecated, in favour of set_minimum and set_maximum (MR #150).System.add_child, add_driver, etc. (MR #145).jupyterlab 3.4 (MR #149).pytest 7.1 (MR #142).Simplification of driver Optimizer:
runner (MR #136). This change introduces new methods set_objective, add_unknowns and add_constraints in driver Optimizer.Optimizer.add_constraints (MR #138).Before:
from cosapp.drivers import Optimizer
s = SomeSystem('s')
optim = s.add_driver(Optimizer('optim'))
optim.runner.set_objective('cost')
optim.runner.add_unknown(['a', 'b', 'p_in.x'])
# Enter constraints as non-negative expressions:
optim.runner.add_constraints([
"b - a", # b >= a
"a", # a >= 0
"1 - a", # a <= 1
])
optim.runner.add_constraints(
"p_out.y",
inequality = False, # p_out.y == 0
)
s.run_drivers()
After:
optim.set_objective('cost')
optim.add_unknown(['a', 'b', 'p_in.x'])
optim.add_constraints(
"b >= a",
"0 <= a <= 1",
"p_out.y == 0",
)
from cosapp.base import Port, System
class XyzPort(Port):
def setup(self):
self.add_variable('x')
self.add_variable('y')
self.add_variable('z')
class SomeSystem(System):
def setup(self):
self.add_input(XyzPort, 'p_in')
self.add_output(XyzPort, 'p_out')
def compute(self):
self.p_out.set_from(self.p_in) # assign values from `p_in`
self.p_out.z = 0.0
s = SomeSystem('s')
# Multi-variable setter `set_values`
s.p_in.set_values(x=1, y=-0.5, z=0.1)
s.run_once()
# Dict-like (key, value) iterator `items`:
for varname, value in s.p_out.items():
print(f"p_out.{varname} = {value})
RunOnce and RunSingleCase recorders with hold=False (MR #130).Implementation of multimode systems and hybrid continuous/discrete time solver (MRs #100, #103, #105-#108, #110-#121):
System.transition describing system transition upon the occurrence of events.ExplicitTimeDriver.Possibility to specify a stop criterion in time simulation scenarios (MR #107).
New module cosapp.base (MR #96) containing base classes for user-defined classes (in particular, Port, System and Driver). Also contains BaseConnector, base class for custom connectors (see "User-defined and peer-to-peer connectors" below), as well as CoSApp-specific exceptions ScopeError, UnitError and ConnectorError.
Note: Port, System and Driver can still be imported from cosapp.ports, cosapp.systems and cosapp.drivers, respectively.
Public API cosapp.base.SurrogateModel to define custom surrogate models used in System.make_surrogate (MR #97).
Pre-defined models have been moved to module cosapp.utils.surrogate_models.
System-to-system connections (MR #94).
class LegacyPortToPort(System):
def setup(self):
a = self.add_child(ModelA('a'))
b = self.add_child(ModelB('b'))
# Explicit port-to-port connections
self.connect(a.p_in, b.p_out)
self.connect(a.outwards, b.inwards, {'y': 'x'})
class Alternative(System):
"""Same as `LegacyPortToPort`, with alternative connection syntax"""
def setup(self):
a = self.add_child(ModelA('a'))
b = self.add_child(ModelB('b'))
# Alternative syntax: connect systems, with port or variable mapping
self.connect(a, b, {'p_in': 'p_out', 'y': 'x'})
import numpy
from copy import deepcopy
from cosapp.base import Port, System, BaseConnector
class DeepCopyConnector(BaseConnector):
"""User-defined deep-copy connector"""
def transfer(self) -> None:
source, sink = self.source, self.sink
for target, origin in self.mapping.items():
value = getattr(source, origin)
setattr(sink, target, deepcopy(value))
class CustomPort(Port):
def setup(self):
self.add_variable('x', 0.0)
self.add_variable('y', 1.0)
class Connector(BaseConnector):
"""Connector for peer-to-peer connections"""
def transfer(self) -> None:
source, sink = self.source, self.sink
sink.x = source.y
sink.y = -source.x
class MyModel(System):
def setup(self):
self.add_input(CustomPort, 'p_in')
self.add_output(CustomPort, 'p_out')
self.add_inward('entry', numpy.identity(3))
self.add_outward('exit', numpy.zeros_like(self.entry))
class Assembly(System):
def setup(self):
a = self.add_child(MyModel('a'))
b = self.add_child(MyModel('b'))
self.connect(a, b, {'exit', 'entry'}, cls=DeepCopyConnector)
self.connect(a.p_in, b.p_out) # will use CustomPort.Connector
engine = Turbofan('engine')
solver = engine.add_driver(NonLinearSolver('solver'))
# Add design points:
takeoff = solver.add_child(RunSingleCase('takeoff'))
cruise = solver.add_child(RunSingleCase('cruise'))
# Unknowns defined at solver level regarded as *design* unknowns
solver.add_unknown(['fan.diameter', 'core.turbine.inlet.area'])
# Local off-design equations can be directly defined at case level
takeoff.add_equation('thrust == 1.2e5')
cruise.add_equation('Mach == 0.8')
tree() for systems and drivers, yielding all elements in a composite tree (MR #68).head = CompositeSystem('head')
bottom_to_top = [s.name for s in head.tree()]
top_to_bottom = [s.name for s in head.tree(downwards=True)]
from cosapp.patterns.visitor import Visitor, send as send_visitor
class DataCollector(Visitor):
def __init__(self):
self.data = {}
def visit_system(self, system):
key = system.full_name()
self.data.setdefault(key, {})
self.data[key]['children'] = [
child.name for child in system.children.values()
]
send_visitor(self, system.inputs.values())
def visit_port(self, port):
# specify what to do with a port
def visit_driver(self, driver):
# specify what to do with a driver
head = CompositeSystem('head')
collector = DataCollector()
send_visitor(collector, head.tree())
print(collector.data)
System.exec_order a view on System.children dictionary keys, rather than an independent attribute (MR #70). Execution order can still be specified, via a dedicated setter for exec_order.add_target (MR #61).jac is None after converting it as a numpy array (MR #56).PortMarkdownFormatter (MR #59).New binder container, allowing anyone to run interactively the tutorials used in the online documentation (MR #30 and #36).
Deferred equations to set targets:
New method add_target, defining a deferred equation on on a target variable (MR #48).
In effect, add_target creates an equation whose right-hand side is evaluated dynamically prior to each execution of the nonlienar solver.
In the example below, the feature is illustrated in design mode. Outward z is a function of two independent variables x and y.
When design method 'target_z' is activated, the actual value of z, set interactively, is used as a target value, with unknown y:
class SystemWithTarget(System):
def setup(self):
self.add_inward('x', 1.0)
self.add_inward('y', 1.0)
self.add_outward('z', 1.0)
# Define design problem with a target on `z`
design = self.add_design_method('target_z')
design.add_unknown('y').add_target('z')
def compute(self):
self.z = self.x * self.y**2
s = SystemWithTarget('s')
solver = s.add_driver(NonLinearSolver('solver', tol=1e-9))
# Activate design method 'target_z': outward `z` becomes a target
solver.runner.design.extend(s.design_methods['target_z'])
s.x = 0.5
s.y = 0.5
s.z = 2.0 # set target
s.run_drivers()
assert s.y == pytest.approx(2) # solution of x * y**2 == 2
assert s.z == pytest.approx(2)
s.z = 4.0 # dynamically set new target
s.run_drivers()
assert s.y == pytest.approx(np.sqrt(8)) # solution of x * y**2 == 4
assert s.z == pytest.approx(4)
Targets can be also be declared in off-design mode, by calling self.add_target(...) in System.setup.
System, Driver, and design methods (MR #41).time in DataFrame recorders attached to a time driver (MR #34).MonteCarlo driver (MR #37).conda by mamba in CI scripts (MR #39).rate type inference (MR #44).point = PointMass('point')
driver = point.add_driver(RungeKutta(order=3, time_interval=(0, 2), dt=0.01))
recorder = driver.add_recorder(recorders.DataFrameRecorder(
includes=['x', 'a', 'norm(v)']), # norm(v) will be recorded in DataFrame
period=0.1,
)
SystemSurrogate (MR #15).NumericalSolver (MR #22).ArithmeticError when an unknown is declared several time (MR #18).numpy (MR #20 and #24).numpy in NonLinearSolver (MR #19).pandas and xlrd (MR #21).System.make_surrogate (MR #3 and #12):plane = Aeroplane('plane') # system with subsystems engine1 and engine2
# Say engine systems have one input parameter `fuel_rate`
# and possibly several outputs, and many sub-systems
# Create training schedule for input data
doe = pandas.DataFrame(
# loads of input data
columns=['fuel_rate', 'fan.diameter', ..] # input names
)
plane.engine1.make_surrogate(doe) # generates output data and train model
plane.run_once() # executes the surrogate model of `engine1` instead of original compute()
# dump model to file
plane.engine1.dump_surrogate('engine.bin')
# load model into `engine2`:
plane.engine2.load_surrogate('engine.bin')
# deactivate surrogate model on demand
plane.engine1.active_surrogate = plane.engine2.active_surrogate = False
Add several US-common unit conversions (MR #2).
New method to export cosapp system structure into a dictionary (MR #5)
Make recorders capture port and system properties (MR #8).
Fix Module/System naming bug: 'inwards' and 'outwards' are allowed as Module/System names (MR #9).
Broad code quality improvement (MR #11).
typing.NoReturn by None when appropriate.DeprecationWarning raised by numpy in class Variable.str.join() for just two elements.Global rewording of tutorial notebooks, including a few error fixes.
First open-source version. No major code change; mostly updates of license files, URLs in docs, and CI scripts.
RunOnce driver, preventing undue call to run_once method.System.add_property allowing users to create read-only properties.AssignString of the kind 'x = [0, 1, 2]' won't change variable x into an array of integers, if x is declared as an array of floats.TimeStackUnknown not able to stack transient variables defined on a children System or with partially pulled transient variable.rate attributes in systems.setup_run are called.IterativeConnector (it equals 1. now)system = MySystem('something') # system with transient variables x and v
driver = system.add_driver(RungeKutta(time_interval=(0, 2), dt=0.01, order=3))
driver.set_scenario(
init = {'x': 0.5, 'v': 0}, # initial conditions
values =
{
'omega': 0.7,
'F_ext': '0.6 * cos(omega * t)' # explicit time-dependency
}
)
DEBUG level will now display the call stack through the systems and driversTimeDriver notebook in tutorials).Ports:
add_variable("x", units="m", types=Number) => add_variable("x", unit="m", dtype=Number)freeze => removedunfreeze => replaced by add_unknown in Systems and Driversconnect_to => replaced by connect at system levelSystems:
time_ref is no longer an argument of method compute:
def compute(self, time_ref): => def compute(self):
Create a new connection between a.in1 and b.out:
self.a.in1.connect_to(self.b.out) => self.connect(self.a.in1, self.b.out)
add_residues => add_equation
set_numerical_default => Pass keyword to add_unknown
add_inward("x", units="m", types=Number) => add_inward("x", unit="m", dtype=Number)
add_outward("x", units="m", types=Number) => add_outward("x", unit="m", dtype=Number)
Drivers:
add_unknowns(maximal_absolute_step, maximal_relative_step, low_bound, high_bound) => add_unknown(max_abs_step, max_rel_step, lower_bound, upper_bound)
add_equations => add_equation
Equations are now represented by a unique string, instead of two strings (left-hand-side, right-hand-side):
add_equations("a", "b") => add_equation("a == b")
add_equations([("x", "2 * y + 1"), ("a", "b")]) => add_equation(["x == 2 * y + 1", "a == b"])
For NonLinearSolver:
fatol and xtol => tol
maxiter => max_iter
For Optimizer:
ftol => tol
maxiter => max_iter
COSAPP_CONFIG_DIRMonteCarlo:
Montecarlo => MonteCarloMontecarlo.add_input_vars => MonteCarlo.add_random_variableMontecarlo.add_response_vars => MonteCarlo.add_responseMonteCarlo has been improved by using Sobol random generator
Viewers code on System is moved in a subpackage of cosapp.tools
Residue reference is now calculated only once
Various bug fix
Variable class to manage variable attributeswatchdog is now optional$HOME/.cosapp.dget_latest_solution => save_solutionload_solver_solution => load_solutionThis release introduces lots of API changes:
cosapp.portscosapp.systemscosapp.driverscosapp.recorderscosapp.toolscosapp.notebook (! this is now a separated package)data have been renamed in inwards and add_data in add_inwardlocals have been renamed in outwards and add_locals in add_outwardBaseRecorder.record_iteration renamed in BaseRecorder.record_statecosapp.notebook has been moved to an independent package cosapp_notebook. But it is still accessible from cosapp.notebook.cosapp.core.signal)
Module.setup_ran: Signal emitted after the call_setup_run executionModule.computed: Signal emitted after the full compute stack (i.e.: _postcompute)Module.clean_ran: Signal emitted after the call_clean_run executionBaseRecorder.state_recorded: Signale emitted after the record_state executionRunSingleCaseMonteCarlo driverSystem and Driver have now a common ancestor Module => Driver variables are now stored as data or localsSystem connections based on N2 graph (syntax: cosapp.viewmodel(mySystem))API changes: System.add_driver and Driver.add_child take now an instance of Driver
SystemFAQs
CoSApp, the Collaborative System Approach.
We found that cosapp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Socket is heading to London! Stop by our booth or schedule a meeting to see what we've been working on.

Security News
OWASP’s 2025 Top 10 introduces Software Supply Chain Failures as a new category, reflecting rising concern over dependency and build system risks.

Research
/Security News
Socket researchers discovered nine malicious NuGet packages that use time-delayed payloads to crash applications and corrupt industrial control systems.