!! The CARMA state module contains the atmospheric data for use with the CARMA !! module. This implementation has been customized to work within other model !! frameworks. CARMA adds a lot of extra state information (atmospheric !! properties, fall velocities, coagulation kernels, growth kernels, ...) and !! thus has a large memory footprint. Because only one column will be operated !! upon at a time per thread, only one cstate object needs to be instantiated !! at a time and each cstate object only represents one column. This keeps !! the memory requirements of CARMA to a minimum. !! !! @version Feb-2009 !! @author Chuck Bardeen, Pete Colarco, Jamie Smith ! ! NOTE: Documentation for this code can be generated automatically using f90doc, ! which is freely available from: ! http://erikdemaine.org/software/f90doc/ ! Comment lines with double comment characters are processed by f90doc, and there are ! some special characters added to the comments to control the documentation process. ! In addition to the special characters mentioned in the f990doc documentation, html ! formatting tags (e.g. , , ...) can also be added to the f90doc ! comments. module carmastate_mod ! This module maps the parents models constants into the constants need by CARMA. ! NOTE: CARMA constants are in CGS units, while the parent models are typically in ! MKS units. use carma_precision_mod use carma_enums_mod use carma_constants_mod use carma_types_mod ! cstate explicitly declares all variables. implicit none ! All cstate variables and procedures are private except those explicitly ! declared to be public. private ! Declare the public methods. public CARMASTATE_Create public CARMASTATE_CreateFromReference public CARMASTATE_Destroy public CARMASTATE_Get public CARMASTATE_GetBin public CARMASTATE_GetDetrain public CARMASTATE_GetGas public CARMASTATE_GetState public CARMASTATE_SetBin public CARMASTATE_SetDetrain public CARMASTATE_SetGas public CARMASTATE_SetState public CARMASTATE_Step contains ! These are the methods that provide the interface between the parent model and ! the atmospheric state data of the CARMA microphysical model. There are many other ! methods that are not in this file that are used to implement the microphysical ! calculations needed by the CARMA model. These other methods are in effect private ! methods of the CARMA module, but are in individual files since that is the way that ! CARMA has traditionally been structured and where users may want to extend or ! replace code to affect the microphysics. !! Create the CARMASTATE object, which contains information about the !! atmospheric state. Internally, CARMA uses CGS units, but this interface uses !! MKS units which are more commonly used in parent models. The units and grid !! orientation depend on the grid type: !! !! - igridh !! - I_CART : Cartesian coordinates, units in [m] !! - I_LL : Lat/Lon coordinates, units in [degrees] !! !! - igridv !! - I_CART : Cartesian coordinates, units in [m], bottom at NZ=1 !! - I_SIG : Sigma coordinates, unitless [P/P0], top at NZ=1 !! - I_HYBRID : Hybrid coordinates, unitless [~P/P0], top at NZ=1 !! !! NOTE: The supplied CARMA object should already have been created, configured, !! and initialized. !! !! NOTE: The relative humidity is optional, but needs to be supplied if particles !! are subject to swelling based upon relative humidity. The specific humdity can !! can be specified instead. If both are specified, then the realtive humidity is !! used. !! !! @author Chuck Bardeen !! @version Feb-2009 !! @see CARMA_Create !! @see CARMA_Initialize !! @see CARMASTATE_Destroy subroutine CARMASTATE_Create(cstate, carma_ptr, time, dtime, NZ, igridv, igridh, & lat, lon, xc, dx, yc, dy, zc, zl, p, pl, t, rc, qh2o, relhum, told, radint) type(carmastate_type), intent(inout) :: cstate !! the carma state object type(carma_type), pointer :: carma_ptr !! (in) the carma object real(kind=f), intent(in) :: time !! the model time [s] real(kind=f), intent(in) :: dtime !! the timestep size [s] integer, intent(in) :: NZ !! the number of vertical grid points integer, intent(in) :: igridv !! vertical grid type integer, intent(in) :: igridh !! horizontal grid type real(kind=f), intent(in) :: lat !! latitude at center [degrees north] real(kind=f), intent(in) :: lon !! longitude at center [degrees east] real(kind=f), intent(in) :: xc(NZ) !! x at center real(kind=f), intent(in) :: dx(NZ) !! ix width real(kind=f), intent(in) :: yc(NZ) !! y at center real(kind=f), intent(in) :: dy(NZ) !! y width real(kind=f), intent(in) :: zc(NZ) !! z at center real(kind=f), intent(in) :: zl(NZ+1) !! z at edge real(kind=f), intent(in) :: p(NZ) !! pressure at center [Pa] real(kind=f), intent(in) :: pl(NZ+1) !! presssure at edge [Pa] real(kind=f), intent(in) :: t(NZ) !! temperature at center [K] integer, intent(out) :: rc !! return code, negative indicates failure real(kind=f), intent(in) , optional :: qh2o(NZ) !! specific humidity at center [mmr] real(kind=f), intent(in) , optional :: relhum(NZ) !! relative humidity at center [fraction] real(kind=f), intent(in) , optional :: told(NZ) !! previous temperature at center [K] real(kind=f), intent(in) , optional :: radint(NZ,carma_ptr%f_NWAVE) !! radiative intensity [W/m2/sr/cm] integer :: iz real(kind=f) :: rvap real(kind=f) :: pvap_liq real(kind=f) :: pvap_ice real(kind=f) :: gc_cgs ! Assume success. rc = RC_OK ! Save the defintion of the number of comonents involved in the microphysics. cstate%f_carma => carma_ptr ! Save the model timing. cstate%f_time = time cstate%f_dtime_orig = dtime cstate%f_dtime = dtime cstate%f_nretries = 0 ! Save the grid dimensions. cstate%f_NZ = NZ cstate%f_NZP1 = NZ+1 ! Save the grid definition. cstate%f_igridv = igridv cstate%f_igridh = igridh ! Store away the grid location information. cstate%f_lat = lat cstate%f_lon = lon ! Allocate all the dynamic variables related to state. call CARMASTATE_Allocate(cstate, rc) if (rc < 0) return cstate%f_xc(:) = xc(:) cstate%f_dx(:) = dx(:) cstate%f_yc(:) = yc(:) cstate%f_dy(:) = dy(:) cstate%f_zc(:) = zc(:) cstate%f_zl(:) = zl(:) ! Store away the grid state, doing any necessary unit conversions from MKS to CGS. cstate%f_p(:) = p(:) * RPA2CGS cstate%f_pl(:) = pl(:) * RPA2CGS cstate%f_t(:) = t(:) cstate%f_pcd(:,:,:) = 0._f if (carma_ptr%f_do_substep) then if (present(told)) then cstate%f_told(:) = told else if (carma_ptr%f_do_print) write(carma_ptr%f_LUNOPRT,*) "CARMASTATE_Create: Error - Need to specify told when substepping." rc = RC_ERROR return end if end if ! Calculate the metrics, ... ! if Cartesian coordinates were specifed, then the units need to be converted ! from MKS to CGS. if (cstate%f_igridh == I_CART) then cstate%f_xc = cstate%f_xc * RM2CGS cstate%f_dx = cstate%f_dx * RM2CGS cstate%f_yc = cstate%f_yc * RM2CGS cstate%f_dy = cstate%f_dy * RM2CGS end if if (cstate%f_igridv == I_CART) then cstate%f_zc = cstate%f_zc * RM2CGS cstate%f_zl = cstate%f_zl * RM2CGS end if ! Initialize the state of the atmosphere. call setupatm(carma_ptr, cstate, carma_ptr%f_do_fixedinit, rc) if (rc < 0) return ! Set the realtive humidity. If necessary, it will be calculated from ! the specific humidity. if (present(relhum)) then cstate%f_relhum(:) = relhum(:) else if (present(qh2o)) then ! Define gas constant for this gas rvap = RGAS/WTMOL_H2O ! Calculate relative humidity do iz = 1, NZ call vaporp_h2o_murphy2005(carma_ptr, cstate, iz, rc, pvap_liq, pvap_ice) if (rc < 0) return gc_cgs = qh2o(iz)*cstate%f_rhoa_wet(iz) / (cstate%f_zmet(iz)*cstate%f_xmet(iz)*cstate%f_ymet(iz)) cstate%f_relhum(iz) = ( gc_cgs * rvap * t(iz)) / pvap_liq enddo end if ! Need for vertical transport. ! ! NOTE: How should these be set? Optional parameters? if (carma_ptr%f_do_vtran) then cstate%f_ftoppart(:,:) = 0._f cstate%f_fbotpart(:,:) = 0._f cstate%f_pc_topbnd(:,:) = 0._f cstate%f_pc_botbnd(:,:) = 0._f end if ! Radiative intensity for particle heating. ! ! W/m2/sr/cm -> erg/s/cm2/sr/cm if (carma_ptr%f_do_grow) then if (present(radint)) cstate%f_radint(:,:) = radint(:,:) * 1e7_f / 1e4_f end if return end subroutine CARMASTATE_Create !! Create the CARMASTATE object, which contains information about the !! atmospheric state. !! !! This call is similar to CARMASTATE_Create, but differs in that all the !! initialization happens here based on the the fixed state information provided rather !! than occurring in CARMASTATE_Step. !! !! This call should be done before CARMASTATE_Create when do_fixedinit has been !! specified. The temperatures and pressures specified here should be the reference !! state used for all columns, not an actual column from the model. !! !! A water vapor profile is optional, but is used whenever either qh2o (preferred) !! or relhum have been provided. If this is not provided, then initialization will !! be done on a dry profile. If particle swelling occurs, initialization will be !! done on the wet radius; however, most of the initialized values will not get !! recalculated as the wet radius changes. !! !! CARMASTATE_Create should still be called again after this call with the actual !! column of state information from the model. The initialization will be done once !! from the reference state, but the microphysical calculations will be done on the !! model state. Multiple CARMASTATE_Create ... CARMASTATE_Step calls can be done !! before a CARMASTATE_Destroy. This reduces the amount of memory allocations and !! when used with do_fixedinit, reduces the amount of time spent initializing. !! !! @author Chuck Bardeen !! @version June-2010 !! @see CARMA_Create !! @see CARMA_Initialize !! @see CARMASTATE_Destroy subroutine CARMASTATE_CreateFromReference(cstate, carma_ptr, time, dtime, NZ, igridv, igridh, & lat, lon, xc, dx, yc, dy, zc, zl, p, pl, t, rc, qh2o, relhum) type(carmastate_type), intent(inout) :: cstate !! the carma state object type(carma_type), pointer :: carma_ptr !! (in) the carma object real(kind=f), intent(in) :: time !! the model time [s] real(kind=f), intent(in) :: dtime !! the timestep size [s] integer, intent(in) :: NZ !! the number of vertical grid points integer, intent(in) :: igridv !! vertical grid type integer, intent(in) :: igridh !! horizontal grid type real(kind=f), intent(in) :: lat !! latitude at center [degrees north] real(kind=f), intent(in) :: lon !! longitude at center [degrees east] real(kind=f), intent(in) :: xc(NZ) !! x at center real(kind=f), intent(in) :: dx(NZ) !! ix width real(kind=f), intent(in) :: yc(NZ) !! y at center real(kind=f), intent(in) :: dy(NZ) !! y width real(kind=f), intent(in) :: zc(NZ) !! z at center real(kind=f), intent(in) :: zl(NZ+1) !! z at edge real(kind=f), intent(in) :: p(NZ) !! pressure at center [Pa] real(kind=f), intent(in) :: pl(NZ+1) !! presssure at edge [Pa] real(kind=f), intent(in) :: t(NZ) !! temperature at center [K] integer, intent(out) :: rc !! return code, negative indicates failure real(kind=f), intent(in) , optional :: qh2o(NZ) !! specific humidity at center [mmr] real(kind=f), intent(in) , optional :: relhum(NZ) !! relative humidity at center [fraction] integer :: iz integer :: igas real(kind=f) :: rvap real(kind=f) :: pvap_liq real(kind=f) :: pvap_ice real(kind=f) :: gc_cgs ! Assume success. rc = RC_OK ! Save the defintion of the number of comonents involved in the microphysics. cstate%f_carma => carma_ptr ! Save the model timing. cstate%f_time = time cstate%f_dtime_orig = dtime cstate%f_dtime = dtime cstate%f_nretries = 0 ! Save the grid dimensions. cstate%f_NZ = NZ cstate%f_NZP1 = NZ+1 ! Save the grid definition. cstate%f_igridv = igridv cstate%f_igridh = igridh ! Store away the grid location information. cstate%f_lat = lat cstate%f_lon = lon ! Allocate all the dynamic variables related to state. call CARMASTATE_Allocate(cstate, rc) if (rc < 0) return cstate%f_xc(:) = xc(:) cstate%f_dx(:) = dx(:) cstate%f_yc(:) = yc(:) cstate%f_dy(:) = dy(:) cstate%f_zc(:) = zc(:) cstate%f_zl(:) = zl(:) ! Store away the grid state, doing any necessary unit conversions from MKS to CGS. cstate%f_p(:) = p(:) * RPA2CGS cstate%f_pl(:) = pl(:) * RPA2CGS cstate%f_t(:) = t(:) cstate%f_pcd(:,:,:) = 0._f ! Calculate the metrics, ... ! if Cartesian coordinates were specifed, then the units need to be converted ! from MKS to CGS. if (cstate%f_igridh == I_CART) then cstate%f_xc = cstate%f_xc * RM2CGS cstate%f_dx = cstate%f_dx * RM2CGS cstate%f_yc = cstate%f_yc * RM2CGS cstate%f_dy = cstate%f_dy * RM2CGS end if if (cstate%f_igridv == I_CART) then cstate%f_zc = cstate%f_zc * RM2CGS cstate%f_zl = cstate%f_zl * RM2CGS end if ! Initialize the state of the atmosphere. call setupatm(carma_ptr, cstate, .false., rc) if (rc < 0) return ! If the model uses a gas, then set the relative and ! specific humidities. if (carma_ptr%f_igash2o /= 0) then if (present(qh2o)) then cstate%f_gc(:, carma_ptr%f_igash2o) = qh2o(:) * cstate%f_rhoa_wet(:) ! Define gas constant for this gas rvap = RGAS/WTMOL_H2O ! Calculate relative humidity do iz = 1, NZ call vaporp_h2o_murphy2005(carma_ptr, cstate, iz, rc, pvap_liq, pvap_ice) if (rc < 0) return gc_cgs = qh2o(iz) * cstate%f_rhoa_wet(iz) / (cstate%f_zmet(iz)*cstate%f_xmet(iz)*cstate%f_ymet(iz)) cstate%f_relhum(iz) = (gc_cgs * rvap * t(iz)) / pvap_liq enddo else if (present(relhum)) then cstate%f_relhum(:) = relhum ! Calculate specific humidity do iz = 1, NZ call vaporp_h2o_murphy2005(carma_ptr, cstate, iz, rc, pvap_liq, pvap_ice) if (rc < 0) return gc_cgs = (rvap * t(iz)) / (pvap_liq * relhum(iz)) cstate%f_gc(iz, carma_ptr%f_igash2o) = gc_cgs * (cstate%f_zmet(iz)*cstate%f_xmet(iz)*cstate%f_ymet(iz)) / cstate%f_rhoa_wet(iz) enddo end if end if ! Determine the gas supersaturations. do iz = 1, cstate%f_NZ do igas = 1, cstate%f_carma%f_NGAS call supersat(cstate%f_carma, cstate, iz, igas, rc) if (rc < 0) return end do end do ! Need for vertical transport. ! ! NOTE: How should these be set? Optional parameters? if (carma_ptr%f_do_vtran) then cstate%f_ftoppart(:,:) = 0._f cstate%f_fbotpart(:,:) = 0._f cstate%f_pc_topbnd(:,:) = 0._f cstate%f_pc_botbnd(:,:) = 0._f end if ! Now do the initialization that is normally done in CARMASTATE_Step. However ! here it is done using the reference atmosphere. ! Determine the particle densities. call rhopart(cstate%f_carma, cstate, rc) if (rc < 0) return ! Save off the wet radius and wet density as reference values to be used ! later to scale process rates based upon changes to the wet radius and ! wet density when particle swelling is used. cstate%f_r_ref(:,:,:) = cstate%f_r_wet(:,:,:) cstate%f_rhop_ref(:,:,:) = cstate%f_rhop_wet(:,:,:) ! If configured for fixed initialization, then we will lose some accuracy ! in the calculation of the fall velocities, growth kernels, ... and in return ! will gain a significant performance by not having to initialize as often. ! Initialize the vertical transport. if (cstate%f_carma%f_do_vtran .or. cstate%f_carma%f_do_coag .or. cstate%f_carma%f_do_grow) then call setupvf(cstate%f_carma, cstate, rc) if (cstate%f_carma%f_do_vdiff) then call setupbdif(cstate%f_carma, cstate, rc) end if end if ! Intialize the nucleation, growth and evaporation. if (cstate%f_carma%f_do_grow) then call setupgrow(cstate%f_carma, cstate, rc) if (rc < 0) return call setupgkern(cstate%f_carma, cstate, rc) if (rc < 0) return call setupnuc(cstate%f_carma, cstate, rc) if (rc < 0) return end if ! Initialize the coagulation. if (cstate%f_carma%f_do_coag) then call setupckern(cstate%f_carma, cstate, rc) if (rc < 0) return end if return end subroutine CARMASTATE_CreateFromReference subroutine CARMASTATE_Allocate(cstate, rc) type(carmastate_type), intent(inout) :: cstate integer, intent(out) :: rc ! Local Variables integer :: ier integer :: NZ integer :: NZP1 integer :: NGROUP integer :: NELEM integer :: NBIN integer :: NGAS integer :: NWAVE ! Assume success. rc = RC_OK ! Check to see if the arrays are already allocated. If so, just reuse the ! existing allocations. ! Allocate the variables needed for setupatm. if (.not. (allocated(cstate%f_xmet))) then NZ = cstate%f_NZ NZP1 = cstate%f_NZP1 NGROUP = cstate%f_carma%f_NGROUP NELEM = cstate%f_carma%f_NELEM NBIN = cstate%f_carma%f_NBIN NGAS = cstate%f_carma%f_NGAS NWAVE = cstate%f_carma%f_NWAVE allocate( & cstate%f_xmet(NZ), & cstate%f_ymet(NZ), & cstate%f_zmet(NZ), & cstate%f_zmetl(NZP1), & cstate%f_xc(NZ), & cstate%f_yc(NZ), & cstate%f_zc(NZ), & cstate%f_dx(NZ), & cstate%f_dy(NZ), & cstate%f_dz(NZ), & cstate%f_zl(NZP1), & cstate%f_pc(NZ,NBIN,NELEM), & cstate%f_pcd(NZ,NBIN,NELEM), & cstate%f_pc_surf(NBIN,NELEM), & cstate%f_sedimentationflux(NBIN,NELEM), & cstate%f_gc(NZ,NGAS), & cstate%f_cldfrc(NZ), & cstate%f_rhcrit(NZ), & cstate%f_rhop(NZ,NBIN,NGROUP), & cstate%f_r_wet(NZ,NBIN,NGROUP), & cstate%f_rlow_wet(NZ,NBIN,NGROUP), & cstate%f_rup_wet(NZ,NBIN,NGROUP), & cstate%f_rhop_wet(NZ,NBIN,NGROUP), & cstate%f_r_ref(NZ,NBIN,NGROUP), & cstate%f_rhop_ref(NZ,NBIN,NGROUP), & cstate%f_rhoa(NZ), & cstate%f_rhoa_wet(NZ), & cstate%f_t(NZ), & cstate%f_p(NZ), & cstate%f_pl(NZP1), & cstate%f_relhum(NZ), & cstate%f_wtpct(NZ), & cstate%f_rmu(NZ), & cstate%f_thcond(NZ), & cstate%f_thcondnc(NZ,NBIN,NGROUP), & cstate%f_dpc_sed(NBIN,NELEM), & cstate%f_pconmax(NZ,NGROUP), & cstate%f_pcl(NZ,NBIN,NELEM), & stat=ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Allocate::ERROR allocating atmosphere arrays, status=", ier rc = RC_ERROR return end if cstate%f_relhum(:) = 0._f cstate%f_pc(:,:,:) = 0._f cstate%f_pcd(:,:,:) = 0._f cstate%f_pc_surf(:,:) = 0._f cstate%f_sedimentationflux(:,:) = 0._f cstate%f_cldfrc(:) = 1._f cstate%f_rhcrit(:) = 1._f ! Allocate the last fields if they are needed for substepping. if (cstate%f_carma%f_do_substep) then allocate( & cstate%f_gcl(NZ,NGAS), & cstate%f_d_gc(NZ,NGAS), & cstate%f_told(NZ), & cstate%f_d_t(NZ), & cstate%f_zsubsteps(NZ), & stat=ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Allocate::ERROR allocating stepping arrays, status=", ier rc = RC_ERROR return endif ! Initialize cstate%f_gcl(:,:) = 0._f cstate%f_d_gc(:,:) = 0._f cstate%f_told(:) = 0._f cstate%f_d_t(:) = 0._f cstate%f_zsubsteps(:) = 0._f ! When substepping is enabled, we want to initialize these statistics once for ! the life of the object. cstate%f_max_nsubstep = 0 cstate%f_max_nretry = 0._f cstate%f_nstep = 0._f cstate%f_nsubstep = 0 cstate%f_nretry = 0._f endif ! Allocate the variables needed for setupvf. ! ! NOTE: Coagulation and dry deposition also need bpm, vf and re. if (cstate%f_carma%f_do_vtran .or. cstate%f_carma%f_do_coag .or. cstate%f_carma%f_do_grow .or. cstate%f_carma%f_do_drydep) then allocate( & cstate%f_bpm(NZ,NBIN,NGROUP), & cstate%f_vf(NZP1,NBIN,NGROUP), & cstate%f_re(NZ,NBIN,NGROUP), & cstate%f_dkz(NZP1,NBIN,NGROUP), & cstate%f_ftoppart(NBIN,NELEM), & cstate%f_fbotpart(NBIN,NELEM), & cstate%f_pc_topbnd(NBIN,NELEM), & cstate%f_pc_botbnd(NBIN,NELEM), & cstate%f_vd(NBIN, NGROUP), & stat=ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Allocate::ERROR allocating vertical transport arrays, status=", ier rc = RC_ERROR return endif ! Initialize cstate%f_bpm(:,:,:) = 0._f cstate%f_vf(:,:,:) = 0._f cstate%f_re(:,:,:) = 0._f cstate%f_dkz(:,:,:) = 0._f cstate%f_ftoppart(:,:) = 0._f cstate%f_fbotpart(:,:) = 0._f cstate%f_pc_topbnd(:,:) = 0._f cstate%f_pc_botbnd(:,:) = 0._f cstate%f_vd(:, :) = 0._f end if if (cstate%f_carma%f_NGAS > 0) then allocate( & cstate%f_pvapl(NZ,NGAS), & cstate%f_pvapi(NZ,NGAS), & cstate%f_supsatl(NZ,NGAS), & cstate%f_supsati(NZ,NGAS), & cstate%f_supsatlold(NZ,NGAS), & cstate%f_supsatiold(NZ,NGAS), & stat=ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Allocate::ERROR allocating gas arrays, status=", ier rc = RC_ERROR return endif end if if (cstate%f_carma%f_do_grow) then allocate( & cstate%f_diffus(NZ,NGAS), & cstate%f_rlhe(NZ,NGAS), & cstate%f_rlhm(NZ,NGAS), & cstate%f_surfctwa(NZ), & cstate%f_surfctiw(NZ), & cstate%f_surfctia(NZ), & cstate%f_akelvin(NZ,NGAS), & cstate%f_akelvini(NZ,NGAS), & cstate%f_ft(NZ,NBIN,NGROUP), & cstate%f_gro(NZ,NBIN,NGROUP), & cstate%f_gro1(NZ,NBIN,NGROUP), & cstate%f_gro2(NZ,NGROUP), & cstate%f_scrit(NZ,NBIN,NGROUP), & cstate%f_rnuclg(NBIN,NGROUP,NGROUP),& cstate%f_rhompe(NBIN,NELEM), & cstate%f_rnucpe(NBIN,NELEM), & cstate%f_pc_nucl(NZ,NBIN,NELEM), & cstate%f_growpe(NBIN,NELEM), & cstate%f_evappe(NBIN,NELEM), & cstate%f_evcore(NELEM), & cstate%f_growlg(NBIN,NGROUP), & cstate%f_evaplg(NBIN,NGROUP), & cstate%f_gasprod(NGAS), & cstate%f_rlheat(NZ), & cstate%f_radint(NZ,NWAVE), & cstate%f_partheat(NZ), & cstate%f_dtpart(NZ,NBIN,NGROUP), & cstate%f_cmf(NBIN,NGROUP), & cstate%f_totevap(NBIN,NGROUP), & stat=ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Allocate::ERROR allocating growth arrays, status=", ier rc = RC_ERROR return endif cstate%f_radint(:,:) = 0._f end if if (cstate%f_carma%f_do_coag) then allocate( & cstate%f_coaglg(NZ,NBIN,NGROUP), & cstate%f_coagpe(NZ,NBIN,NELEM), & cstate%f_ckernel(NZ,NBIN,NBIN,NGROUP,NGROUP), & stat = ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Allocate::ERROR allocating coag arrays, status=", ier rc = RC_ERROR return end if ! Initialize cstate%f_coaglg(:,:,:) = 0._f cstate%f_coagpe(:,:,:) = 0._f cstate%f_ckernel(:,:,:,:,:) = 0._f end if end if return end subroutine CARMASTATE_Allocate !! The routine should be called when the carma state object is no longer needed. !! It deallocates any memory allocations made by CARMA during CARMASTATE_Create(), !! and failure to call this routine could result in memory leaks. !! !! @author Chuck Bardeen !! @version Feb-2009 !! @see CARMASTATE_Create subroutine CARMASTATE_Destroy(cstate, rc) type(carmastate_type), intent(inout) :: cstate integer, intent(out) :: rc ! Local variables integer :: ier ! Assume success. rc = RC_OK ! Check to see if the arrays are already allocated. If so, deallocate them. ! Allocate the variables needed for setupatm. if (allocated(cstate%f_xmet)) then deallocate( & cstate%f_xmet, & cstate%f_ymet, & cstate%f_zmet, & cstate%f_zmetl, & cstate%f_xc, & cstate%f_yc, & cstate%f_zc, & cstate%f_dx, & cstate%f_dy, & cstate%f_dz, & cstate%f_zl, & cstate%f_pc, & cstate%f_pcd, & cstate%f_pc_surf, & cstate%f_sedimentationflux, & cstate%f_gc, & cstate%f_cldfrc, & cstate%f_rhcrit, & cstate%f_rhop, & cstate%f_r_wet, & cstate%f_rlow_wet, & cstate%f_rup_wet, & cstate%f_rhop_wet, & cstate%f_r_ref, & cstate%f_rhop_ref, & cstate%f_rhoa, & cstate%f_rhoa_wet, & cstate%f_t, & cstate%f_p, & cstate%f_pl, & cstate%f_relhum, & cstate%f_wtpct, & cstate%f_rmu, & cstate%f_thcond, & cstate%f_thcondnc, & cstate%f_dpc_sed, & cstate%f_pconmax, & cstate%f_pcl, & stat=ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Destroy::ERROR deallocating atmosphere arrays, status=", ier rc = RC_ERROR return end if ! Allocate the last fields if they are needed for substepping stepping. if (allocated(cstate%f_gcl)) then deallocate( & cstate%f_gcl, & cstate%f_d_gc, & cstate%f_told, & cstate%f_d_t, & cstate%f_zsubsteps, & stat=ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Destroy::ERROR deallocating stepping arrays, status=", ier rc = RC_ERROR return endif endif ! Allocate the variables needed for setupvf. ! ! NOTE: Coagulation also needs bpm, vf and re. if (allocated(cstate%f_bpm)) then deallocate( & cstate%f_bpm, & cstate%f_vf, & cstate%f_re, & cstate%f_dkz, & cstate%f_ftoppart, & cstate%f_fbotpart, & cstate%f_pc_topbnd, & cstate%f_pc_botbnd, & cstate%f_vd, & stat=ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Destroy::ERROR deallocating vertical transport arrays, status=", ier rc = RC_ERROR return endif end if if (allocated(cstate%f_diffus)) then deallocate( & cstate%f_diffus, & cstate%f_rlhe, & cstate%f_rlhm, & cstate%f_surfctwa, & cstate%f_surfctiw, & cstate%f_surfctia, & cstate%f_akelvin, & cstate%f_akelvini, & cstate%f_ft, & cstate%f_gro, & cstate%f_gro1, & cstate%f_gro2, & cstate%f_scrit, & cstate%f_rnuclg,& cstate%f_rnucpe, & cstate%f_rhompe, & cstate%f_pc_nucl, & cstate%f_growpe, & cstate%f_evappe, & cstate%f_evcore, & cstate%f_growlg, & cstate%f_evaplg, & cstate%f_gasprod, & cstate%f_rlheat, & cstate%f_radint, & cstate%f_partheat, & cstate%f_dtpart, & cstate%f_cmf, & cstate%f_totevap, & stat=ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Destroy::ERROR deallocating growth arrays, status=", ier rc = RC_ERROR return endif end if if (allocated(cstate%f_pvapl)) then deallocate( & cstate%f_pvapl, & cstate%f_pvapi, & cstate%f_supsatl, & cstate%f_supsati, & cstate%f_supsatlold, & cstate%f_supsatiold, & stat=ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Destroy::ERROR deallocating gas arrays, status=", ier rc = RC_ERROR return endif end if if (allocated(cstate%f_coaglg)) then deallocate( & cstate%f_coaglg, & cstate%f_coagpe, & cstate%f_ckernel, & stat = ier) if (ier /= 0) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Destroy::ERROR deallocating coag arrays, status=", ier rc = RC_ERROR return end if end if end if return end subroutine CARMASTATE_Destroy !! The routine performs the main CARMA processing for one timestep of !! the parent model. The state variables should have all been set before !! calling CARMASTATE_Step(). When this routine returns, the state will !! have been updated to reflect the changes from the CARMA microphysics. !! If tendencies are desired, then the difference between the final and !! initial state will need to be computed by the caller. !! !! NIOTE: xxxfv, xxxram and xxxfrac need to be specified for dry deposition. !! !! @author Chuck Bardeen !! @version Feb-2009 subroutine CARMASTATE_Step(cstate, rc, cldfrc, rhcrit, lndfv, ocnfv, icefv, lndram, ocnram, iceram, lndfrac, ocnfrac, icefrac) type(carmastate_type), intent(inout) :: cstate integer, intent(out) :: rc real(kind=f), intent(in), optional :: cldfrc(cstate%f_NZ) !! cloud fraction [fraction] real(kind=f), intent(in), optional :: rhcrit(cstate%f_NZ) !! relative humidity for onset of liquid clouds [fraction] real(kind=f), intent(in), optional :: lndfv !! the surface friction velocity over land [m/s] real(kind=f), intent(in), optional :: ocnfv !! the surface friction velocity over ocean [m/s] real(kind=f), intent(in), optional :: icefv !! the surface friction velocity over ice [m/s] real(kind=f), intent(in), optional :: lndram !! the aerodynamic resistance over land [s/m] real(kind=f), intent(in), optional :: ocnram !! the aerodynamic resistance over ocean [s/m] real(kind=f), intent(in), optional :: iceram !! the aerodynamic resistance over ice [s/m] real(kind=f), intent(in), optional :: lndfrac !! land fraction real(kind=f), intent(in), optional :: ocnfrac !! ocn fraction real(kind=f), intent(in), optional :: icefrac !! ice fraction integer :: iz ! vertical index integer :: igas ! gas index integer :: ielem integer :: ibin integer :: igroup logical :: swelling ! Do any groups undergo partcile swelling? integer :: i1, i2, j1, j2 ! Assume success. rc = RC_OK ! Store the cloud fraction if specified cstate%f_cldfrc(:) = 1._f cstate%f_rhcrit(:) = 1._f if (present(cldfrc)) cstate%f_cldfrc(:) = cldfrc(:) if (present(rhcrit)) cstate%f_rhcrit(:) = rhcrit(:) ! Determine the gas supersaturations. do iz = 1, cstate%f_NZ do igas = 1, cstate%f_carma%f_NGAS call supersat(cstate%f_carma, cstate, iz, igas, rc) if (rc < 0) return end do end do ! Determine the particle densities. call rhopart(cstate%f_carma, cstate, rc) if (rc < 0) return ! We have to hold off initialization until now, because the particle density ! (rhop) can not be determined until the particle masses are known (i.e. after ! CARMASTATE_SetBin), because rhop is used to determine the fall velocity. ! ! NOTE: If configured for fixed initialization, then we will lose some accuracy ! in the calculation of the fall velocities, growth kernels, ... and in return ! will gain a significant performance by not having to initialize as often. if (.not. cstate%f_carma%f_do_fixedinit) then ! Initialize the vertical transport. if (cstate%f_carma%f_do_vtran .or. cstate%f_carma%f_do_coag .or. cstate%f_carma%f_do_grow) then call setupvf(cstate%f_carma, cstate, rc) if (cstate%f_carma%f_do_vdiff) then call setupbdif(cstate%f_carma, cstate, rc) end if end if ! intialize the dry deposition if (cstate%f_carma%f_do_drydep) then if (present(lndfv) .and. present(lndram) .and. present(lndfrac) .and. & present(ocnfv) .and. present(ocnram) .and. present(ocnfrac) .and. & present(icefv) .and. present(iceram) .and. present(icefrac)) then ! NOTE: Need to convert surfric and ram from mks to cgs units. call setupvdry(cstate%f_carma, cstate, & lndfv * 100._f, ocnfv * 100._f, icefv * 100._f, & lndram / 100._f, ocnram / 100._f, iceram / 100._f, & lndfrac, ocnfrac, icefrac, rc) if (rc < RC_OK) return else write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_Step: do_drydep requires that the optional inputs xxxfv, xxxram and xxxfrac be provided." rc = RC_ERROR return end if end if ! Intialize the nucleation, growth and evaporation. if (cstate%f_carma%f_do_grow) then call setupgrow(cstate%f_carma, cstate, rc) if (rc < RC_OK) return call setupgkern(cstate%f_carma, cstate, rc) if (rc < RC_OK) return call setupnuc(cstate%f_carma, cstate, rc) if (rc < RC_OK) return end if ! Initialize the coagulation. if (cstate%f_carma%f_do_coag) then call setupckern(cstate%f_carma, cstate, rc) if (rc < RC_OK) return end if end if ! Calculate the impact of microphysics upon the state. call step(cstate%f_carma, cstate, rc) return end subroutine CARMASTATE_Step ! Query, Control and State I/O !! Gets the mass mixing ratio for the gas (igas). After a call to CARMA_Step(), !! the new mass mixing ratio of the gas can be retrieved. !! !! @author Chuck Bardeen !! @version Feb-2009 !! @see CARMA_AddGas !! @see CARMA_GetGas !! @see CARMA_Step !! @see CARMASTATE_SetGas subroutine CARMASTATE_Get(cstate, rc, max_nsubstep, max_nretry, nstep, nsubstep, nretry, zsubsteps, lat, lon) type(carmastate_type), intent(in) :: cstate !! the carma state object integer, intent(out) :: rc !! return code, negative indicates failure integer, optional, intent(out) :: max_nsubstep !! maximum number of substeps in a step real(kind=f), optional, intent(out) :: max_nretry !! maximum number of retries in a step real(kind=f), optional, intent(out) :: nstep !! total number of steps taken integer, optional, intent(out) :: nsubstep !! total number of substeps taken real(kind=f), optional, intent(out) :: nretry !! total number of retries taken real(kind=f), optional, intent(out) :: zsubsteps(cstate%f_NZ) !! number of substeps taken per vertical grid point real(kind=f), optional, intent(out) :: lat !! grid center latitude [deg] real(kind=f), optional, intent(out) :: lon !! grid center longitude [deg] ! Assume success. rc = RC_OK if (present(max_nsubstep)) max_nsubstep = cstate%f_max_nsubstep if (present(max_nretry)) max_nretry = cstate%f_max_nretry if (present(nstep)) nstep = cstate%f_nstep if (present(nsubstep)) nsubstep = cstate%f_nsubstep if (present(nretry)) nretry = cstate%f_nretry if (present(zsubsteps)) zsubsteps = cstate%f_zsubsteps if (present(lat)) lat = cstate%f_lat if (present(lon)) lon = cstate%f_lon return end subroutine CARMASTATE_Get !! Gets the mass of the bins (ibin) for each particle element (ielem). After the !! CARMA_Step() call, new particle concentrations are determined. The number density !! and the nucleation rate are only calculated if the element is the number density !! element for the group. !! !! @author Chuck Bardeen !! @version Feb-2009 !! @see CARMA_AddElement !! @see CARMA_AddGroup !! @see CARMA_Step !! @see CARMASTATE_SetBin subroutine CARMASTATE_GetBin(cstate, ielem, ibin, mmr, rc, & nmr, numberDensity, nucleationRate, r_wet, rhop_wet, & surface, sedimentationflux, vf, vd, dtpart) type(carmastate_type), intent(in) :: cstate !! the carma state object integer, intent(in) :: ielem !! the element index integer, intent(in) :: ibin !! the bin index real(kind=f), intent(out) :: mmr(cstate%f_NZ) !! the bin mass mixing ratio [kg/kg] integer, intent(out) :: rc !! return code negative indicates failure real(kind=f), optional, intent(out) :: nmr(cstate%f_NZ) !! number mixing ratio [#/kg] real(kind=f), optional, intent(out) :: numberDensity(cstate%f_NZ) !! number density [#/cm3] real(kind=f), optional, intent(out) :: nucleationRate(cstate%f_NZ) !! nucleation rate [1/cm3/s] real(kind=f), optional, intent(out) :: r_wet(cstate%f_NZ) !! wet particle radius [cm] real(kind=f), optional, intent(out) :: rhop_wet(cstate%f_NZ) !! wet particle density [g/cm3] real(kind=f), optional, intent(out) :: surface !! particle mass on the surface [kg/m2] real(kind=f), optional, intent(out) :: sedimentationflux !! particle sedimentation mass flux to surface [kg/m2/s] real(kind=f), optional, intent(out) :: vf(cstate%f_NZ+1) !! fall velocity [cm/s] real(kind=f), optional, intent(out) :: vd !! deposition velocity [cm/s] real(kind=f), optional, intent(out) :: dtpart(cstate%f_NZ) !! delta particle temperature [K] integer :: ienconc !! index of element that is the particle concentration for the group integer :: igroup ! Group containing this bin ! Assume success. rc = RC_OK ! Determine the particle group for the bin. igroup = cstate%f_carma%f_element(ielem)%f_igroup ! Make sure there are enough elements allocated. if (ielem > cstate%f_carma%f_NELEM) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_SetBin:: ERROR - The specifed element (", & ielem, ") is larger than the number of elements (", cstate%f_carma%f_NELEM, ")." rc = RC_ERROR return end if ! Make sure there are enough bins allocated. if (ibin > cstate%f_carma%f_NBIN) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMA_SetBin:: ERROR - The specifed bin (", & ibin, ") is larger than the number of bins (", cstate%f_carma%f_NBIN, ")." rc = RC_ERROR return end if ! Use the specified mass mixing ratio and the air density to determine the mass ! of the particles in g/x/y/z. mmr(:) = cstate%f_pc(:, ibin, ielem) / cstate%f_rhoa_wet(:) ! Handle the special cases for different types of elements ... if ((cstate%f_carma%f_element(ielem)%f_itype == I_INVOLATILE) .or. (cstate%f_carma%f_element(ielem)%f_itype == I_VOLATILE)) then mmr(:) = mmr(:) * cstate%f_carma%f_group(igroup)%f_rmass(ibin) else if (cstate%f_carma%f_element(ielem)%f_itype == I_CORE2MOM) then mmr(:) = mmr(:) / cstate%f_carma%f_group(igroup)%f_rmass(ibin) end if ! If the number of particles in the group is less than the minimum value represented ! by CARMA, then return and mmr of 0.0 for all elements. ienconc = cstate%f_carma%f_group(igroup)%f_ienconc ! where (cstate%f_pc(:, ibin, ienconc) <= SMALL_PC) mmr(:) = 0.0_f ! Do they also want the mass concentration of particles at the surface? if (present(surface)) then ! Convert from g/cm2 to kg/m2 surface = cstate%f_pc_surf(ibin, ielem) * 1e4_f / 1e3_f ! Handle the special cases for different types of elements ... if ((cstate%f_carma%f_element(ielem)%f_itype == I_INVOLATILE) .or. (cstate%f_carma%f_element(ielem)%f_itype == I_VOLATILE)) then surface = surface * cstate%f_carma%f_group(igroup)%f_rmass(ibin) else if (cstate%f_carma%f_element(ielem)%f_itype == I_CORE2MOM) then surface = surface / cstate%f_carma%f_group(igroup)%f_rmass(ibin) end if end if ! Do they also want the mass flux of particles that sediment to the surface? if (present(sedimentationflux)) then ! Convert from g/cm2 to kg/m2 sedimentationflux = cstate%f_sedimentationflux(ibin, ielem) * 1e4_f / 1e3_f ! Handle the special cases for different types of elements ... if ((cstate%f_carma%f_element(ielem)%f_itype == I_INVOLATILE) .or. (cstate%f_carma%f_element(ielem)%f_itype == I_VOLATILE)) then sedimentationflux = sedimentationflux * cstate%f_carma%f_group(igroup)%f_rmass(ibin) else if (cstate%f_carma%f_element(ielem)%f_itype == I_CORE2MOM) then sedimentationflux = sedimentationflux / cstate%f_carma%f_group(igroup)%f_rmass(ibin) end if end if ! If this is the partcile # element, then determine some other statistics. if (ienconc == ielem) then if (present(nmr)) nmr(:) = (cstate%f_pc(:, ibin, ielem) / cstate%f_rhoa_wet(:)) * 1000._f if (present(numberDensity)) numberDensity(:) = cstate%f_pc(:, ibin, ielem) / (cstate%f_xmet(:)*cstate%f_ymet(:)*cstate%f_zmet(:)) if (present(r_wet)) r_wet(:) = cstate%f_r_wet(:, ibin, igroup) if (present(rhop_wet)) rhop_wet(:) = cstate%f_rhop_wet(:, ibin, igroup) if (cstate%f_carma%f_do_vtran) then if (present(vf)) vf(:) = cstate%f_vf(:, ibin, igroup) / cstate%f_zmetl(:) else if (present(vf)) vf(:) = CAM_FILL end if if (cstate%f_carma%f_do_drydep) then if (present(vd)) then if (cstate%f_igridv .eq. I_CART) then vd = cstate%f_vd(ibin, igroup) / cstate%f_zmetl(1) else vd = cstate%f_vd(ibin, igroup) / cstate%f_zmetl(cstate%f_NZP1) end if end if else if (present(vd)) vd = CAM_FILL end if if (cstate%f_carma%f_do_grow) then if (present(nucleationRate)) nucleationRate(:) = cstate%f_pc_nucl(:, ibin, ielem) / (cstate%f_xmet(:)*cstate%f_ymet(:)*cstate%f_zmet(:)) / cstate%f_dtime else if (present(nucleationRate)) nucleationRate(:) = CAM_FILL end if if (cstate%f_carma%f_do_pheat) then if (present(dtpart)) dtpart(:) = cstate%f_dtpart(:, ibin, igroup) else if (present(dtpart)) dtpart(:) = CAM_FILL end if else if (present(nmr)) nmr(:) = CAM_FILL if (present(numberDensity)) numberDensity(:) = CAM_FILL if (present(nucleationRate)) nucleationRate(:) = CAM_FILL if (present(r_wet)) r_wet(:) = CAM_FILL if (present(rhop_wet)) rhop_wet(:) = CAM_FILL if (present(dtpart)) dtpart(:) = CAM_FILL if (present(vf)) vf(:) = CAM_FILL if (present(vd)) vd = CAM_FILL end if return end subroutine CARMASTATE_GetBin !! Gets the mass of the detrained condensate for the bins (ibin) for each particle !! element (ielem) in the grid. !! !! !! @author Chuck Bardeen !! @version Feb-2009 !! @see CARMA_AddElement !! @see CARMA_AddGroup !! @see CARMA_Step !! @see CARMASTATE_SetDetrain subroutine CARMASTATE_GetDetrain(cstate, ielem, ibin, mmr, rc, nmr, numberDensity, r_wet, rhop_wet) type(carmastate_type), intent(in) :: cstate !! the carma state object integer, intent(in) :: ielem !! the element index integer, intent(in) :: ibin !! the bin index real(kind=f), intent(out) :: mmr(cstate%f_NZ) !! the bin mass mixing ratio [kg/kg] integer, intent(out) :: rc !! return code negative indicates failure real(kind=f), optional, intent(out) :: nmr(cstate%f_NZ) !! number mixing ratio [#/kg] real(kind=f), optional, intent(out) :: numberDensity(cstate%f_NZ) !! number density [#/cm3] real(kind=f), optional, intent(out) :: r_wet(cstate%f_NZ) !! wet particle radius [cm] real(kind=f), optional, intent(out) :: rhop_wet(cstate%f_NZ) !! wet particle density [g/cm3] integer :: ienconc !! index of element that is the particle concentration for the group integer :: igroup ! Group containing this bin ! Assume success. rc = RC_OK ! Determine the particle group for the bin. igroup = cstate%f_carma%f_element(ielem)%f_igroup ! Make sure there are enough elements allocated. if (ielem > cstate%f_carma%f_NELEM) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_SetDetrain:: ERROR - The specifed element (", & ielem, ") is larger than the number of elements (", cstate%f_carma%f_NELEM, ")." rc = RC_ERROR return end if ! Make sure there are enough bins allocated. if (ibin > cstate%f_carma%f_NBIN) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMA_SetDetrainin:: ERROR - The specifed bin (", & ibin, ") is larger than the number of bins (", cstate%f_carma%f_NBIN, ")." rc = RC_ERROR return end if ! Use the specified mass mixing ratio and the air density to determine the mass ! of the particles in g/x/y/z. mmr(:) = cstate%f_pcd(:, ibin, ielem) / cstate%f_rhoa_wet(:) ! Handle the special cases for different types of elements ... if ((cstate%f_carma%f_element(ielem)%f_itype == I_INVOLATILE) .or. (cstate%f_carma%f_element(ielem)%f_itype == I_VOLATILE)) then mmr(:) = mmr(:) * cstate%f_carma%f_group(igroup)%f_rmass(ibin) else if (cstate%f_carma%f_element(ielem)%f_itype == I_CORE2MOM) then mmr(:) = mmr(:) / cstate%f_carma%f_group(igroup)%f_rmass(ibin) end if ! If this is the partcile # element, then determine some other statistics. ienconc = cstate%f_carma%f_group(igroup)%f_ienconc if (ienconc == ielem) then if (present(nmr)) nmr(:) = (cstate%f_pcd(:, ibin, ielem) / cstate%f_rhoa_wet(:)) * 1000._f if (present(numberDensity)) numberDensity(:) = cstate%f_pcd(:, ibin, ielem) / (cstate%f_xmet(:)*cstate%f_ymet(:)*cstate%f_zmet(:)) if (present(r_wet)) r_wet(:) = cstate%f_r_wet(:, ibin, igroup) if (present(rhop_wet)) rhop_wet(:) = cstate%f_rhop_wet(:, ibin, igroup) else if (present(nmr)) nmr(:) = CAM_FILL if (present(numberDensity)) numberDensity(:) = CAM_FILL end if return end subroutine CARMASTATE_GetDetrain !! Gets the mass mixing ratio for the gas (igas). After a call to CARMA_Step(), !! the new mass mixing ratio of the gas can be retrieved. !! !! @author Chuck Bardeen !! @version Feb-2009 !! @see CARMA_AddGas !! @see CARMA_GetGas !! @see CARMA_Step !! @see CARMASTATE_SetGas subroutine CARMASTATE_GetGas(cstate, igas, mmr, rc, satice, satliq, eqice, eqliq, wtpct) type(carmastate_type), intent(in) :: cstate !! the carma state object integer, intent(in) :: igas !! the gas index real(kind=f), intent(out) :: mmr(cstate%f_NZ) !! the gas mass mixing ratio [kg/kg] integer, intent(out) :: rc !! return code, negative indicates failure real(kind=f), optional, intent(out) :: satice(cstate%f_NZ) !! the gas saturation wrt ice real(kind=f), optional, intent(out) :: satliq(cstate%f_NZ) !! the gas saturation wrt liquid real(kind=f), optional, intent(out) :: eqice(cstate%f_NZ) !! the gas vapor pressure wrt ice real(kind=f), optional, intent(out) :: eqliq(cstate%f_NZ) !! the gas vapor pressure wrt liquid real(kind=f), optional, intent(out) :: wtpct(cstate%f_NZ) !! weight percent aerosol composition ! Assume success. rc = RC_OK ! Make sure there are enough gases allocated. if (igas > cstate%f_carma%f_NGAS) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_GetGas:: ERROR - The specifed gas (", & igas, ") is larger than the number of gases (", cstate%f_carma%f_NGAS, ")." rc = RC_ERROR return end if ! Use the specified mass mixing ratio and the air density to determine the mass ! of the gas in g/x/y/z. mmr(:) = cstate%f_gc(:, igas) / cstate%f_rhoa_wet(:) if (present(satice)) satice(:) = cstate%f_supsati(:, igas) + 1._f if (present(satliq)) satliq(:) = cstate%f_supsatl(:, igas) + 1._f if (present(eqice)) eqice(:) = cstate%f_pvapi(:, igas) / cstate%f_p(:) if (present(eqliq)) eqliq(:) = cstate%f_pvapl(:, igas) / cstate%f_p(:) if (present(wtpct)) wtpct(:) = cstate%f_wtpct(:) return end subroutine CARMASTATE_GetGas !! Gets information about the state of the atmosphere. After the CARMA_Step() call, !! a new atmospheric state is determined. !! !! @author Chuck Bardeen !! @version Feb-2009 !! @see CARMA_Step !! @see CARMASTATE_Create subroutine CARMASTATE_GetState(cstate, rc, t, p, rhoa_wet, rlheat) type(carmastate_type), intent(in) :: cstate !! the carma state object integer, intent(out) :: rc !! return code, negative indicates failure real(kind=f), optional, intent(out) :: t(cstate%f_NZ) !! the air temperature [K] real(kind=f), optional, intent(out) :: p(cstate%f_NZ) !! the air pressure [Pa] real(kind=f), optional, intent(out) :: rhoa_wet(cstate%f_NZ) !! air density [kg m-3] real(kind=f), optional, intent(out) :: rlheat(cstate%f_NZ) !! latent heat [K/s] ! Assume success. rc = RC_OK ! Return the temperature, pressure, and/or density. if (present(t)) t(:) = cstate%f_t(:) ! DYNE -> Pa if (present(p)) p(:) = cstate%f_p(:) / RPA2CGS ! Convert rhoa from the scaled units to mks. if (present(rhoa_wet)) rhoa_wet(:) = (cstate%f_rhoa_wet(:) / (cstate%f_zmet(:)*cstate%f_xmet(:)*cstate%f_ymet(:))) * 1e6_f / 1e3_f if (present(rlheat)) rlheat(:) = cstate%f_rlheat(:) return end subroutine CARMASTATE_GetState !! Sets the mass of the bins (ibin) for each particle element (ielem) in the grid. !! This call should be made after CARMASTATE_Create() and before CARMA_Step(). !! !! @author Chuck Bardeen !! @version Feb-2009 !! @see CARMA_AddBin !! @see CARMA_Step !! @see CARMASTATE_GetBin subroutine CARMASTATE_SetBin(cstate, ielem, ibin, mmr, rc, surface) type(carmastate_type), intent(inout) :: cstate !! the carma state object integer, intent(in) :: ielem !! the element index integer, intent(in) :: ibin !! the bin index real(kind=f), intent(in) :: mmr(cstate%f_NZ) !! the bin mass mixing ratio [kg/kg] integer, intent(out) :: rc !! return code, negative indicates failure real(kind=f), optional, intent(in) :: surface !! particles mass on the surface [kg/m2] integer :: igroup ! Group containing this bin ! Assume success. rc = RC_OK ! Determine the particle group for the bin. igroup = cstate%f_carma%f_element(ielem)%f_igroup ! Make sure there are enough elements allocated. if (ielem > cstate%f_carma%f_NELEM) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_SetBin:: ERROR - The specifed element (", & ielem, ") is larger than the number of elements (", cstate%f_carma%f_NELEM, ")." rc = RC_ERROR return end if ! Make sure there are enough bins allocated. if (ibin > cstate%f_carma%f_NBIN) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_SetBin:: ERROR - The specifed bin (", & ibin, ") is larger than the number of bins (", cstate%f_carma%f_NBIN, ")." rc = RC_ERROR return end if ! Use the specified mass mixing ratio and the air density to determine the mass ! of the particles in g/x/y/z. cstate%f_pc(:, ibin, ielem) = mmr(:) * cstate%f_rhoa_wet(:) ! Handle the special cases for different types of elements ... if ((cstate%f_carma%f_element(ielem)%f_itype == I_INVOLATILE) .or. (cstate%f_carma%f_element(ielem)%f_itype == I_VOLATILE)) then cstate%f_pc(:, ibin, ielem) = cstate%f_pc(:, ibin, ielem) / cstate%f_carma%f_group(igroup)%f_rmass(ibin) else if (cstate%f_carma%f_element(ielem)%f_itype == I_CORE2MOM) then cstate%f_pc(:, ibin, ielem) = cstate%f_pc(:, ibin, ielem) * cstate%f_carma%f_group(igroup)%f_rmass(ibin) end if ! If they specified an initial mass of particles on the surface, then use that ! value. if (present(surface)) then ! Convert from g/cm2 to kg/m2 cstate%f_pc_surf(ibin, ielem) = surface / 1e4_f * 1e3_f ! Handle the special cases for different types of elements ... if ((cstate%f_carma%f_element(ielem)%f_itype == I_INVOLATILE) .or. (cstate%f_carma%f_element(ielem)%f_itype == I_VOLATILE)) then cstate%f_pc_surf(ibin, ielem) = cstate%f_pc_surf(ibin, ielem) / cstate%f_carma%f_group(igroup)%f_rmass(ibin) else if (cstate%f_carma%f_element(ielem)%f_itype == I_CORE2MOM) then cstate%f_pc_surf(ibin, ielem) = cstate%f_pc_surf(ibin, ielem) * cstate%f_carma%f_group(igroup)%f_rmass(ibin) end if else cstate%f_pc_surf(ibin, ielem) = 0.0_f end if return end subroutine CARMASTATE_SetBin !! Sets the mass of the detrained condensate for the bins (ibin) for each particle !! element (ielem) in the grid. This call should be made after CARMASTATE_Create() !! and before CARMA_Step(). !! !! @author Chuck Bardeen !! @version May-2010 !! @see CARMA_AddBin !! @see CARMA_Step !! @see CARMASTATE_GetDetrain subroutine CARMASTATE_SetDetrain(cstate, ielem, ibin, mmr, rc) type(carmastate_type), intent(inout) :: cstate !! the carma state object integer, intent(in) :: ielem !! the element index integer, intent(in) :: ibin !! the bin index real(kind=f), intent(in) :: mmr(cstate%f_NZ) !! the bin mass mixing ratio [kg/kg] integer, intent(out) :: rc !! return code, negative indicates failure integer :: igroup ! Group containing this bin ! Assume success. rc = RC_OK ! Determine the particle group for the bin. igroup = cstate%f_carma%f_element(ielem)%f_igroup ! Make sure there are enough elements allocated. if (ielem > cstate%f_carma%f_NELEM) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_SetDetrain:: ERROR - The specifed element (", & ielem, ") is larger than the number of elements (", cstate%f_carma%f_NELEM, ")." rc = RC_ERROR return end if ! Make sure there are enough bins allocated. if (ibin > cstate%f_carma%f_NBIN) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_SetDetrain:: ERROR - The specifed bin (", & ibin, ") is larger than the number of bins (", cstate%f_carma%f_NBIN, ")." rc = RC_ERROR return end if ! Use the specified mass mixing ratio and the air density to determine the mass ! of the particles in g/x/y/z. cstate%f_pcd(:, ibin, ielem) = mmr(:) * cstate%f_rhoa_wet(:) ! Handle the special cases for different types of elements ... if ((cstate%f_carma%f_element(ielem)%f_itype == I_INVOLATILE) .or. (cstate%f_carma%f_element(ielem)%f_itype == I_VOLATILE)) then cstate%f_pcd(:, ibin, ielem) = cstate%f_pcd(:, ibin, ielem) / cstate%f_carma%f_group(igroup)%f_rmass(ibin) else if (cstate%f_carma%f_element(ielem)%f_itype == I_CORE2MOM) then cstate%f_pcd(:, ibin, ielem) = cstate%f_pcd(:, ibin, ielem) * cstate%f_carma%f_group(igroup)%f_rmass(ibin) end if return end subroutine CARMASTATE_SetDetrain !! Sets the mass of the gas (igas) in the grid. This call should be made after !! CARMASTATE_Create() and before CARMA_Step(). !! !! @author Chuck Bardeen !! @version Feb-2009 !! @see CARMA_AddGas !! @see CARMA_GetGas !! @see CARMA_InitializeStep !! @see CARMA_Step subroutine CARMASTATE_SetGas(cstate, igas, mmr, rc, mmr_old, satice_old, satliq_old) type(carmastate_type), intent(inout) :: cstate !! the carma object integer, intent(in) :: igas !! the gas index real(kind=f), intent(in) :: mmr(cstate%f_NZ) !! the gas mass mixing ratio [kg/kg] integer, intent(out) :: rc !! return code, negative indicates failure real(kind=f), intent(in), optional :: mmr_old(cstate%f_NZ) !! the previous gas mass mixing ratio [kg/kg] real(kind=f), intent(inout), optional :: satice_old(cstate%f_NZ) !! the previous gas saturation wrt ice, calculates if -1 real(kind=f), intent(inout), optional :: satliq_old(cstate%f_NZ) !! the previous gas saturation wrt liquid, calculates if -1 real(kind=f) :: tnew(cstate%f_NZ) integer :: iz logical :: calculateOld ! Assume success. rc = RC_OK ! Make sure there are enough gases allocated. if (igas > cstate%f_carma%f_NGAS) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT, *) "CARMASTATE_SetGas:: ERROR - The specifed gas (", & igas, ") is larger than the number of gases (", cstate%f_carma%f_NGAS, ")." rc = RC_ERROR return end if if (cstate%f_carma%f_do_substep) then if (.not. present(mmr_old)) then if (cstate%f_carma%f_do_print) write(cstate%f_carma%f_LUNOPRT,*) "CARMASTATE_SetGas: Error - Need to specify mmr_old, satic_old, satliq_old when substepping." rc = RC_ERROR return else cstate%f_gcl(:, igas) = mmr_old(:) * cstate%f_rhoa_wet(:) * cstate%f_t(:) / cstate%f_told(:) ! A value of -1 for the saturation ratio means that it needs to be calculated from the old temperature ! and the old gc. ! ! NOTE: This is typically just a problem for the first step, so we just need to get close. calculateOld = .false. if (present(satice_old) .and. present(satliq_old)) then if (any(satice_old(:) == -1._f) .or. any(satliq_old(:) == -1._f)) calculateOld = .true. else calculateOld = .true. end if if (calculateOld) then ! This is a bit of a hack, because of the way CARMA has the vapor pressure and saturation ! routines implemented. ! Temporarily set the temperature and gc of to the old state tnew(:) = cstate%f_t(:) cstate%f_t(:) = cstate%f_told(:) cstate%f_gc(:, igas) = mmr_old(:) * cstate%f_rhoa_wet(:) do iz = 1, cstate%f_NZ call supersat(cstate%f_carma, cstate, iz, igas, rc) if (rc < RC_OK) return if (present(satice_old)) then if (satice_old(iz) == -1._f) then cstate%f_supsatiold(iz, igas) = cstate%f_supsati(iz, igas) else cstate%f_supsatiold(iz, igas) = satice_old(iz) - 1._f endif else cstate%f_supsatiold(iz, igas) = cstate%f_supsati(iz, igas) end if if (present(satliq_old)) then if (satliq_old(iz) == -1._f) then cstate%f_supsatlold(iz, igas) = cstate%f_supsatl(iz, igas) else cstate%f_supsatlold(iz, igas) = satliq_old(iz) - 1._f endif else cstate%f_supsatlold(iz, igas) = cstate%f_supsatl(iz, igas) end if end do cstate%f_t(:) = tnew(:) else cstate%f_supsatiold(:, igas) = satice_old(:) - 1._f cstate%f_supsatlold(:, igas) = satliq_old(:) - 1._f end if end if end if ! Use the specified mass mixing ratio and the air density to determine the mass ! of the gas in g/x/y/z. cstate%f_gc(:, igas) = mmr(:) * cstate%f_rhoa_wet(:) return end subroutine CARMASTATE_SetGas !! Sets information about the state of the atmosphere. !! !! @author Chuck Bardeen !! @version Feb-2009 !! @see CARMA_Step !! @see CARMASTATE_Create subroutine CARMASTATE_SetState(cstate, rc, t, rhoa_wet) type(carmastate_type), intent(inout) :: cstate !! the carma state object integer, intent(out) :: rc !! return code, negative indicates failure real(kind=f), optional, intent(in) :: t(cstate%f_NZ) !! the air temperature [K] real(kind=f), optional, intent(in) :: rhoa_wet(cstate%f_NZ) !! air density [kg m-3] ! Assume success. rc = RC_OK ! Return the temperature or density. if (present(t)) cstate%f_t(:) = t(:) ! Convert rhoa from mks to the scaled units. if (present(rhoa_wet)) cstate%f_rhoa_wet(:) = (rhoa_wet(:) * (cstate%f_zmet(:)*cstate%f_xmet(:)*cstate%f_ymet(:))) / 1e6_f * 1e3_f return end subroutine CARMASTATE_SetState end module