diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a0d97f2..47d2ec2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,6 +29,7 @@ repos: - id: check-merge-conflict - id: pretty-format-json args: [--autofix] + exclude: /.*\.ipynb - id: debug-statements language_version: python3 - repo: https://github.com/pre-commit/mirrors-isort diff --git a/rocket_twin/drivers/mission.py b/rocket_twin/drivers/mission.py index 5544079..d9e6537 100644 --- a/rocket_twin/drivers/mission.py +++ b/rocket_twin/drivers/mission.py @@ -44,10 +44,10 @@ def __init__( init_fuel = init init_flight = { "rocket.stage_1.tank.fuel.w_out_max": 3.0, - "g_tank.w_in": 0.0, + "rocket.controller.is_on_1": True, } - stop_fuel = "rocket.stage_1.tank.weight_prop >= rocket.stage_1.tank.weight_max" + stop_fuel = "rocket.flying == 1." stop_flight = stop # Fueling diff --git a/rocket_twin/notebooks/Demonstration.ipynb b/rocket_twin/notebooks/Demonstration.ipynb new file mode 100644 index 0000000..6901dcf --- /dev/null +++ b/rocket_twin/notebooks/Demonstration.ipynb @@ -0,0 +1,1028 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4d65332f-e939-4aa8-bf47-26bc0d691e7c", + "metadata": {}, + "source": [ + "# **Demonstration - Rocket Twin**" + ] + }, + { + "cell_type": "markdown", + "id": "5e5082c9-2e4a-4d5c-a9f8-e5bd6a336f67", + "metadata": {}, + "source": [ + "## Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e207e054-e7c1-462f-ba12-717bfdde929a", + "metadata": {}, + "outputs": [], + "source": [ + "from rocket_twin.systems import Station, StationControllerFMU, RocketControllerFMU, StageControllerFMU\n", + "from rocket_twin.drivers import FuelingRocket, VerticalFlyingRocket, Mission\n", + "from rocket_twin.utils import run_sequences\n", + "from cosapp.utils import swap_system\n", + "from cosapp.drivers import RungeKutta, NonLinearSolver\n", + "from cosapp.recorders import DataFrameRecorder\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from OCC.Display.SimpleGui import init_display" + ] + }, + { + "cell_type": "markdown", + "id": "853ab237-fc4c-47b2-b139-6605e7293a6f", + "metadata": {}, + "source": [ + "## Station creation\n", + "\n", + "A station is the most basic system of the rocket-twin library. It contains a ground fuel tank, which is connected to a rocket (whose number of stages can be specified by the user) by a pipe. It also has a controller which controls the fuel flux between the tank and the rocket." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "311d6d17-b7a0-41f9-b772-a532de531b4a", + "metadata": {}, + "outputs": [], + "source": [ + "# Number of stages of the rocket\n", + "n_stages = 3\n", + "\n", + "# Radius of the rocket\n", + "r = 1.\n", + "\n", + "# System creation\n", + "sys = Station('sys', n_stages=n_stages)\n", + "\n", + "# Adjust interval between fueling end and launch\n", + "sys.time_int = 10.\n", + "\n", + "# Note that fueling and flying can be false at the same time: this happens between the fueling phase end and the launch time" + ] + }, + { + "cell_type": "markdown", + "id": "07fb5f1a-c45e-4a81-8259-b92dc0564edf", + "metadata": {}, + "source": [ + "## System architecture visualisation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b4dcf43-7c56-48f6-be1d-6c35042f3f5e", + "metadata": {}, + "outputs": [], + "source": [ + "sys" + ] + }, + { + "cell_type": "markdown", + "id": "4df32908-87bf-4fe3-be5a-5437c50727e2", + "metadata": {}, + "source": [ + "We can see that a rocket is composed by its stages and a controller (geom and dyn are physics classes and only perform computations). Each stage is composed by default by an engine, a tube, a fuel tank and also a controller. In addition, the first stage (the lowest) has a set of fins attached to it, while the last (the highest) has a nose." + ] + }, + { + "cell_type": "markdown", + "id": "6de464c0-74ab-481d-995d-a68d097a83ea", + "metadata": {}, + "source": [ + "## Tank parameters\n", + "\n", + "Here we set the parameters for all the fuel tanks (for each stage and the ground). The tanks are assumed to be cylindrical (they have thick walls and a thick bottom), meaning we need to define:\n", + "\n", + "1. The max fuel output rate\n", + "2. The internal radius\n", + "3. The external radius (same as the rocket radius)\n", + "4. The thickness of the bottom surface\n", + "5. The height\n", + "6. The densities for both the structure and the fuel\n", + "7. In the case of the stage tanks, their position\n", + "\n", + "Each tank has a geometry module, which handles the structure and the fuel's shape and properties, and a fuel module, which handles the fuel mass at each instant. Besides their shape and properties (which are directly passed to the geometry module), each tank outputs their current fuel weight, their current fuel exit flow, and the maximum fuel capacity." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74278944-3801-40fc-971a-afee35419490", + "metadata": {}, + "outputs": [], + "source": [ + "# Max fuel output rate\n", + "\n", + "sys.g_tank.fuel.w_out_max = 1.\n", + "sys.rocket.stage_1.tank.fuel.w_out_max = 1.\n", + "sys.rocket.stage_2.tank.fuel.w_out_max = 1.\n", + "sys.rocket.stage_3.tank.fuel.w_out_max = 1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e7ff7cc9-b0a0-4485-88d3-911d52bd398c", + "metadata": {}, + "outputs": [], + "source": [ + "# Internal radius\n", + "\n", + "sys.g_tank.geom.r_int = 0.8\n", + "sys.rocket.stage_1.tank.geom.r_int = 0.8\n", + "sys.rocket.stage_2.tank.geom.r_int = 0.8\n", + "sys.rocket.stage_3.tank.geom.r_int = 0.8" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62f1a722-25d0-4ed2-bc8f-fd332548b5ee", + "metadata": {}, + "outputs": [], + "source": [ + "# External radius\n", + "\n", + "sys.g_tank.geom.r_ext = r\n", + "sys.rocket.stage_1.tank.geom.r_ext = r\n", + "sys.rocket.stage_2.tank.geom.r_ext = r\n", + "sys.rocket.stage_3.tank.geom.r_ext = r" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe0f658e-dad8-4f28-ab76-eebaa9e4c54f", + "metadata": {}, + "outputs": [], + "source": [ + "# Thickness of bottom surface\n", + "\n", + "sys.g_tank.geom.thickness = 0.2\n", + "sys.rocket.stage_1.tank.geom.thickness = 0.2\n", + "sys.rocket.stage_2.tank.geom.thickness = 0.2\n", + "sys.rocket.stage_3.tank.geom.thickness = 0.2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2918b401-bad6-4877-9442-f7ff6e6b70c4", + "metadata": {}, + "outputs": [], + "source": [ + "# Height\n", + "\n", + "sys.g_tank.geom.height = 2.\n", + "sys.rocket.stage_1.tank.geom.height = 1.\n", + "sys.rocket.stage_2.tank.geom.height = 1.2\n", + "sys.rocket.stage_3.tank.geom.height = 1.4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "390b25d1-a7fc-4154-9a7d-8dd0435c4e72", + "metadata": {}, + "outputs": [], + "source": [ + "# Fuel density\n", + "\n", + "rho_fuel = 7.8125 / np.pi\n", + "\n", + "sys.g_tank.geom.rho_fuel = rho_fuel\n", + "sys.rocket.stage_1.tank.geom.rho_fuel = rho_fuel\n", + "sys.rocket.stage_2.tank.geom.rho_fuel = rho_fuel\n", + "sys.rocket.stage_3.tank.geom.rho_fuel = rho_fuel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a53323a-97a5-4a3c-94e3-be556381eb54", + "metadata": {}, + "outputs": [], + "source": [ + "# Structure density\n", + "\n", + "rho_struct = 1 / (0.56 * np.pi)\n", + "\n", + "sys.g_tank.geom.rho_struct = rho_struct\n", + "sys.rocket.stage_1.tank.geom.rho_struct = rho_struct\n", + "sys.rocket.stage_2.tank.geom.rho_struct = rho_struct\n", + "sys.rocket.stage_3.tank.geom.rho_struct = rho_struct" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb0aaa03-cd47-42cc-bcb8-069c501b3130", + "metadata": {}, + "outputs": [], + "source": [ + "# Stage tanks positions\n", + "\n", + "sys.rocket.stage_1.tank.geom.pos = 0.\n", + "sys.rocket.stage_2.tank.geom.pos = 4.\n", + "sys.rocket.stage_3.tank.geom.pos = 8." + ] + }, + { + "cell_type": "markdown", + "id": "622009aa-fa2f-416d-94e3-faab4f7be788", + "metadata": {}, + "source": [ + "## Engine parameters\n", + "\n", + "Here we set the parameters for each stage engine. All engines are assumed to be shaped like a truncated cone, meaning we need to define:\n", + "\n", + "1. The base radius\n", + "2. The top radius\n", + "3. The height\n", + "4. The density of the structure\n", + "5. The position\n", + "6. The ISP (Specific impulse of the propellant)\n", + "\n", + "Each engine has a geometry model, which handles the structure's shape and properties, and a performance model, which computes the thrust force achieved by the engine." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07a77b38-c572-4dc3-a501-5fa697ab780a", + "metadata": {}, + "outputs": [], + "source": [ + "# Base radius\n", + "\n", + "sys.rocket.stage_1.engine.geom.base_radius = 1.\n", + "sys.rocket.stage_2.engine.geom.base_radius = 1.\n", + "sys.rocket.stage_3.engine.geom.base_radius = 1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c091eb9-71a8-4e56-a778-d78655f6a419", + "metadata": {}, + "outputs": [], + "source": [ + "# Top radius\n", + "\n", + "sys.rocket.stage_1.engine.geom.top_radius = 0.5\n", + "sys.rocket.stage_2.engine.geom.top_radius = 0.5\n", + "sys.rocket.stage_3.engine.geom.top_radius = 0.5" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ace3cc11-5ebb-4ae3-83e0-8f196a657fdd", + "metadata": {}, + "outputs": [], + "source": [ + "# Height\n", + "\n", + "sys.rocket.stage_1.engine.geom.height = 1.\n", + "sys.rocket.stage_2.engine.geom.height = 1.\n", + "sys.rocket.stage_3.engine.geom.height = 1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71f7cecf-782c-48e6-a77c-d657d3195e64", + "metadata": {}, + "outputs": [], + "source": [ + "# Density\n", + "\n", + "sys.rocket.stage_1.engine.geom.rho = 12 / (7 * np.pi)\n", + "sys.rocket.stage_2.engine.geom.rho = 12 / (7 * np.pi)\n", + "sys.rocket.stage_3.engine.geom.rho = 12 / (7 * np.pi)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ff07b0d0-7e88-4f98-9738-0ccff2466b02", + "metadata": {}, + "outputs": [], + "source": [ + "# Positions\n", + "\n", + "sys.rocket.stage_1.engine.geom.pos = -1.2\n", + "sys.rocket.stage_2.engine.geom.pos = 2.8\n", + "sys.rocket.stage_3.engine.geom.pos = 6.8" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "adafef6b-cbcf-49d2-bdf5-55ba8fc646d9", + "metadata": {}, + "outputs": [], + "source": [ + "# ISP\n", + "\n", + "isp = 30.\n", + "\n", + "sys.rocket.stage_1.engine.perfo.isp = isp\n", + "sys.rocket.stage_2.engine.perfo.isp = isp\n", + "sys.rocket.stage_3.engine.perfo.isp = isp" + ] + }, + { + "cell_type": "markdown", + "id": "b3dcc237-8b24-4533-88b4-35d65e73f073", + "metadata": {}, + "source": [ + "## Tube parameters\n", + "\n", + "Here we set the parameters for the tube (the body) of each stage of the rocket. We need to define:\n", + "\n", + "1. The radius (the same as the rocket radius)\n", + "2. The lenght\n", + "3. The density\n", + "4. The position" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f0ae3db-00d8-498a-9026-fc60a7eaf5d3", + "metadata": {}, + "outputs": [], + "source": [ + "# Radius\n", + "\n", + "sys.rocket.stage_1.tube.radius = r\n", + "sys.rocket.stage_2.tube.radius = r\n", + "sys.rocket.stage_3.tube.radius = r" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec3bed35-64fa-4ba1-a67b-7f1b14efffe0", + "metadata": {}, + "outputs": [], + "source": [ + "# Length\n", + "\n", + "sys.rocket.stage_1.tube.length = 3.\n", + "sys.rocket.stage_2.tube.length = 3.\n", + "sys.rocket.stage_3.tube.length = 3." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8015a193-b30d-42c1-a74e-c2e6c0bfe02c", + "metadata": {}, + "outputs": [], + "source": [ + "# Density\n", + "\n", + "sys.rocket.stage_1.tube.rho = 0.2 / np.pi\n", + "sys.rocket.stage_2.tube.rho = 0.2 / np.pi\n", + "sys.rocket.stage_3.tube.rho = 0.2 / np.pi" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6009fc0d-123a-4f0a-a34a-f5520f362eab", + "metadata": {}, + "outputs": [], + "source": [ + "# Positions\n", + "\n", + "sys.rocket.stage_1.tube.pos = 1.\n", + "sys.rocket.stage_2.tube.pos = 5.\n", + "sys.rocket.stage_3.tube.pos = 9." + ] + }, + { + "cell_type": "markdown", + "id": "27d773ae-923f-462a-95be-41aafb98dd74", + "metadata": {}, + "source": [ + "## Nose parameters\n", + "\n", + "Here we set the parameters for the rocket's nose, attached to the highest stage. We need to define:\n", + "\n", + "1. The radius (the same as the rocket radius)\n", + "2. The height\n", + "3. The density\n", + "4. The position" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c697aa0c-b08c-4291-9252-233ae6396f6f", + "metadata": {}, + "outputs": [], + "source": [ + "# Radius\n", + "\n", + "sys.rocket.stage_3.nose.radius = r" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ff7653a-3869-4f38-bc48-04db10913f26", + "metadata": {}, + "outputs": [], + "source": [ + "# Height\n", + "\n", + "sys.rocket.stage_3.nose.height = 1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5321dac7-7b27-4b70-a40b-9b2b1d459312", + "metadata": {}, + "outputs": [], + "source": [ + "# Density\n", + "\n", + "sys.rocket.stage_3.nose.rho = 3 / np.pi" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a646f6ab-1d9e-4627-8f86-dcf38ba1a980", + "metadata": {}, + "outputs": [], + "source": [ + "# Position\n", + "\n", + "sys.rocket.stage_3.nose.pos = 12." + ] + }, + { + "cell_type": "markdown", + "id": "b8b515bc-966f-46b9-a4ca-ad54d5a2a6cd", + "metadata": {}, + "source": [ + "## Wings parameters\n", + "\n", + "Here we define the parameters for the set of wings attached to the lowest stage. We need to define:\n", + "\n", + "1. The number of wings\n", + "2. The length of the edge attached to the rocket\n", + "3. The length of the free edge\n", + "4. The span (distance between the free edge and the rocket)\n", + "5. The thickness\n", + "6. The density\n", + "7. The radius (same as the rocket radius)\n", + "8. The position" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f19b613-e749-48c6-b53c-d5ae4a909fec", + "metadata": {}, + "outputs": [], + "source": [ + "# Number of wings\n", + "\n", + "sys.rocket.stage_1.wings.n = 4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1acf4530-c11e-4bde-a287-13db8887abb1", + "metadata": {}, + "outputs": [], + "source": [ + "# Length of the attached edge\n", + "\n", + "sys.rocket.stage_1.wings.l_in = 1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c828bce0-3c5c-42f2-a904-a672c7829053", + "metadata": {}, + "outputs": [], + "source": [ + "# Length of the free edge\n", + "\n", + "sys.rocket.stage_1.wings.l_out = 0.5" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4d195e2-52cf-4e73-9c5e-503c1e1e35d5", + "metadata": {}, + "outputs": [], + "source": [ + "# Span\n", + "\n", + "sys.rocket.stage_1.wings.width = 4 / 3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0445c458-d6ca-4446-852c-1a4641913985", + "metadata": {}, + "outputs": [], + "source": [ + "# Thickness\n", + "\n", + "sys.rocket.stage_1.wings.th = 0.1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e84cfdf-d918-4815-9f6a-f591baf024df", + "metadata": {}, + "outputs": [], + "source": [ + "# Density\n", + "\n", + "sys.rocket.stage_1.wings.rho = 10." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7741d88b-c6f1-4fa2-8be9-c81560efcedf", + "metadata": {}, + "outputs": [], + "source": [ + "# Radius\n", + "\n", + "sys.rocket.stage_1.wings.radius = r" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aebd952d-df10-4293-9ea5-2db4c44867e3", + "metadata": {}, + "outputs": [], + "source": [ + "# Position\n", + "\n", + "sys.rocket.stage_1.wings.pos = 0." + ] + }, + { + "cell_type": "markdown", + "id": "99bec721-1620-4b53-a2b7-550ab77b3329", + "metadata": {}, + "source": [ + "## Choice of control mode\n", + "\n", + "The control of the system can be done through either cosapp or .fmu files. In order to use the fmu control, either a .mo file (from OpenModelica) or an .fmu file should be passed to the system." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9fee58e0-11d3-4ff1-af73-a7384dfec86b", + "metadata": {}, + "outputs": [], + "source": [ + "use_fmu = False\n", + "\n", + "model_path = r\"systems\\control\\station_controller.mo\"\n", + "model_name = \"station_controller\"\n", + "\n", + "model_path_r = r\"systems\\control\\rocket_controller.mo\"\n", + "model_name_r = \"rocket_controller\"\n", + "\n", + "model_path_s = r\"systems\\control\\stage_controller.mo\"\n", + "model_name_s = \"stage_controller\"\n", + "\n", + "if use_fmu:\n", + " swap_system(sys.controller, StationControllerFMU(\"controller\", model_path=model_path, model_name=model_name))\n", + " swap_system(sys.rocket.controller, RocketControllerFMU(\"controller\", model_path=model_path_r, model_name=model_name_r, n_stages=n_stages))\n", + " for i in range(1, n_stages + 1):\n", + " swap_system(sys.rocket[f\"stage_{i}\"].controller, StageControllerFMU(\"controller\", model_path=model_path_s, model_name=model_name_s))" + ] + }, + { + "cell_type": "markdown", + "id": "d341bae3-a636-4204-bee0-23b3210cafb0", + "metadata": {}, + "source": [ + "## Initial conditions\n", + "\n", + "We consider that the rocket, while at the station, is at the origin of our coordinate system and has null speed. Therefore, the only initial conditions to be defined are the fuel masses in each tank and whether the rocket is mid-fueling or mid-flight, which we define below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7370468-2245-498a-88f7-c86f8d1640c2", + "metadata": {}, + "outputs": [], + "source": [ + "# Initial fuel mass\n", + "\n", + "ground_tank_mass = 20.\n", + "stage1_tank_mass = 0.\n", + "stage2_tank_mass = 0.\n", + "stage3_tank_mass = 0.\n", + "\n", + "# Phase\n", + "\n", + "fuel = True\n", + "flight = False" + ] + }, + { + "cell_type": "markdown", + "id": "4c3cff2c-de08-463a-8af2-14b23221f97a", + "metadata": {}, + "source": [ + "## Simulation\n", + "\n", + "The rocket simulation may be done through three different means:\n", + "\n", + "1. By manually adding a time driver (EulerExplicit or RungeKutta) coupled with a NonLinearSolver, which can be imported from cosapp.drivers;\n", + "2. By using the rocket-specific drivers FuelingRocket (which simulates the fueling phase), VerticalFlyingRocket (which simulates the flight), or Mission (which simulates both), which can be imported from rocket_twin.drivers;\n", + "3. By using the command through sequences, importing the function run_sequences from rocket_twin.utils." + ] + }, + { + "cell_type": "markdown", + "id": "1f5f6cf7-dcec-433f-a910-acae75ef7f8f", + "metadata": {}, + "source": [ + "## Simulation by manually adding drivers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46800442-0c21-44fa-9483-0bcd7632d814", + "metadata": {}, + "outputs": [], + "source": [ + "# Time interval and step\n", + "\n", + "man_sim = True\n", + "\n", + "dt = 1.\n", + "T = 50.\n", + "\n", + "init = {\"g_tank.fuel.weight_p\" : ground_tank_mass,\n", + " \"rocket.stage_1.tank.fuel.weight_p\" : stage1_tank_mass,\n", + " \"rocket.stage_2.tank.fuel.weight_p\" : stage2_tank_mass,\n", + " \"rocket.stage_3.tank.fuel.weight_p\" : stage3_tank_mass,\n", + " \"fueling\" : fuel,\n", + " \"rocket.flying\" : flight,\n", + " }\n", + "\n", + "sys.drivers.clear()\n", + "driver = sys.add_driver(RungeKutta(order=4, dt=dt))\n", + "solver = driver.add_child(NonLinearSolver('solver'))\n", + "driver.time_interval = (0, T)\n", + "driver.set_scenario(init=init)\n", + "\n", + "includes = [\"rocket.a\", \"rocket.geom.weight\", \"rocket.weight_prop_1\", \n", + " \"rocket.weight_prop_2\", \"rocket.weight_prop_3\", \"g_tank.weight_prop\", \"rocket.geom.cg\"]\n", + "\n", + "driver.add_recorder(DataFrameRecorder(includes=includes), period=1.0)\n", + "\n", + "if man_sim:\n", + " sys.run_drivers()\n", + " data = driver.recorder.export_data()" + ] + }, + { + "cell_type": "markdown", + "id": "cd6b2576-b5bb-4e71-988f-02af1a468cb6", + "metadata": {}, + "source": [ + "## Simulation by rocket_twin drivers" + ] + }, + { + "cell_type": "markdown", + "id": "0b1fbbe2-a2bf-4616-8c7b-151e5d7895d7", + "metadata": {}, + "source": [ + "### Fueling" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3fba5efd-0cd2-4021-ab22-298aa03bb26a", + "metadata": {}, + "outputs": [], + "source": [ + "fuel_sim = False\n", + "\n", + "dt = 1.\n", + "\n", + "init = {\"g_tank.fuel.weight_p\" : ground_tank_mass,\n", + " \"rocket.stage_1.tank.fuel.weight_p\" : stage1_tank_mass,\n", + " \"rocket.stage_2.tank.fuel.weight_p\" : stage2_tank_mass,\n", + " \"rocket.stage_3.tank.fuel.weight_p\" : stage3_tank_mass,\n", + " \"fueling\" : True,\n", + " \"rocket.flying\" : False,\n", + " }\n", + "\n", + "stop = \"rocket.flying == 1.\"\n", + "\n", + "includes = [\"rocket.a\", \"rocket.geom.weight\", \"rocket.weight_prop_1\", \n", + " \"rocket.weight_prop_2\", \"rocket.weight_prop_3\", \"g_tank.weight_prop\"]\n", + "\n", + "sys.drivers.clear()\n", + "sys.add_driver(FuelingRocket('fr', owner=sys, init=init, stop=stop, includes=includes, dt=dt))\n", + "\n", + "if fuel_sim:\n", + " sys.run_drivers()\n", + " data = sys.drivers[\"fr\"].data" + ] + }, + { + "cell_type": "markdown", + "id": "c9187b92-0273-41a3-befc-d58ffa746a87", + "metadata": {}, + "source": [ + "### Flying" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "380230af-5678-418c-8ba6-6d0995173fa1", + "metadata": {}, + "outputs": [], + "source": [ + "fly_sim = False\n", + "\n", + "dt = 1.\n", + "\n", + "init = {\"g_tank.fuel.weight_p\" : ground_tank_mass,\n", + " \"rocket.stage_1.tank.fuel.weight_p\" : stage1_tank_mass,\n", + " \"rocket.stage_2.tank.fuel.weight_p\" : stage2_tank_mass,\n", + " \"rocket.stage_3.tank.fuel.weight_p\" : stage3_tank_mass,\n", + " \"rocket.controller.is_on_1\" : True,\n", + " \"fueling\" : False,\n", + " \"rocket.flying\" : True,\n", + " }\n", + "\n", + "stop = f\"rocket.stage_{n_stages}.tank.weight_prop <= 0.15\"\n", + "\n", + "includes = [\"rocket.a\", \"rocket.geom.weight\", \"rocket.weight_prop_1\", \n", + " \"rocket.weight_prop_2\", \"rocket.weight_prop_3\", \"g_tank.weight_prop\"]\n", + "\n", + "sys.drivers.clear()\n", + "sys.add_driver(VerticalFlyingRocket('vfr', owner=sys, init=init, stop=stop, includes=includes, dt=dt))\n", + "\n", + "if fly_sim:\n", + " sys.run_drivers()\n", + " data = sys.drivers[\"vfr\"].data" + ] + }, + { + "cell_type": "markdown", + "id": "29b6343b-7ca7-4db4-acff-f33ff3bc745a", + "metadata": {}, + "source": [ + "### Mission (fueling + flying) (bug avec le temps)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0c5e8840-180f-430b-b37a-3592300355a5", + "metadata": {}, + "outputs": [], + "source": [ + "mis_sim = False\n", + "\n", + "dt = 1.\n", + "\n", + "init = {\"g_tank.fuel.weight_p\" : ground_tank_mass,\n", + " \"rocket.stage_1.tank.fuel.weight_p\" : stage1_tank_mass,\n", + " \"rocket.stage_2.tank.fuel.weight_p\" : stage2_tank_mass,\n", + " \"rocket.stage_3.tank.fuel.weight_p\" : stage3_tank_mass,\n", + " \"fueling\" : True,\n", + " \"rocket.flying\" : False,\n", + " }\n", + "\n", + "stop = f\"rocket.stage_{n_stages}.tank.weight_prop <= 0\"\n", + "\n", + "includes = [\"rocket.a\", \"rocket.geom.weight\", \"rocket.weight_prop_1\", \n", + " \"rocket.weight_prop_2\", \"rocket.weight_prop_3\", \"g_tank.weight_prop\"]\n", + "\n", + "sys.drivers.clear()\n", + "sys.add_driver(Mission('mission', owner=sys, init=init, stop=stop, includes=includes, dt=dt))\n", + "\n", + "if mis_sim:\n", + " sys.run_drivers()\n", + " data = sys.drivers[\"mission\"].data" + ] + }, + { + "cell_type": "markdown", + "id": "deee8db0-3aba-4435-93a1-01981c98e8a1", + "metadata": {}, + "source": [ + "## Simulation by cosapp sequences" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "742acffd-710e-478e-bcf6-d6ad9a4a7fe5", + "metadata": {}, + "outputs": [], + "source": [ + "seq_sim = False\n", + "\n", + "#Stop conditions\n", + "stop_fuel = f\"rocket.stage_{n_stages}.tank.weight_prop == rocket.stage_{n_stages}.tank.weight_max\"\n", + "stop_fly = f\"rocket.stage_{n_stages}.tank.weight_prop == 0.\"\n", + "\n", + "#Sequences\n", + "seq_init = [{\"name\" : \"start\", \"init\" : {\"g_tank.fuel.weight_p\" : 20.}, \"type\" : \"static\"}] \n", + "seq_fuel = [{\"name\" : \"fuel\", \"type\" : \"transient\", \"dt\" : 1.0, \"stop\" : stop_fuel}]\n", + "seq_fly = [{\"name\" : \"flight\", \"type\" : \"transient\", \"dt\" : 1.0, \"stop\" : stop_fly}]\n", + "\n", + "seq = [seq_init[0], seq_fuel[0], seq_fly[0]]\n", + "\n", + "includes = [\"rocket.a\", \"rocket.geom.weight\", \"rocket.weight_prop_1\", \n", + " \"rocket.weight_prop_2\", \"rocket.weight_prop_3\", \"g_tank.weight_prop\"]\n", + "\n", + "if seq_sim:\n", + " run_sequences(sys, seq, includes)\n", + " data = sys.drivers['rk'].recorder.export_data()" + ] + }, + { + "cell_type": "markdown", + "id": "c70067ec-90b0-4ee3-9626-78098450de60", + "metadata": {}, + "source": [ + "## Data processing" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b14eb1c5-c5a8-4a0a-a3a5-25bdf048b163", + "metadata": {}, + "outputs": [], + "source": [ + "acel = np.asarray(data[\"rocket.a\"])\n", + "mass = np.asarray(data[\"rocket.geom.weight\"])\n", + "cg = np.asarray(data[\"rocket.geom.cg\"])\n", + "time = np.asarray(data[\"time\"])\n", + "\n", + "fuel1 = np.asarray(data[\"rocket.weight_prop_1\"])\n", + "fuel2 = np.asarray(data[\"rocket.weight_prop_2\"])\n", + "fuel3 = np.asarray(data[\"rocket.weight_prop_3\"])\n", + "fuelg = np.asarray(data[\"g_tank.weight_prop\"])" + ] + }, + { + "cell_type": "markdown", + "id": "f1acb9b0-195e-408b-b04a-bd9688864cc3", + "metadata": {}, + "source": [ + "## Results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be8b8507-a9ff-4643-8fe0-c2d488affedb", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(time, fuel1, label=\"Stage 1 fuel\")\n", + "plt.plot(time, fuel2, label=\"Stage 2 fuel\")\n", + "plt.plot(time, fuel3, label=\"Stage 3 fuel\")\n", + "plt.plot(time, fuelg, label=\"Ground fuel\")\n", + "plt.plot(time, mass, label=\"Rocket mass\")\n", + "plt.axvspan(0, 18, color=\"RED\", alpha=0.4)\n", + "plt.axvspan(28, 50, color=\"GREEN\", alpha=0.4)\n", + "plt.xlabel(\"Time (s)\")\n", + "plt.ylabel(\"Fuel mass (kg)\")\n", + "plt.title(\"Mass over time\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1c58307-4649-4236-bd2a-e0f0893515bd", + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(time, acel, label=\"Rocket Acceleration\")\n", + "plt.axvspan(0, 15, color=\"RED\", alpha=0.4)\n", + "plt.axvspan(25, 50, color=\"GREEN\", alpha=0.4)\n", + "plt.xlabel(\"Time (s)\")\n", + "plt.ylabel(\"Acceleration(m/s²)\")\n", + "plt.title(\"Acceleration over time\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66ea36d0-e91d-4004-b8c0-8f0ac879cfff", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "d18d6b15-c895-4078-b6ee-ab478cec7b4e", + "metadata": {}, + "source": [ + "## System Visualisation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "742bbdab-a4e7-42cf-998d-22283bcdd99c", + "metadata": {}, + "outputs": [], + "source": [ + "view = False\n", + "\n", + "if view:\n", + " shapes = []\n", + " sys.run_once()\n", + " \n", + " def dfs(sys):\n", + " try:\n", + " shapes.append(sys.shape)\n", + " except Exception:\n", + " pass\n", + " for child in sys.children:\n", + " dfs(sys[child])\n", + " \n", + " dfs(sys.rocket)\n", + " display, start_display, add_menu, add_function_to_menu = init_display()\n", + " for shape in shapes:\n", + " if shape is not None:\n", + " display.DisplayShape(shape)\n", + " start_display()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1be4b214-37ab-49aa-86e0-14e0897a324b", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/rocket_twin/systems/control/rocket_controller/rocket_controller.fmu b/rocket_twin/systems/control/rocket_controller/rocket_controller.fmu index 0133f8c..217677e 100644 Binary files a/rocket_twin/systems/control/rocket_controller/rocket_controller.fmu and b/rocket_twin/systems/control/rocket_controller/rocket_controller.fmu differ diff --git a/rocket_twin/systems/control/stage_controller/stage_controller.fmu b/rocket_twin/systems/control/stage_controller/stage_controller.fmu index dfe35c2..2fdaadd 100644 Binary files a/rocket_twin/systems/control/stage_controller/stage_controller.fmu and b/rocket_twin/systems/control/stage_controller/stage_controller.fmu differ diff --git a/rocket_twin/systems/control/station_controller/station_controller.fmu b/rocket_twin/systems/control/station_controller/station_controller.fmu index 41f02bd..76fee4e 100644 Binary files a/rocket_twin/systems/control/station_controller/station_controller.fmu and b/rocket_twin/systems/control/station_controller/station_controller.fmu differ diff --git a/rocket_twin/systems/tank/tank_geom.py b/rocket_twin/systems/tank/tank_geom.py index cbe8fbf..5f0346b 100644 --- a/rocket_twin/systems/tank/tank_geom.py +++ b/rocket_twin/systems/tank/tank_geom.py @@ -58,7 +58,7 @@ def compute(self): self.r_int, self.r_ext, self.height, self.thickness, self.pos ) shape_fuel = CreateCylinder.from_base_and_dir( - gp_Pnt(0, 0, self.pos), gp_Vec(0, 0, height_fuel), self.r_int + gp_Pnt(0, 0, self.pos + 0.000000001), gp_Vec(0, 0, height_fuel), self.r_int ) self.shape = BRepAlgoAPI_Fuse(shape_struct, shape_fuel).Shape() diff --git a/rocket_twin/tests/test_sequences.py b/rocket_twin/tests/test_sequences.py index 55af875..737c7d8 100644 --- a/rocket_twin/tests/test_sequences.py +++ b/rocket_twin/tests/test_sequences.py @@ -8,6 +8,7 @@ class TestSequences: """Tests for command through sequences.""" sys = Station("sys") + includes = ["rocket.a"] def test_init(self): @@ -19,7 +20,7 @@ def test_init(self): } ] - run_sequences(self.sys, seq) + run_sequences(self.sys, seq, includes=self.includes) np.testing.assert_allclose( self.sys.g_tank.weight_prop, self.sys.g_tank.weight_max, atol=10 ** (-6) @@ -39,7 +40,7 @@ def test_fuel(self): } ] - run_sequences(self.sys, seq) + run_sequences(self.sys, seq, includes=self.includes) np.testing.assert_allclose( self.sys.g_tank.weight_prop, @@ -67,7 +68,7 @@ def test_flight(self): } ] - run_sequences(self.sys, seq) + run_sequences(self.sys, seq, includes=self.includes) data = self.sys.drivers["rk"].recorder.export_data() data = data.drop(["Section", "Status", "Error code"], axis=1) @@ -110,7 +111,7 @@ def test_all(self): }, ] - run_sequences(sys2, seq) + run_sequences(sys2, seq, includes=self.includes) data = sys2.drivers["rk"].recorder.export_data() data = data.drop(["Section", "Status", "Error code"], axis=1) diff --git a/rocket_twin/utils/run_sequences.py b/rocket_twin/utils/run_sequences.py index ab9432b..de88744 100644 --- a/rocket_twin/utils/run_sequences.py +++ b/rocket_twin/utils/run_sequences.py @@ -2,7 +2,7 @@ from cosapp.recorders import DataFrameRecorder -def run_sequences(sys, sequences): +def run_sequences(sys, sequences, includes): """Run the command sequences over a system. Inputs @@ -13,7 +13,7 @@ def run_sequences(sys, sequences): the commands to be applied """ rk = sys.add_driver(RungeKutta("rk")) - rk.add_recorder(DataFrameRecorder(includes=["rocket.a"], hold=True)) + rk.add_recorder(DataFrameRecorder(includes=includes, hold=True)) for seq in sequences: print("sequence ", seq["name"])