Example #1
0
    def test_unit_conversion(self):
        self.assertEqual(unit_conversion('km', 'm'), (1000., 0.))

        try:
            unit_conversion('km', 1.0)
        except RuntimeError as err:
            self.assertEqual(str(err), "Cannot convert to new units: '1.0'.")
        else:
            self.fail("Expecting RuntimeError")
Example #2
0
    def test_unit_conversion(self):
        self.assertEqual(unit_conversion('km', 'm'), (1000., 0.))

        try:
            unit_conversion('km', 1.0)
        except ValueError as err:
            self.assertEqual(str(err), "The units '1.0' are invalid.")
        else:
            self.fail("Expecting RuntimeError")
Example #3
0
    def test_atto_seconds(self):
        # The unit 'as' was bugged because it is a python keyword.

        fact = unit_conversion('s', 'as')
        assert_near_equal(fact[0], 1e18)

        # Make sure regex for 'as' doesn't pick up partial words.
        fact = unit_conversion('aslug*as*as', 'aslug*zs*zs')
        assert_near_equal(fact[0], 1e6)

        # Make sure simplification works.
        simple = simplify_unit('m*as/as')
        self.assertEqual(simple, 'm')

        simple = simplify_unit('as**6/as**4')
        self.assertEqual(simple, 'as**2')
Example #4
0
    def get_val(self, name, units=None, indices=None):
        """
        Get an output/input variable.

        Function is used if you want to specify display units.

        Parameters
        ----------
        name : str
            Promoted or relative variable name in the root system's namespace.
        units : str, optional
            Units to convert to before upon return.
        indices : int or list of ints or tuple of ints or int ndarray or Iterable or None, optional
            Indices or slice to return.

        Returns
        -------
        float or ndarray
            The requested output/input variable.
        """
        val = self[name]

        if indices is not None:
            val = val[indices]

        if units is not None:
            base_units = self._get_units(name)
            simp_units = simplify_unit(units)

            if base_units is None:
                msg = "Can't express variable '{}' with units of 'None' in units of '{}'."
                raise TypeError(msg.format(name, simp_units))

            try:
                scale, offset = unit_conversion(base_units, simp_units)
            except TypeError:
                msg = "Can't express variable '{}' with units of '{}' in units of '{}'."
                raise TypeError(msg.format(name, base_units, simp_units))

            val = (val + offset) * scale

        return val
Example #5
0
    def _initialize(self, system):
        """
        Allocate the global matrices.

        Parameters
        ----------
        system : System
            Parent system to this jacobian.
        """
        # var_indices are the *global* indices for variables on this proc
        is_top = system.pathname == ''

        abs2meta_in = system._var_abs2meta['input']
        all_meta = system._var_allprocs_abs2meta

        self._int_mtx = int_mtx = self._matrix_class(system.comm, True)
        ext_mtx = self._matrix_class(system.comm, False)

        iproc = system.comm.rank
        out_ranges = self._out_ranges
        in_ranges = self._in_ranges

        abs2prom_out = system._var_abs2prom['output']
        conns = {} if isinstance(system,
                                 Component) else system._conn_global_abs_in2out
        abs_key2shape = self._abs_key2shape

        # create the matrix subjacs
        for abs_key, info in self._subjacs_info.items():
            res_abs_name, wrt_abs_name = abs_key
            # because self._subjacs_info is shared among all 'related' assembled jacs,
            # we use out_ranges (and later in_ranges) to weed out keys outside of this jac
            if res_abs_name not in out_ranges:
                continue
            res_offset, res_end = out_ranges[res_abs_name]
            res_size = res_end - res_offset

            if wrt_abs_name in abs2prom_out:
                out_offset, out_end = out_ranges[wrt_abs_name]
                out_size = out_end - out_offset
                shape = (res_size, out_size)
                int_mtx._add_submat(abs_key, info, res_offset, out_offset,
                                    None, shape)
            elif wrt_abs_name in in_ranges:
                if wrt_abs_name in conns:  # connected input
                    out_abs_name = conns[wrt_abs_name]
                    if out_abs_name not in out_ranges:
                        continue

                    meta_in = abs2meta_in[wrt_abs_name]
                    all_out_meta = all_meta['output'][out_abs_name]
                    # calculate unit conversion
                    in_units = meta_in['units']
                    out_units = all_out_meta['units']
                    if in_units and out_units and in_units != out_units:
                        factor, _ = unit_conversion(out_units, in_units)
                        if factor == 1.0:
                            factor = None
                    else:
                        factor = None

                    out_offset, out_end = out_ranges[out_abs_name]
                    out_size = out_end - out_offset
                    shape = (res_size, out_size)
                    src_indices = abs2meta_in[wrt_abs_name]['src_indices']

                    if src_indices is not None:
                        # need to add an entry for d(output)/d(source)
                        # instead of d(output)/d(input).  int_mtx is a square matrix whose
                        # rows and columns map to output/resid vars only.
                        abs_key2 = (res_abs_name, out_abs_name)
                        shape = abs_key2shape(abs_key2)

                    int_mtx._add_submat(abs_key, info, res_offset, out_offset,
                                        src_indices, shape, factor)

                elif not is_top:  # input is connected to something outside current system
                    in_offset, in_end = in_ranges[wrt_abs_name]
                    # don't use global offsets for ext_mtx
                    res_offset, res_end = out_ranges[res_abs_name]
                    res_size = res_end - res_offset
                    shape = (res_size, in_end - in_offset)
                    ext_mtx._add_submat(abs_key, info, res_offset, in_offset,
                                        None, shape)

        out_size = len(system._outputs)
        int_mtx._build(out_size, out_size, system)

        if ext_mtx._submats:
            ext_mtx._build(out_size, len(system._vectors['input']['linear']))
        else:
            ext_mtx = None

        self._ext_mtx[system.pathname] = ext_mtx
Example #6
0
    def _add_output_configure(self, name, units, shape, desc='', src=None):
        """
        Add a single timeseries output.

        Can be called by parent groups in configure.

        Parameters
        ----------
        name : str
            name of the variable in this component's namespace.
        shape : int or tuple or list or None
            Shape of this variable, only required if val is not an array.
            Default is None.
        units : str or None
            Units in which the output variables will be provided to the component during execution.
            Default is None, which means it has no units.
        desc : str
            description of the timeseries output variable.
        src : str
            The src path of the variables input, used to prevent redundant inputs.

        Returns
        -------
        bool
            True if a new input was added for the output, or False if it reuses an existing input.
        """
        input_num_nodes = self.input_num_nodes
        output_num_nodes = self.output_num_nodes
        added_source = False

        if name in self._vars:
            return False

        if src in self._sources:
            # If we're already pulling the source into this timeseries, use that as the
            # input for this output.
            input_name = self._sources[src]
            input_units = self._units[input_name]
        else:
            input_name = f'input_values:{name}'
            self.add_input(input_name,
                           shape=(input_num_nodes, ) + shape,
                           units=units,
                           desc=desc)
            self._sources[src] = input_name
            input_units = self._units[input_name] = units
            added_source = True

        output_name = name
        self.add_output(output_name,
                        shape=(output_num_nodes, ) + shape,
                        units=units,
                        desc=desc)

        self._vars[name] = (input_name, output_name, shape)

        size = np.prod(shape)
        rs = cs = np.arange(output_num_nodes * size, dtype=int)

        # There's a chance that the input for this output was pulled from another variable with
        # different units, so account for that with a conversion.
        if None in {input_units, units}:
            scale = 1.0
            offset = 0
        else:
            scale, offset = unit_conversion(input_units, units)
        self._conversion_factors[output_name] = scale, offset

        self.declare_partials(of=output_name,
                              wrt=input_name,
                              rows=rs,
                              cols=cs,
                              val=scale)

        return added_source
    def add_var(self, name, shape, units, disc_src, col_src):
        """
        Add a variable to be interleaved.

        In general these need to be variables whose values are stored separately for state
        discretization or collocation nodes (such as states or ODE outputs).

        Parameters
        ----------
        name : str
            The name of variable as it should appear in the outputs of the
            component ('interleave_comp.all_values:{name}').
        shape : tuple
            The shape of the variable at each instance in time.
        units : str
            The units of the variable.
        disc_src : str
            The source path of the variable's inputs at the discretization nodes.
        col_src : str
            The source path of the variable's inputs at the collocation nodes.

        Returns
        -------
        bool
            True if the variable was added to the interleave comp, False if not due to it already
            being there.
        """
        if name in self._varnames:
            return False

        num_disc_nodes = self.options['grid_data'].subset_num_nodes['state_disc']
        num_col_nodes = self.options['grid_data'].subset_num_nodes['col']
        num_nodes = self.options['grid_data'].subset_num_nodes['all']
        added_source = False

        size = np.prod(shape)

        self._varnames[name] = {}
        self._varnames[name]['state_disc'] = f'disc_values:{name}'
        self._varnames[name]['col'] = f'col_values:{name}'
        self._varnames[name]['all'] = f'all_values:{name}'

        # Check to see if the given disc source has already been used
        # We'll assume that the col source will be the same as well, no need to check both.
        if disc_src in self._sources['state_disc']:
            self._varnames[name]['state_disc'] = self._sources['state_disc'][disc_src]
            self._varnames[name]['col'] = self._sources['col'][col_src]
            input_units = self._units[self._varnames[name]['state_disc']]
        else:
            self.add_input(
                name=self._varnames[name]['state_disc'],
                shape=(num_disc_nodes,) + shape,
                desc=f'Values of {name} at discretization nodes',
                units=units)
            self.add_input(
                name=self._varnames[name]['col'],
                shape=(num_col_nodes,) + shape,
                desc=f'Values of {name} at collocation nodes',
                units=units)
            self._sources['state_disc'][disc_src] = self._varnames[name]['state_disc']
            self._sources['col'][col_src] = self._varnames[name]['col']
            input_units = self._units[self._varnames[name]['state_disc']] = units
            added_source = True

        self.add_output(
            name=self._varnames[name]['all'],
            shape=(num_nodes,) + shape,
            desc=f'Values of {name} at all nodes',
            units=units)

        start_rows = self.options['grid_data'].subset_node_indices['state_disc'] * size
        r = (start_rows[:, np.newaxis] + np.arange(size, dtype=int)).ravel()
        c = np.arange(size * num_disc_nodes, dtype=int)

        # There's a chance that the input for this output was pulled from another variable with
        # different units, so account for that with a conversion.
        if None in {input_units, units}:
            scale = 1.0
            offset = 0
        else:
            scale, offset = unit_conversion(input_units, units)
        self._conversion_factors[self._varnames[name]['all']] = scale, offset

        self.declare_partials(of=self._varnames[name]['all'],
                              wrt=self._varnames[name]['state_disc'],
                              rows=r, cols=c, val=scale)

        start_rows = self.options['grid_data'].subset_node_indices['col'] * size
        r = (start_rows[:, np.newaxis] + np.arange(size, dtype=int)).ravel()
        c = np.arange(size * num_col_nodes, dtype=int)

        self.declare_partials(of=self._varnames[name]['all'],
                              wrt=self._varnames[name]['col'],
                              rows=r, cols=c, val=scale)

        return added_source