1 模拟效果示例
2 高斯模型
2.1 高斯烟团模型
突发性泄漏事故中,经常发生污染源在短时间内突然释放大量的有害气体,此时对地面污染浓度的求解适合采用高斯烟团模型。烟团模型假定污染气云的体积沿水平和垂直方向增长,模拟污染气云在时间和空间上的变化。
2.1.1 方程
式中c为污染物浓度(单位:mg/m3)
Q为源强(单位:mg)
u为泄漏高度的平均风速(单位:m/s)
y、z分别用浓度标准偏差表示的y轴及z轴上的扩散参数
H为泄漏有效高度(单位:m)
2.1.2 适用条件
(1)污染物浓度在y、z轴上的分布符合高斯分布(正态分布);
(2)在全部空间中风速是均匀的、稳定的;
(3)源强一次性排放,短时间释放大量有毒气体;
(4)在扩散过程中污染物质量是守恒的(不考虑转化)。
2.2 高斯烟羽模型
高斯烟羽模式是计算释入大气中的气载污染物下风向浓度的应用最广的方法。此模式假定烟羽中污染物浓度分布在水平方向和垂直方向都遵循高斯分布。对于在恒定气象条件(指风向、风速、大气稳定度不随时间而变)高架点源的连续排放,在考虑了烟羽在地面的全反射后,下风向任一点的污染物浓度C (r,y,z)可由高斯烟羽公式进行模拟。
2.2.1 方程
式中c为污染物浓度(单位:mg/m3)
Q为源强(单位:mg/s)
u为泄漏高度的平均风速(单位:m/s)
y、z分别用浓度标准偏差表示的y轴及z轴上的扩散参数
H为泄漏有效高度(单位:m)
2.2.2 适用条件
(1)污染物浓度在y、z轴上的分布符合高斯分布(正态分布);
(2)在全部空间中风速是均匀的、稳定的;
(3)源强是连续均匀的;
(4)在扩散过程中污染物质量是守恒的(不考虑转化)。
3 高斯羽烟模型参数
3.1 坐标系
高斯模式的坐标系如下图所示,其原点为排放点(无界点源或地面源)或高架源排放点在地面的投影点,x轴正向为平均风向,y轴在水平面上垂直于x轴,正向在x轴的左侧,z轴垂直于水平面xoy,向上为正向,即为右手坐标系。
3.2 烟气抬升
有效源高H,等于烟囱的几何高度与烟气抬升高度△H之和,即
对于一确定的烟囱,其几何高度H是确定值,只要计算出抬升高度△H值,就可以求出有效源高H。我国“制订地方大气污染物排放标准的技术方法”(GB/T13201-91)中规定选用Briggs 公式作为适用计算模型,请参考相关文献。
3.3 大气稳定度
大气稳定度指大气中某一高度上的气团在垂直方向上的相对稳定程度。如果给一团空气一个初始作用力,使其作向上的垂直运动,垂直运动的气块在外力消失后,又逐渐回到原来的位置,这种状况的大气是稳定的;当外力消失后,气块仍继续上升,甚至加速前进,这种状况的大气是不稳定的;当外力消失后,气块停留在其已到达的位置,既不上升也不下降,这种状况的大气处于中性状态。
我国《环境影响评价技术导则》中推荐了用常规地面观测资料划分大气稳定度的方法。大气稳定度的分类方法采用经过修正的帕斯奎尔(Pasquill)稳定度分级法(Ps),将大气扩散稳定度分为强不稳定、不稳定、弱不稳定、中性、弱稳定和稳定六级,分别用A、B、C、D、E、F来表示。确定等级时首先根据云量与太阳高度角按下表查出太阳辐射等级数,再由太阳辐射等级数与地面风速按下表查找稳定度等级。
4 关键技术代码实现
风向因素:计算需使用三角函数进行转换。
package cn.interpolation.controller;
import cn.interpolation.common.HttpUtils;
import cn.interpolation.common.InterpolationUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "高斯羽烟")
@RestController
@RequestMapping("/api/GaussPlume")
public class GaussPlumeController {
private static final Logger logger = LoggerFactory.getLogger(GaussPlumeController.class);
/********************************************** 图片路径 ***********************************************************/
Double TransferDouble(Object v){
try {
double vr = Double.valueOf(v.toString());
return vr;
}
catch (Exception e){
return 0.0;
}
}
@ApiOperation(value = "GaussPlume")
@GetMapping(value = "/GaussPlumeVec")
// @Scheduled(cron = "0 10 * * * ?")
public String gaussPlumeVec() {
int[] size = new int[]{400, 400};
boolean isclip = true;
double[] dataInterval = new double[]{0.0001, 10, 25, 50, 100, 250} ;
calGaussPlumePoints(80,60,3.16 ,1000000000,225,116.2,41.2,dataInterval, size);
return strJson;
}
}
/**
* 生成高斯羽烟等值面格网点
*
* @param z 高度
* @param q 污染物释放速率,单位mg/s
* @param u 平均风速
* @param wind_direction 风向
* @param longitude 源经度
* @param latitude 源纬度
* @param dataInterval 数据间隔double[0.0001,25,50,100,200,300]
* @param size 大小,宽,高new int[]{100, 100};
* @return
*/
public static String calGaussPlumePoints(double z, double h, double u, double q, double wind_direction, double longitude, double latitude, double[] dataInterval, int[] size) {
StringBuffer geojsonpoints = new StringBuffer();
geojsonpoints.append("{\"type\": \"FeatureCollection\", \"crs\": {\"type\": \"name\",\"properties\": { \"name\": \"EPSG:4326\" }},\"features\": [");
try {
double _undefData = -9999.0;
// 多边形集合
SimpleFeatureCollection polygonCollection = null;
// 多边形线集合
List<PolyLine> cPolylineList = new ArrayList<PolyLine>();
// 多边形List
List<Polygon> cPolygonList = new ArrayList<Polygon>();
int width = size[0], height = size[1];
double[] _X = new double[width];
double[] _Y = new double[height];
double minX=longitude - 0.00001 * width;
double minY=latitude - 0.00001 * height;
double maxX=longitude + 0.00001 * width;
double maxY=latitude + 0.00001 * height;
createGridXY_Num(minX, minY, maxX, maxY, _X, _Y);
double[][] _gridData = new double[width][height];
// 数据间隔长度
int nc = dataInterval.length;
// 高斯羽烟插值 (宽(栅格X阵)、高(栅格Y阵)、默认数(最近邻居数))
interpolation_GaussPlume( _X, _Y, z, longitude, latitude, u, q, wind_direction,h);
int rowNum, colNum;
colNum = _X.length;
rowNum = _Y.length;
int i, j;
//---- Do interpolation
for (i = 0; i < rowNum; i++) {
for (j = 0; j < colNum; j++) {
geojsonpoints.append("{\"type\": \"Feature\",\"geometry\": {\"type\": \"Point\",\"coordinates\": [ " +
_X[i] +", "+ _Y[j] +" ]},\"properties\": { \"val\":" +
_gridData[i][j] +" }},");
}
}
geojsonpoints.append("]}");
} catch (Exception e) {
e.printStackTrace();
}
return geojsonpoints.toString();
}
try (NDManager manager = NDManager.newBaseManager()) {
// 1. 三角函数
NDArray a = manager.create(new int[]{0, 30, 45, 60, 90});
out.println("不同角度的正弦值:");
// 通过乘 pi/180 转化为弧度
PI / 180).sin();
out.println(b.toDebugString(100, 10, 100, 100));
out.println("数组中角度的余弦值:");
PI / 180).cos();
out.println(b.toDebugString(100, 10, 100, 100));
out.println("数组中角度的正切值:");
PI / 180).tan();
out.println(b.toDebugString(100, 10, 100, 100));
// 2. 反三角函数
a = manager.create(new int[]{0, 30, 45, 60, 90});
out.println("含有正弦值的数组:");
PI / 180).sin();
out.println(sin.toDebugString(100, 10, 100, 100));
out.println("计算角度的反正弦,返回值以弧度为单位:");
NDArray inv = sin.asin();
out.println(inv.toDebugString(100, 10, 100, 100));
out.println("通过转化为角度制来检查结果:");
b = inv.toDegrees();
out.println(b.toDebugString(100, 10, 100, 100));
out.println("arccos 和 arctan 函数行为类似:");
PI / 180).cos();
out.println(cos.toDebugString(100, 10, 100, 100));
out.println("反余弦:");
inv = cos.acos();
out.println(inv.toDebugString(100, 10, 100, 100));
out.println("角度制单位:");
b = inv.toDegrees();
out.println(b.toDebugString(100, 10, 100, 100));
out.println("tan 函数:");
PI / 180).tan();
out.println(tan.toDebugString(100, 10, 100, 100));
out.println("反正切:");
inv = tan.atan();
out.println(inv.toDebugString(100, 10, 100, 100));
out.println("角度制单位:");
b = inv.toDegrees();
out.println(b.toDebugString(100, 10, 100, 100));
// 3. 舍入函数
// 3.1 四舍五入
a = manager.create(new double[]{1.0, 5.55, 123, 0.567, 25.532});
out.println("原数组:");
out.println(a.toDebugString(100, 10, 100, 100));
out.println("舍入后:");
b = a.round();
out.println(b.toDebugString(100, 10, 100, 100));
// 3.2 向下取整
a = manager.create(new double[]{-1.7, 1.5, -0.2, 0.6, 10});
out.println("提供的数组:");
out.println(a.toDebugString(100, 10, 100, 100));
out.println("修改后的数组:");
b = a.floor();
out.println(b.toDebugString(100, 10, 100, 100));
// 3.3 向上取整
a = manager.create(new double[]{-1.7, 1.5, -0.2, 0.6, 10});
out.println("提供的数组:");
out.println(a.toDebugString(100, 10, 100, 100));
out.println("修改后的数组:");
b = a.ceil();
out.println(b.toDebugString(100, 10, 100, 100));
}
调用生成的geojson成果及拖入查看:
插值成面部分代码
public String gaussPlumePolygonVec(double wd,double z,double height,double u,double q,double lon,double lat, int colums,int rows,int scale) {
int[] size = new int[]{colums, rows};
double[] dataInterval = new double[]{0, 30, 50, 70, 90, 150} ;
calGaussPlumeEquiSurface(z,height,u ,q,wd,lon,lat, dataInterval,size,scale);
return strJson;
}
插值成面调用示例
插值成面效果
无风环境模拟可参照:
结合风场数据模拟非直线风污染模拟: