code: def _read_dipole_text(fname):
      """Read a dipole text file."""
      # Figure out the special fields
      need_header = True
      def_line = name = None
      # There is a bug in older np.loadtxt regarding skipping fields,
      # so just read the data ourselves (need to get name and header anyway)
      data = list()
      with open(fname) as fid:
          for line in fid:
              if not (line.startswith("%") or line.startswith("#")):
                  need_header = False
                  data.append(line.strip().split())
              else:
                  if need_header:
                      def_line = line
                  if line.startswith("##") or line.startswith("%%"):
                      m = re.search('Name "(.*) dipoles"', line)
                      if m:
                          name = m.group(1)
          del line
      data = np.atleast_2d(np.array(data, float))
      if def_line is None:
          raise OSError(
              "Dipole text file is missing field definition comment, cannot parse "
              f"{fname}"
          )
      # actually parse the fields
      def_line = def_line.lstrip("%").lstrip("#").strip()
      # MNE writes it out differently than Elekta, let's standardize them...
      fields = re.sub(
          r"([X|Y|Z] )\(mm\)",  # "X (mm)", etc.
          lambda match: match.group(1).strip() + "/mm",
          def_line,
      )
      fields = re.sub(
          r"\((.*?)\)",
          lambda match: "/" + match.group(1),
          fields,  # "Q(nAm)", etc.
      )
      fields = re.sub(
          "(begin|end) ",  # "begin" and "end" with no units
          lambda match: match.group(1) + "/ms",
          fields,
      )
      fields = fields.lower().split()
      required_fields = (
          "begin/ms",
          "x/mm",
          "y/mm",
          "z/mm",
          "q/nam",
          "qx/nam",
          "qy/nam",
          "qz/nam",
          "g/%",
      )
      optional_fields = (
          "khi^2",
          "free",  # standard ones
          # now the confidence fields (up to 5!)
          "vol/mm^3",
          "depth/mm",
          "long/mm",
          "trans/mm",
          "qlong/nam",
          "qtrans/nam",
      )
      conf_scales = [1e-9, 1e-3, 1e-3, 1e-3, 1e-9, 1e-9]
      missing_fields = sorted(set(required_fields) - set(fields))
      if len(missing_fields) > 0:
          raise RuntimeError(
              f"Could not find necessary fields in header: {missing_fields}"
          )
      handled_fields = set(required_fields) | set(optional_fields)
      assert len(handled_fields) == len(required_fields) + len(
          optional_fields
      )
      ignored_fields = sorted(
          set(fields) - set(handled_fields) - {"end/ms"}
      )
      if len(ignored_fields) > 0:
          warn(
              f"Ignoring extra fields in dipole file: {ignored_fields}"
          )
      if len(fields) != data.shape[1]:
          raise OSError(
              f"More data fields ({len(fields)}) found than data columns ({data.shape[1]}"
              f"): {fields}"
          )
  
      logger.info(f"{len(data)} dipole(s) found")
  
      if "end/ms" in fields:
          if np.diff(
              data[
                  :, [fields.index("begin/ms"), fields.index("end/ms")]
              ],
              1,
              -1,
          ).any():
              warn(
                  "begin and end fields differed, but only begin will be used "
                  "to store time values"
              )
  
      # Find the correct column in our data array, then scale to proper units
      idx = [fields.index(field) for field in required_fields]
      assert len(idx) >= 9
      times = data[:, idx[0]] / 1000.0
      pos = 1e-3 * data[:, idx[1:4]]  # put data in meters
      amplitude = data[:, idx[4]]
      norm = amplitude.copy()
      amplitude /= 1e9
      norm[norm == 0] = 1
      ori = data[:, idx[5:8]] / norm[:, np.newaxis]
      gof = data[:, idx[8]]
      # Deal with optional fields
      optional = [None] * 2
      for fi, field in enumerate(optional_fields[:2]):
          if field in fields:
              optional[fi] = data[:, fields.index(field)]
      khi2, nfree = optional
      conf = dict()
      for field, scale in zip(
          optional_fields[2:], conf_scales
      ):  # confidence
          if field in fields:
              conf[field.split("/")[0]] = (
                  scale * data[:, fields.index(field)]
              )
      return Dipole(
          times, pos, amplitude, ori, gof, name, conf, khi2, nfree
      )
  
imports: import functools
  import re
  from copy import deepcopy
  from functools import partial
  import numpy
  from scipy.linalg import eigh
  from scipy.optimize import fmin_cobyla
  from ._fiff.constants import FIFF
  from ._fiff.pick import pick_types
  from ._fiff.proj import _needs_eeg_average_ref_proj, make_projector
  from ._freesurfer import _get_aseg, head_to_mni, head_to_mri, read_freesurfer_lut
  from .bem import _bem_find_surface, _bem_surf_name, _fit_sphere
  from .cov import _ensure_cov, compute_whitener
  from .evoked import _aspect_rev, _read_evoked, _write_evokeds
  from .fixes import _safe_svd, pinvh
  from .forward._compute_forward import _compute_forwards_meeg, _prep_field_computation
  from .forward._make_forward import _get_trans, _prep_eeg_channels, _prep_meg_channels, _setup_bem
  from .parallel import parallel_func
  from .source_space._source_space import SourceSpaces, _make_volume_source_space
  from .surface import _compute_nearest, _points_outside_surface, transform_surface_to
  from .transforms import _coord_frame_name, _print_coord_trans, apply_trans
  from .utils import ExtendedTimeMixin, TimeMixin, _check_fname, _check_option, _get_blas_funcs, _pl, _repeated_svd, _svd_lwork, _time_mask, _validate_type, _verbose_safe_false, check_fname, copy_function_doc_to_method_doc, fill_doc, logger, verbose, warn
  from .viz import plot_dipole_amplitudes, plot_dipole_locations
  from .viz.evoked import _plot_evoked
label_desc: Use image node_id 3 for calling a global function with example usage: _read_dipole_text(fname) and returns: Dipole