Example #1
0
def test_dissolution(oil, temp, num_elems, on):
    '''
    Fuel Oil #6 does not exist...
    '''
    et = floating(substance=oil)
    diss = Dissolution(waves)
    (sc, time_step) = weathering_data_arrays(diss.array_types,
                                             water,
                                             element_type=et,
                                             num_elements=num_elems)[:2]

    print 'num spills:', len(sc.spills)
    print 'spill[0] amount:', sc.spills[0].amount

    model_time = (sc.spills[0].get('release_time') +
                  timedelta(seconds=time_step))

    diss.on = on
    diss.prepare_for_model_run(sc)
    diss.initialize_data(sc, sc.num_released)

    diss.prepare_for_model_step(sc, time_step, model_time)
    diss.weather_elements(sc, time_step, model_time)

    if on:
        print sc._data_arrays
        assert all(np.isclose(sc._data_arrays['partition_coeff'], 511.445))

        assert sc.mass_balance['dissolution'] > 0
        print "sc.mass_balance['dissolution']"
        print sc.mass_balance['dissolution']
    else:
        assert 'dissolution' not in sc.mass_balance
Example #2
0
def test_dissolution_k_ow(oil, temp, num_elems, k_ow, on):
    '''
        Here we are testing that the molar averaged oil/water partition
        coefficient (K_ow) is getting calculated with reasonable values
    '''
    et = floating(substance=oil)
    diss = Dissolution(waves)
    (sc, time_step) = weathering_data_arrays(diss.array_types,
                                             water,
                                             element_type=et,
                                             num_elements=num_elems)[:2]

    print 'num spills:', len(sc.spills)
    print 'spill[0] amount:', sc.spills[0].amount

    # we don't want to query the oil database, but get the sample oil
    assert sc.spills[0].element_type.substance.record.id is None

    model_time = (sc.spills[0].release_time +
                  timedelta(seconds=time_step))

    diss.on = on
    diss.prepare_for_model_run(sc)
    diss.initialize_data(sc, sc.num_released)

    diss.prepare_for_model_step(sc, time_step, model_time)
    diss.weather_elements(sc, time_step, model_time)

    assert all(np.isclose(sc._data_arrays['partition_coeff'], k_ow))
Example #3
0
def test_prepare_for_model_run():
    'test sort order for Dissolution weatherer'
    diss = Dissolution(waves, wind)

    (sc, _time_step) = weathering_data_arrays(diss.array_types,
                                              water)[:2]

    assert 'partition_coeff' in sc.data_arrays
    assert 'dissolution' not in sc.mass_balance

    diss.prepare_for_model_run(sc)

    assert 'dissolution' in sc.mass_balance
Example #4
0
def test_prepare_for_model_run():
    'test sort order for Dissolution weatherer'
    et = floating(substance='ABU SAFAH')
    diss = Dissolution(waves)

    (sc, time_step) = weathering_data_arrays(diss.array_types,
                                             water,
                                             element_type=et)[:2]

    assert 'partition_coeff' in sc.data_arrays
    assert 'dissolution' not in sc.mass_balance

    diss.prepare_for_model_run(sc)

    assert 'dissolution' in sc.mass_balance
Example #5
0
def test_prepare_for_model_run():
    'test sort order for Dissolution weatherer'
    et = floating(substance='ABU SAFAH')
    diss = Dissolution(waves)

    (sc, time_step) = weathering_data_arrays(diss.array_types,
                                             water,
                                             element_type=et)[:2]

    assert 'partition_coeff' in sc.data_arrays
    assert 'dissolution' not in sc.mass_balance

    diss.prepare_for_model_run(sc)

    assert 'dissolution' in sc.mass_balance
Example #6
0
def test_sort_order():
    'test sort order for Dissolution weatherer'
    wind = constant_wind(15., 0)
    waves = Waves(wind, Water())
    diss = Dissolution(waves)

    assert weatherer_sort(diss) == 8
Example #7
0
def test_prepare_for_model_run():
    'test sort order for Dissolution weatherer'
    et = floating(substance='oil_bahia')
    diss = Dissolution(waves)

    # we don't want to query the oil database, but get the sample oil
    assert et.substance.record.id is None

    (sc, _time_step) = weathering_data_arrays(diss.array_types,
                                              water,
                                              element_type=et)[:2]

    assert 'partition_coeff' in sc.data_arrays
    assert 'dissolution' not in sc.mass_balance

    diss.prepare_for_model_run(sc)

    assert 'dissolution' in sc.mass_balance
Example #8
0
def test_prepare_for_model_run():
    'test sort order for Dissolution weatherer'
    et = floating(substance='oil_bahia')
    diss = Dissolution(waves)

    # we don't want to query the oil database, but get the sample oil
    assert et.substance.record.id is None

    (sc, _time_step) = weathering_data_arrays(diss.array_types,
                                              water,
                                              element_type=et)[:2]

    assert 'partition_coeff' in sc.data_arrays
    assert 'dissolution' not in sc.mass_balance

    diss.prepare_for_model_run(sc)

    assert 'dissolution' in sc.mass_balance
Example #9
0
def test_init():
    'test initialization'
    wind = constant_wind(15., 0)
    waves = Waves(wind, Water())
    diss = Dissolution(waves)

    print diss.array_types
    assert all([(at in diss.array_types)
                for at in ('mass', 'viscosity', 'density')])
Example #10
0
def test_dissolution_droplet_size(oil, temp, num_elems, drop_size, on):
    '''
        Here we are testing that the molar averaged oil/water partition
        coefficient (K_ow) is getting calculated with reasonable values
    '''
    et = floating(substance=oil)

    disp = NaturalDispersion(waves, water)
    diss = Dissolution(waves)

    (sc, time_step) = weathering_data_arrays(diss.array_types,
                                             water,
                                             element_type=et,
                                             num_elements=num_elems)[:2]

    print 'num_spills:', len(sc.spills)
    print 'spill[0] amount:', sc.spills[0].amount, sc.spills[0].units

    model_time = (sc.spills[0]
                  .release_time + timedelta(seconds=time_step))
    print 'model_time = ', model_time
    print 'time_step = ', time_step

    # we don't want to query the oil database, but get the sample oil
    assert sc.spills[0].element_type.substance.record.id is None

    disp.on = on
    diss.on = on

    disp.prepare_for_model_run(sc)
    diss.prepare_for_model_run(sc)

    disp.initialize_data(sc, sc.num_released)
    diss.initialize_data(sc, sc.num_released)

    for i in range(3):
        disp.prepare_for_model_step(sc, time_step, model_time)
        diss.prepare_for_model_step(sc, time_step, model_time)

        disp.weather_elements(sc, time_step, model_time)
        diss.weather_elements(sc, time_step, model_time)

        print 'droplet_avg_size:', sc._data_arrays['droplet_avg_size']
        assert np.allclose(sc._data_arrays['droplet_avg_size'], drop_size[i])
Example #11
0
def test_full_run_no_evap(sample_model_fcn2, oil, temp, expected_balance):
    '''
    test dissolution outputs post step for a full run of model. Dump json
    for 'weathering_model.json' in dump directory
    '''
    low_wind = constant_wind(1., 270, 'knots')
    low_waves = Waves(low_wind, Water(temp))
    model = sample_model_weathering2(sample_model_fcn2, oil, temp)
    model.environment += [Water(temp), low_wind, low_waves]
    # model.weatherers += Evaporation(Water(temp), low_wind)
    model.weatherers += NaturalDispersion(low_waves, Water(temp))
    model.weatherers += Dissolution(low_waves, low_wind)

    print ('Model start time: {}, Duration: {}, Time step: {}'
           .format(model.start_time, model.duration, model.time_step))

    for sc in model.spills.items():
        print '\nSpill dict keys: ', sc.__dict__.keys()
        print '\nSpill data arrays: ', sc._data_arrays

        print 'num spills:', len(sc.spills)
        print ('spill[0] amount: {} {} ({})'
               .format(sc.spills[0].amount, sc.spills[0].units,
                       sc.spills[0].substance.name)
               )
        original_amount = sc.spills[0].amount

    # set make_default_refs to True for objects contained in model after adding
    # objects to the model
    model.set_make_default_refs(True)
    model.setup_model_run()

    dissolved = []
    for step_num, step in enumerate(model):
        for sc in model.spills.items():
            if step['step_num'] > 0:
                assert (sc.mass_balance['dissolution'] > 0)
                assert (sc.mass_balance['natural_dispersion'] > 0)
                assert (sc.mass_balance['sedimentation'] > 0)

            dissolved.append(sc.mass_balance['dissolution'])

            print ('\n#Step: {}'.format(step_num))
            print ("Dissolved: {0}".
                   format(sc.mass_balance['dissolution']))
            print ("Mass: {0}".
                   format(sc._data_arrays['mass']))
            print ("Mass Components: {0}".
                   format(sc._data_arrays['mass_components']))

    print ('Fraction dissolved after full run: {}'
           .format(dissolved[-1] / original_amount))

    assert dissolved[0] == 0.0
    assert np.isclose(dissolved[-1], expected_balance)
Example #12
0
def test_serialize_deseriailize():
    'test serialize/deserialize for webapi'
    wind = constant_wind(15., 0)
    water = Water()
    waves = Waves(wind, water)

    diss = Dissolution(waves)
    json_ = diss.serialize()
    pp.pprint(json_)

    assert json_['waves'] == waves.serialize()

    # deserialize and ensure the dict's are correct
    d_ = Dissolution.deserialize(json_)
    assert d_['waves'] == Waves.deserialize(json_['waves'])

    d_['waves'] = waves
    diss.update_from_dict(d_)

    assert diss.waves is waves
Example #13
0
def test_full_run(sample_model_fcn2, oil, temp, expected_balance):
    '''
    test dissolution outputs post step for a full run of model. Dump json
    for 'weathering_model.json' in dump directory
    '''
    model = sample_model_weathering2(sample_model_fcn2, oil, temp)
    model.environment += [Water(temp), wind,  waves]
    model.weatherers += Evaporation()
    model.weatherers += NaturalDispersion()
    model.weatherers += Dissolution(waves, wind)

    for sc in model.spills.items():
        print sc.__dict__.keys()
        print sc._data_arrays

        print 'num spills:', len(sc.spills)
        print 'spill[0] amount:', sc.spills[0].amount
        original_amount = sc.spills[0].amount

        # we don't want to query the oil database, but get the sample oil
        #assert sc.spills[0].substance.record.id is None

    # set make_default_refs to True for objects contained in model after adding
    # objects to the model
    model.set_make_default_refs(True)
    model.setup_model_run()

    dissolved = []
    for step in model:
        for sc in model.spills.items():
            if step['step_num'] > 0:
                assert (sc.mass_balance['dissolution'] > 0)
                assert (sc.mass_balance['natural_dispersion'] > 0)
                assert (sc.mass_balance['sedimentation'] > 0)

            dissolved.append(sc.mass_balance['dissolution'])

            # print ("\nDissolved: {0}".
            #        format(sc.mass_balance['dissolution']))
            # print ("Mass: {0}".
            #        format(sc._data_arrays['mass']))
            # print ("Mass Components: {0}".
            #        format(sc._data_arrays['mass_components']))

    print ('Fraction dissolved after full run: {}'
           .format(dissolved[-1] / original_amount))

    assert dissolved[0] == 0.0
    assert np.isclose(dissolved[-1], expected_balance, rtol=1e-4)
Example #14
0
def test_sort_order():
    'test sort order for Dissolution weatherer'
    wind = constant_wind(15., 0)
    waves = Waves(wind, Water())

    diss = Dissolution(waves, wind)
    disp = NaturalDispersion(waves=waves, water=waves.water)
    weathering_data = WeatheringData(water=waves.water)

    # dissolution is dependent upon droplet distribution generated by
    # natural dispersion
    assert weatherer_sort(disp) < weatherer_sort(diss)

    # dissolution needs to happen before we treat our weathering data
    assert weatherer_sort(diss) < weatherer_sort(weathering_data)
Example #15
0
def test_full_run_dissolution_not_active(sample_model_fcn):
    'no water/wind/waves object and no evaporation'
    model = sample_model_weathering(sample_model_fcn, 'oil_4')
    model.environment += [Water(288.7), wind,  waves]
    model.weatherers += Evaporation()
    model.weatherers += NaturalDispersion()
    model.weatherers += Dissolution(waves=waves, wind=wind, on=False)

    model.outputters += WeatheringOutput()
    for step in model:
        '''
        if no weatherers, then no weathering output - need to add on/off
        switch to WeatheringOutput
        '''
        assert 'dissolution' not in step['WeatheringOutput']
        assert ('time_stamp' in step['WeatheringOutput'])
        print ("Completed step: {0}".format(step['step_num']))
Example #16
0
def test_dissolution_not_active(oil, temp, num_elems):
    '''
    Fuel Oil #6 does not exist...
    '''
    diss = Dissolution(waves)
    et = floating(substance=oil)
    (sc, time_step) = weathering_data_arrays(diss.array_types, water,
                                             element_type=et)[:2]

    sc.amount = 10000
    model_time = (sc.spills[0].get('release_time') +
                  timedelta(seconds=time_step))

    diss.prepare_for_model_run(sc)

    new_model_time = (sc.spills[0].get('release_time') +
                      timedelta(seconds=3600))

    diss.active_start = new_model_time
    diss.prepare_for_model_step(sc, time_step, model_time)
    diss.weather_elements(sc, time_step, model_time)

    assert np.all(sc.mass_balance['dissolution'] == 0)
Example #17
0
def test__deserialize():
    'test serialize/deserialize for webapi'
    wind = constant_wind(15., 0)
    water = Water()
    waves = Waves(wind, water)

    diss = Dissolution(waves, wind)
    json_ = diss.serialize()
    pp.pprint(json_)

    assert json_['waves'] == waves.serialize()

    # deserialize and ensure the dict's are correct
    d_ = Dissolution.deserialize(json_)
    assert d_['waves'] == Waves.deserialize(json_['waves'])

    d_['waves'] = waves
    diss.update_from_dict(d_)

    assert diss.waves is waves
Example #18
0
def test_dissolution_mass_balance(oil, temp, num_elems, expected_mb, on):
    '''
    Fuel Oil #6 does not exist...
    '''
    et = floating(substance=oil)
    diss = Dissolution(waves)
    (sc, time_step) = weathering_data_arrays(diss.array_types,
                                             water,
                                             element_type=et,
                                             num_elements=num_elems)[:2]

    print 'num spills:', len(sc.spills)
    print 'spill[0] amount:', sc.spills[0].amount

    model_time = (sc.spills[0].get('release_time') +
                  timedelta(seconds=time_step))

    diss.on = on
    diss.prepare_for_model_run(sc)
    diss.initialize_data(sc, sc.num_released)

    diss.prepare_for_model_step(sc, time_step, model_time)
    diss.weather_elements(sc, time_step, model_time)

    if on:
        assert np.isclose(sc.mass_balance['dissolution'], expected_mb)
    else:
        assert 'dissolution' not in sc.mass_balance
Example #19
0
def test_dissolution_droplet_size(oil, temp, num_elems, drop_size, on):
    '''
        Here we are testing that the molar averaged oil/water partition
        coefficient (K_ow) is getting calculated with reasonable values
    '''

    disp = NaturalDispersion(waves, water)
    diss = Dissolution(waves, wind)

    (sc, time_step) = weathering_data_arrays(diss.array_types,
                                             water,
                                             num_elements=num_elems)[:2]

    print 'num_spills:', len(sc.spills)
    print 'spill[0] amount:', sc.spills[0].amount, sc.spills[0].units

    model_time = (sc.spills[0]
                  .release_time + timedelta(seconds=time_step))
    print 'model_time = ', model_time
    print 'time_step = ', time_step

    # we don't want to query the oil database, but get the sample oil
    #assert sc.spills[0].substance.record.id is None

    disp.on = on
    diss.on = on

    disp.prepare_for_model_run(sc)
    diss.prepare_for_model_run(sc)

    disp.initialize_data(sc, sc.num_released)
    diss.initialize_data(sc, sc.num_released)

    for i in range(3):
        disp.prepare_for_model_step(sc, time_step, model_time)
        diss.prepare_for_model_step(sc, time_step, model_time)

        disp.weather_elements(sc, time_step, model_time)
        diss.weather_elements(sc, time_step, model_time)

        print 'droplet_avg_size:', sc._data_arrays['droplet_avg_size']
        assert np.allclose(sc._data_arrays['droplet_avg_size'], drop_size[i])
Example #20
0
def test_dissolution_k_ow(oil, temp, num_elems, k_ow, on):
    '''
        Here we are testing that the molar averaged oil/water partition
        coefficient (K_ow) is getting calculated with reasonable values
        Note: for now droplets are calculated in natural dispersion so
        natural dispersion is required for the dissolution algorithm
    '''
    diss = Dissolution(waves, wind)
    disp = NaturalDispersion(waves, water)
    (sc, time_step) = weathering_data_arrays(diss.array_types,
                                             water,
                                             substance=oil,
                                             num_elements=num_elems)[:2]

    print 'num spills:', len(sc.spills)
    print 'spill[0] amount:', sc.spills[0].amount

    # we don't want to query the oil database, but get the sample oil
    #assert sc.spills[0].substance.record.id is None

    model_time = (sc.spills[0].release_time +
                  timedelta(seconds=time_step))

    disp.on = on
    diss.on = on
    disp.prepare_for_model_run(sc)
    diss.prepare_for_model_run(sc)
    disp.initialize_data(sc, sc.num_released)
    diss.initialize_data(sc, sc.num_released)

    disp.prepare_for_model_step(sc, time_step, model_time)
    diss.prepare_for_model_step(sc, time_step, model_time)
    disp.weather_elements(sc, time_step, model_time)
    diss.weather_elements(sc, time_step, model_time)

    assert all(np.isclose(sc._data_arrays['partition_coeff'], k_ow))
Example #21
0
def test_dissolution_k_ow(oil, temp, num_elems, k_ow, on):
    '''
        Here we are testing that the molar averaged oil/water partition
        coefficient (K_ow) is getting calculated with reasonable values
    '''
    et = floating(substance=oil)
    diss = Dissolution(waves)
    (sc, time_step) = weathering_data_arrays(diss.array_types,
                                             water,
                                             element_type=et,
                                             num_elements=num_elems)[:2]

    print 'num spills:', len(sc.spills)
    print 'spill[0] amount:', sc.spills[0].amount

    # we don't want to query the oil database, but get the sample oil
    assert sc.spills[0].element_type.substance.record.id is None

    model_time = (sc.spills[0].release_time + timedelta(seconds=time_step))

    diss.on = on
    diss.prepare_for_model_run(sc)
    diss.initialize_data(sc, sc.num_released)

    diss.prepare_for_model_step(sc, time_step, model_time)
    diss.weather_elements(sc, time_step, model_time)

    assert all(np.isclose(sc._data_arrays['partition_coeff'], k_ow))
Example #22
0
def test_dissolution_mass_balance(oil, temp, wind_speed,
                                  num_elems, expected_mb, on):
    '''
    Test a single dissolution step.
    - for this, we need a dispersion weatherer to give us a droplet size
      distribution.
    Fuel Oil #6 does not exist...
    '''
    et = floating(substance=oil)

    waves = build_waves_obj(wind_speed, 'knots', 270, temp)
    water = waves.water

    disp = NaturalDispersion(waves, water)
    diss = Dissolution(waves)

    all_array_types = diss.array_types.union(disp.array_types)

    (sc, time_step) = weathering_data_arrays(all_array_types,
                                             water,
                                             element_type=et,
                                             num_elements=num_elems,
                                             units='kg',
                                             amount_per_element=1.0
                                             )[:2]

    print 'time_step: {}'.format(time_step)
    print 'num spills:', len(sc.spills)
    print 'spill[0] amount: {} {}'.format(sc.spills[0].amount,
                                          sc.spills[0].units)
    print 'temperature = ', temp
    print 'wind = ',
    print '\n'.join(['\t{} {}'.format(ts[1][0], waves.wind.units)
                     for ts in waves.wind.timeseries])
    print

    # we don't want to query the oil database, but get the sample oil
    assert sc.spills[0].element_type.substance.record.id is None

    initial_amount = sc.spills[0].amount
    model_time = (sc.spills[0].release_time +
                  timedelta(seconds=time_step))

    disp.on = on
    disp.prepare_for_model_run(sc)
    disp.initialize_data(sc, sc.num_released)

    diss.on = on
    diss.prepare_for_model_run(sc)
    diss.initialize_data(sc, sc.num_released)

    disp.prepare_for_model_step(sc, time_step, model_time)
    diss.prepare_for_model_step(sc, time_step, model_time)

    disp.weather_elements(sc, time_step, model_time)
    diss.weather_elements(sc, time_step, model_time)

    if on:
        print ('fraction dissolved: {}'
               .format(sc.mass_balance['dissolution'] / initial_amount)
               )
        print ('fraction dissolved: {:.2%}'
               .format(sc.mass_balance['dissolution'] / initial_amount)
               )
        print sc.mass_balance['dissolution'], expected_mb
        assert np.isclose(sc.mass_balance['dissolution'], expected_mb)
    else:
        assert 'dissolution' not in sc.mass_balance
Example #23
0
def make_model():
    """
    Set up a GNOME simulation that uses TAMOC

    Set up a spill scenario in GNOME that uses TAMOC to simulate a subsurface
    blowout and then pass the TAMOC solution to GNOME for the far-field
    particle tracking

    """
    # Set up the directory structure for the model
    base_dir, images_dir, outfiles_dir = set_directory_structure()

    # Set up the modeling environment
    print '\n-- Initializing the Model         --'
    start_time = "2019-06-01T12:00"
    model = gs.Model(start_time=start_time,
                     duration=gs.days(3),
                     time_step=gs.minutes(30),
                     uncertain=False)

    # Add a map
    print '\n-- Adding a Map                   --'
    model.map = gs.GnomeMap()

    # Add image output
    print '\n-- Adding Image Outputters        --'
    renderer = gs.Renderer(output_dir=images_dir,
                           image_size=(1024, 768),
                           output_timestep=gs.hours(1),
                           viewport=((-0.15, -0.35), (0.15, 0.35)))
    model.outputters += renderer

    # Add NetCDF output
    print '\n-- Adding NetCDF Outputter        --'
    if not os.path.exists(outfiles_dir):
        os.mkdir(outfiles_dir)
    netcdf_file = os.path.join(outfiles_dir, 'script_tamoc.nc')
    gs.remove_netcdf(netcdf_file)
    file_writer = gs.NetCDFOutput(netcdf_file,
                                  which_data='all',
                                  output_timestep=gs.hours(2))
    model.outputters += file_writer

    # Add a spill object
    print '\n-- Adding a Point Spill           --'
    end_release_time = model.start_time + gs.hours(12)
    point_source = ts.TamocSpill(num_elements=100,
                                 start_position=(0.0, 0.0, 1000.),
                                 release_duration=gs.hours(24),
                                 release_time=start_time,
                                 substance='AD01554',
                                 release_rate=20000.,
                                 units='bbl/day',
                                 gor=500.,
                                 d0=0.5,
                                 phi_0=-np.pi / 2.,
                                 theta_0=0.,
                                 windage_range=(0.01, 0.04),
                                 windage_persist=900,
                                 name='Oil Well Blowout')

    model.spills += point_source

    # Create an ocean environment
    water, wind, waves = base_environment(water_temp=273.15 + 21.,
                                          wind_speed=5.,
                                          wind_dir=225.)

    # Add a uniform current in the easterly direction
    print '\n-- Adding Currents                --'
    uniform_current = gs.SimpleMover(velocity=(0.1, 0.0, 0.))
    model.movers += uniform_current

    # Add a wind mover
    wind_mover = gs.WindMover(wind)
    model.movers += wind_mover

    # Add particle diffusion...note, units are in cm^2/s
    print '\n-- Adding Particle Diffusion      --'
    particle_diffusion = gs.RandomMover3D(
        horizontal_diffusion_coef_above_ml=100000.,
        horizontal_diffusion_coef_below_ml=10000.,
        vertical_diffusion_coef_above_ml=100.,
        vertical_diffusion_coef_below_ml=10.,
        mixed_layer_depth=15.)
    model.movers += particle_diffusion

    # Add rise velocity for droplets
    print '\n-- Adding Particle Rise Velocity  --'
    # fixme: we do have code for rise velocity:
    #  gnome.movers.RiseVelocityMover
    #  let's test that later
    slip_velocity = gs.SimpleMover(velocity=(0.0, 0.0, -0.1))
    model.movers += slip_velocity

    # Add dissolution
    print '\n-- Adding Weathering              --'
    evaporation = Evaporation(water=water, wind=wind)
    model.weatherers += evaporation

    dissolution = Dissolution(waves=waves, wind=wind)
    model.weatherers += dissolution

    return model
Example #24
0
def test_sort_order():
    assert weatherer_sort(Dissolution()) > weatherer_sort(NaturalDispersion())
Example #25
0
def test_dissolution_mass_balance(oil, temp, wind_speed,
                                  num_elems, expected_mb, on):
    '''
    Test a single dissolution step.
    - for this, we need a dispersion weatherer to give us a droplet size
      distribution.
    Fuel Oil #6 does not exist...
    '''

    waves = build_waves_obj(wind_speed, 'knots', 270, temp)
    wind = waves.wind
    water = waves.water

    disp = NaturalDispersion(waves, water)
    diss = Dissolution(waves, wind)

    all_array_types = diss.array_types.union(disp.array_types)

    (sc, time_step) = weathering_data_arrays(all_array_types,
                                             water,
                                             num_elements=num_elems,
                                             units='kg',
                                             amount_per_element=1.0
                                             )[:2]

    print 'time_step: {}'.format(time_step)
    print 'num spills:', len(sc.spills)
    print 'spill[0] amount: {} {}'.format(sc.spills[0].amount,
                                          sc.spills[0].units)
    print 'temperature = ', temp
    print 'wind = ',
    print '\n'.join(['\t{} {}'.format(ts[1][0], waves.wind.units)
                     for ts in waves.wind.timeseries])
    print

    # we don't want to query the oil database, but get the sample oil
    #assert sc.spills[0].substance.record.id is None

    initial_amount = sc.spills[0].amount
    model_time = (sc.spills[0].release_time +
                  timedelta(seconds=time_step))

    disp.on = on
    disp.prepare_for_model_run(sc)
    disp.initialize_data(sc, sc.num_released)

    diss.on = on
    diss.prepare_for_model_run(sc)
    diss.initialize_data(sc, sc.num_released)

    disp.prepare_for_model_step(sc, time_step, model_time)
    diss.prepare_for_model_step(sc, time_step, model_time)

    disp.weather_elements(sc, time_step, model_time)
    diss.weather_elements(sc, time_step, model_time)

    if on:
        print ('fraction dissolved: {}'
               .format(sc.mass_balance['dissolution'] / initial_amount)
               )
        print ('fraction dissolved: {:.2%}'
               .format(sc.mass_balance['dissolution'] / initial_amount)
               )
        print sc.mass_balance['dissolution'], expected_mb
        assert np.isclose(sc.mass_balance['dissolution'], expected_mb,
                          rtol=1e-4)
    else:
        assert 'dissolution' not in sc.mass_balance