Ejemplo n.º 1
0
def reaction() -> None:
    """A simple exponential reaction model on a 1D grid.
    """
    instance = Instance({
            Operator.F_INIT: ['initial_state'],     # list of float
            Operator.O_F: ['final_state']})         # list of float

    while instance.reuse_instance():
        # F_INIT
        t_max = instance.get_setting('t_max', 'float')
        dt = instance.get_setting('dt', 'float')
        k = instance.get_setting('k', 'float')

        msg = instance.receive('initial_state')
        U = np.array(msg.data)

        t_cur = msg.timestamp
        while t_cur + dt < t_max:
            # O_I

            # S
            U += k * U * dt
            t_cur += dt

        # O_F
        instance.send('final_state', Message(t_cur, None, U.tolist()))
Ejemplo n.º 2
0
def explicit_relay():
    """Intermediate component with explicit settings.

    Sends and receives overlay settings explicitly, rather than
    having MUSCLE handle them. This just passes all information on.
    """
    instance = Instance({
            Operator.F_INIT: ['in[]'], Operator.O_F: ['out[]']})

    while instance.reuse_instance(False):
        # f_init
        assert instance.get_setting('test2', 'float') == 13.3
        assert instance.get_port_length('in') == instance.get_port_length(
                'out')

        msgs = list()
        for slot in range(instance.get_port_length('in')):
            msg = instance.receive_with_settings('in', slot)
            assert msg.data.startswith('testing')
            assert msg.settings['test2'] == 14.4
            msgs.append(msg)

        assert instance.get_setting('test2') == 13.3

        # o_f
        for slot in range(instance.get_port_length('out')):
            instance.send('out', msgs[slot], slot)
Ejemplo n.º 3
0
def diffusion() -> None:
    """A simple diffusion model on a 1d grid.

    The state of this model is a 1D grid of concentrations. It sends
    out the state on each timestep on `state_out`, and can receive an
    updated state on `state_in` at each state update.
    """
    logger = logging.getLogger()
    instance = Instance({
        Operator.O_I: ['state_out'],
        Operator.S: ['state_in']
    })

    while instance.reuse_instance():
        # F_INIT
        t_max = instance.get_setting('t_max', 'float')
        dt = instance.get_setting('dt', 'float')
        x_max = instance.get_setting('x_max', 'float')
        dx = instance.get_setting('dx', 'float')
        d = instance.get_setting('d', 'float')

        U = np.zeros(int(round(x_max / dx)))
        U[25] = 2.0
        U[50] = 2.0
        U[75] = 2.0
        Us = U

        t_cur = 0.0
        while t_cur + dt <= t_max:
            # O_I
            t_next = t_cur + dt
            if t_next + dt > t_max:
                t_next = None
            cur_state_msg = Message(t_cur, t_next, U.tolist())
            instance.send('state_out', cur_state_msg)

            # S
            msg = instance.receive('state_in', default=cur_state_msg)
            if msg.timestamp > t_cur + dt:
                logger.warning('Received a message from the future!')
            U = np.array(msg.data)

            dU = np.zeros_like(U)
            dU[1:-1] = d * laplacian(U, dx) * dt
            dU[0] = dU[1]
            dU[-1] = dU[-2]

            U += dU
            Us = np.vstack((Us, U))
            t_cur += dt

        plt.figure()
        plt.imshow(np.log(Us + 1e-20))
        plt.show()
Ejemplo n.º 4
0
def macro():
    instance = Instance({
            Operator.O_I: ['out'],
            Operator.S: ['in']})

    while instance.reuse_instance():
        # f_init
        assert instance.get_setting('test1') == 13

        for i in range(2):
            # o_i
            test_array = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
            assert test_array.shape == (2, 3)
            assert test_array.flags.c_contiguous
            data = {
                    'message': 'testing',
                    'test_grid': test_array}
            instance.send('out', Message(i * 10.0, (i + 1) * 10.0, data))

            # s/b
            msg = instance.receive('in')
            assert msg.data['reply'] == 'testing back {}'.format(i)
            assert msg.data['test_grid'].array.dtype.kind == 'i'
            assert msg.data['test_grid'].array.dtype.itemsize == 8
            assert msg.data['test_grid'].array[0][1] == 2
            assert msg.timestamp == i * 10.0
Ejemplo n.º 5
0
def micro():
    """Micro model implementation.
    """
    instance = Instance({Operator.F_INIT: ['in'], Operator.O_F: ['out']})

    while instance.reuse_instance():
        # f_init
        assert instance.get_setting('test3', 'str') == 'testing'
        assert instance.get_setting('test4', 'bool') is True
        assert instance.get_setting('test6', '[[float]]')[0][1] == 2.0

        msg = instance.receive('in')
        assert msg.data == 'testing'

        # o_f
        instance.send('out', Message(0.1, None, 'testing back'))
Ejemplo n.º 6
0
def micro():
    """Micro model implementation.
    """
    instance = Instance({Operator.F_INIT: ['in'], Operator.O_F: ['out']})

    assert instance.get_setting('test2') == 13.3
    while instance.reuse_instance():
        # f_init
        assert instance.get_setting('test2', 'float') == 14.4
        msg = instance.receive('in')
        assert msg.data == 'testing'

        # with pytest.raises(RuntimeError):
        #     instance.receive_with_settings('in')

        # o_f
        instance.send('out', Message(0.1, None, 'testing back'))
Ejemplo n.º 7
0
def micro():
    """Micro model implementation.
    """
    instance = Instance({Operator.F_INIT: ['in'], Operator.O_F: ['out']})

    while instance.reuse_instance():
        # f_init
        assert instance.get_setting('test3', 'str') == 'testing'
        assert instance.get_setting('test4', 'bool') is True
        assert instance.get_setting('test6', '[[float]]')[0][1] == 2.0

        msg = instance.receive('in')
        assert msg.data == 'testing'

        # o_f
        result = {
            'string': 'testing back',
            'int': 42,
            'float': 3.1416,
            'grid': Grid(np.array([[12.0, 34.0, 56.0], [1.0, 2.0, 3.0]]))
        }
        instance.send('out', Message(0.1, None, result))
Ejemplo n.º 8
0
def macro():
    """Macro model implementation.
    """
    instance = Instance({
            Operator.O_I: ['out'], Operator.S: ['in']})

    while instance.reuse_instance():
        # f_init
        assert instance.get_setting('test2') == 14.4
        # o_i
        instance.send('out', Message(0.0, 10.0, 'testing'))
        # s/b
        msg = instance.receive('in')
        assert msg.data == 'testing back'
Ejemplo n.º 9
0
def macro():
    instance = Instance({Operator.O_I: ['out'], Operator.S: ['in']})

    while instance.reuse_instance():
        # f_init
        assert instance.get_setting('test1') == 13

        for i in range(2):
            # o_i
            instance.send('out', Message(i * 10.0, (i + 1) * 10.0, 'testing'))

            # s/b
            msg = instance.receive('in')
            assert msg.data == 'testing back {}'.format(i)
            assert msg.timestamp == i * 10.0
Ejemplo n.º 10
0
def macro():
    """Macro model implementation.
    """
    instance = Instance({Operator.O_I: ['out[]'], Operator.S: ['in[]']})

    while instance.reuse_instance():
        # f_init
        assert instance.get_setting('test1') == 13

        # o_i
        assert instance.is_vector_port('out')
        for slot in range(10):
            instance.send('out', Message(0.0, 10.0, 'testing'), slot)

        # s/b
        for slot in range(10):
            msg = instance.receive('in', slot)
            assert msg.data == 'testing back'
Ejemplo n.º 11
0
def macro():
    """Macro model implementation.
    """
    instance = Instance({Operator.O_I: ['out[]'], Operator.S: ['in[]']})

    while instance.reuse_instance():
        # f_init
        assert instance.get_setting('test1') == 13

        # o_i
        assert instance.is_vector_port('out')
        for slot in range(10):
            instance.send('out', Message(0.0, 10.0, 'testing'), slot)

        # s/b
        for slot in range(10):
            msg = instance.receive('in', slot)
            assert msg.data['string'] == 'testing back'
            assert msg.data['int'] == 42
            assert msg.data['float'] == 3.1416
            assert msg.data['grid'].array.dtype == np.float64
            assert msg.data['grid'].array[0, 1] == 34.0
Ejemplo n.º 12
0
def gray_scott_macro():
    #######################
    # MUSCLE modification #
    #######################

    # define the MUSCLE in and out ports
    instance = Instance({Operator.O_I: ['state_out'], Operator.S: ['sgs_in']})

    while instance.reuse_instance():

        # Main script

        # gray scott parameters
        feed = instance.get_setting('feed')
        kill = instance.get_setting('kill')

        HOME = os.path.abspath(os.path.dirname(__file__))

        ###########################
        # End MUSCLE modification #
        ###########################

        # number of gridpoints in 1D
        I = 7
        N = 2**I
        N_ref = 2**(I + 1)

        # number of time series to track
        N_Q = 2

        # domain size [-L, L]
        L = 1.25

        # user flags
        store = True
        state_store = True
        restart = False

        sim_ID = 'test_gray_scott'

        # TRAINING DATA SET
        QoI = ['Q_HF', 'Q_ref']
        Q = len(QoI)

        # allocate memory
        samples = {}

        if store:
            samples['N'] = N

            for q in range(Q):
                samples[QoI[q]] = []

        # 2D grid, scaled by L
        xx, yy = get_grid(N, L)
        xx_ref, yy_ref = get_grid(N_ref, L)

        # spatial derivative operators
        kx, ky = get_derivative_operator(N, L)
        kx_ref, ky_ref = get_derivative_operator(N_ref, L)

        # Laplace operator
        k_squared = kx**2 + ky**2
        k_squared_ref = kx_ref**2 + ky_ref**2

        # diffusion coefficients
        epsilon_u = 2e-5
        epsilon_v = 1e-5

        # time step parameters
        dt = 0.5
        n_steps = int(5000 / dt)
        store_frame_rate = 1
        t = 0.0

        # Initial condition
        if restart:

            fname = HOME + '/restart/' + sim_ID + '_t_' + str(np.around(
                t, 1)) + '.hdf5'

            # if fname does not exist, select restart file via GUI
            if os.path.exists(fname) == False:
                root = tk.Tk()
                root.withdraw()
                fname = filedialog.askopenfilename(
                    initialdir=HOME + '/restart',
                    title="Open restart file",
                    filetypes=(('HDF5 files', '*.hdf5'), ('All files', '*.*')))

            # create HDF5 file
            h5f = h5py.File(fname, 'r')

            for key in h5f.keys():
                print(key)
                vars()[key] = h5f[key][:]

            h5f.close()
        else:
            u_hat, v_hat = initial_cond(xx, yy)
            u_hat_ref, v_hat_ref = initial_cond(xx_ref, yy_ref)

        # Integrating factors
        int_fac_u, int_fac_u2, int_fac_v, int_fac_v2 = \
            integrating_factors(k_squared, dt, epsilon_u, epsilon_v)

        int_fac_u_ref, int_fac_u2_ref, int_fac_v_ref, int_fac_v2_ref = \
            integrating_factors(k_squared_ref, dt, epsilon_u, epsilon_v)

        # counters
        j = 0
        j2 = 0

        V_hat_1 = np.fft.fft2(np.ones([N, N]))
        V_hat_1_ref = np.fft.fft2(np.ones([N_ref, N_ref]))

        t0 = time.time()

        samples_uq = np.zeros([n_steps, 8])

        # time stepping
        for n in range(n_steps):

            u_hat_ref, v_hat_ref = rk4(u_hat_ref, v_hat_ref, int_fac_u_ref,
                                       int_fac_u2_ref, int_fac_v_ref,
                                       int_fac_v2_ref, dt, feed, kill)

            # compute reference stats
            Q_HF = np.zeros(2 * N_Q)
            Q_HF[0] = compute_int(V_hat_1, u_hat, N)
            Q_HF[1] = 0.5 * compute_int(u_hat, u_hat, N)
            Q_HF[2] = compute_int(V_hat_1, v_hat, N)
            Q_HF[3] = 0.5 * compute_int(v_hat, v_hat, N)

            Q_ref = np.zeros(2 * N_Q)
            Q_ref[0] = compute_int(V_hat_1_ref, u_hat_ref, N_ref)
            Q_ref[1] = 0.5 * compute_int(u_hat_ref, u_hat_ref, N_ref)
            Q_ref[2] = compute_int(V_hat_1_ref, v_hat_ref, N_ref)
            Q_ref[3] = 0.5 * compute_int(v_hat_ref, v_hat_ref, N_ref)

            samples_uq[n, 0:2 * N_Q] = Q_HF
            samples_uq[n, 2 * N_Q:] = Q_ref

            if np.mod(n, 100) == 0:
                print('time step %d of %d' % (n, n_steps))
                print(Q_HF)
                print(Q_ref)

            #######################
            # MUSCLE modification #
            #######################

            # MUSCLE O_I port (state_out)
            t_cur = n * dt
            t_next = t_cur + dt
            if n == n_steps - 1:
                t_next = None

            # split state vars in real and imag part (temporary fix)
            V_hat_1_re = np.copy(V_hat_1.real)
            V_hat_1_im = np.copy(V_hat_1.imag)
            u_hat_re = np.copy(u_hat.real)
            u_hat_im = np.copy(u_hat.imag)
            v_hat_re = np.copy(v_hat.real)
            v_hat_im = np.copy(v_hat.imag)

            # create a MUSCLE Message object, to be sent to the micro model
            cur_state = Message(
                t_cur, t_next, {
                    'V_hat_1_re': V_hat_1_re,
                    'V_hat_1_im': V_hat_1_im,
                    'u_hat_re': u_hat_re,
                    'u_hat_im': u_hat_im,
                    'v_hat_re': v_hat_re,
                    'v_hat_im': v_hat_im,
                    'Q_ref': Q_ref,
                    'Q_model': Q_HF
                })
            # send the message to the micro model
            instance.send('state_out', cur_state)

            # reveive a message from the micro model, i.e. the two reduced subgrid-scale terms
            msg = instance.receive('sgs_in')
            reduced_sgs_u_re = msg.data['reduced_sgs_u_re'].array
            reduced_sgs_u_im = msg.data['reduced_sgs_u_im'].array
            reduced_sgs_v_re = msg.data['reduced_sgs_v_re'].array
            reduced_sgs_v_im = msg.data['reduced_sgs_v_im'].array

            # recreate the reduced subgrid-scale terms for the u and v pde
            reduced_sgs_u = reduced_sgs_u_re + 1.0j * reduced_sgs_u_im
            reduced_sgs_v = reduced_sgs_v_re + 1.0j * reduced_sgs_v_im

            ###########################
            # End MUSCLE modification #
            ###########################

            if np.mod(n, 1000) == 0:
                print('time step %d of %d' % (n, n_steps))

            # evolve the state in time, with the new reduced sgs terms
            u_hat, v_hat = rk4(u_hat,
                               v_hat,
                               int_fac_u,
                               int_fac_u2,
                               int_fac_v,
                               int_fac_v2,
                               dt,
                               feed,
                               kill,
                               reduced_sgs_u=reduced_sgs_u,
                               reduced_sgs_v=reduced_sgs_v)

            j += 1
            j2 += 1
            t += dt

            if j2 == store_frame_rate and store:
                j2 = 0

                for qoi in QoI:
                    samples[qoi].append(eval(qoi))
        # foo = False

    t1 = time.time()
    print('*************************************')
    print('Simulation time = %f [s]' % (t1 - t0))
    print('*************************************')

    # output csv file
    header = 'Q1,Q2,Q3,Q4,Q1_HF,Q2_HF,Q3_HF,Q4_HF'
    fname = 'output_f%.5f_k%.5f.csv' % (feed, kill)
    np.savetxt(fname, samples_uq, delimiter=",", comments='', header=header)

    # store the state of the system to allow for a simulation restart at t > 0
    if state_store:

        keys = ['u_hat', 'v_hat']

        if os.path.exists(HOME + '/restart') == False:
            os.makedirs(HOME + '/restart')

        fname = HOME + '/restart/' + sim_ID + '_t_' + str(np.around(
            t, 1)) + '.hdf5'

        # create HDF5 file
        h5f = h5py.File(fname, 'w')

        # store numpy sample arrays as individual datasets in the hdf5 file
        for key in keys:
            qoi = eval(key)
            h5f.create_dataset(key, data=qoi)

        h5f.close()
Ejemplo n.º 13
0
def diffusion() -> None:
    """A simple diffusion model on a 1d grid.

    The state of this model is a 1D grid of concentrations. It sends
    out the state on each timestep on `state_out`, and can receive an
    updated state on `state_in` at each state update.
    """
    logger = logging.getLogger()
    instance = Instance({
        Operator.O_I: ['state_out'],
        Operator.S: ['state_in']
    })

    while instance.reuse_instance():
        # F_INIT
        t_max = instance.get_setting('t_max', 'float')
        dt = instance.get_setting('dt', 'float')
        x_max = instance.get_setting('x_max', 'float')
        dx = instance.get_setting('dx', 'float')
        d = instance.get_setting('d', 'float')

        U = np.zeros(int(round(x_max / dx))) + 1e-20
        U[25] = 2.0
        U[50] = 2.0
        U[75] = 2.0
        Us = U

        t_cur = 0.0
        while t_cur + dt <= t_max:
            # O_I
            t_next = t_cur + dt
            if t_next + dt > t_max:
                t_next = None
            cur_state_msg = Message(t_cur, t_next, Grid(U, ['x']))
            instance.send('state_out', cur_state_msg)

            # S
            msg = instance.receive('state_in', default=cur_state_msg)
            if msg.timestamp > t_cur + dt:
                logger.warning('Received a message from the future!')
            np.copyto(U, msg.data.array)

            dU = np.zeros_like(U)
            dU[1:-1] = d * laplacian(U, dx) * dt
            dU[0] = dU[1]
            dU[-1] = dU[-2]

            U += dU
            Us = np.vstack((Us, U))
            t_cur += dt

        if 'DONTPLOT' not in os.environ:
            from matplotlib import pyplot as plt
            plt.figure()
            plt.imshow(np.log(Us + 1e-20),
                       origin='upper',
                       extent=[
                           -0.5 * dx, x_max - 0.5 * dx,
                           (t_max - 0.5 * dt) * 1000.0, -0.5 * dt * 1000.0
                       ],
                       interpolation='none',
                       aspect='auto')
            cbar = plt.colorbar()
            cbar.set_label('log(Concentration)', rotation=270, labelpad=20)
            plt.xlabel('x')
            plt.ylabel('t (ms)')
            plt.title('Concentration over time')
            plt.show()
Ejemplo n.º 14
0
def qmc_driver() -> None:
    """A driver for quasi-Monte Carlo Uncertainty Quantification.

    This component attaches to a collection of model instances, and
    feeds in different parameter values generated using a Sobol
    sequence.
    """
    instance = Instance({
            Operator.O_I: ['parameters_out[]'],
            Operator.S: ['states_in[]']})

    while instance.reuse_instance():
        # F_INIT
        # get and check parameter distributions
        n_samples = instance.get_setting('n_samples', 'int')
        d_min = instance.get_setting('d_min', 'float')
        d_max = instance.get_setting('d_max', 'float')
        k_min = instance.get_setting('k_min', 'float')
        k_max = instance.get_setting('k_max', 'float')

        if d_max < d_min:
            instance.error_shutdown('Invalid settings: d_max < d_min')
            exit(1)
        if k_max < k_min:
            instance.error_shutdown('Invalid settings: k_max < k_min')
            exit(1)

        # generate UQ parameter values
        sobol_sqn = sobol_seq.i4_sobol_generate(2, n_samples)
        ds = d_min + sobol_sqn[:, 0] * (d_max - d_min)
        ks = k_min + sobol_sqn[:, 1] * (k_max - k_min)

        # configure output port
        if not instance.is_resizable('parameters_out'):
            instance.error_shutdown('This component needs a resizable'
                                ' parameters_out port, but it is connected to'
                                ' something that cannot be resized. Maybe try'
                                ' adding a load balancer.')
            exit(1)

        instance.set_port_length('parameters_out', n_samples)

        # run ensemble
        Us = None
        # O_I
        for sample in range(n_samples):
            uq_parameters = Settings({
                'd': ds[sample],
                'k': ks[sample]})
            msg = Message(0.0, None, uq_parameters)
            instance.send('parameters_out', msg, sample)

        # S
        for sample in range(n_samples):
            msg = instance.receive_with_settings('states_in', sample)
            U = np.array(msg.data)
            # accumulate
            if Us is None:
                Us = U
            else:
                Us = np.vstack((Us, U))

        mean = np.mean(Us, axis=0)

        # O_F
        if 'DONTPLOT' not in os.environ:
            from matplotlib import pyplot as plt

            t_max = instance.get_setting('t_max', 'float')
            dt = instance.get_setting('dt', 'float')
            x_max = instance.get_setting('x_max', 'float')
            dx = instance.get_setting('dx', 'float')

            plt.figure()
            plt.imshow(
                    np.log(Us + 1e-20),
                    origin='upper',
                    extent=[
                        -0.5*dx, x_max - 0.5*dx,
                        n_samples-0.5, -0.5],
                    interpolation='none',
                    aspect='auto'
                    )
            cbar = plt.colorbar()
            cbar.set_label('log(Concentration)', rotation=270, labelpad=20)
            plt.xlabel('x')
            plt.ylabel('Sample')
            plt.title('Final states')
            plt.show()
Ejemplo n.º 15
0
def qmc_driver() -> None:
    """A driver for quasi-Monte Carlo Uncertainty Quantification.

    This component attaches to a collection of model instances, and
    feeds in different parameter values generated using a Sobol
    sequence.
    """
    instance = Instance({
        Operator.O_I: ['parameters_out[]'],
        Operator.S: ['states_in[]']
    })

    while instance.reuse_instance():
        # F_INIT
        # get and check parameter distributions
        n_samples = instance.get_setting('n_samples', 'int')
        d_min = instance.get_setting('d_min', 'float')
        d_max = instance.get_setting('d_max', 'float')
        k_min = instance.get_setting('k_min', 'float')
        k_max = instance.get_setting('k_max', 'float')

        if d_max < d_min:
            instance.error_shutdown('Invalid settings: d_max < d_min')
            exit(1)
        if k_max < k_min:
            instance.error_shutdown('Invalid settings: k_max < k_min')
            exit(1)

        # generate UQ parameter values
        sobol_sqn = sobol_seq.i4_sobol_generate(2, n_samples)
        ds = d_min + sobol_sqn[:, 0] * (d_max - d_min)
        ks = k_min + sobol_sqn[:, 1] * (k_max - k_min)

        # configure output port
        if not instance.is_resizable('parameters_out'):
            instance.error_shutdown(
                'This component needs a resizable'
                ' parameters_out port, but it is connected to'
                ' something that cannot be resized. Maybe try'
                ' adding a load balancer.')
            exit(1)

        instance.set_port_length('parameters_out', n_samples)

        # run ensemble
        Us = None
        # O_I
        for sample in range(n_samples):
            uq_parameters = Settings({'d': ds[sample], 'k': ks[sample]})
            msg = Message(0.0, None, uq_parameters)
            instance.send('parameters_out', msg, sample)

        # S
        for sample in range(n_samples):
            msg = instance.receive_with_settings('states_in', sample)
            U = np.array(msg.data)
            # accumulate
            if Us is None:
                Us = U
            else:
                Us = np.vstack((Us, U))

        mean = np.mean(Us, axis=0)
        plt.figure()
        plt.imshow(np.log(Us + 1e-20))
        plt.show()
Ejemplo n.º 16
0
def reduced_sgs():
    """
    An EasySurrogate Reduced micro model, executed in a separate file and linked to the
    macro model via MUSCLE3
    """

    instance = Instance({
        Operator.F_INIT: ['state_in'],  # a dict with state values
        Operator.O_F: ['sgs_out']
    })  # a dict with subgrid-scale terms

    # get some parameters
    N_Q = instance.get_setting('N_Q')  # the number of QoI to track, per PDE
    N_LF = instance.get_setting(
        'N_LF')  # the number of gridpoints in 1 dimension
    # t_max = instance.get_setting('t_max')   #the simulation time, per time-step of macro
    # dt = instance.get_setting('dt')         #the micro time step

    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    # Create an EasySurrogate RecucedSurrogate object
    surrogate = es.methods.Reduced_Surrogate(N_Q, N_LF)

    while instance.reuse_instance():

        # receive the state from the macro model
        msg = instance.receive('state_in')
        V_hat_1_re = msg.data['V_hat_1_re'].array.T
        V_hat_1_im = msg.data['V_hat_1_im'].array.T
        u_hat_re = msg.data['u_hat_re'].array.T
        u_hat_im = msg.data['u_hat_im'].array.T
        v_hat_re = msg.data['v_hat_re'].array.T
        v_hat_im = msg.data['v_hat_im'].array.T
        Q_ref = msg.data['Q_ref'].array.T
        Q_model = msg.data['Q_model'].array.T

        # recreate the Fourier coefficients (temporary fix)
        V_hat_1 = V_hat_1_re + 1.0j * V_hat_1_im
        u_hat = u_hat_re + 1.0j * u_hat_im
        v_hat = v_hat_re + 1.0j * v_hat_im

        # #time of the macro model
        t_cur = msg.timestamp

        # train the two reduced sgs source terms using the recieved reference data Q_ref
        reduced_dict_u = surrogate.train([V_hat_1, u_hat],
                                         Q_ref[0:N_Q] - Q_model[0:N_Q])
        reduced_dict_v = surrogate.train([V_hat_1, v_hat],
                                         Q_ref[N_Q:] - Q_model[N_Q:])

        # get the two reduced sgs terms from the dict
        reduced_sgs_u = np.fft.ifft2(reduced_dict_u['sgs_hat'])
        reduced_sgs_v = np.fft.ifft2(reduced_dict_v['sgs_hat'])

        # MUSCLE O_F port (sgs), send the subgrid-scale terms back to the macro model
        reduced_sgs_u_re = np.copy(reduced_sgs_u.real)
        reduced_sgs_u_im = np.copy(reduced_sgs_u.imag)
        reduced_sgs_v_re = np.copy(reduced_sgs_v.real)
        reduced_sgs_v_im = np.copy(reduced_sgs_v.imag)

        instance.send(
            'sgs_out',
            Message(
                t_cur, None, {
                    'reduced_sgs_u_re': reduced_sgs_u_re,
                    'reduced_sgs_u_im': reduced_sgs_u_im,
                    'reduced_sgs_v_re': reduced_sgs_v_re,
                    'reduced_sgs_v_im': reduced_sgs_v_im
                }))