Skip to content

solver_handler

SolverHandler

Creates and runs a solver instance.

This may be an optimiser (e.g. VMCON) or an equation solver (e.g. fsolve).

Parameters:

Name Type Description Default
models Models

physics and engineering model objects

required
solver_name str

which solver to use, as specified in solver.py

required
Source code in process/core/solver/solver_handler.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
class SolverHandler:
    """Creates and runs a solver instance.

    This may be an optimiser (e.g. VMCON) or an equation solver (e.g. fsolve).

    Parameters
    ----------
    models : process.main.Models
        physics and engineering model objects
    solver_name : str
        which solver to use, as specified in solver.py
    """

    def __init__(self, models, solver_name):
        self.models = models
        self.solver_name = solver_name

    def run(self):
        """Run solver and retry if it fails in certain ways."""
        # Initialise iteration variables and bounds in Fortran
        load_iteration_variables()
        load_scaled_bounds()

        # Initialise iteration variables and bounds in Python: relies on Fortran
        # iteration variables being defined above
        # Trim maximum size arrays down to actually used size
        n = numerics.nvar
        x = numerics.xcm[:n]
        bndl = numerics.itv_scaled_lower_bounds[:n]
        bndu = numerics.itv_scaled_upper_bounds[:n]

        # Define total number of constraints and equality constraints
        m = numerics.neqns + numerics.nineqns
        meq = numerics.neqns

        # Evaluators() calculates the objective and constraint functions and
        # their gradients for a given vector x
        evaluators = Evaluators(self.models, x)

        # Configure solver for problem
        self.solver = get_solver(self.solver_name)
        self.solver.set_evaluators(evaluators)
        self.solver.set_bounds(bndl, bndu)
        self.solver.set_opt_params(x)
        self.solver.set_constraints(m, meq)
        ifail = self.solver.solve()

        # If VMCON optimisation has failed then try altering value of epsfcn
        if self.solver_name == "vmcon":
            if ifail != 1:
                print("Trying again with new epsfcn")
                # epsfcn is only used in evaluators.Evaluators()
                # TODO epsfcn could be set in Evaluators instance now, don't need to
                # set/unset in numerics module
                numerics.epsfcn = numerics.epsfcn * 10  # try new larger value
                print("new epsfcn = ", numerics.epsfcn)

                ifail = self.solver.solve()
                # First solution attempt failed (ifail != 1): supply ifail value
                # to next attempt
                numerics.epsfcn = numerics.epsfcn / 10  # reset value

            if ifail != 1:
                print("Trying again with new epsfcn")
                numerics.epsfcn = numerics.epsfcn / 10  # try new smaller value
                print("new epsfcn = ", numerics.epsfcn)
                ifail = self.solver.solve()
                numerics.epsfcn = numerics.epsfcn * 10  # reset value

            # If VMCON has exited with error code 5 try another run using a multiple
            # of the identity matrix as input for the Hessian b(n,n)
            # Only do this if VMCON has not iterated (nviter=1)
            if ifail == 5 and numerics.nviter < 2:
                print(
                    "VMCON error code = 5.  Rerunning VMCON with a new initial "
                    "estimate of the second derivative matrix."
                )
                self.solver.set_b(2.0)
                ifail = self.solver.solve()

        self.output()

        return ifail

    def output(self):
        """Store results back in Fortran numerics module.

        Objective function value, solution vector and constraints vector.
        """
        numerics.norm_objf = self.solver.objf
        # Slicing required due to Fortran arrays being maximum possible, rather
        # than required, size
        numerics.xcm[: self.solver.x.shape[0]] = self.solver.x
        numerics.rcm[: self.solver.conf.shape[0]] = self.solver.conf

models = models instance-attribute

solver_name = solver_name instance-attribute

run()

Run solver and retry if it fails in certain ways.

Source code in process/core/solver/solver_handler.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
def run(self):
    """Run solver and retry if it fails in certain ways."""
    # Initialise iteration variables and bounds in Fortran
    load_iteration_variables()
    load_scaled_bounds()

    # Initialise iteration variables and bounds in Python: relies on Fortran
    # iteration variables being defined above
    # Trim maximum size arrays down to actually used size
    n = numerics.nvar
    x = numerics.xcm[:n]
    bndl = numerics.itv_scaled_lower_bounds[:n]
    bndu = numerics.itv_scaled_upper_bounds[:n]

    # Define total number of constraints and equality constraints
    m = numerics.neqns + numerics.nineqns
    meq = numerics.neqns

    # Evaluators() calculates the objective and constraint functions and
    # their gradients for a given vector x
    evaluators = Evaluators(self.models, x)

    # Configure solver for problem
    self.solver = get_solver(self.solver_name)
    self.solver.set_evaluators(evaluators)
    self.solver.set_bounds(bndl, bndu)
    self.solver.set_opt_params(x)
    self.solver.set_constraints(m, meq)
    ifail = self.solver.solve()

    # If VMCON optimisation has failed then try altering value of epsfcn
    if self.solver_name == "vmcon":
        if ifail != 1:
            print("Trying again with new epsfcn")
            # epsfcn is only used in evaluators.Evaluators()
            # TODO epsfcn could be set in Evaluators instance now, don't need to
            # set/unset in numerics module
            numerics.epsfcn = numerics.epsfcn * 10  # try new larger value
            print("new epsfcn = ", numerics.epsfcn)

            ifail = self.solver.solve()
            # First solution attempt failed (ifail != 1): supply ifail value
            # to next attempt
            numerics.epsfcn = numerics.epsfcn / 10  # reset value

        if ifail != 1:
            print("Trying again with new epsfcn")
            numerics.epsfcn = numerics.epsfcn / 10  # try new smaller value
            print("new epsfcn = ", numerics.epsfcn)
            ifail = self.solver.solve()
            numerics.epsfcn = numerics.epsfcn * 10  # reset value

        # If VMCON has exited with error code 5 try another run using a multiple
        # of the identity matrix as input for the Hessian b(n,n)
        # Only do this if VMCON has not iterated (nviter=1)
        if ifail == 5 and numerics.nviter < 2:
            print(
                "VMCON error code = 5.  Rerunning VMCON with a new initial "
                "estimate of the second derivative matrix."
            )
            self.solver.set_b(2.0)
            ifail = self.solver.solve()

    self.output()

    return ifail

output()

Store results back in Fortran numerics module.

Objective function value, solution vector and constraints vector.

Source code in process/core/solver/solver_handler.py
 94
 95
 96
 97
 98
 99
100
101
102
103
def output(self):
    """Store results back in Fortran numerics module.

    Objective function value, solution vector and constraints vector.
    """
    numerics.norm_objf = self.solver.objf
    # Slicing required due to Fortran arrays being maximum possible, rather
    # than required, size
    numerics.xcm[: self.solver.x.shape[0]] = self.solver.x
    numerics.rcm[: self.solver.conf.shape[0]] = self.solver.conf