Source code for openalea.fspmwheat.elongwheat_facade

# -*- coding: latin-1 -*-
import numpy as np

from openalea.elongwheat import converter, simulation
from openalea.fspmwheat import tools

"""
    fspmwheat.elongwheat_facade
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~

    The module :mod:`fspmwheat.elongwheat_facade` is a facade of the model ElongWheat.

    This module permits to initialize and run the model ElongWheat from a :class:`MTG <openalea.mtg.mtg.MTG>`
    in a convenient and transparent way, wrapping all the internal complexity of the model, and dealing
    with all the tedious initialization and conversion processes.

"""

SHARED_AXES_INPUTS_OUTPUTS_INDEXES = ['plant', 'axis']

SHARED_HIDDENZONES_INPUTS_OUTPUTS_INDEXES = ['plant', 'axis', 'metamer']

SHARED_ELEMENTS_INPUTS_OUTPUTS_INDEXES = ['plant', 'axis', 'metamer', 'organ', 'element']

ELEMENT_TYPES = ('HiddenElement', 'StemElement', 'LeafElement1')

ELEMENTS_DATA_FROM_ORGAN_DATA = {'width', 'diameter'}


[docs] class ElongWheatFacade(object): """ The ElongWheatFacade class permits to initialize, run the model ElongWheat from a :class:`MTG <openalea.mtg.mtg.MTG>`, and update the MTG and the dataframes shared between all models. Use :meth:`run` to run the model. """ def __init__(self, shared_mtg, delta_t, model_axes_inputs_df, model_hiddenzones_inputs_df, model_elements_inputs_df, shared_axes_inputs_outputs_df, shared_hiddenzones_inputs_outputs_df, shared_elements_inputs_outputs_df, geometrical_model, phytoT=None, update_parameters=None, update_shared_df=True, option_static=False): """ :param openalea.mtg.mtg.MTG shared_mtg: The MTG shared between all models. :param int delta_t: The delta between two runs, in seconds. :param pandas.DataFrame model_axes_inputs_df: the inputs of the model at axes scale. :param pandas.DataFrame model_hiddenzones_inputs_df: the inputs of the model at hiddenzones scale. :param pandas.DataFrame model_elements_inputs_df: the inputs of the model at elements scale. :param pandas.DataFrame shared_axes_inputs_outputs_df: the dataframe of inputs and outputs at axes scale shared between all models. :param pandas.DataFrame shared_hiddenzones_inputs_outputs_df: the dataframe of inputs and outputs at hiddenzones scale shared between all models. :param pandas.DataFrame shared_elements_inputs_outputs_df: the dataframe of inputs and outputs at elements scale shared between all models. :param openalea.adel.adel_dynamic.AdelWheatDyn geometrical_model: The model which deals with geometry. This model must implement a method `add_metamer(mtg, phytoT, plant_index, axis_label)` to add a metamer to a specific axis of a plant in a MTG. :param str phytoT: a csv file generated by adel model :param dict update_parameters: A dictionary with the parameters to update, should have the form {'param1': value1, 'param2': value2, ...}. :param bool update_shared_df: If `True` update the shared dataframes at init and at each run (unless stated otherwise) :param bool option_static: Whether the model should be run for a static plant architecture """ self._shared_mtg = shared_mtg #: the MTG shared between all models self._simulation = simulation.Simulation(delta_t=delta_t, update_parameters=update_parameters) #: the simulator to use to run the model self.geometrical_model = geometrical_model #: the model which deals with geometry self._phytoT = phytoT #: dataframe generated by adel and needed by the method add_metamer() all_elongwheat_inputs_dict = converter.from_dataframes(model_hiddenzones_inputs_df, model_elements_inputs_df, model_axes_inputs_df) self._update_shared_MTG(all_elongwheat_inputs_dict['hiddenzone'], all_elongwheat_inputs_dict['elements'], all_elongwheat_inputs_dict['axes'], option_static) self._shared_axes_inputs_outputs_df = shared_axes_inputs_outputs_df #: the dataframe at axes scale shared between all models self._shared_hiddenzones_inputs_outputs_df = shared_hiddenzones_inputs_outputs_df #: the dataframe at hiddenzones scale shared between all models self._shared_elements_inputs_outputs_df = shared_elements_inputs_outputs_df #: the dataframe at elements scale shared between all models self._update_shared_df = update_shared_df if self._update_shared_df: self._update_shared_dataframes(model_hiddenzones_inputs_df, model_elements_inputs_df, model_axes_inputs_df)
[docs] def run(self, Tair, Tsoil, option_static=False, optimal_growth_option=False, update_shared_df=None): """ Run the model and update the MTG and the dataframes shared between all models. :param float Tair: Air temperature at t (degree Celsius) :param float Tsoil: Soil temperature at t (degree Celsius) :param bool option_static: Whether the model should be run for a static plant architecture :param bool optimal_growth_option: if True the model will assume optimal growth conditions :param bool update_shared_df: if 'True', update the shared dataframes at this time step. """ self._initialize_model() self._simulation.run(Tair, Tsoil, optimal_growth_option) self._update_shared_MTG(self._simulation.outputs['hiddenzone'], self._simulation.outputs['elements'], self._simulation.outputs['axes'], option_static) if update_shared_df or (update_shared_df is None and self._update_shared_df): elongwheat_hiddenzones_outputs_df, elongwheat_elements_outputs_df, elongwheat_SAM_temperature_outputs_df = converter.to_dataframes(self._simulation.outputs) self._update_shared_dataframes(elongwheat_hiddenzones_outputs_df, elongwheat_elements_outputs_df, elongwheat_SAM_temperature_outputs_df)
def _initialize_model(self): """ Initialize the inputs of the model from the MTG shared between all models. """ all_elongwheat_hiddenzones_dict = {} all_elongwheat_elements_dict = {} all_elongwheat_SAM_temperature_dict = {} all_elongwheat_length_dict = {} elongwheat_cumulated_internode_length = {} for mtg_plant_vid in self._shared_mtg.components_iter(self._shared_mtg.root): mtg_plant_index = int(self._shared_mtg.index(mtg_plant_vid)) # Axis scale for mtg_axis_vid in self._shared_mtg.components_iter(mtg_plant_vid): mtg_axis_label = self._shared_mtg.label(mtg_axis_vid) # if mtg_axis_label != 'MS': # continue mtg_axis_properties = self._shared_mtg.get_vertex_property(mtg_axis_vid) axis_id = (mtg_plant_index, mtg_axis_label) elongwheat_SAM_temperature_inputs_dict = {} is_valid_axis = True for axis_input_name in simulation.AXIS_INPUTS: if axis_input_name in mtg_axis_properties: # use the input from the MTG elongwheat_SAM_temperature_inputs_dict[axis_input_name] = mtg_axis_properties[axis_input_name] else: is_valid_axis = False break if is_valid_axis: all_elongwheat_SAM_temperature_dict[axis_id] = elongwheat_SAM_temperature_inputs_dict # Complete dict of lengths all_elongwheat_length_dict[axis_id] = {} for i in range(mtg_axis_properties['nb_leaves']): all_elongwheat_length_dict[axis_id][i + 1] = {'sheath': [], 'cumulated_internode': []} elongwheat_cumulated_internode_length[axis_id] = [] # Metamer scale for mtg_metamer_vid in self._shared_mtg.components_iter(mtg_axis_vid): mtg_metamer_index = int(self._shared_mtg.index(mtg_metamer_vid)) elongwheat_hiddenzone_data_from_mtg_organs_data = {} # TODO: a voir si c'est toujours utile mtg_metamer_properties = self._shared_mtg.get_vertex_property(mtg_metamer_vid) if 'hiddenzone' in mtg_metamer_properties: hiddenzone_id = (mtg_plant_index, mtg_axis_label, mtg_metamer_index) mtg_hiddenzone_properties = mtg_metamer_properties['hiddenzone'] elongwheat_hiddenzone_inputs_dict = {} is_valid_hiddenzone = True for hiddenzone_input_name in simulation.HIDDENZONE_INPUTS: if hiddenzone_input_name in mtg_hiddenzone_properties: # use the input from the MTG elongwheat_hiddenzone_inputs_dict[hiddenzone_input_name] = mtg_hiddenzone_properties[hiddenzone_input_name] elif hiddenzone_input_name in elongwheat_hiddenzone_data_from_mtg_organs_data: elongwheat_hiddenzone_inputs_dict[hiddenzone_input_name] = elongwheat_hiddenzone_data_from_mtg_organs_data[hiddenzone_input_name] else: print('Invalid hiddenzone {}: input {} is missing'.format(hiddenzone_id, hiddenzone_input_name)) is_valid_hiddenzone = False break if is_valid_hiddenzone: all_elongwheat_hiddenzones_dict[hiddenzone_id] = elongwheat_hiddenzone_inputs_dict # Complete dict of lengths if mtg_hiddenzone_properties['leaf_is_emerged'] and mtg_hiddenzone_properties['leaf_is_growing']: growing_sheath_length = max(0, mtg_hiddenzone_properties['leaf_L'] - mtg_hiddenzone_properties['lamina_Lmax']) # TODO: mettre ce calcul ailleurs certainement. all_elongwheat_length_dict[axis_id][mtg_metamer_index]['sheath'].append(growing_sheath_length) if mtg_hiddenzone_properties['internode_is_growing']: elongwheat_cumulated_internode_length[axis_id].append(mtg_hiddenzone_properties['internode_L']) all_elongwheat_length_dict[axis_id][mtg_metamer_index]['cumulated_internode'].extend(elongwheat_cumulated_internode_length[axis_id]) else: internode_organ_vid = self._shared_mtg.components_at_scale(mtg_metamer_vid, 4)[0] assert self._shared_mtg.label(internode_organ_vid) == 'internode' internode_element_labels = [self._shared_mtg.label(internode_element) for internode_element in self._shared_mtg.components_at_scale(internode_organ_vid, 5)] if internode_element_labels == ['baseElement', 'topElement']: all_elongwheat_length_dict[axis_id][mtg_metamer_index]['cumulated_internode'].extend(elongwheat_cumulated_internode_length[axis_id]) # Organ scale for mtg_organ_vid in self._shared_mtg.components_iter(mtg_metamer_vid): mtg_organ_label = self._shared_mtg.label(mtg_organ_vid) mtg_organ_properties = self._shared_mtg.get_vertex_property(mtg_organ_vid) if np.nan_to_num(self._shared_mtg.property('length').get(mtg_organ_vid, 0)) == 0: continue if mtg_organ_label == 'blade': elongwheat_hiddenzone_data_from_mtg_organs_data['lamina_Lmax'] = mtg_organ_properties['shape_mature_length'] elongwheat_hiddenzone_data_from_mtg_organs_data['leaf_Wmax'] = mtg_organ_properties['shape_max_width'] # Element scale for mtg_element_vid in self._shared_mtg.components_iter(mtg_organ_vid): mtg_element_label = self._shared_mtg.label(mtg_element_vid) mtg_element_properties = self._shared_mtg.get_vertex_property(mtg_element_vid) if np.nan_to_num(self._shared_mtg.property('length').get(mtg_element_vid, 0)) == 0: continue if np.isnan(mtg_element_properties.get('age', 0)): mtg_element_properties['age'] = 0. if set(mtg_element_properties).issuperset(simulation.ELEMENT_INPUTS): elongwheat_element_inputs_dict = {} for elongwheat_element_input_name in simulation.ELEMENT_INPUTS: elongwheat_element_inputs_dict[elongwheat_element_input_name] = mtg_element_properties[elongwheat_element_input_name] element_id = (mtg_plant_index, mtg_axis_label, mtg_metamer_index, mtg_organ_label, mtg_element_label) all_elongwheat_elements_dict[element_id] = elongwheat_element_inputs_dict # Complete dict of lengths if mtg_organ_label == 'sheath' and not mtg_element_properties['is_growing']: all_elongwheat_length_dict[axis_id][mtg_metamer_index]['sheath'].append(mtg_element_properties['length']) elif mtg_organ_label == 'internode' and not mtg_element_properties['is_growing']: # This algo won't copy previous internode length for a phytomer without internode elongwheat_cumulated_internode_length[axis_id].append(mtg_element_properties['length']) if all_elongwheat_length_dict[axis_id][mtg_metamer_index]['cumulated_internode'] is None: # if empty for that phytomer, the list of all phytomer lengths is all_elongwheat_length_dict[axis_id][mtg_metamer_index]['cumulated_internode'].extend(elongwheat_cumulated_internode_length[axis_id]) else: # only the last internode length is written (case of organs with hidden and visible part) all_elongwheat_length_dict[axis_id][mtg_metamer_index]['cumulated_internode'].append(mtg_element_properties['length']) self._simulation.initialize({'hiddenzone': all_elongwheat_hiddenzones_dict, 'elements': all_elongwheat_elements_dict, 'axes': all_elongwheat_SAM_temperature_dict, 'sheath_internode_lengths': all_elongwheat_length_dict}) def _update_shared_MTG(self, all_elongwheat_hiddenzones_data_dict, all_elongwheat_elements_data_dict, all_elongwheat_axes_data_dict, option_static=False): """ Update the MTG shared between all models from the inputs or the outputs of the model. :param dict all_elongwheat_hiddenzones_data_dict: Elong-Wheat outputs at hidden zone scale :param dict all_elongwheat_elements_data_dict: Elong-Wheat outputs at element scale :param dict all_elongwheat_axes_data_dict: Elong-Wheat outputs at axes scale :param bool option_static: Whether the model should be run for a static plant architecture """ # add the properties if needed mtg_property_names = self._shared_mtg.property_names() for elongwheat_data_name in set(simulation.HIDDENZONE_INPUTS_OUTPUTS + simulation.ELEMENT_INPUTS_OUTPUTS + simulation.AXIS_INPUTS_OUTPUTS): if elongwheat_data_name not in mtg_property_names: self._shared_mtg.add_property(elongwheat_data_name) if 'hiddenzone' not in mtg_property_names: self._shared_mtg.add_property('hiddenzone') # add new metamer(s) if not option_static: axis_to_metamers_mapping = {} for metamer_id in sorted(all_elongwheat_elements_data_dict.keys()): axis_id = (metamer_id[0], metamer_id[1]) if axis_id not in axis_to_metamers_mapping: axis_to_metamers_mapping[axis_id] = [] axis_to_metamers_mapping[axis_id].append(metamer_id[:3]) for metamer_id in sorted(all_elongwheat_hiddenzones_data_dict.keys()): axis_id = (metamer_id[0], metamer_id[1]) if axis_id not in axis_to_metamers_mapping: axis_to_metamers_mapping[axis_id] = [] axis_to_metamers_mapping[axis_id].append(metamer_id) for mtg_plant_vid in self._shared_mtg.components_iter(self._shared_mtg.root): mtg_plant_index = int(self._shared_mtg.index(mtg_plant_vid)) for mtg_axis_vid in self._shared_mtg.components_iter(mtg_plant_vid): mtg_axis_label = self._shared_mtg.label(mtg_axis_vid) mtg_metamer_ids = set([(mtg_plant_index, mtg_axis_label, int(self._shared_mtg.index(mtg_metamer_vid))) for mtg_metamer_vid in self._shared_mtg.components_iter(mtg_axis_vid)]) if (mtg_plant_index, mtg_axis_label) not in axis_to_metamers_mapping: continue new_metamer_ids = set(axis_to_metamers_mapping[(mtg_plant_index, mtg_axis_label)]).difference(mtg_metamer_ids) for _ in new_metamer_ids: self.geometrical_model.add_metamer(self._shared_mtg, self._phytoT, plant=mtg_plant_index, axe=mtg_axis_label) # Add new metamer with only top and base element # default value to age property in order to run update_geometry() for mtg_organ_vid in self._shared_mtg.components_at_scale(mtg_axis_vid, 4): if self._shared_mtg.property('label')[mtg_organ_vid] == 'blade': self._shared_mtg.property('age')[mtg_organ_vid] = self._shared_mtg.property('age').get(mtg_organ_vid, 0) self.geometrical_model.update_geometry(self._shared_mtg) # Add HiddenElement and Stem/Leaf Element # update the properties of the MTG for mtg_plant_vid in self._shared_mtg.components_iter(self._shared_mtg.root): mtg_plant_index = int(self._shared_mtg.index(mtg_plant_vid)) # Axis scale for mtg_axis_vid in self._shared_mtg.components_iter(mtg_plant_vid): mtg_axis_label = self._shared_mtg.label(mtg_axis_vid) axis_id = (mtg_plant_index, mtg_axis_label) if axis_id in all_elongwheat_axes_data_dict: elongwheat_axis_data_dict = all_elongwheat_axes_data_dict[axis_id] for axis_data_name, axis_data_value in elongwheat_axis_data_dict.items(): self._shared_mtg.property(axis_data_name)[mtg_axis_vid] = axis_data_value if option_static: continue # metamer scale for mtg_metamer_vid in self._shared_mtg.components_iter(mtg_axis_vid): mtg_metamer_index = int(self._shared_mtg.index(mtg_metamer_vid)) hiddenzone_id = (mtg_plant_index, mtg_axis_label, mtg_metamer_index) mtg_organs_data_from_elongwheat_hiddenzone_data = {} if hiddenzone_id in all_elongwheat_hiddenzones_data_dict: elongwheat_hiddenzone_data_dict = all_elongwheat_hiddenzones_data_dict[hiddenzone_id] mtg_metamer_properties = self._shared_mtg.get_vertex_property(mtg_metamer_vid) if 'hiddenzone' not in mtg_metamer_properties: self._shared_mtg.property('hiddenzone')[mtg_metamer_vid] = {} for hiddenzone_data_name, hiddenzone_data_value in elongwheat_hiddenzone_data_dict.items(): self._shared_mtg.property('hiddenzone')[mtg_metamer_vid][hiddenzone_data_name] = hiddenzone_data_value if hiddenzone_data_name in ('lamina_Lmax', 'leaf_Wmax'): mtg_organs_data_from_elongwheat_hiddenzone_data[hiddenzone_data_name] = hiddenzone_data_value # To be stored at organ scale (see below) elif 'hiddenzone' in self._shared_mtg.get_vertex_property(mtg_metamer_vid): # remove the 'hiddenzone' property from this metamer del self._shared_mtg.property('hiddenzone')[mtg_metamer_vid] # Organ scale for mtg_organ_vid in self._shared_mtg.components_iter(mtg_metamer_vid): mtg_organ_label = self._shared_mtg.label(mtg_organ_vid) if mtg_organ_label not in ('blade', 'sheath', 'internode'): continue organ_id = (mtg_plant_index, mtg_axis_label, mtg_metamer_index, mtg_organ_label) mtg_organ_properties = self._shared_mtg.get_vertex_property(mtg_organ_vid) mtg_elements_data_from_organ_data = {} # Extract data from organs to be stored at element scale (see below) for organ_data_name in ELEMENTS_DATA_FROM_ORGAN_DATA: if organ_data_name in mtg_organ_properties.keys(): mtg_elements_data_from_organ_data[organ_data_name] = mtg_organ_properties[organ_data_name] # Data from hidden zones to be stored at organ scale if mtg_organ_label == 'blade': if len(mtg_organs_data_from_elongwheat_hiddenzone_data) != 0: self._shared_mtg.property('shape_mature_length')[mtg_organ_vid] = mtg_organs_data_from_elongwheat_hiddenzone_data['lamina_Lmax'] self._shared_mtg.property('shape_max_width')[mtg_organ_vid] = mtg_organs_data_from_elongwheat_hiddenzone_data['leaf_Wmax'] else: blade_id = organ_id + ('LeafElement1',) if blade_id in all_elongwheat_elements_data_dict.keys(): self._shared_mtg.property('shape_mature_length')[mtg_organ_vid] = all_elongwheat_elements_data_dict[organ_id + ('LeafElement1',)]['length'] self._shared_mtg.property('shape_max_width')[mtg_organ_vid] = all_elongwheat_elements_data_dict[organ_id + ('LeafElement1',)]['Wmax'] # Update of organ scale from elements dataframe # Organ length should be correct in order to get correct lengths at both organ and element scales after performing the update_geometry() organ_visible_length = all_elongwheat_elements_data_dict.get(organ_id + ('LeafElement1',), {}).get('length', 0.) + \ all_elongwheat_elements_data_dict.get(organ_id + ('StemElement',), {}).get('length', 0.) self._shared_mtg.property('visible_length')[mtg_organ_vid] = organ_visible_length self._shared_mtg.property('age')[mtg_organ_vid] = all_elongwheat_elements_data_dict.get(organ_id + ('LeafElement1',), {}).get('age', 0.) organ_hidden_length = all_elongwheat_elements_data_dict.get(organ_id + ('HiddenElement',), {}).get('length', 0.) total_organ_length = organ_visible_length + organ_hidden_length self._shared_mtg.property('length')[mtg_organ_vid] = total_organ_length # Element scale. Most of the code is temporary, waiting for an update of adel in order that the model could update organ properties from elements. mtg_element_labels = {} for actual_element_vid in self._shared_mtg.components_iter(mtg_organ_vid): actual_element_label = self._shared_mtg.label(actual_element_vid) mtg_element_labels[actual_element_label] = actual_element_vid potential_element_ids = [organ_id + (element_type,) for element_type in ELEMENT_TYPES] for element_id in potential_element_ids: element_label = element_id[-1] if element_id in all_elongwheat_elements_data_dict: elongwheat_element_data_dict = all_elongwheat_elements_data_dict[element_id] # Element just created by elongwheat but not yet in MTG if element_label not in mtg_element_labels.keys(): # MG : This case does not seem to be usefull if element_label in ('StemElement', 'LeafElement1'): self._shared_mtg.property('visible_length')[mtg_organ_vid] = elongwheat_element_data_dict['length'] self.geometrical_model.update_geometry(self._shared_mtg) # Update element scale based on organ infos mtg_element_vid = [vid for vid in self._shared_mtg.components_iter(mtg_organ_vid) if self._shared_mtg.label(vid) == element_label] # if self._shared_mtg.label(vid) in ('StemElement', 'LeafElement1') for element_data_name, element_data_value in elongwheat_element_data_dict.items(): self._shared_mtg.property(element_data_name)[mtg_element_vid[0]] = element_data_value # Already existant element else: mtg_element_vid = mtg_element_labels[element_label] for element_data_name, element_data_value in elongwheat_element_data_dict.items(): self._shared_mtg.property(element_data_name)[mtg_element_vid] = element_data_value # Put some properties from organ scale at element scale for organ_data_name in mtg_elements_data_from_organ_data: self._shared_mtg.property(organ_data_name)[mtg_element_vid] = mtg_elements_data_from_organ_data[organ_data_name] # # update of organ scale from elements # new_mtg_element_labels = {} # for new_element_vid in self._shared_mtg.components_iter(mtg_organ_vid): # new_element_label = self._shared_mtg.label(new_element_vid) # new_mtg_element_labels[new_element_label] = new_element_vid # # if mtg_organ_label == 'blade' and 'LeafElement1' in new_mtg_element_labels.keys(): # organ_visible_length = self._shared_mtg.property('length')[new_mtg_element_labels['LeafElement1']] # self._shared_mtg.property('visible_length')[mtg_organ_vid] = organ_visible_length # self._shared_mtg.property('age')[mtg_organ_vid] = self._shared_mtg.property('age').get(new_mtg_element_labels['LeafElement1'], 0) # elif mtg_organ_label in ('sheath', 'internode') and 'StemElement' in new_mtg_element_labels.keys(): # organ_visible_length = self._shared_mtg.property('length')[new_mtg_element_labels['StemElement']] # self._shared_mtg.property('visible_length')[mtg_organ_vid] = organ_visible_length # else: # organ_visible_length = 0 # # if 'HiddenElement' in new_mtg_element_labels.keys(): # organ_hidden_length = self._shared_mtg.property('length')[new_mtg_element_labels['HiddenElement']] # else: # organ_hidden_length = 0 # # total_organ_length = organ_visible_length + organ_hidden_length # self._shared_mtg.property('length')[mtg_organ_vid] = total_organ_length def _update_shared_dataframes(self, elongwheat_hiddenzones_data_df, elongwheat_elements_data_df, elongwheat_axes_data_df): """ Update the dataframes shared between all models from the inputs dataframes or the outputs dataframes of the model. :param pandas.DataFrame elongwheat_hiddenzones_data_df: Elong-Wheat shared dataframe at hidden zone scale :param pandas.DataFrame elongwheat_elements_data_df: Elong-Wheat shared dataframe at element scale :param pandas.DataFrame elongwheat_axes_data_df: Elong-Wheat shared dataframe at axes scale """ for elongwheat_data_df, \ shared_inputs_outputs_indexes, \ shared_inputs_outputs_df in ((elongwheat_hiddenzones_data_df, SHARED_HIDDENZONES_INPUTS_OUTPUTS_INDEXES, self._shared_hiddenzones_inputs_outputs_df), (elongwheat_elements_data_df, SHARED_ELEMENTS_INPUTS_OUTPUTS_INDEXES, self._shared_elements_inputs_outputs_df), (elongwheat_axes_data_df, SHARED_AXES_INPUTS_OUTPUTS_INDEXES, self._shared_axes_inputs_outputs_df)): tools.combine_dataframes_inplace(elongwheat_data_df, shared_inputs_outputs_indexes, shared_inputs_outputs_df)