docs: def get_model_elements(model_str):
      """
      Takes in a string representing model text and splits it into elements
  
      I think we're making the assumption that all newline characters are removed...
  
      Parameters
      ----------
      model_str : string
  
  
      Returns
      -------
      entries : array of dictionaries
          Each dictionary contains the components of a different model element, separated into the
          equation, units, and docstring.
  
      Examples
      --------
  
      # Basic Parsing:
      >>> get_model_elements(r'a~b~c| d~e~f| g~h~i|')
      [{'doc': 'c', 'unit': 'b', 'eqn': 'a'}, {'doc': 'f', 'unit': 'e', 'eqn': 'd'}, {'doc': 'i', 'unit': 'h', 'eqn': 'g'}]
  
      # Special characters are escaped within double-quotes:
      >>> get_model_elements(r'a~b~c| d~e"~"~f| g~h~i|')
      [{'doc': 'c', 'unit': 'b', 'eqn': 'a'}, {'doc': 'f', 'unit': 'e"~"', 'eqn': 'd'}, {'doc': 'i', 'unit': 'h', 'eqn': 'g'}]
      >>> get_model_elements(r'a~b~c| d~e~"|"f| g~h~i|')
      [{'doc': 'c', 'unit': 'b', 'eqn': 'a'}, {'doc': '"|"f', 'unit': 'e', 'eqn': 'd'}, {'doc': 'i', 'unit': 'h', 'eqn': 'g'}]
  
      # Double-quotes within escape groups are themselves escaped with backslashes:
      >>> get_model_elements(r'a~b~c| d~e"\\\"~"~f| g~h~i|')
      [{'doc': 'c', 'unit': 'b', 'eqn': 'a'}, {'doc': 'f', 'unit': 'e"\\\\"~"', 'eqn': 'd'}, {'doc': 'i', 'unit': 'h', 'eqn': 'g'}]
      >>> get_model_elements(r'a~b~c| d~e~"\\\"|"f| g~h~i|')
      [{'doc': 'c', 'unit': 'b', 'eqn': 'a'}, {'doc': '"\\\\"|"f', 'unit': 'e', 'eqn': 'd'}, {'doc': 'i', 'unit': 'h', 'eqn': 'g'}]
      >>> get_model_elements(r'a~b~c| d~e"x\\nx"~f| g~h~|')
      [{'doc': 'c', 'unit': 'b', 'eqn': 'a'}, {'doc': 'f', 'unit': 'e"x\\\\nx"', 'eqn': 'd'}, {'doc': '', 'unit': 'h', 'eqn': 'g'}]
  
      # Todo: Handle model-level or section-level documentation
      >>> get_model_elements(r'*** .model doc ***~ Docstring!| d~e~f| g~h~i|')
      [{'doc': 'Docstring!', 'unit': '', 'eqn': ''}, {'doc': 'f', 'unit': 'e', 'eqn': 'd'}, {'doc': 'i', 'unit': 'h', 'eqn': 'g'}]
  
      # Handle control sections, returning appropriate docstring pieces
      >>> get_model_elements(r'a~b~c| ****.Control***~ Simulation Control Parameters | g~h~i|')
      [{'doc': 'c', 'unit': 'b', 'eqn': 'a'}, {'doc': 'i', 'unit': 'h', 'eqn': 'g'}]
  
      # Handle the model display elements (ignore them)
      >>> get_model_elements(r'a~b~c| d~e~f| \\\---///junk|junk~junk')
      [{'doc': 'c', 'unit': 'b', 'eqn': 'a'}, {'doc': 'f', 'unit': 'e', 'eqn': 'd'}]
  
  
      Notes
      -----
      - Tildes and pipes are not allowed in element docstrings, but we should still handle them there
  
      """
  
      model_structure_grammar = _include_common_grammar(r"""
      model = (entry / section)+ sketch?
      entry = element "~" element "~" element ("~" element)? "|"
      section = element "~" element "|"
      sketch = ~r".*"  #anything
  
      # Either an escape group, or a character that is not tilde or pipe
      element = (escape_group / ~r"[^~|]")*
      """)
  
      parser = parsimonious.Grammar(model_structure_grammar)
      tree = parser.parse(model_str)
  
      class ModelParser(parsimonious.NodeVisitor):
          def __init__(self, ast):
              self.entries = []
              self.visit(ast)
  
          def visit_entry(self, n, vc):
              units, lims = parse_units(vc[2].strip())
              self.entries.append({'eqn': vc[0].strip(),
                                   'unit': units,
                                   'lims': str(lims),
                                   'doc': vc[4].strip(),
                                   'kind': 'entry'})
  
          def visit_section(self, n, vc):
              if vc[2].strip() != "Simulation Control Parameters":
                  self.entries.append({'eqn': '',
                                       'unit': '',
                                       'lims': '',
                                       'doc': vc[2].strip(),
                                       'kind': 'section'})
  
          def generic_visit(self, n, vc):
              return ''.join(filter(None, vc)) or n.text or ''
  
      return ModelParser(tree).entries