From 10eb1635c532a0feb5bb5d3f4775fd043c3fdf29 Mon Sep 17 00:00:00 2001 From: Luca Miguel Date: Wed, 23 Aug 2023 16:02:49 +0200 Subject: [PATCH] Readded fmu controller --- rocket_twin/systems/__init__.py | 3 +- rocket_twin/systems/control/__init__.py | 3 +- rocket_twin/systems/control/controller.mo | 14 +-- .../systems/control/controller_cosapp.py | 2 +- rocket_twin/systems/control/controller_fmu.py | 27 +----- .../systems/control/rocket_controller.mo | 49 ++++++++-- .../control/rocket_controller_cosapp.py | 9 +- .../systems/control/rocket_controller_fmu.py | 90 +++++++++++++++++++ rocket_twin/systems/rocket/rocket.py | 2 +- rocket_twin/systems/station/station.py | 2 +- rocket_twin/tests/test_controller_cosapp.py | 6 +- rocket_twin/tests/test_controller_fmu.py | 56 ------------ rocket_twin/tests/test_sequences.py | 10 +-- rocket_twin/tests/test_stage.py | 73 ++++++++------- test_controller_fmu.py | 64 +++++++++++++ 15 files changed, 256 insertions(+), 154 deletions(-) create mode 100644 rocket_twin/systems/control/rocket_controller_fmu.py delete mode 100644 rocket_twin/tests/test_controller_fmu.py create mode 100644 test_controller_fmu.py diff --git a/rocket_twin/systems/__init__.py b/rocket_twin/systems/__init__.py index 832183e..4506d75 100644 --- a/rocket_twin/systems/__init__.py +++ b/rocket_twin/systems/__init__.py @@ -1,4 +1,4 @@ -from rocket_twin.systems.control import ControllerCoSApp, RocketControllerCoSApp, ControllerFMU +from rocket_twin.systems.control import ControllerCoSApp, RocketControllerCoSApp, ControllerFMU, RocketControllerFMU from rocket_twin.systems.engine import Engine, EngineGeom, EnginePerfo from rocket_twin.systems.ground import Ground from rocket_twin.systems.physics import Dynamics @@ -24,6 +24,7 @@ "ControllerCoSApp", "RocketControllerCoSApp", "ControllerFMU", + "RocketControllerFMU", "NoseGeom", "TubeGeom", "WingsGeom", diff --git a/rocket_twin/systems/control/__init__.py b/rocket_twin/systems/control/__init__.py index d1fc3cf..19c02b9 100644 --- a/rocket_twin/systems/control/__init__.py +++ b/rocket_twin/systems/control/__init__.py @@ -2,5 +2,6 @@ from rocket_twin.systems.control.rocket_controller_cosapp import RocketControllerCoSApp from rocket_twin.systems.control.controller_fmu import ControllerFMU # isort: skip +from rocket_twin.systems.control.rocket_controller_fmu import RocketControllerFMU # isort: skip -__all__ = ["ControllerCoSApp", "ControllerFMU", "RocketControllerCoSApp"] +__all__ = ["ControllerCoSApp", "ControllerFMU", "RocketControllerCoSApp", "RocketControllerFMU"] diff --git a/rocket_twin/systems/control/controller.mo b/rocket_twin/systems/control/controller.mo index e7064ba..003162d 100644 --- a/rocket_twin/systems/control/controller.mo +++ b/rocket_twin/systems/control/controller.mo @@ -1,18 +1,8 @@ model controller - input Real ti; - input Real weight; - parameter Real weight_max; - parameter Real tl; + input Real is_on; output Real w; - output Boolean engine_on; equation - if (ti < tl) then - engine_on = false; - else - engine_on = true; - end if; - - if (weight < weight_max and engine_on == false) then + if (is_on > 0.5) then w = 1.; else w = 0.; diff --git a/rocket_twin/systems/control/controller_cosapp.py b/rocket_twin/systems/control/controller_cosapp.py index 1866668..6578724 100644 --- a/rocket_twin/systems/control/controller_cosapp.py +++ b/rocket_twin/systems/control/controller_cosapp.py @@ -15,7 +15,7 @@ class ControllerCoSApp(System): def setup(self): - self.add_inward_modevar("is_on", 0, desc="Whether the fuel flow is allowed or not") + self.add_inward("is_on", 0, desc="Whether the fuel flow is allowed or not") self.add_outward("w", 1., desc="Ratio of command fuel flow to maximum flow", unit="") diff --git a/rocket_twin/systems/control/controller_fmu.py b/rocket_twin/systems/control/controller_fmu.py index 8fd9399..9e76b02 100644 --- a/rocket_twin/systems/control/controller_fmu.py +++ b/rocket_twin/systems/control/controller_fmu.py @@ -27,37 +27,12 @@ class ControllerFMU(System): def setup(self, model_path, model_name): - self.add_inward("time_var", 0.0, desc="System time", unit="") - self.add_inward("time_int", 0.0, desc="Interval between fueling end and launch", unit="") - self.add_inward("time_lnc", 100000.0, desc="Launch time", unit="") - self.add_transient("x", der="1") - - pulling = { - "w": "w", - "weight": "weight_prop", - "weight_max": "weight_max", - "tl": "time_lnc", - "ti": "time_var", - } - fmu_path = self.create_fmu(model_path, model_name) self.add_child( FMUSystem("fmu_controller", fmu_path=fmu_path), - pulling=pulling, + pulling=["w", "is_on"], ) - self.add_event("full_tank", trigger="weight_prop > 0.9999*weight_max") - - def compute(self): - - self.time_var = self.time - - def transition(self): - - if self.full_tank.present: - - self.time_lnc = self.time_var + self.time_int - def create_fmu(self, model_path, model_name): """Create an fmu file in the control folder from an mo file. diff --git a/rocket_twin/systems/control/rocket_controller.mo b/rocket_twin/systems/control/rocket_controller.mo index bf47c65..e522eba 100644 --- a/rocket_twin/systems/control/rocket_controller.mo +++ b/rocket_twin/systems/control/rocket_controller.mo @@ -1,21 +1,52 @@ model rocket_controller input Real ti; - input Real weight; - parameter Real weight_max; + input Real weight_1; + input Real weight_2; + input Real weight_3; parameter Real tl; - output Real w; - output Boolean engine_on; + parameter Real weight_max_1; + parameter Real weight_max_2; + parameter Real weight_max_3; + output Real is_on_1; + output Real is_on_2; + output Real is_on_3; + output Real fueling; + output Real flying; equation + if (flying < 0.5 and weight_3 < weight_max_3) then + fueling = 1.; + else + fueling = 0.; + end if; + if (ti < tl) then - engine_on = false; + flying = 0.; else - engine_on = true; + flying = 1.; end if; - if (weight > 0. and engine_on == true) then - w = 1.; + if (flying < 0.5) then + is_on_1 = 0.; + is_on_2 = 0.; + is_on_3 = 0.; else - w = 0.; + if (weight_1 >= 0.1) then + is_on_1 = 1.; + is_on_2 = 0.; + is_on_3 = 0.; + elseif (weight_2 >= 0.1) then + is_on_1 = 0.; + is_on_2 = 1.; + is_on_3 = 0.; + elseif (weight_3 >= 0.1) then + is_on_1 = 0.; + is_on_2 = 0.; + is_on_3 = 1.; + else + is_on_1 = 0.; + is_on_2 = 0.; + is_on_3 = 0.; + end if; end if; end rocket_controller; diff --git a/rocket_twin/systems/control/rocket_controller_cosapp.py b/rocket_twin/systems/control/rocket_controller_cosapp.py index 42e9059..f344158 100644 --- a/rocket_twin/systems/control/rocket_controller_cosapp.py +++ b/rocket_twin/systems/control/rocket_controller_cosapp.py @@ -10,16 +10,15 @@ def setup(self, n_stages): for i in range(1, n_stages + 1): self.add_inward(f"weight_prop_{i}", 0., desc=f"Stage {i} propellant weight", unit='kg') self.add_inward(f"weight_max_{i}", 1., desc=f"Stage {i} maximum propellant weight", unit='kg') - self.add_outward_modevar(f"is_on_{i}", 0, desc=f"Whether the stage {i} is on or not") + self.add_outward(f"is_on_{i}", 0, desc=f"Whether the stage {i} is on or not") - self.add_inward("time_int", 0., desc="Interval between fueling end and launch", unit='s') + self.add_inward("time_int", 5., desc="Interval between fueling end and launch", unit='s') self.add_inward("time_lnc", 100000., desc="Launch time", unit='s') - self.add_outward_modevar("fueling", 1, desc="Whether the rocket is fueling or not") - self.add_outward_modevar("flying", 0, desc="Whether the rocket is flying or not") + self.add_outward("fueling", 1, desc="Whether the rocket is fueling or not") + self.add_outward("flying", 0, desc="Whether the rocket is flying or not") self.add_event("full", trigger = "weight_prop_1 == weight_max_1") - self.add_event("fuel_end", trigger=f"weight_prop_{n_stages} == weight_max_{n_stages}") self.add_event("launch", trigger="t == time_lnc") self.add_event("drop", trigger="weight_prop_1 == 0.") diff --git a/rocket_twin/systems/control/rocket_controller_fmu.py b/rocket_twin/systems/control/rocket_controller_fmu.py new file mode 100644 index 0000000..84547a4 --- /dev/null +++ b/rocket_twin/systems/control/rocket_controller_fmu.py @@ -0,0 +1,90 @@ +import os + +from cosapp.base import System +from cosapp_fmu.FMUsystem import FMUSystem +from OMPython import ModelicaSystem + +import rocket_twin.systems.control + +class RocketControllerFMU(System): + + def setup(self, n_stages, model_path, model_name): + + self.add_inward("n_stages", n_stages, desc="number of stages") + self.add_inward("stage", 1, desc="Current active stage") + + self.add_inward("time_var", 0.0, desc="System time", unit="") + self.add_inward("time_int", 0.0, desc="Interval between fueling end and launch", unit="") + self.add_inward("time_lnc", 100000.0, desc="Launch time", unit="") + self.add_transient("x", der="1") + + pulling = {"flying" : "flying", "fueling" : "fueling", "tl" : "time_lnc", "ti" : "time_var"} + + for i in range(1, n_stages + 1): + self.add_outward(f"is_on_{i}", 0, desc=f"Whether the stage {i} is on or not") + pulling[f"weight_{i}"] = f"weight_prop_{i}" + pulling[f"weight_max_{i}"] = f"weight_max_{i}" + pulling[f"is_on_{i}"] = f"is_on_{i}" + + fmu_path = self.create_fmu(model_path, model_name) + self.add_child( + FMUSystem("fmu_controller", fmu_path=fmu_path), + pulling=pulling, + ) + + self.add_event("full", trigger = "weight_prop_1 > 0.9999*weight_max_1") + self.add_event("drop", trigger="weight_prop_1 < 0.1") + + def compute(self): + + self.time_var = self.time + + def transition(self): + + if self.full.present: + if self.stage < self.n_stages: + self.stage += 1 + self.full.trigger = f"weight_prop_{self.stage} > 0.9999 * weight_max_{self.stage}" + else: + self.time_lnc = self.time + self.time_int + self.stage = 1 + + if self.drop.present: + if self.stage < self.n_stages: + self.stage += 1 + self.drop.trigger = f"weight_prop_{self.stage} < 0.1" + + def create_fmu(self, model_path, model_name): + """Create an fmu file in the control folder from an mo file. + + Inputs + ------ + model_path: string + the path of the .mo file + model_name: string + the name of the .fmu file to be created + + Outputs + ------ + + fmu: string + the path to the .fmu file + """ + + fmu_path = os.path.join(rocket_twin.systems.control.__path__[0], model_name) + model_path = os.path.join(rocket_twin.__path__[0], model_path) + model_path = model_path.replace("\\", "/") + try: + os.mkdir(fmu_path) + except OSError: + pass + os.chdir(fmu_path) + mod = ModelicaSystem(model_path, model_name) + fmu = mod.convertMo2Fmu() + for filename in os.listdir(fmu_path): + if filename != (model_name + ".fmu"): + os.remove(filename) + + return fmu + + diff --git a/rocket_twin/systems/rocket/rocket.py b/rocket_twin/systems/rocket/rocket.py index b58b904..7479a2a 100644 --- a/rocket_twin/systems/rocket/rocket.py +++ b/rocket_twin/systems/rocket/rocket.py @@ -58,7 +58,7 @@ def setup(self, n_stages=1): self.add_child(Dynamics("dyn", forces=forces, weights=["weight_rocket"]), pulling=["a"]) for i in range(1, n_stages + 1): - self.connect(self.controller.modevars_out, self[f"stage_{i}"].modevars_in, {f"is_on_{i}" : "is_on"}) + self.connect(self.controller.outwards, self[f"stage_{i}"].inwards, {f"is_on_{i}" : "is_on"}) self.connect(self[f"stage_{i}"].outwards, self.controller.inwards, {"weight_prop" : f"weight_prop_{i}", "weight_max" : f"weight_max_{i}"}) self.connect(self[f"stage_{i}"].outwards, self.geom.inwards, {"props": f"stage_{i}"}) self.connect(self[f"stage_{i}"].outwards, self.dyn.inwards, {"thrust": f"thrust_{i}"}) diff --git a/rocket_twin/systems/station/station.py b/rocket_twin/systems/station/station.py index 94d600c..204f01e 100644 --- a/rocket_twin/systems/station/station.py +++ b/rocket_twin/systems/station/station.py @@ -27,7 +27,7 @@ def setup(self, n_stages=1): self.connect(self.g_tank.outwards, self.pipe.inwards, {"w_out": "w_in"}) self.connect(self.pipe.outwards, self.rocket.inwards, {"w_out": "w_in_1"}) - self.connect(self.rocket.modevars_out, self.controller.modevars_in, {'fueling' : 'is_on'}) + self.connect(self.rocket.outwards, self.controller.inwards, {'fueling' : 'is_on'}) self.connect(self.controller.outwards, self.g_tank.inwards, {"w": "w_command"}) self.g_tank.geom.height = 2.0 diff --git a/rocket_twin/tests/test_controller_cosapp.py b/rocket_twin/tests/test_controller_cosapp.py index 93f29ca..9adbb10 100644 --- a/rocket_twin/tests/test_controller_cosapp.py +++ b/rocket_twin/tests/test_controller_cosapp.py @@ -21,7 +21,6 @@ def test_control(self): "rocket.stage_1.tank.fuel.w_out_max": 1.0, "rocket.stage_2.tank.fuel.w_out_max": 1.0, "rocket.stage_3.tank.fuel.w_out_max": 1.0, - "rocket.controller.fueling" : 1, "rocket.controller.time_int" : 5. } @@ -30,6 +29,7 @@ def test_control(self): "rocket.weight_prop_1", "rocket.weight_prop_2", "rocket.weight_prop_3", + "rocket.a" ] driver = sys.add_driver(RungeKutta("rk", order=4, dt=1)) @@ -43,8 +43,10 @@ def test_control(self): data = driver.recorder.export_data() data1 = data.drop(["Section", "Status", "Error code", "rocket.weight_prop_2", "rocket.weight_prop_3"], axis=1) data2 = data.drop(["Section", "Status", "Error code", "rocket.dyn.weight", "rocket.weight_prop_1"], axis=1) + acel = np.asarray(data["rocket.a"]) print(data1) print(data2) - np.testing.assert_allclose(1,2, atol=0.1) \ No newline at end of file + np.testing.assert_allclose(sys.rocket.geom.weight, 4., atol=10 ** (0)) + np.testing.assert_allclose(acel[-3], 40., atol=0.1) \ No newline at end of file diff --git a/rocket_twin/tests/test_controller_fmu.py b/rocket_twin/tests/test_controller_fmu.py deleted file mode 100644 index 659b026..0000000 --- a/rocket_twin/tests/test_controller_fmu.py +++ /dev/null @@ -1,56 +0,0 @@ -import numpy as np -from cosapp.drivers import NonLinearSolver, RungeKutta -from cosapp.recorders import DataFrameRecorder -from cosapp.utils import swap_system - -from rocket_twin.systems import ControllerFMU, Station - - -class TestControllerFMU: - def test_controller_fmu(self): - - model_path = r"systems\control\controller.mo" - model_name = "controller" - - model_path_r = r"systems\control\rocket_controller.mo" - model_name_r = "rocket_controller" - - sys = Station("sys") - swap_system( - sys.controller, - ControllerFMU("controller", model_path=model_path, model_name=model_name), - ) - swap_system( - sys.rocket.stage_1.controller, - ControllerFMU("controller", model_path=model_path_r, model_name=model_name_r), - ) - - sys.connect(sys.rocket.outwards, sys.controller.inwards, {"weight_max_1" : "weight_max","weight_prop_1" : "weight_prop"}) - sys.rocket.stage_1.connect( - sys.rocket.stage_1.tank.outwards, - sys.rocket.stage_1.controller.inwards, - ["weight_max", "weight_prop"], - ) - - driver = sys.add_driver(RungeKutta(order=4, time_interval=[0, 18], dt=0.1)) - solver = driver.add_child(NonLinearSolver("solver")) - init = {"g_tank.fuel.weight_p": 10.0, "rocket.stage_1.tank.fuel.weight_p": 0.0} - values = { - "g_tank.fuel.w_out_max": 1.0, - "rocket.stage_1.tank.fuel.w_out_max": 0.5, - "controller.time_int": 3.0, - "rocket.stage_1.controller.time_int": 3.0, - } - driver.set_scenario(init=init, values=values) - driver.add_recorder( - DataFrameRecorder(includes=["controller.weight_max_1", "rocket.stage_1.weight_max"]), - period=1.0, - ) - sys.run_drivers() - data = driver.recorder.export_data() - data = data.drop(["Section", "Status", "Error code"], axis=1) - print(data) - - np.testing.assert_allclose(sys.rocket.a, 2.5, atol=10 ** (0)) - np.testing.assert_allclose(sys.g_tank.weight_prop, 5.0, atol=10 ** (0)) - np.testing.assert_allclose(sys.rocket.stage_1.tank.weight_prop, 0.0, atol=10 ** (0)) diff --git a/rocket_twin/tests/test_sequences.py b/rocket_twin/tests/test_sequences.py index 595f1ba..989c1d1 100644 --- a/rocket_twin/tests/test_sequences.py +++ b/rocket_twin/tests/test_sequences.py @@ -32,7 +32,7 @@ def test_fuel(self): { "name": "fuel", "type": "transient", - "init": {"g_tank.fuel.w_out_max": 1.0, "controller.w_temp": 1.0}, + "init": {"g_tank.fuel.w_out_max": 1.0}, "dt": 0.1, "stop": "rocket.stage_1.tank.weight_prop == rocket.stage_1.tank.weight_max", } @@ -59,10 +59,7 @@ def test_flight(self): "name": "flight", "type": "transient", "init": { - "rocket.flying": True, "rocket.stage_1.tank.fuel.w_out_max": 0.5, - "controller.w_temp": 0.0, - "rocket.stage_1.controller.w_temp": 1.0, }, "dt": 0.1, "stop": "rocket.stage_1.tank.weight_prop == 0", @@ -97,7 +94,7 @@ def test_all(self): { "name": "fuel", "type": "transient", - "init": {"g_tank.fuel.w_out_max": 1.0, "controller.w_temp": 1.0}, + "init": {"g_tank.fuel.w_out_max": 1.0}, "dt": 0.1, "stop": "rocket.stage_1.tank.weight_prop == rocket.stage_1.tank.weight_max", }, @@ -105,10 +102,7 @@ def test_all(self): "name": "flight", "type": "transient", "init": { - "rocket.flying": True, "rocket.stage_1.tank.fuel.w_out_max": 0.5, - "controller.w_temp": 0.0, - "rocket.stage_1.controller.w_temp": 1.0, }, "dt": 0.1, "stop": "rocket.stage_1.tank.weight_prop == 0", diff --git a/rocket_twin/tests/test_stage.py b/rocket_twin/tests/test_stage.py index 621df32..dc908db 100644 --- a/rocket_twin/tests/test_stage.py +++ b/rocket_twin/tests/test_stage.py @@ -1,23 +1,24 @@ import numpy as np -from cosapp.drivers import RungeKutta +from cosapp.drivers import RungeKutta, NonLinearSolver from cosapp.recorders import DataFrameRecorder from rocket_twin.systems import Rocket, Station class TestStage: + + sys = Station("sys", n_stages=3) + def test_run_once(self): - sys = Rocket("sys") + sys2 = Rocket("sys") - sys.run_once() + sys2.run_once() def test_fuel(self): - sys = Station("sys", n_stages=3) init = { "g_tank.fuel.weight_p": 20.0, "g_tank.fuel.w_out_max": 1.0, - "controller.w_temp": 1.0, } includes = [ @@ -27,53 +28,63 @@ def test_fuel(self): "rocket.weight_prop_3", ] - driver = sys.add_driver(RungeKutta("rk", order=4, dt=1)) + driver = self.sys.add_driver(RungeKutta("rk", order=4, dt=1)) + solver = driver.add_child(NonLinearSolver('solver')) driver.time_interval = (0, 20) driver.set_scenario(init=init) driver.add_recorder(DataFrameRecorder(includes=includes), period=1.0) - sys.run_drivers() + self.sys.run_drivers() - np.testing.assert_allclose(sys.rocket.weight_prop_1, 5.0, rtol=10 ** (-1)) - np.testing.assert_allclose(sys.rocket.weight_prop_2, 5.0, rtol=10 ** (-1)) - np.testing.assert_allclose(sys.rocket.weight_prop_3, 5.0, rtol=10 ** (-1)) - np.testing.assert_allclose(sys.g_tank.weight_prop, 5.0, atol=10 ** (-2)) + data = driver.recorder.export_data() + data1 = data.drop(["Section", "Status", "Error code", "rocket.weight_prop_2", "rocket.weight_prop_3"], axis=1) + data2 = data.drop(["Section", "Status", "Error code", "g_tank.weight_prop", "rocket.weight_prop_1"], axis=1) - def test_flight(self): + print(data1) + print(data2) - sys = Station("sys", n_stages=3) + np.testing.assert_allclose(self.sys.rocket.weight_prop_1, 5.0, rtol=10 ** (-1)) + np.testing.assert_allclose(self.sys.rocket.weight_prop_2, 5.0, rtol=10 ** (-1)) + np.testing.assert_allclose(self.sys.rocket.weight_prop_3, 5.0, rtol=10 ** (-1)) + np.testing.assert_allclose(self.sys.g_tank.weight_prop, 5.0, atol=10 ** (-2)) + + def test_flight(self): init = { - "controller.w_temp": 0.0, - "rocket.flying": True, - "rocket.stage_1.controller.w_temp": 1.0, "rocket.stage_1.tank.fuel.w_out_max": 1.0, - "rocket.stage_1.tank.fuel.weight_p": 5.0, "rocket.stage_2.tank.fuel.w_out_max": 1.0, - "rocket.stage_2.tank.fuel.weight_p": 5.0, "rocket.stage_3.tank.fuel.w_out_max": 1.0, - "rocket.stage_3.tank.fuel.weight_p": 5.0, } stop = "rocket.weight_prop_3 == 0." - includes = ["rocket.a"] + includes = [ + "g_tank.weight_prop", + "rocket.weight_prop_1", + "rocket.weight_prop_2", + "rocket.weight_prop_3", + ] - driver = sys.add_driver(RungeKutta("rk", order=4, dt=1)) - driver.time_interval = (0, 20) + driver = self.sys.add_driver(RungeKutta("rk", order=4, dt=1)) + solver = driver.add_child(NonLinearSolver('solver')) + driver.time_interval = (20, 40) driver.set_scenario(init=init, stop=stop) driver.add_recorder(DataFrameRecorder(includes=includes), period=1.0) - sys.run_drivers() + self.sys.run_drivers() data = driver.recorder.export_data() - data = data.drop(["Section", "Status", "Error code"], axis=1) + data1 = data.drop(["Section", "Status", "Error code", "rocket.weight_prop_2", "rocket.weight_prop_3"], axis=1) + data2 = data.drop(["Section", "Status", "Error code", "g_tank.weight_prop", "rocket.weight_prop_1"], axis=1) + + print(data1) + print(data2) - acel = np.asarray(data["rocket.a"]) - print(data) + #acel = np.asarray(data["rocket.a"]) + #print(data) - np.testing.assert_allclose(sys.rocket.weight_prop_1, 0.0, rtol=10 ** (-1)) - np.testing.assert_allclose(sys.rocket.weight_prop_2, 0.0, rtol=10 ** (-1)) - np.testing.assert_allclose(sys.rocket.weight_prop_3, 0.0, rtol=10 ** (-1)) - np.testing.assert_allclose(sys.rocket.geom.weight, 4.0, rtol=10 ** (-1)) - np.testing.assert_allclose(acel[-2], 40.0, atol=10 ** (-2)) + np.testing.assert_allclose(self.sys.rocket.weight_prop_1, 0.0, rtol=10 ** (-1)) + np.testing.assert_allclose(self.sys.rocket.weight_prop_2, 0.0, rtol=10 ** (-1)) + np.testing.assert_allclose(self.sys.rocket.weight_prop_3, 0.0, rtol=10 ** (-1)) + np.testing.assert_allclose(self.sys.rocket.geom.weight, 4.0, rtol=10 ** (-1)) + #np.testing.assert_allclose(acel[-2], 40.0, atol=10 ** (-2)) diff --git a/test_controller_fmu.py b/test_controller_fmu.py new file mode 100644 index 0000000..5975fe0 --- /dev/null +++ b/test_controller_fmu.py @@ -0,0 +1,64 @@ +import numpy as np +from cosapp.drivers import NonLinearSolver, RungeKutta +from cosapp.recorders import DataFrameRecorder +from cosapp.utils import swap_system + +from rocket_twin.systems import ControllerFMU, RocketControllerFMU, Station + + +class TestControllerFMU: + def test_controller_fmu(self): + + n_stages = 3 + + model_path = r"systems\control\controller.mo" + model_name = "controller" + + model_path_r = r"systems\control\rocket_controller.mo" + model_name_r = "rocket_controller" + + sys = Station("sys", n_stages=n_stages) + swap_system( + sys.controller, + ControllerFMU("controller", model_path=model_path, model_name=model_name), + ) + swap_system( + sys.rocket.controller, + RocketControllerFMU("controller", n_stages=n_stages, model_path=model_path_r, model_name=model_name_r), + ) + for i in range(1, n_stages + 1): + swap_system( + sys.rocket[f"stage_{i}"].controller, + ControllerFMU("controller", model_path=model_path, model_name=model_name) + ) + + driver = sys.add_driver(RungeKutta(order=4, time_interval=[0, 40], dt=0.1)) + solver = driver.add_child(NonLinearSolver("solver")) + init = {"g_tank.fuel.weight_p": 20.0, + "rocket.stage_1.tank.fuel.weight_p": 0.0, + "rocket.stage_2.tank.fuel.weight_p": 0.0, + "rocket.stage_3.tank.fuel.weight_p": 0.0} + values = { + "g_tank.fuel.w_out_max": 1.0, + "rocket.controller.time_int": 5.0, + "rocket.stage_1.tank.fuel.w_out_max": 1., + "rocket.stage_2.tank.fuel.w_out_max": 1., + "rocket.stage_3.tank.fuel.w_out_max": 1., + } + driver.set_scenario(init=init, values=values) + driver.add_recorder( + DataFrameRecorder(includes=["rocket.weight_prop_3", "rocket.geom.weight", "rocket.a"]), + period=1.0, + ) + sys.run_drivers() + data = driver.recorder.export_data() + data1 = data.drop(["Section", "Status", "Error code", "rocket.a"], axis=1) + data2 = data.drop(["Section", "Status", "Error code", "rocket.geom.weight", "rocket.weight_prop_3"], axis=1) + + print(data1) + print(data2) + + np.testing.assert_allclose(sys.rocket.geom.weight, 4., atol=10 ** (0)) + np.testing.assert_allclose(sys.rocket.a, 40., rtol=0.1) + #np.testing.assert_allclose(sys.g_tank.weight_prop, 5.0, atol=10 ** (0)) + #np.testing.assert_allclose(sys.rocket.stage_1.tank.weight_prop, 0.0, atol=10 ** (0))