Orekit/Dash integration issue

All

I have a rather unusual issue. I am doing some complex computations for which I need to use the Java Orekit library, and I want to put in a progress bar so users don’t get confused as to why the code is taking a minute to give them results. I managed to implement the progress bar just fine, and the Java code was working just fine before I put in the progress bar, but when I put the two together the progress bar stays up and doesn’t come back down until I push the “cancel” button, even if the code being run is a very simple test function. Here is the version of the code that works normally (i.e. the Java was removed)

# Calculates the satellite pass values
@callback(
    output=[
        Output('time-series-chart', 'figure', allow_duplicate=True),],
    inputs=[
        Input('calculate-passes-button', 'n_clicks'),],
    background=True,
    running=[
        (Output('calculate-passes-button', 'disabled'), True, False),
        (Output('progress_bar_modal', 'is_open'), True, False)],
    progress=[
        Output("progress_bar", "value"),
        Output("progress_bar", "max")],
    cancel=[
        Input('cancel_progress_modal_button', 'n_clicks')],
    prevent_initial_call=True
)
def calculate_satellite_passes(set_progress, n_clicks):
    
    return_value = 1
    
    msg = "Test message"
    with concurrent.futures.ThreadPoolExecutor() as executor:
        future = executor.submit(test_function, msg)
        return_value = future.result()
    
    print(return_value)
    set_progress((str(return_value), str(return_value)))
    
    fig = go.Figure()
    
    return [fig]


def test_function(msg) :
    
    print(msg)
        
    return 3

And here is the code that freezes on me (i.e. java added)

# Calculates the satellite pass values
@callback(
    output=[
        Output('time-series-chart', 'figure', allow_duplicate=True),],
    inputs=[
        Input('calculate-passes-button', 'n_clicks'),],
    background=True,
    running=[
        (Output('calculate-passes-button', 'disabled'), True, False),
        (Output('progress_bar_modal', 'is_open'), True, False)],
    progress=[
        Output("progress_bar", "value"),
        Output("progress_bar", "max")],
    cancel=[
        Input('cancel_progress_modal_button', 'n_clicks')],
    prevent_initial_call=True
)
def calculate_satellite_passes(set_progress, n_clicks):
    
    return_value = 1
    
    msg = "Test message"
    with concurrent.futures.ThreadPoolExecutor() as executor:
        future = executor.submit(test_function, msg)
        return_value = future.result()
    
    print(return_value)
    set_progress((str(return_value), str(return_value)))
    
    fig = go.Figure()
    
    return [fig]


def test_function(msg) :
    
    print(msg)
    
    # Attaches the thread to the Java virtual environment
        # Must be placed at the start of any function that uses Orekit!!!
    if orekit.getVMEnv().isCurrentThreadAttached() is not True:
        orekit.getVMEnv().attachCurrentThread()   
        
    utc_scale = TimeScalesFactory.getUTC()
    
    # Detaches the thread from the Java virtual environment
    if orekit.getVMEnv().isCurrentThreadAttached() :
        orekit.getVMEnv().detachCurrentThread()
        
    return 3

To be clear, the Java code works just fine with the progress bar removed, and the progress bar works just fine with the Java code removed, it’s only the combination that fails to work properly.

Note: The multithreading was implemented in the hopes that moving the Orekit code to an unambiguously separate thread would resolve the problem. It did not. I have no inherent problem with a solution that does not require multithreading as long as it works.

I figured out a decent workaround interim solution. Basically I created a modal with a “your data is processing” message, and triggered it to open when the “calculate” button was clicked and then close when the calculation results updated.

@callback(
    [Output('progress-modal', 'is_open', allow_duplicate=True),],
    [Input('calculate-passes-button', 'n_clicks'),],
    prevent_initial_call=True
)
def open_progress_modal(_) :
    return [True]


@callback(
    [Output('progress-modal', 'is_open', allow_duplicate=True),],
    [Input('time-series-chart', 'figure'),],
    prevent_initial_call=True
)
def close_progress_modal(_) :
    return [False]

It’s not a perfect solution, but it gets most of the job done. Ideally would still like a process bar , but I can probably come up with a multithreading solution for that.

1 Like