Esempio n. 1
0
def taylor_test(function,
                gradient,
                m,
                delta_m=None,
                verbose=False,
                ratio_tol=1.90):
    """
    Apply a 'Taylor test' to verify that the provided `gradient` function is a consistent
    approximation to the provided `function` at point `m`. This is done by choosing a random search
    direction and constructing a sequence of finite difference approximations. If the gradient is
    consistent then the associated Taylor remainder will decrease quadratically.

    :arg function: a scalar valued function with a single vector argument.
    :arg gradient: proposed gradient of above function, to be tested.
    :arg m: vector at which to perform the test.
    :arg delta_m: search direction in which to perform the test.
    :kwarg verbose: toggle printing to screen.
    :kwarg ratio_tol: value which must be exceeded for convergence.
    """
    if verbose:
        print_output(24 * "=" + "TAYLOR TEST" + 24 * "=")
    m = np.array(m).flatten()
    if delta_m is None:
        delta_m = np.random.normal(loc=0.0, scale=1.0, size=m.shape)
    assert len(m) == len(delta_m)

    # Evaluate the reduced functional and gradient at the specified control value
    Jm = function(m)
    dJdm = gradient(m).flatten()
    assert len(m) == len(dJdm)

    # Check that the Taylor remainders decrease quadratically
    remainders = np.zeros(4)
    epsilons = [0.01 * 0.5**i for i in range(4)]
    rates = np.zeros(3)
    passed = True
    for i in range(4):
        h = epsilons[i]
        if verbose:
            print_output("h = {:.4e}".format(h))
        J_step = function(m + h * delta_m)
        remainders[i] = np.abs(J_step - Jm - h * np.dot(dJdm, delta_m))
        if i == 0:
            if verbose:
                print_output("remainder = {:.4e}".format(remainders[i]))
        elif i > 0:
            rates[i - 1] = np.log(remainders[i] / remainders[i - 1]) / np.log(
                epsilons[i] / epsilons[i - 1])
            if verbose:
                msg = "remainder = {:.4e}  convergence rate = {:.2f}"
                print_output(msg.format(remainders[i], rates[i - 1]))
            passed = passed and (rates[i - 1] > ratio_tol)
    if verbose:
        print_output(
            20 * "=" +
            "TAYLOR TEST {:s}!".format("PASSED" if passed else "FAILED") +
            20 * "=")
    return min(rates)
def export_final_state(
    inputdir,
    uv,
    elev,
):
    """
    Export fields to be used in a subsequent simulation
    """
    if not os.path.exists(inputdir):
        os.makedirs(inputdir)
    th.print_output("Exporting fields for subsequent simulation")
    chk = th.DumbCheckpoint(inputdir + "/velocity", mode=th.FILE_CREATE)
    chk.store(uv, name="velocity")
    th.File(inputdir + '/velocityout.pvd').write(uv)
    chk.close()
    chk = th.DumbCheckpoint(inputdir + "/elevation", mode=th.FILE_CREATE)
    chk.store(elev, name="elevation")
    th.File(inputdir + '/elevationout.pvd').write(elev)
    chk.close()
Esempio n. 3
0
parser.add_argument("-debug", help="Print all debugging statements")
parser.add_argument("-debug_mode", help="Choose debugging mode from 'basic' and 'full'")

args = parser.parse_args()


# --- Set parameters

approach = 'fixed_mesh'
level = int(args.level or 0)
kwargs = {
    'approach': approach,
    'level': level,
    'num_meshes': int(args.num_meshes or 1),
    'plot_pvd': True,
    'debug': bool(args.debug or False),
    'debug_mode': args.debug_mode or 'basic',
}
op = IdealisedDesalinationOutfallOptions(**kwargs)


# --- Run model

tp = AdaptiveDesalinationProblem(op)
cpu_timestamp = perf_counter()
tp.solve_adjoint()
cpu_time = perf_counter() - cpu_timestamp
logstr = "Total CPU time: {:.1f} seconds / {:.1f} minutes / {:.3f} hours\n"
logstr = logstr.format(cpu_time, cpu_time/60, cpu_time/3600)
print_output(logstr)
Esempio n. 4
0
def to_latlon(easting,
              northing,
              zone_number,
              zone_letter=None,
              northern=None,
              force_longitude=False,
              coords=None):
    """
    Convert UTM coordinates to latitude-longitude, courtesy of Tobias Bieniek, 2012 (with some
    minor edits).

    :arg easting: eastward-measured Cartesian geographic distance.
    :arg northing: northward-measured Cartesian geographic distance.
    :arg zone_number: UTM zone number (increasing eastward).
    :param zone_letter: UTM zone letter (increasing alphabetically northward).
    :param northern: specify northern or southern hemisphere.
    :param coords: coordinate field of mesh (used to check validity of coordinates).
    :return: latitude-longitude coordinate pair.
    """
    if not zone_letter and northern is None:
        raise ValueError('either zone_letter or northern needs to be set')

    elif zone_letter and northern is not None:
        raise ValueError('set either zone_letter or northern, but not both')

    if not force_longitude:
        if not 100000 <= easting < 1000000:
            raise OutOfRangeError(
                'easting {:f} out of range (must be between 100,000 m and 999,999 m)'
                .format(easting))

    msg = 'northing out of range (must be between 0 m and 10,000,000 m)'
    if isinstance(northing, ufl.indexed.Indexed):
        from firedrake import sin, cos, sqrt
        if coords is None:
            if os.environ.get('WARNINGS', '0') != '0':
                print_output("WARNING: Cannot check validity of coordinates.")
        else:
            minval, maxval = coords.dat.data[:, 1].min(
            ), coords.dat.data[:, 1].max()
            if not (0 <= minval and maxval <= 10000000):
                raise OutOfRangeError(msg)
    elif isinstance(northing, np.ndarray):
        from numpy import sin, cos, sqrt
        minval, maxval = northing.min(), northing.max()
        if not (0 <= minval and maxval <= 10000000):
            raise OutOfRangeError(msg)
    else:
        from math import sin, cos, sqrt
        if not 0 <= northing <= 10000000:
            raise OutOfRangeError(msg)
    if not 1 <= zone_number <= 60:
        raise OutOfRangeError(
            'zone number out of range (must be between 1 and 60)')

    if zone_letter:
        zone_letter = zone_letter.upper()

        if not 'C' <= zone_letter <= 'X' or zone_letter in ['I', 'O']:
            raise OutOfRangeError(
                'zone letter out of range (must be between C and X)')

        northern = zone_letter >= 'N'

    x = easting - 500000
    y = northing

    if not northern:
        y -= 10000000

    m = y / K0
    mu = m / R / M1

    p_rad = (mu + P2 * sin(2 * mu) + P3 * sin(4 * mu) + P4 * sin(6 * mu) +
             P5 * sin(8 * mu))

    p_sin = sin(p_rad)
    p_sin2 = p_sin * p_sin

    p_cos = cos(p_rad)

    p_tan = p_sin / p_cos
    p_tan2 = p_tan * p_tan
    p_tan4 = p_tan2 * p_tan2

    ep_sin = 1 - E * p_sin2
    ep_sin_sqrt = sqrt(1 - E * p_sin2)

    n = R / ep_sin_sqrt
    r = (1 - E) / ep_sin

    c = _E * p_cos**2
    c2 = c * c

    d = x / n / K0
    d2 = d * d
    d3 = d2 * d
    d4 = d3 * d
    d5 = d4 * d
    d6 = d5 * d

    latitude = (
        p_rad - p_tan / r * (d2 / 2 - d4 / 24 *
                             (5 + 3 * p_tan2 + 10 * c - 4 * c2 - 9 * E_P2)) +
        d6 / 720 *
        (61 + 90 * p_tan2 + 298 * c + 45 * p_tan4 - 252 * E_P2 - 3 * c2))

    longitude = (
        d - d3 / 6 * (1 + 2 * p_tan2 + c) + d5 / 120 *
        (5 - 2 * c + 28 * p_tan2 - 3 * c2 + 8 * E_P2 + 24 * p_tan4)) / p_cos

    return degrees(latitude), degrees(
        longitude) + zone_number_to_central_longitude(zone_number)
Esempio n. 5
0
def from_latlon(latitude,
                longitude,
                force_zone_number=None,
                zone_info=False,
                coords=None):
    """
    Convert latitude-longitude coordinates to UTM, courtesy of Tobias Bieniek, 2012.

    :arg latitude: northward anglular position, origin at the Equator.
    :arg longitude: eastward angular position, with origin at the Greenwich Meridian.
    :param force_zone_number: force coordinates to fall within a particular UTM zone.
    :param zone_info: output zone letter and number.
    :param coords: coordinate field of mesh (used to check validity of coordinates).
    :return: UTM coordinate 4-tuple.
    """
    lat_msg = 'latitude out of range (must be between 80 deg S and 84 deg N)'
    lon_msg = 'longitude out of range (must be between 180 deg W and 180 deg E)'
    if isinstance(latitude, ufl.indexed.Indexed):
        from firedrake import sin, cos, sqrt
        if coords is None:
            if os.environ.get('WARNINGS', '0') != '0':
                print_output("WARNING: Cannot check validity of coordinates.")
        else:
            minval, maxval = coords.dat.data[:, 0].min(
            ), coords.dat.data[:, 0].max()
            if not (-80.0 <= minval and maxval <= 84.0):
                raise OutOfRangeError(lon_msg)
            minval, maxval = coords.dat.data[:, 1].min(
            ), coords.dat.data[:, 1].max()
            if not (-180.0 <= minval and maxval <= 180.0):
                raise OutOfRangeError(lat_msg)
    elif isinstance(latitude, np.ndarray):
        from numpy import sin, cos, sqrt
        minval, maxval = longitude.min(), longitude.max()
        if not (-180.0 <= minval and maxval <= 180.0):
            raise OutOfRangeError(lon_msg)
        minval, maxval = latitude.min(), latitude.max()
        if not (-80.0 <= minval and maxval <= 84.0):
            raise OutOfRangeError(lat_msg)
    else:
        from math import sin, cos, sqrt
        if not -180.0 <= longitude <= 180.0:
            raise OutOfRangeError(lon_msg)
        if not -80.0 <= latitude <= 84.0:
            raise OutOfRangeError(lat_msg)

    lat_rad = radians(latitude)
    lat_sin = sin(lat_rad)
    lat_cos = cos(lat_rad)

    lat_tan = lat_sin / lat_cos
    lat_tan2 = lat_tan * lat_tan
    lat_tan4 = lat_tan2 * lat_tan2

    if force_zone_number is None:
        zone_number = latlon_to_zone_number(latitude, longitude)
    else:
        zone_number = force_zone_number

    lon_rad = radians(longitude)
    central_lon_rad = radians(zone_number_to_central_longitude(zone_number))

    n = R / sqrt(1 - E * lat_sin**2)
    c = E_P2 * lat_cos**2

    a = lat_cos * (lon_rad - central_lon_rad)
    a2 = a * a
    a3 = a2 * a
    a4 = a3 * a
    a5 = a4 * a
    a6 = a5 * a

    m = R * (M1 * lat_rad - M2 * sin(2 * lat_rad) + M3 * sin(4 * lat_rad) -
             M4 * sin(6 * lat_rad))

    easting = K0 * n * (
        a + a3 / 6 * (1 - lat_tan2 + c) + a5 / 120 *
        (5 - 18 * lat_tan2 + lat_tan4 + 72 * c - 58 * E_P2)) + 500000

    northing = K0 * (m + n * lat_tan *
                     (a2 / 2 + a4 / 24 *
                      (5 - lat_tan2 + 9 * c + 4 * c**2) + a6 / 720 *
                      (61 - 58 * lat_tan2 + lat_tan4 + 600 * c - 330 * E_P2)))

    if isinstance(latitude, ufl.indexed.Indexed):
        if coords.dat.data[:, 1].min() < 0:
            northing += 10000000
    elif isinstance(latitude, np.ndarray):
        if latitude.min() < 0:
            northing += 10000000
    else:
        if latitude < 0:
            northing += 10000000

    if zone_info:
        return easting, northing, zone_number, latitude_to_zone_letter(
            latitude)
    else:
        return easting, northing
    'locations': locations,

    # I/O and debugging
    'debug': bool(args.debug or False),
    'debug_mode': args.debug_mode or 'basic',
}
levels = int(args.levels or 5)
di = create_directory(
    os.path.join(os.path.dirname(__file__), 'outputs', 'qmesh'))

# --- Loop over mesh hierarchy

qois = []
num_cells = []
for level in range(levels):
    print_output("Running qmesh convergence on level {:d}".format(level))

    # Set parameters
    kwargs['level'] = level
    op = TohokuHazardOptions(**kwargs)
    kwargs.pop('level')

    # Solve
    swp = AdaptiveTsunamiProblem(op, nonlinear=nonlinear, print_progress=False)
    swp.solve_forward()
    qoi = swp.quantity_of_interest()
    print_output("Quantity of interest: {:.8e}".format(qoi))

    # Diagnostics
    qois.append(qoi)
    num_cells.append(swp.num_cells[0][0])
Esempio n. 7
0
def hydrodynamics_only(boundary_conditions_fn,
                       mesh2d,
                       bathymetry_2d,
                       uv_init,
                       elev_init,
                       average_size,
                       dt,
                       t_end,
                       wetting_and_drying=False,
                       wetting_alpha=0.1,
                       friction='nikuradse',
                       friction_coef=0,
                       fluc_bcs=False,
                       viscosity=10**(-6),
                       sponge_viscosity=th.Constant(1.0)):
    """
    Sets up a simulation with only hydrodynamics until a quasi-steady state when it can be used as an initial
    condition for the full morphological model. We update the bed friction at each time step.
    The actual run of the model are done outside the function

    Inputs:
    boundary_consditions_fn - function defining boundary conditions for problem
    mesh2d - define mesh working on
    bathymetry2d - define bathymetry of problem
    uv_init - initial velocity of problem
    elev_init - initial elevation of problem
    average_size - average sediment size
    dt - timestep
    t_end - end time
    wetting_and_drying - wetting and drying switch
    wetting_alpha - wetting and drying parameter
    friction - choice of friction formulation - nikuradse and manning
    friction_coef - value of friction coefficient used in manning
    viscosity - viscosity of hydrodynamics. The default value should be 10**(-6)

    Outputs:
    solver_obj - solver which we need to run to solve the problem
    update_forcings_hydrodynamics - function defining the updates to the model performed at each timestep
    outputdir - directory of outputs
    """
    def update_forcings_hydrodynamics(t_new):
        # update boundary conditions if have fluctuating conditions
        if fluc_bcs:
            in_fn, out_fn = boundary_conditions_fn(bathymetry_2d,
                                                   t_new=t_new,
                                                   state='update')
            for j in range(len(in_fn)):
                exec('constant_in' + str(j) + '.assign(' + str(in_fn[j]) + ')')

            for k in range(len(out_fn)):
                exec('constant_out' + str(k) + '.assign(' + str(out_fn[k]) +
                     ')')
        # update bed friction
        if friction == 'nikuradse':
            uv1, elev1 = solver_obj.fields.solution_2d.split()

            if wetting_and_drying:
                wd_bath_displacement = solver_obj.depth.wd_bathymetry_displacement
                depth.project(elev1 + wd_bath_displacement(elev1) +
                              bathymetry_2d)
            else:
                depth.interpolate(elev1 + bathymetry_2d)

        # calculate skin friction coefficient
        cfactor.interpolate(2 * (0.4**2) / ((th.ln(11.036 * depth /
                                                   (ksp)))**2))

    # choose directory to output results
    ts = time.time()
    st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
    outputdir = 'outputs' + st

    # export interval in seconds
    if t_end < 40:
        t_export = 0.1
    else:
        t_export = np.round(t_end / 40, 0)

    th.print_output('Exporting to ' + outputdir)

    # define function spaces
    V = th.FunctionSpace(mesh2d, 'CG', 1)
    P1_2d = th.FunctionSpace(mesh2d, 'DG', 1)

    # define parameters
    ksp = th.Constant(3 * average_size)

    # define depth
    if wetting_and_drying:
        H = th.Function(V).project(elev_init + bathymetry_2d)
        depth = th.Function(V).project(
            H + (0.5 * (th.sqrt(H**2 + wetting_alpha**2) - H)))
    else:
        depth = th.Function(V).project(elev_init + bathymetry_2d)

    # define bed friction
    cfactor = th.Function(P1_2d).interpolate(
        2 * (0.4**2) / ((th.ln(11.036 * depth / (ksp)))**2))

    # set up solver
    solver_obj = th.solver2d.FlowSolver2d(mesh2d, bathymetry_2d)
    options = solver_obj.options
    options.simulation_export_time = t_export
    options.simulation_end_time = t_end
    options.output_directory = outputdir

    options.check_volume_conservation_2d = True

    options.fields_to_export = ['uv_2d', 'elev_2d']
    options.solve_tracer = False
    options.use_lax_friedrichs_tracer = False
    if friction == 'nikuradse':
        options.quadratic_drag_coefficient = cfactor
    elif friction == 'manning':
        if friction_coef == 0:
            friction_coef = 0.02
        options.manning_drag_coefficient = th.Constant(friction_coef)
    else:
        print('Undefined friction')
    options.horizontal_viscosity = th.Function(P1_2d).interpolate(
        sponge_viscosity * th.Constant(viscosity))

    # crank-nicholson used to integrate in time system of ODEs resulting from application of galerkin FEM
    options.timestepper_type = 'CrankNicolson'
    options.timestepper_options.implicitness_theta = 1.0
    options.use_wetting_and_drying = wetting_and_drying
    options.wetting_and_drying_alpha = th.Function(V).interpolate(
        th.Constant(wetting_alpha))
    options.norm_smoother = th.Constant(1.0)

    if not hasattr(options.timestepper_options, 'use_automatic_timestep'):
        options.timestep = dt

    # set boundary conditions

    swe_bnd, left_bnd_id, right_bnd_id, in_constant, out_constant, left_string, right_string = boundary_conditions_fn(
        bathymetry_2d, flag='hydro')

    for j in range(len(in_constant)):
        exec(
            'constant_in' + str(j) + ' = th.Constant(' + str(in_constant[j]) +
            ')', globals())

    str1 = '{'
    if len(left_string) > 0:
        for i in range(len(left_string)):
            str1 += "'" + str(left_string[i]) + "': constant_in" + str(i) + ","
        str1 = str1[0:len(str1) - 1] + "}"
        exec('swe_bnd[left_bnd_id] = ' + str1)

    for k in range(len(out_constant)):
        exec(
            'constant_out' + str(k) + '= th.Constant(' + str(out_constant[k]) +
            ')', globals())

    str2 = '{'
    if len(right_string) > 0:
        for i in range(len(right_string)):
            str2 += "'" + str(
                right_string[i]) + "': constant_out" + str(i) + ","
        str2 = str2[0:len(str2) - 1] + "}"
        exec('swe_bnd[right_bnd_id] = ' + str2)

    solver_obj.bnd_functions['shallow_water'] = swe_bnd
    solver_obj.assign_initial_conditions(uv=uv_init, elev=elev_init)

    return solver_obj, update_forcings_hydrodynamics, outputdir
def morphological(boundary_conditions_fn,
                  morfac,
                  morfac_transport,
                  suspendedload,
                  convectivevel,
                  bedload,
                  angle_correction,
                  slope_eff,
                  seccurrent,
                  mesh2d,
                  bathymetry_2d,
                  input_dir,
                  viscosity_hydro,
                  ks,
                  average_size,
                  dt,
                  final_time,
                  beta_fn,
                  surbeta2_fn,
                  alpha_secc_fn,
                  friction='nikuradse',
                  friction_coef=0,
                  d90=0,
                  fluc_bcs=False,
                  bed_form='meyer',
                  sus_form='vanrijn',
                  diffusivity=0.15):
    """
    Set up a full morphological model simulation using as an initial condition the results of a hydrodynamic only model.

    Inputs:
    boundary_consditions_fn - function defining boundary conditions for problem
    morfac - morphological scale factor
    morfac_transport - switch to turn on morphological component
    suspendedload - switch to turn on suspended sediment transport
    convectivevel - switch on convective velocity correction factor in sediment concentration equation
    bedload - switch to turn on bedload transport
    angle_correction - switch on slope effect angle correction
    slope_eff - switch on slope effect magnitude correction
    seccurrent - switch on secondary current for helical flow effect
    mesh2d - define mesh working on
    bathymetry2d - define bathymetry of problem
    input_dir - folder containing results of hydrodynamics model which are used as initial conditions here
    viscosity_hydro - viscosity value in hydrodynamic equations
    ks - bottom friction coefficient for quadratic drag coefficient
    average_size - average sediment size
    dt - timestep
    final_time - end time
    beta_fn - magnitude slope effect parameter
    surbeta2_fn - angle correction slope effect parameter
    alpha_secc_fn - secondary current parameter
    friction - choice of friction formulation - nikuradse and manning
    friction_coef - value of friction coefficient used in manning
    d90 - sediment size which 90% of the sediment are below
    fluc_bcs - if true this allows the user to impose boundary conditions which vary with time
    bed_form - choice of bedload formula between 'meyer' (meyer-peter-muller) and 'soulsby' (soulsby-van-rijn)
    sus_form - choice of suspended load formula between 'vanrijn' (van Rijn 1984) and 'soulsby' (soulsby-van-rijn)
    diffusivity - diffusivity value in suspended sediment concentration equation. The default value (found by callibration) should be 0.15

    Outputs:
    solver_obj - solver which we need to run to solve the problem
    update_forcings_hydrodynamics - function defining the updates to the model performed at each timestep
    diff_bathy - bedlevel evolution
    diff_bathy_file - bedlevel evolution file
    """
    t_list = []

    def update_forcings_tracer(t_new):

        # update bathymetry
        old_bathymetry_2d.assign(bathymetry_2d)

        # extract new elevation and velocity and project onto CG space
        uv1, elev1 = solver_obj.fields.solution_2d.split()
        uv_cg.project(uv1)
        elev_cg.project(elev1)
        depth.project(elev_cg + old_bathymetry_2d)

        horizontal_velocity.interpolate(uv_cg[0])
        vertical_velocity.interpolate(uv_cg[1])

        # update boundary conditions if have fluctuating conditions
        if fluc_bcs:
            in_fn, out_fn = boundary_conditions_fn(orig_bathymetry,
                                                   flag='morpho',
                                                   morfac=morfac,
                                                   t_new=t_new,
                                                   state='update')
            for j in range(len(in_fn)):
                exec('constant_in' + str(j) + '.assign(' + str(in_fn[j]) + ')')

            for k in range(len(out_fn)):
                exec('constant_out' + str(k) + '.assign(' + str(out_fn[k]) +
                     ')')

        # update bedfriction
        hc.interpolate(th.conditional(depth > 0.001, depth, 0.001))
        aux.assign(
            th.conditional(11.036 * hc / ks > 1.001, 11.036 * hc / ks, 1.001))
        qfc.assign(2 / (th.ln(aux) / 0.4)**2)

        # calculate skin friction coefficient
        hclip.interpolate(th.conditional(ksp > depth, ksp, depth))
        cfactor.interpolate(
            th.conditional(depth > ksp,
                           2 * ((2.5 * th.ln(11.036 * hclip / ksp))**(-2)),
                           th.Constant(0.0)))

        if morfac_transport:

            # if include tracer solver then update_forcings is run twice but only want to update bathymetry once
            t_list.append(t_new)
            double_factor = False
            if suspendedload:

                if len(t_list) > 1:
                    if t_list[len(t_list) - 1] == t_list[len(t_list) - 2]:
                        double_factor = True
            else:
                # if have no tracer then update_forcings is only run once so update bathymetry at each step
                double_factor = True

            if double_factor:
                z_n.assign(old_bathymetry_2d)

                # mu - ratio between skin friction and normal friction
                mu.assign(th.conditional(qfc > 0, cfactor / qfc, 0))

                # bed shear stress
                unorm.interpolate((horizontal_velocity**2) +
                                  (vertical_velocity**2))
                TOB.interpolate(1000 * 0.5 * qfc * unorm)

                # calculate gradient of bed (noting bathymetry is -bed)
                dzdx.interpolate(old_bathymetry_2d.dx(0))
                dzdy.interpolate(old_bathymetry_2d.dx(1))

                # initialise exner equation
                f = 0

                if suspendedload:
                    # source term

                    # deposition flux - calculating coefficient to account for stronger conc at bed
                    B.interpolate(th.conditional(a > depth, a / a, a / depth))
                    ustar.interpolate(th.sqrt(0.5 * qfc * unorm))
                    exp1.assign(
                        th.conditional(
                            (th.conditional(
                                (settling_velocity / (0.4 * ustar)) - 1 > 0,
                                (settling_velocity / (0.4 * ustar)) - 1,
                                -(settling_velocity /
                                  (0.4 * ustar)) + 1)) > 10**(-4),
                            th.conditional(
                                (settling_velocity / (0.4 * ustar)) - 1 > 3, 3,
                                (settling_velocity / (0.4 * ustar)) - 1), 0))
                    coefftest.assign(
                        th.conditional((th.conditional(
                            (settling_velocity / (0.4 * ustar)) - 1 > 0,
                            (settling_velocity / (0.4 * ustar)) - 1,
                            -(settling_velocity /
                              (0.4 * ustar)) + 1)) > 10**(-4),
                                       B * (1 - B**exp1) / exp1,
                                       -B * th.ln(B)))
                    coeff.assign(
                        th.conditional(coefftest > 0, 1 / coefftest, 0))

                    if sus_form == 'vanrijn':
                        # erosion flux - above critical velocity bed is eroded
                        s0.assign(
                            (th.conditional(1000 * 0.5 * qfc * unorm * mu > 0,
                                            1000 * 0.5 * qfc * unorm * mu, 0) -
                             taucr) / taucr)
                        ceq.assign(0.015 * (average_size / a) *
                                   ((th.conditional(s0 < 0, 0, s0))**(1.5)) /
                                   (dstar**0.3))
                    elif sus_form == 'soulsby':
                        ucr.interpolate(0.19 * (average_size**0.1) *
                                        (th.ln(4 * depth / d90) / th.ln(10)))
                        s0.assign(
                            th.conditional((th.sqrt(unorm) - ucr)**2.4 > 0,
                                           (th.sqrt(unorm) - ucr)**2.4, 0))
                        ceq.interpolate(ass * s0 / depth)
                    else:
                        print(
                            'Unrecognised suspended sediment transport formula. Please choose "vanrijn" or "soulsby"'
                        )

                    # calculate depth-averaged source term for sediment concentration equation

                    source.interpolate(-(settling_velocity * coeff *
                                         solver_obj.fields.tracer_2d / depth) +
                                       (settling_velocity * ceq / depth))
                    # update sediment rate to ensure equilibrium at inflow
                    sediment_rate.assign(ceq.at([0, 0]) / coeff.at([0, 0]))

                    if convectivevel:
                        # correction factor to advection velocity in sediment concentration equation

                        Bconv.interpolate(
                            th.conditional(depth > 1.1 * ksp, ksp / depth,
                                           ksp / (1.1 * ksp)))
                        Aconv.interpolate(
                            th.conditional(depth > 1.1 * a, a / depth,
                                           a / (1.1 * a)))

                        # take max of value calculated either by ksp or depth
                        Amax.assign(th.conditional(Aconv > Bconv, Aconv,
                                                   Bconv))

                        r1conv.assign(1 - (1 / 0.4) * th.conditional(
                            settling_velocity / ustar < 1, settling_velocity /
                            ustar, 1))

                        Ione.assign(
                            th.conditional(
                                r1conv > 10**(-8), (1 - Amax**r1conv) / r1conv,
                                th.conditional(r1conv < -10**(-8),
                                               (1 - Amax**r1conv) / r1conv,
                                               th.ln(Amax))))

                        Itwo.assign(
                            th.conditional(
                                r1conv > 10**(-8),
                                -(Ione + (th.ln(Amax) *
                                          (Amax**r1conv))) / r1conv,
                                th.conditional(
                                    r1conv < -10**(-8),
                                    -(Ione + (th.ln(Amax) * (Amax**r1conv))) /
                                    r1conv, -0.5 * th.ln(Amax)**2)))

                        alpha.assign(-(Itwo -
                                       (th.ln(Amax) - th.ln(30)) * Ione) /
                                     (Ione * ((th.ln(Amax) - th.ln(30)) + 1)))

                        # final correction factor
                        alphatest2.assign(
                            th.conditional(
                                th.conditional(alpha > 1, 1, alpha) < 0, 0,
                                th.conditional(alpha > 1, 1, alpha)))

                        # multiply correction factor by velocity and insert back into sediment concentration equation
                        corrective_velocity.interpolate(alphatest2 * uv1)
                    else:
                        corrective_velocity.interpolate(uv1)

                if bedload:

                    # calculate angle of flow
                    calfa.interpolate(horizontal_velocity / th.sqrt(unorm))
                    salfa.interpolate(vertical_velocity / th.sqrt(unorm))
                    div_function.interpolate(th.as_vector((calfa, salfa)))

                    if slope_eff:
                        # slope effect magnitude correction due to gravity where beta is a parameter normally set to 1.3
                        # we use z_n1 and equals so that we can use an implicit method in Exner
                        slopecoef = (1 + beta *
                                     (z_n1.dx(0) * calfa + z_n1.dx(1) * salfa))
                    else:
                        slopecoef = th.Constant(1.0)

                    if angle_correction:
                        # slope effect angle correction due to gravity
                        tt1.interpolate(
                            th.conditional(
                                1000 * 0.5 * qfc * unorm > 10**(-10),
                                th.sqrt(cparam / (1000 * 0.5 * qfc * unorm)),
                                th.sqrt(cparam / (10**(-10)))))
                        # add on a factor of the bed gradient to the normal
                        aa.assign(salfa + tt1 * dzdy)
                        bb.assign(calfa + tt1 * dzdx)
                        norm.assign(
                            th.conditional(
                                th.sqrt(aa**2 + bb**2) > 10**(-10),
                                th.sqrt(aa**2 + bb**2), 10**(-10)))
                        # we use z_n1 and equals so that we can use an implicit method in Exner
                        calfamod = (calfa + (tt1 * z_n1.dx(0))) / norm
                        salfamod = (salfa + (tt1 * z_n1.dx(1))) / norm

                    if seccurrent:
                        # accounts for helical flow effect in a curver channel

                        # again use z_n1 and equals so can use an implicit method in Exner
                        free_surface_dx = depth.dx(0) - z_n1.dx(0)
                        free_surface_dy = depth.dx(1) - z_n1.dx(1)

                        velocity_slide = (horizontal_velocity * free_surface_dy
                                          ) - (vertical_velocity *
                                               free_surface_dx)

                        tandelta_factor.interpolate(
                            7 * 9.81 * 1000 * depth * qfc /
                            (2 * alpha_secc * ((horizontal_velocity**2) +
                                               (vertical_velocity**2))))

                        if angle_correction:
                            # if angle has already been corrected we must alter the corrected angle to obtain the corrected secondary current angle
                            t_1 = (TOB * slopecoef * calfamod) + (
                                vertical_velocity * tandelta_factor *
                                velocity_slide)
                            t_2 = (TOB * slopecoef * salfamod) - (
                                horizontal_velocity * tandelta_factor *
                                velocity_slide)
                        else:
                            t_1 = (TOB * slopecoef *
                                   calfa) + (vertical_velocity *
                                             tandelta_factor * velocity_slide)
                            t_2 = ((TOB * slopecoef * salfa) -
                                   (horizontal_velocity * tandelta_factor *
                                    velocity_slide))

                        # calculated to normalise the new angles
                        t4 = th.sqrt((t_1**2) + (t_2**2))

                        # updated magnitude correction and angle corrections
                        slopecoef = t4 / TOB

                        calfanew = t_1 / t4
                        salfanew = t_2 / t4

                    if bed_form == 'meyer':
                        # implement meyer-peter-muller bedload transport formula
                        thetaprime.interpolate(
                            mu * (1000 * 0.5 * qfc * unorm) /
                            ((2650 - 1000) * 9.81 * average_size))

                        # if velocity above a certain critical value then transport occurs
                        phi.assign(
                            th.conditional(thetaprime < thetacr, 0,
                                           8 * (thetaprime - thetacr)**1.5))

                        # bedload transport flux with magnitude correction
                        qb_total = slopecoef * phi * th.sqrt(
                            g * (2650 / 1000 - 1) * average_size**3)
                    elif bed_form == 'soulsby':
                        abb.interpolate(
                            th.conditional(
                                depth >= average_size, 0.005 * depth *
                                ((average_size / depth)**1.2) / coeff_soulsby,
                                0.005 * depth / coeff_soulsby))
                        ucr_bed.interpolate(
                            th.conditional(
                                depth > d90, 0.19 * (average_size**0.1) *
                                (th.ln(4 * depth / d90)) / (th.ln(10)),
                                0.19 * (average_size**0.1) * (th.ln(4)) /
                                (th.ln(10))))
                        s0_bed.interpolate(
                            th.conditional((th.sqrt(unorm) - ucr_bed)**2.4 > 0,
                                           (th.sqrt(unorm) - ucr_bed)**2.4, 0))
                        qb_total = slopecoef * abb * s0_bed * th.sqrt(unorm)
                    else:
                        print(
                            'Unrecognised bedload transport formula. Please choose "meyer" or "soulsby"'
                        )

                    # add time derivative to exner equation with a morphological scale factor
                    f += (((1 - porosity) * (z_n1 - z_n) /
                           (dt * morfac)) * v) * fire.dx

                    # formulate bedload transport flux with correct angle depending on corrections implemented
                    if angle_correction and seccurrent is False:
                        qbx = qb_total * calfamod
                        qby = qb_total * salfamod
                    elif seccurrent:
                        qbx = qb_total * calfanew
                        qby = qb_total * salfanew
                    else:
                        qbx = qb_total * calfa
                        qby = qb_total * salfa

                    # add bedload transport to exner equation
                    f += -(v * (
                        (qbx * n[0]) + (qby * n[1]))) * fire.ds(1) - (v * (
                            (qbx * n[0]) +
                            (qby * n[1]))) * fire.ds(2) + (qbx *
                                                           (v.dx(0)) + qby *
                                                           (v.dx(1))) * fire.dx

                else:
                    # if no bedload transport component initialise exner equation with time derivative
                    f = (((1 - porosity) * (z_n1 - z_n) /
                          (dt * morfac)) * v) * fire.dx

                if suspendedload:
                    # add suspended sediment transport to exner equation multiplied by depth as the exner equation is not depth-averaged

                    qbsourcedepth.interpolate(source * depth)
                    f += -(qbsourcedepth * v) * fire.dx

                # solve exner equation using finite element methods
                fire.solve(f == 0, z_n1)

                # update bed
                bathymetry_2d.assign(z_n1)

                if round(t_new, 2) % t_export == 0:
                    # calculate difference between original bathymetry and new bathymetry
                    diff_bathy.interpolate(-bathymetry_2d + orig_bathymetry)
                    diff_bathy_file.write(diff_bathy)

    # choose directory to output results
    ts = time.time()
    st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
    outputdir = 'outputs' + st

    # final time of simulation
    t_end = final_time / morfac

    # export interval in seconds
    t_export = np.round(t_end / 100, 0)

    th.print_output('Exporting to ' + outputdir)

    x, y = th.SpatialCoordinate(mesh2d)

    # define function spaces
    P1_2d = th.FunctionSpace(mesh2d, 'DG', 1)
    vectorP1_2d = th.VectorFunctionSpace(mesh2d, 'DG', 1)
    V = th.FunctionSpace(mesh2d, 'CG', 1)
    vector_cg = th.VectorFunctionSpace(mesh2d, 'CG', 1)

    # define test functions on mesh
    v = fire.TestFunction(V)
    n = th.FacetNormal(mesh2d)
    z_n1 = fire.Function(V, name="z^{n+1}")
    z_n = fire.Function(V, name="z^{n}")

    # define original bathymetry before bedlevel changes
    orig_bathymetry = th.Function(V).interpolate(bathymetry_2d)

    # calculate bed evolution
    diff_bathy = th.Function(V).interpolate(-bathymetry_2d + orig_bathymetry)

    # define output file for bed evolution
    diff_bathy_file = th.File(outputdir + "/diff_bathy.pvd")
    diff_bathy_file.write(diff_bathy)

    # define parameters
    g = th.Constant(9.81)
    porosity = th.Constant(0.4)

    ksp = th.Constant(3 * average_size)
    a = th.Constant(ks / 2)
    viscosity = th.Constant(10**(-6))

    # magnitude slope effect parameter
    beta = th.Constant(beta_fn)
    # angle correction slope effect parameters
    surbeta2 = th.Constant(surbeta2_fn)
    cparam = th.Constant((2650 - 1000) * 9.81 * average_size * (surbeta2**2))
    # secondary current parameter
    alpha_secc = th.Constant(alpha_secc_fn)

    # calculate critical shields parameter thetacr
    R = th.Constant(2650 / 1000 - 1)
    dstar = th.Constant(average_size * ((g * R) / (viscosity**2))**(1 / 3))
    if max(dstar.dat.data[:] < 1):
        print('ERROR: dstar value less than 1')
    elif max(dstar.dat.data[:] < 4):
        thetacr = th.Constant(0.24 * (dstar**(-1)))
    elif max(dstar.dat.data[:] < 10):
        thetacr = th.Constant(0.14 * (dstar**(-0.64)))
    elif max(dstar.dat.data[:] < 20):
        thetacr = th.Constant(0.04 * (dstar**(-0.1)))
    elif max(dstar.dat.data[:] < 150):
        thetacr = th.Constant(0.013 * (dstar**(0.29)))
    else:
        thetacr = th.Constant(0.055)

    # critical bed shear stress
    taucr = th.Constant((2650 - 1000) * g * average_size * thetacr)

    # calculate settling velocity
    if average_size <= 100 * (10**(-6)):
        settling_velocity = th.Constant(9.81 * (average_size**2) *
                                        ((2650 / 1000) - 1) / (18 * viscosity))
    elif average_size <= 1000 * (10**(-6)):
        settling_velocity = th.Constant(
            (10 * viscosity / average_size) *
            (th.sqrt(1 + 0.01 * ((((2650 / 1000) - 1) * 9.81 *
                                  (average_size**3)) / (viscosity**2))) - 1))
    else:
        settling_velocity = th.Constant(1.1 * th.sqrt(9.81 * average_size *
                                                      ((2650 / 1000) - 1)))

    # initialise velocity, elevation and depth
    elev_init, uv_init = initialise_fields(mesh2d, input_dir, outputdir)

    uv_cg = th.Function(vector_cg).interpolate(uv_init)

    elev_cg = th.Function(V).interpolate(elev_init)

    depth = th.Function(V).project(elev_cg + bathymetry_2d)

    old_bathymetry_2d = th.Function(V).interpolate(bathymetry_2d)

    horizontal_velocity = th.Function(V).interpolate(uv_cg[0])
    vertical_velocity = th.Function(V).interpolate(uv_cg[1])

    # define bed friction
    hc = th.Function(P1_2d).interpolate(
        th.conditional(depth > 0.001, depth, 0.001))
    aux = th.Function(P1_2d).interpolate(
        th.conditional(11.036 * hc / ks > 1.001, 11.036 * hc / ks, 1.001))
    qfc = th.Function(P1_2d).interpolate(2 / (th.ln(aux) / 0.4)**2)
    # skin friction coefficient
    hclip = th.Function(P1_2d).interpolate(
        th.conditional(ksp > depth, ksp, depth))
    cfactor = th.Function(P1_2d).interpolate(
        th.conditional(depth > ksp,
                       2 * ((2.5 * th.ln(11.036 * hclip / ksp))**(-2)),
                       th.Constant(0.0)))
    # mu - ratio between skin friction and normal friction
    mu = th.Function(P1_2d).interpolate(
        th.conditional(qfc > 0, cfactor / qfc, 0))

    # calculate bed shear stress
    unorm = th.Function(P1_2d).interpolate((horizontal_velocity**2) +
                                           (vertical_velocity**2))
    TOB = th.Function(V).interpolate(1000 * 0.5 * qfc * unorm)

    # define bed gradient
    dzdx = th.Function(V).interpolate(old_bathymetry_2d.dx(0))
    dzdy = th.Function(V).interpolate(old_bathymetry_2d.dx(1))

    if suspendedload:
        # deposition flux - calculating coefficient to account for stronger conc at bed
        B = th.Function(P1_2d).interpolate(
            th.conditional(a > depth, a / a, a / depth))
        ustar = th.Function(P1_2d).interpolate(th.sqrt(0.5 * qfc * unorm))
        exp1 = th.Function(P1_2d).interpolate(
            th.conditional(
                (th.conditional(
                    (settling_velocity / (0.4 * ustar)) - 1 > 0,
                    (settling_velocity /
                     (0.4 * ustar)) - 1, -(settling_velocity /
                                           (0.4 * ustar)) + 1)) > 10**(-4),
                th.conditional((settling_velocity / (0.4 * ustar)) - 1 > 3, 3,
                               (settling_velocity / (0.4 * ustar)) - 1), 0))
        coefftest = th.Function(P1_2d).interpolate(
            th.conditional((th.conditional(
                (settling_velocity / (0.4 * ustar)) - 1 > 0,
                (settling_velocity /
                 (0.4 * ustar)) - 1, -(settling_velocity /
                                       (0.4 * ustar)) + 1)) > 10**(-4),
                           B * (1 - B**exp1) / exp1, -B * th.ln(B)))
        coeff = th.Function(P1_2d).interpolate(
            th.conditional(coefftest > 0, 1 / coefftest, 0))

        if sus_form == 'vanrijn':
            # erosion flux - above critical velocity bed is eroded
            s0 = th.Function(P1_2d).interpolate(
                (th.conditional(1000 * 0.5 * qfc * unorm * mu > 0, 1000 * 0.5 *
                                qfc * unorm * mu, 0) - taucr) / taucr)
            ceq = th.Function(P1_2d).interpolate(
                0.015 * (average_size / a) *
                ((th.conditional(s0 < 0, 0, s0))**(1.5)) / (dstar**0.3))
        elif sus_form == 'soulsby':
            if d90 == 0:
                # if the value of d90 is unspecified set d90 = d50
                d90 = th.Constant(average_size)
            else:
                d90 = th.Constant(d90)
            coeff_soulsby = th.Constant((R * g * average_size)**1.2)
            ass = th.Constant(0.012 * average_size * (dstar**(-0.6)) /
                              coeff_soulsby)
            ucr = th.Function(P1_2d).interpolate(0.19 * (average_size**0.1) *
                                                 (th.ln(4 * depth / d90)) /
                                                 (th.ln(10)))
            s0 = th.Function(P1_2d).interpolate(
                th.conditional((th.sqrt(unorm) - ucr)**2.4 > 0,
                               (th.sqrt(unorm) - ucr)**2.4, 0))
            ceq = th.Function(P1_2d).interpolate(ass * s0 / depth)
        else:
            print(
                'Unrecognised suspended sediment transport formula. Please choose "vanrijn" or "soulsby"'
            )

        # update sediment rate to ensure equilibrium at inflow

        sediment_rate = th.Constant(ceq.at([0, 0]) / coeff.at([0, 0]))
        testtracer = th.Function(P1_2d).interpolate(ceq / coeff)
        # calculate depth-averaged source term for sediment concentration equation
        source = th.Function(P1_2d).interpolate(
            -(settling_velocity * coeff * sediment_rate / depth) +
            (settling_velocity * ceq / depth))

        # add suspended sediment transport to exner equation multiplied by depth as the exner equation is not depth-averaged
        qbsourcedepth = th.Function(V).interpolate(source * depth)

        if convectivevel:
            # correction factor to advection velocity in sediment concentration equation

            Bconv = th.Function(P1_2d).interpolate(
                th.conditional(depth > 1.1 * ksp, ksp / depth,
                               ksp / (1.1 * ksp)))
            Aconv = th.Function(P1_2d).interpolate(
                th.conditional(depth > 1.1 * a, a / depth, a / (1.1 * a)))

            # take max of value calculated either by ksp or depth
            Amax = th.Function(P1_2d).interpolate(
                th.conditional(Aconv > Bconv, Aconv, Bconv))

            r1conv = th.Function(P1_2d).interpolate(
                1 - (1 / 0.4) * th.conditional(settling_velocity / ustar < 1,
                                               settling_velocity / ustar, 1))

            Ione = th.Function(P1_2d).interpolate(
                th.conditional(
                    r1conv > 10**(-8), (1 - Amax**r1conv) / r1conv,
                    th.conditional(r1conv < -10**(-8),
                                   (1 - Amax**r1conv) / r1conv, th.ln(Amax))))

            Itwo = th.Function(P1_2d).interpolate(
                th.conditional(
                    r1conv > 10**(-8),
                    -(Ione + (th.ln(Amax) * (Amax**r1conv))) / r1conv,
                    th.conditional(
                        r1conv < -10**(-8),
                        -(Ione + (th.ln(Amax) * (Amax**r1conv))) / r1conv,
                        -0.5 * th.ln(Amax)**2)))

            alpha = th.Function(P1_2d).interpolate(
                -(Itwo - (th.ln(Amax) - th.ln(30)) * Ione) /
                (Ione * ((th.ln(Amax) - th.ln(30)) + 1)))

            # final correction factor
            alphatest2 = th.Function(P1_2d).interpolate(
                th.conditional(
                    th.conditional(alpha > 1, 1, alpha) < 0, 0,
                    th.conditional(alpha > 1, 1, alpha)))

            # multiply correction factor by velocity and insert back into sediment concentration equation
            corrective_velocity = th.Function(vectorP1_2d).interpolate(
                alphatest2 * uv_init)
        else:
            corrective_velocity = th.Function(vectorP1_2d).interpolate(uv_init)

    if bedload:
        # calculate angle of flow
        calfa = th.Function(V).interpolate(horizontal_velocity /
                                           th.sqrt(unorm))
        salfa = th.Function(V).interpolate(vertical_velocity / th.sqrt(unorm))
        div_function = th.Function(vector_cg).interpolate(
            th.as_vector((calfa, salfa)))

        if slope_eff:
            # slope effect magnitude correction due to gravity where beta is a parameter normally set to 1.3
            slopecoef = th.Function(V).interpolate(
                1 + beta * (dzdx * calfa + dzdy * salfa))
        else:
            slopecoef = th.Function(V).interpolate(th.Constant(1.0))

        if angle_correction:
            # slope effect angle correction due to gravity
            tt1 = th.Function(V).interpolate(
                th.conditional(1000 * 0.5 * qfc * unorm > 10**(-10),
                               th.sqrt(cparam / (1000 * 0.5 * qfc * unorm)),
                               th.sqrt(cparam / (10**(-10)))))
            # add on a factor of the bed gradient to the normal
            aa = th.Function(V).interpolate(salfa + tt1 * dzdy)
            bb = th.Function(V).interpolate(calfa + tt1 * dzdx)
            norm = th.Function(V).interpolate(
                th.conditional(
                    th.sqrt(aa**2 + bb**2) > 10**(-10), th.sqrt(aa**2 + bb**2),
                    10**(-10)))

        if seccurrent:
            # accounts for helical flow effect in a curver channel
            free_surface_dx = th.Function(V).interpolate(elev_cg.dx(0))
            free_surface_dy = th.Function(V).interpolate(elev_cg.dx(1))

            velocity_slide = (horizontal_velocity * free_surface_dy) - (
                vertical_velocity * free_surface_dx)

            tandelta_factor = th.Function(V).interpolate(
                7 * 9.81 * 1000 * depth * qfc / (2 * alpha_secc *
                                                 ((horizontal_velocity**2) +
                                                  (vertical_velocity**2))))

            t_1 = (TOB * slopecoef * calfa) + (
                vertical_velocity * tandelta_factor * velocity_slide)
            t_2 = ((TOB * slopecoef * salfa) -
                   (horizontal_velocity * tandelta_factor * velocity_slide))

            # calculated to normalise the new angles
            t4 = th.sqrt((t_1**2) + (t_2**2))

            # updated magnitude correction and angle corrections
            slopecoef = t4 / TOB

        if bed_form == 'meyer':
            # implement meyer-peter-muller bedload transport formula
            thetaprime = th.Function(V).interpolate(
                mu * (1000 * 0.5 * qfc * unorm) /
                ((2650 - 1000) * 9.81 * average_size))

            # if velocity above a certain critical value then transport occurs
            phi = th.Function(V).interpolate(
                th.conditional(thetaprime < thetacr, 0,
                               8 * (thetaprime - thetacr)**1.5))

        elif bed_form == 'soulsby':
            if d90 == 0:
                d90 = th.Constant(average_size)
            coeff_soulsby = th.Constant((R * g * average_size)**1.2)
            abb = th.Function(P1_2d).interpolate(
                th.conditional(
                    depth >= average_size, 0.005 * depth *
                    ((average_size / depth)**1.2) / coeff_soulsby,
                    0.005 * depth / coeff_soulsby))
            ucr_bed = th.Function(P1_2d).interpolate(
                th.conditional(
                    depth > d90, 0.19 * (average_size**0.1) *
                    (th.ln(4 * depth / d90)) / (th.ln(10)),
                    0.19 * (average_size**0.1) * (th.ln(4)) / (th.ln(10))))
            s0_bed = th.Function(P1_2d).interpolate(
                th.conditional((th.sqrt(unorm) - ucr_bed)**2.4 > 0,
                               (th.sqrt(unorm) - ucr_bed)**2.4, 0))
        else:
            print(
                'Unrecognised bedload transport formula. Please choose "meyer" or "soulsby"'
            )
    # set up solver
    solver_obj = th.solver2d.FlowSolver2d(mesh2d, bathymetry_2d)
    options = solver_obj.options
    options.simulation_export_time = t_export
    options.simulation_end_time = t_end
    options.output_directory = outputdir
    options.check_volume_conservation_2d = True
    if suspendedload:
        # switch on tracer calculation if using sediment transport component
        options.solve_tracer = True
        options.fields_to_export = [
            'uv_2d', 'elev_2d', 'tracer_2d', 'bathymetry_2d'
        ]
        options.tracer_advective_velocity = corrective_velocity
        options.tracer_source_2d = source
    else:
        options.solve_tracer = False
        options.fields_to_export = ['uv_2d', 'elev_2d', 'bathymetry_2d']
    options.use_lax_friedrichs_tracer = False
    # set bed friction
    if friction == 'nikuradse':
        options.quadratic_drag_coefficient = cfactor
    elif friction == 'manning':
        if friction_coef == 0:
            friction_coef = 0.02
        options.manning_drag_coefficient = th.Constant(friction_coef)
    else:
        print('Undefined friction')
    # set horizontal diffusivity parameter
    options.horizontal_diffusivity = th.Constant(diffusivity)
    options.horizontal_viscosity = th.Constant(viscosity_hydro)
    # crank-nicholson used to integrate in time system of ODEs resulting from application of galerkin FEM
    options.timestepper_type = 'CrankNicolson'
    options.timestepper_options.implicitness_theta = 1.0

    if not hasattr(options.timestepper_options, 'use_automatic_timestep'):
        options.timestep = dt

    # set boundary conditions
    swe_bnd, left_bnd_id, right_bnd_id, in_constant, out_constant, left_string, right_string = boundary_conditions_fn(
        orig_bathymetry, flag='morpho')

    for j in range(len(in_constant)):
        exec(
            'constant_in' + str(j) + ' = th.Constant(' + str(in_constant[j]) +
            ')', globals())

    str1 = '{'
    for i in range(len(left_string)):
        str1 += "'" + str(left_string[i]) + "': constant_in" + str(i) + ","
    str1 = str1[0:len(str1) - 1] + "}"
    exec('swe_bnd[left_bnd_id] = ' + str1)

    for k in range(len(out_constant)):
        exec(
            'constant_out' + str(k) + '= th.Constant(' + str(out_constant[k]) +
            ')', globals())

    str2 = '{'
    for i in range(len(right_string)):
        str2 += "'" + str(right_string[i]) + "': constant_out" + str(i) + ","
    str2 = str2[0:len(str2) - 1] + "}"
    exec('swe_bnd[right_bnd_id] = ' + str2)

    solver_obj.bnd_functions['shallow_water'] = swe_bnd

    if suspendedload:
        solver_obj.bnd_functions['tracer'] = {1: {'value': sediment_rate}}

        # set initial conditions
        solver_obj.assign_initial_conditions(uv=uv_init,
                                             elev=elev_init,
                                             tracer=testtracer)

    else:
        # set initial conditions
        solver_obj.assign_initial_conditions(uv=uv_init, elev=elev_init)

    return solver_obj, update_forcings_tracer, diff_bathy, diff_bathy_file
Esempio n. 9
0

# --- Create solver and copy initial solution

ep = AdaptiveProblem(CosinePrescribedVelocityOptions(**kwargs))
ep.set_initial_condition()
init_sol = ep.fwd_solutions_tracer[0].copy(deepcopy=True)
init_norm = norm(init_sol)


# --- Eulerian interpretation

ep.solve_forward()
final_sol_eulerian = ep.fwd_solutions_tracer[-1]
relative_error_eulerian = abs(errornorm(init_sol, final_sol_eulerian)/init_norm)
print_output("Relative error in Eulerian case:   {:.2f}%".format(100*relative_error_eulerian))


# --- Lagrangian interpretation

kwargs['approach'] = 'lagrangian'
lp = AdaptiveProblem(CosinePrescribedVelocityOptions(**kwargs))
lp.set_initial_condition()
init_sol = lp.fwd_solutions_tracer[0].copy(deepcopy=True)
init_coords = lp.meshes[0].coordinates.dat.data.copy()
lp.solve_forward()
final_sol_lagrangian = lp.fwd_solutions_tracer[-1]

final_coords = lp.meshes[-1].coordinates.dat.data
final_coords[:] -= [10.0, 0.0]  # TODO: Implement periodicity
if not np.allclose(init_coords, final_coords):  # FIXME
Esempio n. 10
0
)
parser.add_argument("-level", help="Mesh resolution level (default 0)")
parser.add_argument("-noisy_data", help="Toggle whether to sample noisy data (default False)")


# --- Set parameters

# Parsed arguments
args = parser.parse_args()
basis = args.basis
level = int(args.level or 0)
plot = parser.plotting_args()

# Do not attempt to plot in parallel
if COMM_WORLD.size > 1:
    print_output('Will not attempt to plot in parallel.')
    sys.exit(0)

# Setup output directories
dirname = os.path.join(os.path.dirname(__file__))
extension = lambda fpath: fpath if args.extension is None else '_'.join([fpath, args.extension])
data_dir = create_directory(os.path.join(dirname, basis, 'outputs', extension('realistic')))
plot_dir = create_directory(os.path.join(dirname, 'plots', extension('realistic'), basis))

# Collect initialisation parameters
kwargs = {
    'level': level,
    'synthetic': False,
    'noisy_data': bool(args.noisy_data or False),
}
if basis == 'box':
Esempio n. 11
0
parser.add_argument("-debug", help="Print all debugging statements")
parser.add_argument("-debug_mode",
                    help="Choose debugging mode from 'basic' and 'full'")

args = parser.parse_args()

# --- Set parameters

approach = 'fixed_mesh'
level = int(args.level or 0)
kwargs = {
    'approach': approach,
    'level': level,
    'num_meshes': int(args.num_meshes or 1),
    'plot_pvd': True,
    'debug': bool(args.debug or False),
    'debug_mode': args.debug_mode or 'basic',
}
op = IdealisedDesalinationOutfallOptions(**kwargs)

# --- Run model

tp = AdaptiveDesalinationProblem(op)
cpu_timestamp = perf_counter()
tp.solve_forward()
cpu_time = perf_counter() - cpu_timestamp
logstr = "Total CPU time: {:.1f} seconds / {:.1f} minutes / {:.3f} hours\n"
logstr = logstr.format(cpu_time, cpu_time / 60, cpu_time / 3600)
print_output(logstr)
print_output("Quantity of interest: {:.4e}".format(tp.quantity_of_interest()))
Esempio n. 12
0
    Toggle whether to consider timeseries data which has *not* been sampled (default False).
    """)
parser.add_argument("-continuous_timeseries",
                    help="Toggle discrete or continuous timeseries")
parser.add_argument("-plot_initial_guess",
                    help="Plot initial guess timeseries")

# --- Set parameters

# Parsed arguments
args = parser.parse_args()
basis = args.basis
levels = int(args.levels or 3)
plot = parser.plotting_args()
if len(plot.extensions) == 0:
    print_output("Nothing to plot.")
    sys.exit(0)
plot_init = bool(args.plot_initial_guess or False)
timeseries_type = 'timeseries'
if bool(args.continuous_timeseries or False):
    timeseries_type = '_'.join([timeseries_type, 'smooth'])

# Do not attempt to plot in parallel
if COMM_WORLD.size > 1:
    print_output("Will not attempt to plot in parallel.")
    sys.exit(0)

# Collect initialisation parameters
if basis == 'box':
    from adapt_utils.case_studies.tohoku.options.box_options import TohokuBoxBasisOptions
    constructor = TohokuBoxBasisOptions
Esempio n. 13
0
from thetis import print_output, COMM_WORLD
from turbine_adapt import *
from turbine_adapt.adapt import GoalOrientedTidalFarm
from options import ArrayOptions
import datetime


print_output(f"Start time: {datetime.datetime.now()}")

# Parse arguments
parser = Parser("test_cases/array/run_adapt.py")
parser.add_argument(
    "config",
    help="Name defining test case configuration",
    choices=["aligned", "staggered"],
)
parser.add_argument(
    "-c",
    "--columns",
    nargs="+",
    help="Turbine columns to use in QoI",
    default=[0, 1, 2, 3, 4],
)
parser.add_argument(
    "--no-final-run",
    help="Skip the final forward run",
    action="store_true",
)
parser.parse_setup()
parser.parse_convergence_criteria()
parser.parse_metric_parameters()
Esempio n. 14
0
parser.add_argument(
    "-levels", help="Number of mesh resolution levels considered (default 3)")
parser.add_argument("-noisy_data",
                    help="""
    Toggle whether to consider timeseries data which has *not* been sampled (default False).
    """)  # TODO

# --- Set parameters

# Parsed arguments
args = parser.parse_args()
bases = args.bases.split(',')
levels = int(args.levels or 3)
plot = parser.plotting_args()
if len(plot.extensions) == 0 or len(bases) == 0:
    print_output("Nothing to plot.")
    sys.exit(0)

# Do not attempt to plot in parallel
if COMM_WORLD.size > 1:
    print_output("Will not attempt to plot in parallel.")
    sys.exit(0)

# Plotting parameters
fontsize = 22
fontsize_tick = 18
fontsize_legend = 18
kwargs = {'markevery': 5}

# Paths
dirname = os.path.dirname(__file__)