def confront(self, m): obs, mod = self.stageData(m) obs_area = obs.integrateInSpace().convert("1e6 km2") mod_area = mod.integrateInSpace().convert("1e6 km2") # Interpolate to a composed grid lat, lon, lat_bnds, lon_bnds = il._composeGrids(obs, mod) OBS = obs.interpolate(lat=lat, lon=lon, lat_bnds=lat_bnds, lon_bnds=lon_bnds) MOD = mod.interpolate(lat=lat, lon=lon, lat_bnds=lat_bnds, lon_bnds=lon_bnds) # Compute the different extent areas o_mask = OBS.data.mask o_land = (OBS.area > 1e-12) * (o_mask == 0) o_data = np.copy(OBS.data.data) o_data[np.where(OBS.data.mask)] = 0. m_mask = MOD.data.mask m_land = (MOD.area > 1e-12) m_data = np.copy(MOD.data.data) m_data[np.where(MOD.data.mask)] = 0. o_and_m = (np.abs(o_data - 1) < 1e-12) * (np.abs(m_data - 1) < 1e-12) o_not_m_land = (np.abs(o_data - 1) < 1e-12) * (np.abs(m_data) < 1e-12) * (m_land == 0) o_not_m_miss = (np.abs(o_data - 1) < 1e-12) * (np.abs(m_data) < 1e-12) * (m_land == 1) o_zones = 1. * o_and_m o_zones += 2. * o_not_m_land o_zones += 4. * o_not_m_miss o_zones = np.ma.masked_array(o_zones, mask=o_mask) m_and_o = (np.abs(m_data - 1) < 1e-12) * (np.abs(o_data - 1) < 1e-12) m_not_o_land = (np.abs(m_data - 1) < 1e-12) * (np.abs(o_data) < 1e-12) * (o_land == 0) m_not_o_miss = (np.abs(m_data - 1) < 1e-12) * (np.abs(o_data) < 1e-12) * (o_land == 1) m_zones = 1. * m_and_o m_zones += 2. * m_not_o_land m_zones += 4. * m_not_o_miss m_zones = np.ma.masked_array(m_zones, mask=m_mask) zones = 1. * o_and_m zones += 2. * o_not_m_miss zones += 4. * m_not_o_miss zones += 8. * m_not_o_land zones = np.ma.masked_less(zones, 1) for i, u in enumerate(np.unique(zones.compressed())): zones[np.where(zones == u)] = i # compute the intersection of obs and mod obs_and_mod = Variable(name="obs_and_mod", unit="1", data=np.ma.masked_values(zones == 0, 0).astype(float), lat=lat, lon=lon, area=MOD.area) # compute the obs that is not the mod data = (OBS.data.mask == 0) * (MOD.data.mask == 1) obs_not_mod = Variable(name="obs_not_mod", unit="1", data=np.ma.masked_values(zones == 1, 0).astype(float), lat=lat, lon=lon, area=OBS.area) # compute the mod that is not the obs data = (OBS.data.mask == 1) * (MOD.data.mask == 0) mod_not_obs = Variable(name="mod_not_obs", unit="1", data=np.ma.masked_values(zones == 2, 0).astype(float), lat=lat, lon=lon, area=MOD.area) # compute the mod that is not the obs but because of land representation data = (OBS.data.mask == 1) * (MOD.data.mask == 0) mod_not_obs_land = Variable(name="mod_not_obs_land", unit="1", data=np.ma.masked_values(zones == 3, 0).astype(float), lat=lat, lon=lon, area=MOD.area) # compute areas obs_and_mod_area = obs_and_mod.integrateInSpace().convert("1e6 km2") obs_not_mod_area = obs_not_mod.integrateInSpace().convert("1e6 km2") mod_not_obs_area = mod_not_obs.integrateInSpace().convert("1e6 km2") mod_not_obs_land_area = mod_not_obs_land.integrateInSpace().convert( "1e6 km2") # determine score obs_score = Variable(name="Missed Score global", unit="1", data=obs_and_mod_area.data / (obs_and_mod_area.data + obs_not_mod_area.data)) mod_score = Variable(name="Excess Score global", unit="1", data=obs_and_mod_area.data / (obs_and_mod_area.data + mod_not_obs_area.data)) # Write to datafiles -------------------------------------- obs_area.name = "Total Area" mod_area.name = "Total Area" obs_and_mod_area.name = "Overlap Area" obs_not_mod_area.name = "Missed Area" mod_not_obs_area.name = "Excess Area" mod_not_obs_land_area.name = "Excess Area (Land Representation)" results = Dataset("%s/%s_%s.nc" % (self.output_path, self.name, m.name), mode="w") results.setncatts({"name": m.name, "color": m.color}) mod.toNetCDF4(results, group="MeanState") obs_and_mod.toNetCDF4(results, group="MeanState") obs_not_mod.toNetCDF4(results, group="MeanState") mod_not_obs.toNetCDF4(results, group="MeanState") mod_not_obs_land.toNetCDF4(results, group="MeanState") mod_area.toNetCDF4(results, group="MeanState") obs_and_mod_area.toNetCDF4(results, group="MeanState") obs_not_mod_area.toNetCDF4(results, group="MeanState") mod_not_obs_area.toNetCDF4(results, group="MeanState") mod_not_obs_land_area.toNetCDF4(results, group="MeanState") obs_score.toNetCDF4(results, group="MeanState") mod_score.toNetCDF4(results, group="MeanState") results.close() if self.master: results = Dataset("%s/%s_Benchmark.nc" % (self.output_path, self.name), mode="w") results.setncatts({ "name": "Benchmark", "color": np.asarray([0.5, 0.5, 0.5]) }) obs_area.toNetCDF4(results, group="MeanState") results.close()
def stageData(self,m): r"""Extracts model data which is comparable to the observations. The observational data measure the anomaly in terrestrial water storage, 'twsa' in terms of [kg m-2]. We convert this unit to [cm] using the density of water. The models are expected to provide the terrestrial water storage variable, 'tws', and we need to find the anomaly. First, the model result is trimmed to match the temporal extents of the observational data. Then, to get the anomaly, we subtract off the temporal mean, .. math:: \mathit{twsa}(t,\mathbf{x}) = tws(t,\mathbf{x}) - \frac{1}{t_f-t_0}\int_{t_0}^{t_f} tws(t,\mathbf{x})\ dt We do this for the model 'tws' variable, and optionally for the observation, treating its 'twsa' variable as 'tws' in the above expression. This is because the observational data can have a small mean even though it represents only the anomaly. Parameters ---------- m : ILAMB.ModelResult.ModelResult the model result context Returns ------- obs : ILAMB.Variable.Variable the variable context associated with the observational dataset mod : ILAMB.Variable.Variable the variable context associated with the model result """ # get the observational data obs = Variable(filename = self.source, variable_name = self.variable, alternate_vars = self.alternate_vars).convert("cm") # get the model data, in the units of the obseravtions mod = m.extractTimeSeries(self.variable, alt_vars = self.alternate_vars, expression = self.derived, initial_time = obs.time_bnds[ 0,0], final_time = obs.time_bnds[-1,1]) # if the derived expression is used, then we get a mass flux # rate and need to accumulate try: mod.convert(obs.unit) except: mod = mod.accumulateInTime() mod.name = obs.name obs,mod = il.MakeComparable(obs,mod,clip_ref=True) # subtract off the mean mean = obs.integrateInTime(mean=True) obs.data -= mean.data mean = mod.integrateInTime(mean=True) mod.data -= mean.data # compute mean values over each basin odata = np.ma.zeros((obs.time.size,len(self.basins))) mdata = np.ma.zeros((mod.time.size,len(self.basins))) for i,basin in enumerate(self.basins): odata[:,i] = obs.integrateInSpace(region=basin,mean=True).data mdata[:,i] = mod.integrateInSpace(region=basin,mean=True).data obs.data = odata; obs.ndata = odata.shape[1]; obs.spatial = False mod.data = mdata; mod.ndata = mdata.shape[1]; mod.spatial = False mod.data.mask = obs.data.mask return obs,mod
def confront(self, m): obs, mod = self.stageData(m) obs_area = obs.integrateInSpace().convert("1e6 km2") mod_area = mod.integrateInSpace().convert("1e6 km2") # interpolate to a composed grid lat, lon = il.ComposeSpatialGrids(obs, mod) iobs = obs.interpolate(lat=lat, lon=lon) imod = mod.interpolate(lat=lat, lon=lon) # compute the intersection of obs and mod data = (iobs.data.mask == 0) * (imod.data.mask == 0) obs_and_mod = Variable(name="obs_and_mod", unit="1", data=np.ma.masked_values(data, 0).astype(float), lat=lat, lon=lon) # compute the obs that is not the mod data = (iobs.data.mask == 0) * (imod.data.mask == 1) obs_not_mod = Variable(name="obs_not_mod", unit="1", data=np.ma.masked_values(data, 0).astype(float), lat=lat, lon=lon) # compute the mod that is not the obs data = (iobs.data.mask == 1) * (imod.data.mask == 0) mod_not_obs = Variable(name="mod_not_obs", unit="1", data=np.ma.masked_values(data, 0).astype(float), lat=lat, lon=lon) # compute areas obs_and_mod_area = obs_and_mod.integrateInSpace().convert("1e6 km2") obs_not_mod_area = obs_not_mod.integrateInSpace().convert("1e6 km2") mod_not_obs_area = mod_not_obs.integrateInSpace().convert("1e6 km2") # determine score obs_score = Variable(name="Obs Score global", unit="1", data=obs_and_mod_area.data / obs_area.data) mod_score = Variable(name="Mod Score global", unit="1", data=obs_and_mod_area.data / mod_area.data) # Write to datafiles -------------------------------------- obs_area.name = "Total Area" mod_area.name = "Total Area" obs_and_mod_area.name = "Overlap Area" obs_not_mod_area.name = "Missed Area" mod_not_obs_area.name = "Excess Area" results = Dataset("%s/%s_%s.nc" % (self.output_path, self.name, m.name), mode="w") results.setncatts({"name": m.name, "color": m.color}) mod.toNetCDF4(results, group="MeanState") obs_and_mod.toNetCDF4(results, group="MeanState") obs_not_mod.toNetCDF4(results, group="MeanState") mod_not_obs.toNetCDF4(results, group="MeanState") mod_area.toNetCDF4(results, group="MeanState") obs_and_mod_area.toNetCDF4(results, group="MeanState") obs_not_mod_area.toNetCDF4(results, group="MeanState") mod_not_obs_area.toNetCDF4(results, group="MeanState") obs_score.toNetCDF4(results, group="MeanState") mod_score.toNetCDF4(results, group="MeanState") results.close() if self.master: results = Dataset("%s/%s_Benchmark.nc" % (self.output_path, self.name), mode="w") results.setncatts({ "name": "Benchmark", "color": np.asarray([0.5, 0.5, 0.5]) }) obs_area.toNetCDF4(results, group="MeanState") results.close()