rb.ComplexRBSystem.java Source code

Java tutorial

Introduction

Here is the source code for rb.ComplexRBSystem.java

Source

package rb;

// rbAPPmit: An Android front-end for the Certified Reduced Basis Method
// Copyright (C) 2010 David J. Knezevic and Phuong Huynh
//
// This file is part of rbAPPmit
//
// @ref rbappmit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// @ref rbappmit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with rbAPPmit. If not, see <http://www.gnu.org/licenses/>.

import jarmos.ComplexSolutionField;
import jarmos.FieldDescriptor;
import jarmos.Log;
import jarmos.SimulationResult;
import jarmos.geometry.MeshTransform;
import jarmos.io.AModelManager;
import jarmos.io.MathObjectReader;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.apache.commons.math.complex.Complex;
import org.apache.commons.math.linear.Array2DRowFieldMatrix;
import org.apache.commons.math.linear.ArrayFieldVector;
import org.apache.commons.math.linear.FieldMatrix;
import org.apache.commons.math.linear.FieldVector;

/**
 * RB system class for complex-valued fields.
 * 
 * @author Daniel Wirtz @date 07.08.2013
 * 
 */
public class ComplexRBSystem extends RBSystem {

    // Logging tag
    private static final String DEBUG_TAG = "ComplexRBSystem";

    public Complex[] RB_outputs;
    public Complex[] RB_output_error_bounds;

    protected FieldVector<Complex> RB_solution;

    public Complex[][] output_dual_norms;
    protected FieldVector<Complex>[][] RB_output_vectors;
    protected FieldMatrix<Complex>[] RB_A_q_vector;
    protected FieldVector<Complex>[] RB_F_q_vector;

    protected FieldVector<Complex> theta_a;

    protected Complex[] Fq_representor_norms;
    protected Complex[][][] Fq_Aq_representor_norms;
    protected Complex[][][] Aq_Aq_representor_norms;

    protected Complex[][][] Z_vector;
    protected Complex[][] uL_vector;

    @Override
    public void loadOfflineData_rbappmit(AModelManager m) throws IOException {

        isReal = false;

        // Find out dimension of the RB space
        {
            BufferedReader reader = m.getBufReader("n_bfs.dat");

            set_n_basis_functions(Integer.parseInt(reader.readLine()));

            reader.close();
        }

        Log.d(DEBUG_TAG, "Finished reading n_bfs.dat");

        // Read in output data
        if (getNumOutputs() > 0) {
            // Get output dual norms
            {
                RB_output_vectors = (FieldVector<Complex>[][]) new FieldVector<?>[getNumOutputs()][];
                output_dual_norms = new Complex[getNumOutputs()][];
                for (int i = 0; i < getNumOutputs(); i++) {

                    String[] dual_norms_tokens;
                    {
                        BufferedReader reader = m
                                .getBufReader("output_" + String.format("%03d", i) + "_dual_norms.dat");

                        dual_norms_tokens = reader.readLine().split(" ");
                        reader.close();
                    }

                    {
                        int Q_l_hat = getQl(i) * (getQl(i) + 1) / 2;
                        output_dual_norms[i] = new Complex[Q_l_hat];
                        for (int q = 0; q < Q_l_hat; q++) {
                            output_dual_norms[i][q] = new Complex(Double.parseDouble(dual_norms_tokens[q]),
                                    Double.parseDouble(dual_norms_tokens[Q_l_hat + q]));
                        }
                    }

                    {
                        RB_output_vectors[i] = (FieldVector<Complex>[]) new FieldVector<?>[getQl(i)];
                        String[] output_i_tokens;
                        for (int q_l = 0; q_l < getQl(i); q_l++) {
                            // Now read in the RB output vectors
                            {
                                BufferedReader reader_i = m.getBufReader("output_" + String.format("%03d", i) + "_"
                                        + String.format("%03d", q_l) + ".dat");

                                output_i_tokens = reader_i.readLine().split(" ");
                                reader_i.close();
                            }

                            RB_output_vectors[i][q_l] = new ArrayFieldVector<Complex>(getNBF(),
                                    new Complex(0d, 0d));
                            for (int j = 0; j < getNBF(); j++) {
                                RB_output_vectors[i][q_l].setEntry(j,
                                        new Complex(Double.parseDouble(output_i_tokens[j]),
                                                Double.parseDouble(output_i_tokens[getNBF() + j])));
                            }
                        }
                    }
                }
            }
            Log.d(DEBUG_TAG, "Finished reading output data");
        } else
            Log.d(DEBUG_TAG, "No output data set. (get_n_outputs() == 0)");

        /*
         * // Read in the inner product matrix { InputStreamReader isr; String
         * dataString = directory_name + "/RB_inner_product_matrix.dat";
         * 
         * if(!isAssetFile) { HttpGet request = new HttpGet(dataString);
         * HttpResponse response = client.execute(request); isr = new
         * InputStreamReader(response.getEntity() .getContent()); } else { //
         * Read from assets isr = new InputStreamReader(
         * context.getAssets().open(dataString)); } BufferedReader
         * BufferedReader reader = new BufferedReader(isr,buffer_size);
         * 
         * String String line = reader.readLine(); String[] tokens =
         * line.split(" ");
         * 
         * // Set the size of the inner product matrix RB_inner_product_matrix =
         * new Array2DRowRealMatrix(n_bfs, n_bfs);
         * 
         * // Fill the matrix int count = 0; for (int i = 0; i < n_bfs; i++) for
         * (int j = 0; j < n_bfs; j++) { RB_inner_product_matrix.setEntry(i, j,
         * Double .parseDouble(tokens[count])); count++; } reader.close(); }
         * 
         * Log.d(DEBUG_TAG, "Finished reading RB_inner_product_matrix.dat");
         */

        // Read in the F_q vectors
        {
            RB_F_q_vector = (FieldVector<Complex>[]) new FieldVector<?>[getQf()];
            String[] tokens;
            for (int q_f = 0; q_f < getQf(); q_f++) {
                {
                    BufferedReader reader = m.getBufReader("RB_F_" + String.format("%03d", q_f) + ".dat");

                    tokens = reader.readLine().split(" ");
                    reader.close();
                }

                // Set the size of the inner product matrix
                RB_F_q_vector[q_f] = new ArrayFieldVector<Complex>(getNBF(), new Complex(0d, 0d));

                // Fill the vector
                for (int i = 0; i < getNBF(); i++) {
                    RB_F_q_vector[q_f].setEntry(i,
                            new Complex(Double.parseDouble(tokens[i]), Double.parseDouble(tokens[getNBF() + i])));
                }
            }
            Log.d(DEBUG_TAG, "Finished reading RB_F_q data");
        }

        // Read in the A_q matrices
        {
            RB_A_q_vector = (FieldMatrix<Complex>[]) Array.newInstance(FieldMatrix.class, getQa());// (FieldMatrix<Complex>[])
            // new
            // FieldMatrix<?>[get_Q_a()];
            String[] tokens;
            for (int q_a = 0; q_a < getQa(); q_a++) {
                {
                    BufferedReader reader = m.getBufReader("RB_A_" + String.format("%03d", q_a) + ".dat");
                    tokens = reader.readLine().split(" ");
                    reader.close();
                }

                // Set the size of the inner product matrix
                RB_A_q_vector[q_a] = new Array2DRowFieldMatrix<Complex>((new Complex(0, 0)).getField(), getNBF(),
                        getNBF());

                // Fill the vector
                int count = 0;
                for (int i = 0; i < getNBF(); i++)
                    for (int j = 0; j < getNBF(); j++) {
                        RB_A_q_vector[q_a].setEntry(i, j, new Complex(Double.parseDouble(tokens[count]),
                                Double.parseDouble(tokens[count + getNBF() * getNBF()])));
                        count++;
                    }
            }
            Log.d(DEBUG_TAG, "Finished reading RB_A_q data");
        }

        // Read in F_q representor norm data
        {
            BufferedReader reader = m.getBufReader("Fq_norms.dat");

            String[] tokens = reader.readLine().split(" ");
            reader.close();

            // Declare the array
            int Q_f_hat = getQf() * (getQf() + 1) / 2;
            Fq_representor_norms = new Complex[Q_f_hat];

            // Fill it
            for (int i = 0; i < Q_f_hat; i++) {
                Fq_representor_norms[i] = new Complex(Double.parseDouble(tokens[i * 2 + 0]),
                        Double.parseDouble(tokens[i * 2 + 1]));
            }

            Log.d(DEBUG_TAG, "Finished reading Fq_norms.dat");
        }

        // Read in Fq_Aq representor norm data
        {
            BufferedReader reader = m.getBufReader("Fq_Aq_norms.dat");

            String[] tokens = reader.readLine().split(" ");
            reader.close();
            reader = null;

            // Declare the array
            Fq_Aq_representor_norms = new Complex[getQf()][getQa()][getNBF()];

            double[][][] Rdata = new double[getQf()][getQa()][getNBF()];
            double[][][] Idata = new double[getQf()][getQa()][getNBF()];
            // Fill it
            int count = 0;
            for (int q_f = 0; q_f < getQf(); q_f++)
                for (int q_a = 0; q_a < getQa(); q_a++) {
                    for (int i = 0; i < getNBF(); i++) {
                        Rdata[q_f][q_a][i] = Double.parseDouble(tokens[count]);
                        count++;
                    }
                    for (int i = 0; i < getNBF(); i++) {
                        Idata[q_f][q_a][i] = Double.parseDouble(tokens[count]);
                        count++;
                    }
                }

            for (int q_f = 0; q_f < getQf(); q_f++)
                for (int q_a = 0; q_a < getQa(); q_a++)
                    for (int i = 0; i < getNBF(); i++)
                        Fq_Aq_representor_norms[q_f][q_a][i] = new Complex(Rdata[q_f][q_a][i], Idata[q_f][q_a][i]);

            Log.d(DEBUG_TAG, "Finished reading Fq_Aq_norms.dat");
        }

        MathObjectReader mr = m.getMathObjReader();
        // Read in Aq_Aq representor norm data
        {
            // Declare the array
            int Q_a_hat = getQa() * (getQa() + 1) / 2;
            Aq_Aq_representor_norms = new Complex[Q_a_hat][getNBF()][getNBF()];

            int count = 0;
            double[][] Rdata2 = null, Idata2 = null;
            for (int i = 0; i < getQa(); i++)
                for (int j = i; j < getQa(); j++) {
                    String file = "Aq_Aq_" + String.format("%03d", i) + "_" + String.format("%03d", j)
                            + "_norms.bin";

                    int n = getNBF();
                    InputStream in = m.getInStream(file);
                    try {
                        Rdata2 = mr.readRawDoubleMatrix(in, n, n);
                        Idata2 = mr.readRawDoubleMatrix(in, n, n);
                    } catch (IOException io) {
                        Log.e("ComplexRBSystem", "IOException with file " + file, io);
                    } finally {
                        in.close();
                        in = null;
                    }
                    for (int k = 0; k < n; k++)
                        for (int l = 0; l < n; l++)
                            Aq_Aq_representor_norms[count][k][l] = new Complex(Rdata2[k][l], Idata2[k][l]);

                    count++;
                }
            Log.d(DEBUG_TAG, "Finished reading Aq_Aq_norms.dat");
        }

        // // Read calN number
        // {
        // if (getNumFields() > 0) {
        // BufferedReader reader = m.getBufReader("calN.dat");
        //
        // String line = reader.readLine();
        //
        // set_calN(Integer.parseInt(line));
        // reader.close();
        // }
        //
        // Log.d(DEBUG_TAG, "Finished reading calN.dat");
        // }

        int n = getGeometry().getNumVertices();
        // Reading uL data
        {
            if (getQuL() > 0) {
                uL_vector = new Complex[getQuL()][n];
                float[] Rdata3, Idata3;
                for (int q_uL = 0; q_uL < getQuL(); q_uL++) {
                    InputStream in = m.getInStream("uL_" + String.format("%03d", q_uL) + ".bin");
                    try {
                        Rdata3 = mr.readRawFloatVector(in, n);
                        Idata3 = mr.readRawFloatVector(in, n);
                    } finally {
                        in.close();
                    }
                    for (int i = 0; i < n; i++)
                        uL_vector[q_uL][i] = new Complex(Rdata3[i], Idata3[i]);
                }
            }
            Log.d(DEBUG_TAG, "Finished reading uL.dat");
        }

        // Read in Z data
        {
            if (getNumDoFFields() > 0) {
                Z_vector = new Complex[getNumDoFFields()][getNBF()][n];
                float[] Rdata3, Idata3;
                for (int imf = 0; imf < getNumDoFFields(); imf++)
                    for (int inbfs = 0; inbfs < getNBF(); inbfs++) {
                        InputStream in = m.getInStream(
                                "Z_" + String.format("%03d", imf) + "_" + String.format("%03d", inbfs) + ".bin");
                        try {
                            Rdata3 = mr.readRawFloatVector(in, n);
                            Idata3 = mr.readRawFloatVector(in, n);
                        } finally {
                            in.close();
                        }
                        for (int i = 0; i < n; i++)
                            Z_vector[imf][inbfs][i] = new Complex(Rdata3[i], Idata3[i]);
                    }
            }
            Log.d(DEBUG_TAG, "Finished reading Z.dat");
        }

        initialize_data_vectors();
    }

    protected void initialize_data_vectors() {
        // Also, resize RB_outputs and RB_output_error_error_bounds arrays
        RB_outputs = new Complex[getNumOutputs()];
        RB_output_error_bounds = new Complex[getNumOutputs()];
    }

    @Override
    public double RB_solve(int N) {

        current_N = N;

        theta_a = complex_eval_theta_q_a();

        if (N > getNBF()) {
            throw new RuntimeException(
                    "ERROR: N cannot be larger than the number " + "of basis functions in RB_solve");
        }
        if (N == 0) {
            throw new RuntimeException("ERROR: N must be greater than 0 in RB_solve");
        }

        // Assemble the RB system
        FieldMatrix<Complex> RB_system_matrix_N = new Array2DRowFieldMatrix<Complex>((new Complex(0, 0)).getField(),
                N, N);

        for (int q_a = 0; q_a < getQa(); q_a++) {
            RB_system_matrix_N = RB_system_matrix_N
                    .add(RB_A_q_vector[q_a].getSubMatrix(0, N - 1, 0, N - 1).scalarMultiply(theta_a.getEntry(q_a)));
            // scalarMultiply(complex_eval_theta_q_a(q_a) ) );
        }

        // Assemble the RB rhs
        FieldVector<Complex> RB_rhs_N = new ArrayFieldVector<Complex>(N, new Complex(0d, 0d));

        for (int q_f = 0; q_f < getQf(); q_f++) {
            // Note getSubVector takes an initial index and the number of
            // entries
            // i.e. the interface is a bit different to getSubMatrix
            RB_rhs_N = RB_rhs_N.add(RB_F_q_vector[q_f].getSubVector(0, N).mapMultiply(complex_eval_theta_q_f(q_f)));
        }

        // Solve the linear system by Gaussian elimination

        RB_solution = new ArrayFieldVector<Complex>(N, new Complex(0., 0.));
        for (int j = 1; j < N; j++)
            for (int i = j; i < N; i++) {
                Complex m = RB_system_matrix_N.getEntry(i, j - 1).divide(RB_system_matrix_N.getEntry(j - 1, j - 1));
                for (int k = 0; k < N; k++)
                    RB_system_matrix_N.setEntry(i, k, RB_system_matrix_N.getEntry(i, k)
                            .subtract(RB_system_matrix_N.getEntry(j - 1, k).multiply(m)));
                RB_rhs_N.setEntry(i, RB_rhs_N.getEntry(i).subtract(m.multiply(RB_rhs_N.getEntry(j - 1))));
            }
        RB_solution.setEntry(N - 1, RB_rhs_N.getEntry(N - 1).divide(RB_system_matrix_N.getEntry(N - 1, N - 1)));
        for (int j = N - 2; j >= 0; j--) {
            Complex m = new Complex(0., 0.);
            for (int i = j + 1; i < N; i++)
                m = m.add(RB_system_matrix_N.getEntry(j, i).multiply(RB_solution.getEntry(i)));
            RB_solution.setEntry(j, (RB_rhs_N.getEntry(j).subtract(m)).divide(RB_system_matrix_N.getEntry(j, j)));
        }

        // Evaluate the dual norm of the residual for RB_solution_vector
        double epsilon_N = compute_residual_dual_norm(N);

        // Get lower bound for coercivity constant
        double alpha_LB = get_SCM_lower_bound();

        // If SCM lower bound is negative
        if (alpha_LB < 0) { // Get an upper bound instead
            alpha_LB = get_SCM_upper_bound();
        }

        // Store (absolute) error bound
        double abs_error_bound = epsilon_N / residual_scaling_denom(alpha_LB);

        // Compute the norm of RB_solution
        /*
         * RealMatrix RB_inner_product_matrix_N =
         * RB_inner_product_matrix.getSubMatrix(0, N-1, 0, N-1);
         */

        double RB_solution_norm = 0.0d;
        for (int i = 0; i < N; i++)
            RB_solution_norm += ((RB_solution.getEntry(i)).multiply((RB_solution.getEntry(i)).conjugate()))
                    .getReal();
        RB_solution_norm = Math.sqrt(RB_solution_norm);

        // Now compute the outputs and associated errors
        FieldVector<Complex> RB_output_vector_N = new ArrayFieldVector<Complex>(N, new Complex(0d, 0d));
        for (int i = 0; i < getNumOutputs(); i++) {
            RB_outputs[i] = new Complex(0., 0.);

            RB_output_vector_N = (RB_output_vectors[i][0].getSubVector(0, N))
                    .mapMultiply(complex_eval_theta_q_l(i, 0));
            for (int q_l = 1; q_l < getQl(i); q_l++)
                RB_output_vector_N = RB_output_vector_N.add(
                        (RB_output_vectors[i][q_l].getSubVector(0, N)).mapMultiply(complex_eval_theta_q_l(i, q_l)));
            for (int j = 0; j < N; j++)
                RB_outputs[i] = RB_outputs[i]
                        .add((RB_output_vector_N.getEntry(j).conjugate()).multiply((RB_solution.getEntry(j))));

            RB_output_error_bounds[i] = new Complex(compute_output_dual_norm(i, 0) // Zero
                    // means
                    // no
                    // time
                    // used
                    // here
                    * abs_error_bound,
                    compute_output_dual_norm(i, 0) // Zero
                            // means
                            // no
                            // time
                            // used
                            // here
                            * abs_error_bound);
        }

        cal_derived_output();

        return (return_rel_error_bound ? abs_error_bound / RB_solution_norm : abs_error_bound);
    }

    @Override
    protected double compute_residual_dual_norm(int N) {

        // Use the stored representor inner product values
        // to evaluate the residual norm
        double res_ff = 0;
        double res_af = 0;
        double res_aa = 0;

        int q = 0;
        for (int q_f1 = 0; q_f1 < getQf(); q_f1++) {
            for (int q_f2 = q_f1; q_f2 < getQf(); q_f2++) {
                double delta = (q_f1 == q_f2) ? 1. : 2.;
                res_ff += delta * ((complex_eval_theta_q_f(q_f1).multiply(complex_eval_theta_q_f(q_f2).conjugate()))
                        .multiply(Fq_representor_norms[q])).getReal();
                q++;
            }
        }

        for (int q_f = 0; q_f < getQf(); q_f++) {
            for (int q_a = 0; q_a < getQa(); q_a++) {
                for (int i = 0; i < N; i++) {
                    res_af += 2. * (get_complex_soln_coeff(i).conjugate().multiply(
                            // complex_eval_theta_q_f(q_f).multiply(complex_eval_theta_q_a(q_a).conjugate())
                            complex_eval_theta_q_f(q_f).multiply(theta_a.getEntry(q_a).conjugate()))
                            .multiply(Fq_Aq_representor_norms[q_f][q_a][i])).getReal();
                }
            }
        }

        q = 0;
        for (int q_a1 = 0; q_a1 < getQa(); q_a1++) {
            for (int q_a2 = q_a1; q_a2 < getQa(); q_a2++) {
                for (int i = 0; i < N; i++) {
                    for (int j = 0; j < N; j++) {
                        double delta = (q_a1 == q_a2) ? 1. : 2.;
                        res_aa += delta
                                * ((get_complex_soln_coeff(i).conjugate().multiply(get_complex_soln_coeff(j)))
                                        .multiply(
                                                // (complex_eval_theta_q_a(q_a1).conjugate().multiply(complex_eval_theta_q_a(q_a2)))
                                                (theta_a.getEntry(q_a1).conjugate()
                                                        .multiply(theta_a.getEntry(q_a2))))
                                        .multiply(Aq_Aq_representor_norms[q][i][j])).getReal();
                    }
                }
                q++;
            }
        }

        double residual_norm_sq = res_ff + res_af + res_aa;

        if (residual_norm_sq < 0.) {
            // Sometimes this is negative due to rounding error,
            // but error is on the order of 1.e-10, so shouldn't
            // affect error bound much...
            residual_norm_sq = Math.abs(residual_norm_sq);
        }

        return Math.sqrt(residual_norm_sq);
    }

    /**
     * @see rb.RBSystem#compute_output_dual_norm(int, double) TODO: make use of the t variable and change the
     * complex_eval_theta etc
     */
    @Override
    protected double compute_output_dual_norm(int i, double t) {

        // Use the stored representor inner product values
        // to evaluate the output dual norm
        double output_norm_sq = 0.;

        int q = 0;
        for (int q_l1 = 0; q_l1 < getQl(i); q_l1++) {
            for (int q_l2 = q_l1; q_l2 < getQl(i); q_l2++) {
                if (q_l1 == q_l2)
                    output_norm_sq += 1. * ((complex_eval_theta_q_l(i, q_l1)
                            .multiply(complex_eval_theta_q_l(i, q_l2).conjugate()))
                                    .multiply(output_dual_norms[i][q])).getReal();
                else
                    output_norm_sq += 2. * ((complex_eval_theta_q_l(i, q_l1)
                            .multiply(complex_eval_theta_q_l(i, q_l2).conjugate()))
                                    .multiply(output_dual_norms[i][q])).getReal();
                q++;
            }
        }

        return Math.sqrt(output_norm_sq);
    }

    Complex get_complex_soln_coeff(int i) {
        return RB_solution.getEntry(i);
    }

    @Override
    public double[][] get_RBsolution() {
        Complex[] c = RB_solution.toArray();
        double[][] d = new double[2][c.length];
        for (int i = 0; i < c.length; i++) {
            d[0][i] = c[i].getReal();
            d[1][i] = c[i].getImaginary();
        }
        return d;
    }

    @Override
    public double get_RB_output(int n_output, boolean Rpart) {
        if (Rpart)
            return RB_outputs[n_output].getReal();
        else
            return RB_outputs[n_output].getImaginary();
    }

    @Override
    public double get_RB_output_error_bound(int n_output, boolean Rpart) {
        if (Rpart)
            return RB_output_error_bounds[n_output].getReal();
        else
            return RB_output_error_bounds[n_output].getImaginary();
    }

    /**
     * Returns the results of the complex RB simulation.
     * 
     * Formerly, the returned float[][] array was again sorted into visualization data in RBVisualization.onCreate. As
     * there have only been @ref rbappmit models with complex field data, which has been split into real, imaginary and
     * norm field values for visualization, this new method is the native way of transforming the results into
     * ComplexSolutionFields, however many there may be. For each field the visualization will automatically create
     * views for real, imaginary and norm value fields.
     */
    @Override
    public SimulationResult getSimulationResults() {
        int N = RB_solution.getDimension();
        SimulationResult res = new SimulationResult(1);

        int fnumcnt = 0;
        for (FieldDescriptor sftype : logicalFieldTypes) {
            if (fnumcnt + sftype.Type.requiredDoFFields > getNumDoFFields()) {
                throw new RuntimeException("Too many output fields used by current "
                        + "SolutionFieldTypes set in RBSystem. Check your model.xml.");
            }
            int numDoF = Z_vector[fnumcnt][0].length;
            switch (sftype.Type) {
            case ComplexValue: {
                ComplexSolutionField f = new ComplexSolutionField(sftype, numDoF);
                Complex tmpval;
                for (int nodenr = 0; nodenr < numDoF; nodenr++) {
                    tmpval = new Complex(0., 0.);
                    for (int rbdim = 0; rbdim < N; rbdim++) {
                        tmpval = tmpval
                                .add(Z_vector[fnumcnt][rbdim][nodenr].multiply(get_complex_soln_coeff(rbdim)));
                    }
                    f.setComplexValue(nodenr, tmpval);
                }
                if (getQuL() > 0) {
                    for (int q_uL = 0; q_uL < getQuL(); q_uL++)
                        for (int i = 0; i < numDoF; i++) {
                            f.addComplexValue(i, (float) uL_vector[q_uL][i].getReal(),
                                    (float) uL_vector[q_uL][i].getImaginary());
                        }
                }
                res.addField(f);
                break;
            }
            default:
                throw new RuntimeException(
                        "Invalid/unimplemented solution field type '" + sftype.Type + "' for complex RB system");
            }
            /*
             * Increase field counter by the amount the current solution field
             * used
             */
            fnumcnt += sftype.Type.requiredDoFFields;
        }
        for (MeshTransform m : transforms) {
            res.addTransform(m);
        }
        return res;
    }

    @Override
    public SimulationResult getSweepSimResults() {
        int N = RB_sweep_solution[0][0].length;
        int numSweep = RB_sweep_solution.length;

        /*
         * Preparations
         */
        Complex[][] RB_sweep_sol = new Complex[numSweep][N];
        for (int i = 0; i < numSweep; i++)
            for (int j = 0; j < N; j++)
                RB_sweep_sol[i][j] = new Complex(RB_sweep_solution[i][0][j], RB_sweep_solution[i][1][j]);
        SimulationResult res = new SimulationResult(numSweep);
        int fnumcnt = 0;
        for (FieldDescriptor sftype : logicalFieldTypes) {
            if (fnumcnt + sftype.Type.requiredDoFFields > getNumDoFFields()) {
                throw new RuntimeException("Too many output fields used by current "
                        + "SolutionFieldTypes set in RBSystem. Check your model.xml.");
            }
            int numDoF = Z_vector[fnumcnt][0].length;
            switch (sftype.Type) {
            case ComplexValue: {
                ComplexSolutionField f = new ComplexSolutionField(sftype, numDoF * numSweep);
                Complex tmpval;
                for (int iSweep = 0; iSweep < numSweep; iSweep++) {
                    for (int i = 0; i < numDoF; i++) {
                        tmpval = new Complex(0., 0.);
                        for (int rbdim = 0; rbdim < N; rbdim++) {
                            tmpval = tmpval.add(Z_vector[fnumcnt][rbdim][i].multiply(RB_sweep_sol[iSweep][rbdim]));
                        }
                        f.setComplexValue(iSweep * numDoF + i, tmpval);
                    }
                }
                if (getQuL() > 0) {
                    for (int q_uL = 0; q_uL < getQuL(); q_uL++)
                        for (int iSweep = 0; iSweep < numSweep; iSweep++)
                            for (int nodenr = 0; nodenr < numDoF; nodenr++) {
                                f.addComplexValue(iSweep * numDoF + nodenr, uL_vector[q_uL][nodenr]);
                            }
                }
                res.addField(f);
                break;
            }
            default:
                throw new RuntimeException("Invalid/unimplemented solution field type '" + sftype.Type
                        + "' for complex RB system sweep");
            }
            /*
             * Increase field counter by the amount the current solution field
             * used
             */
            fnumcnt += sftype.Type.requiredDoFFields;
        }
        for (MeshTransform m : transforms) {
            res.addTransform(m);
        }
        return res;
    }

    public boolean is_derived_output() {
        Method meth;

        try {
            Class<?> partypes[] = null;
            meth = oldAffFcnCl.getMethod("is_derived_output", partypes);
        } catch (NoSuchMethodException nsme) {
            return false;
        }

        try {
            Object arglist[] = null;
            Object theta_obj = meth.invoke(oldAffFcnObj, arglist);
            boolean val = (Boolean) theta_obj;
            return val;
        } catch (IllegalAccessException iae) {
            throw new RuntimeException(iae);
        } catch (InvocationTargetException ite) {
            throw new RuntimeException(ite.getCause());
        }
    }

    public void cal_derived_output() {
        if (is_derived_output()) {
            Method meth;

            try {

                Class<?> partypes[] = new Class[1];
                partypes[0] = double[].class;

                meth = oldAffFcnCl.getMethod("cal_derived_output", partypes);
            } catch (NoSuchMethodException nsme) {
                throw new RuntimeException("getMethod for cal_derived_output failed", nsme);
            }

            try {

                Object arglist[] = new Object[1];
                double[] input = new double[4];
                for (int i = 0; i < getNumOutputs(); i++) {
                    input[0] = RB_outputs[i].getReal();
                    input[1] = RB_outputs[i].getImaginary();
                    input[2] = RB_output_error_bounds[i].getReal();
                    input[3] = RB_output_error_bounds[i].getImaginary();

                    arglist[0] = input;

                    Object theta_obj = meth.invoke(oldAffFcnObj, arglist);
                    double[] output = (double[]) theta_obj;
                    RB_outputs[i] = new Complex(output[0], output[1]);
                    RB_output_error_bounds[i] = new Complex(output[2], output[3]);
                }
            } catch (IllegalAccessException iae) {
                throw new RuntimeException(iae);
            } catch (InvocationTargetException ite) {
                throw new RuntimeException(ite.getCause());
            }

        }
    }
}