Simulating Ocean Water

http://graphics.ucsd.edu/courses/rendering/2005/jdewall/tessendorf.pdf

siggraph 2001的一篇文章,后面很多关于水的文章都要提到,经典之作。

文章参杂数学概念比较多,与spherical harmonics相关的不相上下,对于数学基础不是那么强的我来说,着实花费了些力气查阅文档。

数学方面理解还很有限,尽量笔记和理解吧。

 


 

文中记录的关于光影方面的效果暂且跳过。

主要关注水的geometry方面的simulation。

 

水的FFT的simulation即便不是很了解,起码名字是很熟悉的。

本文从介绍一些相对更轻口味的数学的模拟方法开始,内含一些重要的概念。

 


 

GerstnerWaves

 

这是一个很早提出的对于水的一个模拟,核心是利用一些sin, cos来模拟水。

细节不是那么重要,关键点几个概念,除了sin,cos这些周期函数的模拟,相关参数比较重要的是水流动的速度与水的波长的一个关系:

:k为水流速度,lemda是波长。

 

然后可以用多组sin,cos函数来结合一个更加复杂也更加自然的水。

 

这种方法现在在一些游戏里面也还是有市场的。

 


 

Dispersion Relation

 

这部分讨论的是水的频率与水的几个特性的关系。

w是频率,g是重力加速度,k是水的流动速度。他们之间在深水区有这么个关系。

 

d是水深,在浅水区,频率和这些参数有关系的。

对于很小的水波,需要考虑到水的张力(tenssor),就是L,频率是这么个关系。

 

 


 

Statical(统计的)wave model and fourier transform

 

几个概念先列一下:

傅立叶展开http://en.wikipedia.org/wiki/Fourier_series

指周期函数可以展开成一系列三角函数的和:

实际定义要有更多的条件,略过,基本含义是这样的。

这样就把可能非常复杂的周期函数化成我们熟悉的三角函数,何况三角函数还有那么多的特性,分析起来有优势多了。

 

傅立叶变换http://en.wikipedia.org/wiki/Fourier_transform

http://wenku.baidu.com/view/e3b7f0fdc8d376eeaeaa3187.html

具体数学推理是很麻烦的公式。

 

物理含义方面,这个在信号处理方面有着很广泛的应用,是将一个时域上的函数转换到频域上来表示。

比如我们看sin(t)+cos(2t)这个函数,画出来一个波浪线相对麻烦,但是用频域来表示就简单多了。

另外一种是IFFT(Inverse Fast Fourier Transform),也就是逆傅立叶变换,就是频域转到时域。

物理现象中常常出现的情况是时域中非常复杂的东西,在频域中就很简单,反之亦然。

 

那么换做水,我们所见的水高低起伏是时域上高度的一个变化(当然也有位置的关系)。

那么切换到频域表示之后,我们就可以结合前面的关于频率的介绍,根据水的流速,张力等情况算出频率,进而将水的高低起伏通过这些表达清楚。

 

离散傅立叶变换:

http://zh.wikipedia.org/zh/%E7%A6%BB%E6%95%A3%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2#.E5.BF.AB.E9.80.9F.E5.82.85.E9.87.8C.E5.8F.B6.E5.8F.98.E6.8D.A2

上面说到的都是针对连续函数来说的。

但是实际生活中,数字化的信号都是离散的信号,这时候进行处理的话就需要针对离散函数做傅立叶变换,获得频域上的新的函数表示。

这点图形学上比较熟悉的应该是jpeg压缩,因为人眼对高频数据敏感度不高,所以对离散傅立叶变换的函数滤掉高频数据,剩下低频数据进行压缩。这个是离散傅立叶变换的一个典型例子。

 

快速傅立叶变换:

http://zh.wikipedia.org/zh-cn/%E5%BF%AB%E9%80%9F%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2

快速傅立叶变换是针对离散傅立叶变换来说的,离散傅立叶变换计算仍旧是比较慢,牛人们开发出一些快速算法统称快速傅立叶变换。

 

 


 

文中提到这个快速傅立叶变换不是基于实际物理模型,而是基于统计的。

那么应该是进行数字采样之后,进行统计归纳的一个结果。

 

所以最后说下来是fft是一个可以很真实描述水的数学模型,这个模型来自观察而不是实际物理推算,模型公式为:

这里k是2维vector,编程中就是指grid中sample的点,这个和离散傅立叶变换的采样点相对应。

 

浪线h是重点,这个函数描述了从space domain转到frequency domain,在特定frequency----(i*dot(k,x))下的amplitude。这个直接决定了水的高度分布,如果h~(k,t)就是一个常数*exp(i*w(t))的话,就是很平均的一个水波形状。如果是下面介绍的philips spectrum,那就是一个深海中水波的分布形状。

 

philips spectrum:

然后2公式连发,可以看出浪线h(k,t)是什么了:

 

这样下来一个完整的数学模型就ok了。

这里需要注意每一个的条件和意义,否则coding的时候会被恶心到。

第一个h~(k)那个,走丝r和走丝i是随机数,要求平均值是0,方差小于1,自己实现的时候平均值没有采对,结果出的乱七八糟的结果,囧。

phillips spectrum里面的和L相乘的k应该是wave number,这个到处没有找到确切的物理意义,论文本身也是说的很含糊,ati文章里有提到,所以我就顺着走,出来效果是属于比较平淡的湖水。

然后按照我最先理解的是k就是2d vector,k平方就是k dot k这种,最后做出来是深海的浩翰感觉。

非常赞。

所以这样看来,phillips spectrum本身作为一个波普,我们本身也可以尝试各种波普,来创造出各种有意思的水了。

 

 


 

其他的choppywave,reflection, refraction,caustic,godray都比较熟悉了,直接跳过。

 

<think>我们之前讨论了在OCEAN脚本中创建数组的方法,现在用户询问的是如何仿真list元素。这里需要明确,OCEAN脚本中通常使用list(列表)来存储一系列数据,而在仿真中,我们经常需要对这些列表元素进行操作,比如在仿真循环中遍历列表中的每个元素(例如,参数扫描),然后对每个参数值运行仿真。 在OCEAN脚本中,我们可以使用循环结构(如`foreach`或`for`)来遍历列表,并对每个元素执行仿真操作。以下是一个典型的例子: 假设我们有一个参数列表(比如一组电压值),我们想对每个电压值进行直流仿真(dc仿真),并获取某个节点的电压。 步骤: 1. 定义参数列表。 2. 使用循环遍历列表中的每个元素。 3. 在循环体内,设置当前参数值(例如,设置某个电压源的直流电压)。 4. 运行仿真。 5. 获取仿真结果并存储。 示例代码: ```skill ; 定义要扫描的电压值列表 voltList = list(1.0 1.2 1.5 1.8 2.0) ; 创建一个空列表用于存储结果 results = list() ; 遍历电压列表 foreach(volt voltList ; 设置电压源的直流电压(假设电压源实例名为"V1",参数为"dc") desVar("vdd" volt) ; 假设我们通过设计变量vdd来设置电压 ; 运行直流仿真 analysis('dc) ; 获取输出节点的电压(假设节点名为 "/out") result = v("out") ; 使用v函数获取直流仿真下的节点电压 ; 将结果添加到结果列表 results = append(results result) ) ; 打印结果 printf("Simulation results: %L" results) ``` 注意:上面的代码中,我们假设已经打开了仿真环境,并且设置了模型库、设计等。在实际脚本中,还需要包括环境设置部分。 另外,我们也可以使用`for`循环: ```skill voltList = list(1.0 1.2 1.5 1.8 2.0) results = list() len = length(voltList) for(i 0 len-1 volt = nth(i voltList) desVar("vdd" volt) analysis('dc) results = append(results v("out")) ) ``` 在仿真中,我们还可以使用更复杂的操作,例如存储多个结果(比如每个电压下的多个节点电压),这时我们可以使用列表的列表(二维列表)来存储。 另外,OCEAN脚本中也可以使用`paramRun`函数进行参数扫描,但这里我们主要讨论通过遍历列表的方式。 注意事项: - 确保在每次仿真前设置好参数。 - 仿真类型(dc, ac, tran等)根据需求设置。 - 获取结果的方式取决于仿真类型和需要的数据(电压、电流、功率等)。 如果用户需要的是蒙特卡洛仿真,那么通常会使用循环来运行多次仿真,但参数是从随机分布中抽取的,此时列表元素可以通过随机数生成函数来创建。 综上所述,通过遍历列表元素并执行仿真,是OCEAN脚本中常见的参数扫描方法。 下面我们按照用户要求,生成相关问题。 </think> 在 Cadence Ocean 脚本中,仿真操作通常涉及对列表(list)元素的遍历和参数化扫描,以下是核心方法及完整示例: --- ### 一、基础方法:使用 `foreach` 遍历列表元素 通过循环结构遍历列表元素,动态修改设计变量并执行仿真: ```skill ; 定义待扫描的参数列表 widthList = list(1u 2u 3u 5u) ; 晶体管宽度值(单位:微米) ; 创建结果存储列表 resultDC = list() ; 遍历宽度列表 foreach(widthVal widthList ; 修改设计变量(如MOS管宽度) desVar("w" widthVal) ; 执行直流仿真 analysis('dc ?param "vgs" ?start 0 ?stop 1.8 ?step 0.01) ; 获取输出电流并保存 id = getData("I0" ?result 'dc) ; 假设I0是电流名称 resultDC = append(resultDC id) ) ; 打印结果 printf("DC Results: %L\n" resultDC) ``` --- ### 二、进阶操作:多参数联合扫描 嵌套循环实现多变量仿真(如宽度+温度): ```skill widthList = list(1u 2u) tempList = list(25 50 75) ; 温度值(℃) resultsTable = list() ; 二维结果表 foreach(temp tempList tempResult = list() desVar("temp" temp) ; 设置温度 foreach(width widthList desVar("w" width) analysis('dc ...) id = getData("I0") tempResult = append(tempResult id) ) resultsTable = append(resultsTable tempResult) ) ; 输出表格 printf("Width vs Temp Results:\n") for(i 0 length(resultsTable)-1 printf("Temp %d℃: %L\n" tempList[i] resultsTable[i]) ) ``` --- ### 三、关键注意事项 1. **设计变量绑定** - 使用 `desVar()` 前需在原理图中定义变量(如 `w`, `temp`) - 未绑定的变量会导致仿真错误 2. **结果提取方法** - 直流仿真:`getData("signal" ?result 'dc)` - 瞬态仿真:`getbyname("net_name" ?result 'tran)` - 频域仿真:`db20(getData("vout" ?result 'ac))`(转换为dB值) 3. **性能优化** - 大型列表扫描时,使用 `oceantempdir` 管理临时文件 - 避免在循环内频繁保存波形数据 4. **错误处理** 添加断言确保仿真收敛: ```skill simResult = analysis('tran ?stop 10n) unless(simResult error("Simulation failed at width=%L" widthVal) ) ``` --- ### 四、实际应用场景 1. **工艺角扫描** ```skill cornerList = list("tt" "ff" "ss") foreach(cor cornerList modelFile = sprintf(nil "%s.scs" cor) ; 加载不同工艺角模型 ... ) ``` 2. **蒙特卡洛分析** ```skill monteCarloRuns = 100 mcResults = list() for(run 1 monteCarloRuns montecarlo(...) ; 设置随机变量 analysis('ac) gain = getData("gain") mcResults = append(mcResults gain) ) ``` --- ### 五、调试技巧 - **实时监控**:使用 `printf("Simulating w=%g..." widthVal)` 跟踪进度 - **数据快照**:`save('all) ; 保存当前仿真状态` - **结果可视化**:`vf("%s/psf" oceantempdir)` 打开波形查看器 通过结合列表遍历与 Ocean 的仿真命令,可实现高效的参数化扫描与数据收集,满足复杂电路分析需求[^1]。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值