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

Model reference architecture & Cohesion between Rust and Python #12

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

joeyame
Copy link
Owner

@joeyame joeyame commented Jul 27, 2022

This pull request contains the following improvements:

  1. Python models can now create rust models with only 1 line of code.
  2. Switched the source.sh file to use absolute paths to ensure all commands work no matter what level of the repository you are in.

@joeyame joeyame linked an issue Jul 27, 2022 that may be closed by this pull request
@joeyame joeyame self-assigned this Jul 27, 2022
@joeyame
Copy link
Owner Author

joeyame commented Jul 28, 2022

Added functionality to pass arguments to sim setup tools (python scripts)

Now, when using the "sim run {scenario}" command, any arguments passed after the scenario will be made available to the python scripts that set up every scenario. They can then be processed to determine which models to activate, which inputs to set, etc. The limitation is your imagination!

@joeyame
Copy link
Owner Author

joeyame commented Jul 29, 2022

Initial cut of model reference architecture

Model references can be made and managed with the ReferenceList. This is a struct that contains a vector of int "ids" and a vector of weak model pointers. (All models should use weak pointers to each other to keep the sim from leaking memory in the case of circular dependencies).

The IDs are filled out by the python input infrastructure, and then the first step in the runtime after creating all the models is going into those references and filling out their pointer list with the pointers that correspond to the IDs already there.

Model references can either be explicitly passed in:

# Create models here
force = ForceEffector( 0, 10, 20, ModelBase( 100, 200 ) )
eom = EOM(
    x=0,
    y=0,
    z=0,
    force_effectors=ReferenceList( force ),
    base=ModelBase( 100, 200 )
)

or they can be grouped together. All models that are grouped together have references to each other - independent of their type. All of this is handled on the python input side.

ModelGroup( eom, force )

These group references, or "local_refs," also do not need to be manually handled on the Rust side. Instead, it is all automatically taken care of by the runtime:

pub trait SimModelTrait: Debug {
    fn resolve_references( &mut self, global_model_list: &HashMap<ModelID, ModelPtr> ) {
        let locals = &mut self.get_details().local_refs;
        for id in &locals.reference_ids {
            // println!( "{}", id );
            locals.reference_list.push( 
                Rc::downgrade( &global_model_list.get( id ).unwrap() )
            )
        }
    }
}

Models like EOM can then choose to use their own ForceEffector lists, the local list (if in a group), or even (preferred) move ForceEffectors from the local list into the ForceEffector list.

Unfortunately as it is, using these references is extremely verbose:

        for reference in &self.base.local_refs.reference_list {
            let upgraded = reference.upgrade().unwrap();
            let mut contents = upgraded.deref().borrow_mut();
            if let Some( force_model ) = contents.as_any().downcast_mut::<ForceEffector>() {
                println!( " - Force ID: {}", force_model.get_details().id );
                force_model.get_details().id = 5;
                println!( " - Force ID (new): {}", force_model.get_details().id );
            }
        }

My focus now will be creating a more streamlined way to create and access these references.

@joeyame
Copy link
Owner Author

joeyame commented Jul 30, 2022

In trying to bring a lot of the boilerplate into the class itself, I have determined that the ReferenceList architecture has some cumbersome limitations, namely that it is impossible to go from SimModelTrait RefCells to Sim Model RefCells.

It appears as though I might have to make my own kind of RefCell that has this functionality baked in.

@joeyame
Copy link
Owner Author

joeyame commented Aug 28, 2022

After spending a long time investigating my previous report, I have found an alternative way this could work - Enums. Here is a minimal-reproducible example.

use std::{rc::{Rc, Weak}, cell::RefCell, borrow::Borrow};

trait SimModelTrait
{
    fn doSomething(&self);
}

/////////////////////////////////// POINT
struct Point
{
    x: f64,
    y: f64,
}

impl SimModelTrait for Point
{
    fn doSomething(&self) {}
}

impl FromModel for Point {
    fn from_model_mutref(model: &mut Models) -> &mut Self {
        match model {
            Models::Point( point ) => point,
            _ => panic!("Tried")
        }
    }
}

///////////////////////////////////// Models
enum Models
{
    Point( Point )
}

impl Models {
    fn downcast_mut<T: FromModel>( &mut self ) -> &mut T {
        FromModel::from_model_mutref( self )
    }
}

trait FromModel {
    fn from_model_mutref( model: &mut Models ) -> &mut Self;
}

fn main()
{
    let mut point = Models::Point( Point { x: 0.0, y: 1.01 } );

    let concrete = point.downcast_mut::<Point>();
    concrete.y = 1000000.0;

    let concrete2 = point.downcast_mut::<Point>();
    concrete2.x = 1992.0;

    if let Models::Point( p ) = point {
        println!("x: {}, y: {}", &p.x, &p.y);
    }
}

In this example, we can call "downcast_mut" on the Models enum to turn it into a specific type, or panic. This could also be handled with an Ok(T) or Err(()), to prevent crashes. As shown, it is clear that we can have multiple mutable references to the actual Model enum entity. This is the behavior I am going for, but I am unsure how safe it is to use with multiple threads. I very much want to implement it in a way that we can use a refcell to enforce borrowing. Further investigation will look into that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Scenario Arguments Add Model Reference Architecture More Cohesion Between Python Inputs and Actual Models
1 participant