Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adapt simulation to take into account electrical profiles and adapt endpoints #2886

Merged
merged 1 commit into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/editoast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,12 @@ jobs:
with:
command: test
args: --manifest-path editoast/Cargo.toml --verbose -- --test-threads 1

- name: Install tarpaulin
run: cargo install cargo-tarpaulin

- name: Coverage
uses: actions-rs/tarpaulin@v0.1
with:
args: "-r ./editoast -- --test-threads 1"
run: cargo tarpaulin -r ./editoast --out Xml -- --test-threads 1

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
*.nix
*.backup
__pycache__
60 changes: 51 additions & 9 deletions api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -461,9 +461,15 @@ paths:
properties:
infra:
type: number
electrical_profile_set:
type: number
nullable: true
description: Electrical profile set id (if any)
name:
type: string
required: true
required:
- infra
- name
responses:
201:
description: The timetable created
Expand All @@ -478,6 +484,9 @@ paths:
type: string
infra:
type: number
electrical_profile_set:
type: number
nullable: true
/timetable/{id}/:
get:
tags:
Expand All @@ -504,6 +513,9 @@ paths:
type: string
infra:
type: number
electrical_profile_set:
Castavo marked this conversation as resolved.
Show resolved Hide resolved
type: number
nullable: true
train_schedule:
type: array
items:
Expand Down Expand Up @@ -553,7 +565,12 @@ paths:
type: number
name:
type: string
required: true
electrical_profile_set:
type: number
nullable: true
required:
- infra
- name
responses:
200:
description: The timetable updated
Expand All @@ -572,7 +589,7 @@ paths:
post:
tags:
- train_schedule
summary: Create a batch of train schedule
summary: Create a batch of train schedule and run simulations accordingly
requestBody:
description: Standalone simulation parameters
content:
Expand All @@ -582,7 +599,7 @@ paths:
required: true
responses:
201:
description: The id of the train_schedule created
description: The ids of the train_schedules created
content:
application/json:
schema:
Expand Down Expand Up @@ -630,7 +647,7 @@ paths:
patch:
tags:
- train_schedule
summary: Update a train_schedule
summary: Update a train_schedule and run a simulation accordingly
parameters:
- in: path
name: id
Expand All @@ -656,7 +673,7 @@ paths:
get:
tags:
- train_schedule
summary: Compute and format the simulation result
summary: Retrieve a simulation result
parameters:
- in: path
name: id
Expand All @@ -680,7 +697,7 @@ paths:
get:
tags:
- train_schedule
summary: Compute and format the simulation result of multiple train schedule
summary: Retrieve the simulation result of multiple train schedules
parameters:
- in: query
name: path
Expand Down Expand Up @@ -949,8 +966,7 @@ components:
items: { type: number, format: float }
minItems: 2
maxItems: 2
mechanical_energy_consumed:
type: { type: number, format: float }
mechanical_energy_consumed: { type: number, format: float }
TrainScheduleResult:
properties:
id:
Expand Down Expand Up @@ -996,6 +1012,32 @@ components:
- type: object
properties:
error: { type: string }
modes_and_profiles:
Castavo marked this conversation as resolved.
Show resolved Hide resolved
type: array
items:
description: The catenary mode and electrical profiles seen along the path. <mode_seen> and <profile_seen> are filled only if they differ from those used.
type: array
items:
type: object
properties:
start:
type: number
format: double
stop:
type: number
format: double
mode_used:
type: string
profile_used:
type: string
nullable: true
mode_seen:
type: string
nullable: true
profile_seen:
type: string
nullable: true

PathQuery:
properties:
infra:
Expand Down
4 changes: 2 additions & 2 deletions api/osrd_infra/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from osrd_infra.models import ( # misc; timetable
MODEL_TO_OBJ,
ElectricalProfilesSet,
ElectricalProfileSet,
Infra,
PathModel,
RollingStock,
Expand All @@ -22,7 +22,7 @@ class InfraAdmin(admin.ModelAdmin):
# misc
PathModel,
RollingStock,
ElectricalProfilesSet,
ElectricalProfileSet,
# timetable
Timetable,
TrainScheduleModel,
Expand Down
2 changes: 1 addition & 1 deletion api/osrd_infra/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ class Migration(migrations.Migration):
models.JSONField(
validators=[
osrd_infra.utils.PydanticValidator(
osrd_infra.schemas.external_generated_inputs.ElectricalProfilesList
osrd_infra.schemas.external_generated_inputs.ElectricalProfileSet
)
]
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Generated by Django 4.1.5 on 2023-01-25 11:32

from django.db import migrations, models
import django.db.models.deletion
import osrd_infra.schemas.train_schedule
import osrd_infra.utils
from osrd_infra.migrations import run_sql_add_foreign_key


class Migration(migrations.Migration):

dependencies = [
("osrd_infra", "0001_initial"),
]

operations = [
migrations.RenameModel(
old_name="ElectricalProfilesSet",
new_name="ElectricalProfileSet",
),
migrations.AlterModelOptions(
name="electricalprofileset",
options={"verbose_name_plural": "Electrical profile sets"},
),
run_sql_add_foreign_key("timetable", "electrical_profile_set", "electricalprofileset", nullable=True),
migrations.AddField(
model_name="trainschedulemodel",
name="modes_and_profiles",
field=models.JSONField(default=list),
),
migrations.AlterField(
model_name="trainschedulemodel",
name="allowances",
field=models.JSONField(
default=list,
validators=[osrd_infra.utils.PydanticValidator(osrd_infra.schemas.train_schedule.Allowances)],
),
),
migrations.AlterField(
model_name="trainschedulemodel",
name="labels",
field=models.JSONField(
default=list,
validators=[osrd_infra.utils.PydanticValidator(osrd_infra.schemas.train_schedule.TrainScheduleLabels)],
),
),
]
6 changes: 4 additions & 2 deletions api/osrd_infra/migrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from django.db import migrations, models


def run_sql_add_foreign_key(model_name: str, field_name: str, link_model: str):
def run_sql_add_foreign_key(model_name: str, field_name: str, link_model: str, nullable: bool = False):
return migrations.RunSQL(
f"""ALTER TABLE osrd_infra_{model_name}
ADD {field_name}_id INTEGER,
ADD {field_name}_id INTEGER {"NULL" if nullable else ""},
ADD CONSTRAINT osrd_{link_model}_{model_name}_fkey FOREIGN KEY ({field_name}_id)
REFERENCES osrd_infra_{link_model}(id) ON DELETE CASCADE
""",
Expand All @@ -16,6 +16,8 @@ def run_sql_add_foreign_key(model_name: str, field_name: str, link_model: str):
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=f"osrd_infra.{link_model}",
null=nullable,
blank=nullable,
),
),
],
Expand Down
2 changes: 1 addition & 1 deletion api/osrd_infra/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from osrd_infra.models.generated import * # noqa
from osrd_infra.models.infra import * # noqa
from osrd_infra.models.electrical_profiles import * # noqa
from osrd_infra.models.path import * # noqa
from osrd_infra.models.rolling_stock import * # noqa
from osrd_infra.models.timetable import * # noqa
from osrd_infra.models.train_schedule import * # noqa
from osrd_infra.models.external_generated_inputs import * # noqa
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from django.contrib.gis.db import models
from osrd_infra.utils import PydanticValidator

from osrd_infra.schemas.external_generated_inputs import ElectricalProfilesList
from osrd_infra.schemas.external_generated_inputs import ElectricalProfileSet as ElectricalProfileSetSchema


class ElectricalProfilesSet(models.Model):
class ElectricalProfileSet(models.Model):
"""A set of electrical profiles, which model the power loss along the catenaries of an infra."""
name = models.CharField(max_length=128)
data = models.JSONField(validators=[PydanticValidator(ElectricalProfilesList)])
data = models.JSONField(validators=[PydanticValidator(ElectricalProfileSetSchema)])

class Meta:
verbose_name_plural = "Electrical profiles sets"
verbose_name_plural = "Electrical profile sets"
3 changes: 2 additions & 1 deletion api/osrd_infra/models/timetable.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from django.contrib.gis.db import models

from osrd_infra.models import Infra
from osrd_infra.models import Infra, ElectricalProfileSet


class Timetable(models.Model):
infra = models.ForeignKey(Infra, on_delete=models.CASCADE)
electrical_profile_set = models.ForeignKey(ElectricalProfileSet, on_delete=models.CASCADE, null=True, blank=True)
Castavo marked this conversation as resolved.
Show resolved Hide resolved
name = models.CharField(max_length=128)
5 changes: 3 additions & 2 deletions api/osrd_infra/models/train_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ class TrainScheduleModel(models.Model):
departure_time = models.FloatField()
path = models.ForeignKey(PathModel, on_delete=models.CASCADE)
initial_speed = models.FloatField()
labels = models.JSONField(default=[], validators=[PydanticValidator(TrainScheduleLabels)])
allowances = models.JSONField(default=[], validators=[PydanticValidator(Allowances)])
labels = models.JSONField(default=list, validators=[PydanticValidator(TrainScheduleLabels)])
allowances = models.JSONField(default=list, validators=[PydanticValidator(Allowances)])
mrsp = models.JSONField(validators=[PydanticValidator(MRSP)])
base_simulation = models.JSONField()
eco_simulation = models.JSONField(null=True)
speed_limit_composition = models.CharField(max_length=128, null=True)
comfort = models.CharField(
max_length=8, choices=[(x.value, x.name) for x in ComfortType], default=ComfortType.STANDARD
)
modes_and_profiles = models.JSONField(default=list)
15 changes: 9 additions & 6 deletions api/osrd_infra/schemas/external_generated_inputs.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
from typing import List
from typing import Dict, List

from pydantic import BaseModel, Field

from .infra import TrackRange


class ElectricalProfile(BaseModel):
"""This class is used to define the electrical profile of a track section.
"""This class is used to model the power loss along a catenary (thus along ranges of track sections).
There should be one value per power class, on every electrified track."""

value: str = Field(description="Category of power loss along the range")
power_class: str = Field(description="Category of rolling stock power usage this profile applies to")
track_ranges: List[TrackRange] = Field(description="List of locations where this profile is applied")


class ElectricalProfilesList(BaseModel):
"""This class is used for storage schema validation."""
class ElectricalProfileSet(BaseModel):
"""This class is used to represent a set of electrical profiles, to use in simulation along an infrastructure."""

__root__: List[ElectricalProfile]
levels: List[ElectricalProfile] = Field(description="The list of electrical profiles")
level_order: Dict[str, List[str]] = Field(
description="A mapping from catenary modes to the electrical profile levels in decreasing order of magnitude"
)


if __name__ == "__main__":
print(ElectricalProfile.schema_json(indent=4))
print(ElectricalProfileSet.schema_json(indent=4))
5 changes: 3 additions & 2 deletions api/osrd_infra/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import Dict
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer, Serializer
from rest_framework_gis.fields import GeometryField
Expand Down Expand Up @@ -131,14 +132,14 @@ class Meta:
path = serializers.PrimaryKeyRelatedField(queryset=PathModel.objects.all())
schedules = serializers.ListField(min_length=1, child=Schedule())

def validate(self, data):
def validate(self, data: Dict) -> Dict:
path = data["path"]
timetable = data["timetable"]
if path.infra != timetable.infra:
raise serializers.ValidationError("path and timteable doesn't have the same infra")
return data

def create(self, validated_data):
def create(self, validated_data) -> list[TrainScheduleModel]:
schedules = []
timetable = validated_data["timetable"]
path = validated_data["path"]
Expand Down
1 change: 1 addition & 0 deletions api/osrd_infra/tests/test_stdcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def mock_api_call(payload):
"base_simulations": sim,
"eco_simulations": sim,
"speed_limits": [[]],
"modes_and_profiles": [[]]
},
"departure_time": 0,
}
Expand Down
Loading