1use super::*;
4use crate::runtime::BUILTIN_CONST_NDIM;
5use std::num::IntErrorKind;
6
7fn make_binop(lhs: Expression, rhs: Expression, op: BinaryOperator) -> Expression {
9 Expression::BinaryOperation(BinaryOperation {
10 lhs: Box::new(lhs),
11 rhs: Box::new(rhs),
12 op
13 })
14}
15
16peg::parser!{
17 pub grammar fips_parser() for str {
18
19 pub rule unit() -> Unit
21 = _ members:unit_member() ** _ _ {
22 let mut unit = Unit::new();
23 for member in members {
24 match member {
25 UnitMember::GlobalStateMember(global_state_member) => unit.global_state_members.push(global_state_member),
26 UnitMember::Particle(particle) => unit.particles.push(particle),
27 UnitMember::Interaction(interaction) => unit.interactions.push(interaction),
28 UnitMember::Simulation(simulation) => unit.simulations.push(simulation),
29 UnitMember::ExternFunctionDecl(extern_functiondecl) => unit.extern_functiondecls.push(extern_functiondecl),
30 }
31 }
32 unit
33 }
34
35 pub(crate) rule unit_member() -> UnitMember
36 = global_state_member:global_state_member() { UnitMember::GlobalStateMember(global_state_member) }
37 / particle:particle() { UnitMember::Particle(particle) }
38 / interaction:interaction() { UnitMember::Interaction(interaction) }
39 / simulation:simulation() { UnitMember::Simulation(simulation) }
40 / extern_funcdecl:extern_functiondecl() { UnitMember::ExternFunctionDecl(extern_funcdecl) }
41
42 rule global_state_member() -> GlobalStateMember
44 = "global" __ name:identifier_not_ignored() _ ":" _ mutable:("mut" __)? _ typ:typ() ";"
45 {
46 let mutable = mutable.is_some();
47 GlobalStateMember { name, mutable, typ}
48 }
49
50 pub rule extern_functiondecl() -> ExternFunctionDecl
52 = "extern" __ "fn" __ name:identifier_not_ignored() _
53 "(" _ parameter_types:typ() ** (_ "," _) _ ")" _
54 "->" _ return_type:typ()
55 {
56 ExternFunctionDecl { name, parameter_types, return_type }
57 }
58
59 pub rule particle() -> Particle
61 = "particle" __ name:identifier_not_ignored() _
62 "{" _ members:(
63 _ "}" { Vec::new() } / (m:particle_member() ** (_ "," _) _ "}" {m})
65 )
66 {
67 Particle { name, members }
68 }
69
70 rule particle_member() -> ParticleMember
71 = name:identifier_not_ignored() _ ":" _ mutable:("mut" __)? _ typ_pos:typ_or_position() {
72 ParticleMember { name, mutable: !mutable.is_none(), typ: typ_pos.0, is_position: typ_pos.1 }
73 }
74
75 pub rule interaction() -> Interaction
77 = "interaction" __ name:identifier_not_ignored() _
78 "(" _ name_a:identifier() _ ":" _ type_a:identifier_not_ignored() _
79 "," _ name_b:identifier() _ ":" _ type_b:identifier_not_ignored() _ ")" _
80 "for" distance_vec:( __ "|" _ d:identifier_not_ignored() _ "|" _ "=" {d})? __ distance:identifier() _ "<" _ cutoff:float_ctc() _
81 "{" _ common_block:("common" _ "{" _ b:statement() ** (_ ";" _) _ ";" _ "}" {b})? _ quantities:(
83 _ "}" { Vec::new() } / (q:interaction_quantity() ** ( _ ) _ "}" {q})
85 )
86 {
87 Interaction {
88 name,
89 name_a, type_a,
90 name_b, type_b,
91 distance, distance_vec, cutoff,
92 common_block, quantities
93 }
94 }
95
96 rule interaction_quantity() -> InteractionQuantity
97 = "quantity" _ name:identifier_not_ignored() _
98 "-" _ "[" _ reduction_method:reduction_method() _ "]" _ "->" _
99 "(" _ target_a:identifier_not_ignored() _ "," _ symmetry:interaction_symmetry()? _ target_b:identifier_not_ignored() _ ")" _
100 "{" _ expression:expression_block() _ "}"
101 {
102 let symmetry = symmetry.unwrap_or(InteractionSymmetry::Symmetric);
103 InteractionQuantity {
104 name,
105 reduction_method,
106 target_a,
107 target_b,
108 symmetry,
109 expression
110 }
111 }
112
113 rule reduction_method() -> ReductionMethod
114 = "sum" { ReductionMethod::Sum }
115
116 rule interaction_symmetry() -> InteractionSymmetry
117 = "-" { InteractionSymmetry::Antisymmetric }
118 / "!" { InteractionSymmetry::Asymmetric }
119
120 pub rule simulation() -> Simulation
122 = "simulation" _ name:identifier_not_ignored() _ "{" _
123 default_particle:("default" _ "particle" _ dp:identifier_not_ignored() _ ";" {dp})? _
124 blocks:simulation_block() ** _ _
125 "}"
126 {
127 Simulation {
128 name,default_particle,blocks
129 }
130 }
131
132 rule simulation_block() -> SimulationBlock
133 = "once" __ step:nat_ctc() _ "{" _ subblocks:simulation_sub_block()* _ "}" {
134 SimulationBlock::Once(OnceBlock {
135 step, subblocks
136 })
137 }
138 / "step" __ step_range:step_range()? _ "{" _ subblocks:simulation_sub_block() ** ( _ ) _ "}" {
139 SimulationBlock::Step( StepBlock {
140 step_range: step_range.unwrap_or_default(), subblocks
141 })
142 }
143
144 rule simulation_sub_block() -> SimulationSubBlock
145 = "particle" __ particle:identifier_not_ignored() _ statements:statement_block()
146 {
147 SimulationSubBlock {
148 statements, particle: Some(particle)
149 }
150 }
151 / &statement() _ statements:statement() ** ( _ ";" _ ) _ ";" {
152 SimulationSubBlock {
153 statements, particle: None
154 }
155 }
156
157 pub(crate) rule step_range() -> StepRange
158 = start:nat_ctc()? _ ".." _ end:nat_ctc()? _ step:("," _ s:nat_ctc() {s})? {
159 let mut range: StepRange = Default::default();
160 range.start = start.unwrap_or(range.start);
161 range.end = end.unwrap_or(range.end);
162 range.step = step.unwrap_or(range.step);
163 range
164 }
165 / step:nat_ctc() { StepRange {
166 step, ..Default::default()
167 }}
168
169 rule expression_block() -> Expression
173 = statements:(s:statement() ** ( _ ";" _ ) _ ";" {s})? _ expr:expression() {
174 let statements = statements.unwrap_or(vec![]);
175 Expression::Block(BlockExpression {
176 statements, expression: Box::new(expr)
177 })
178 }
179
180 pub rule expression() -> Expression = precedence!{
182 lhs:(@) _ "+" _ rhs:@ { make_binop(lhs,rhs,BinaryOperator::Add) }
183 lhs:(@) _ "-" _ rhs:@ { make_binop(lhs,rhs,BinaryOperator::Sub) }
184 --
185 lhs:(@) _ "*" _ rhs:@ { make_binop(lhs,rhs,BinaryOperator::Mul) }
186 lhs:(@) _ "/" _ rhs:@ { make_binop(lhs,rhs,BinaryOperator::Div) }
187 --
188 fn_name:identifier_not_ignored() _ "(" _ parameters:expression() ** (_ "," _) _ ")" {
189 Expression::FunctionCall(FunctionCall {
190 fn_name,
191 parameters
192 })
193 }
194 namespace:identifier_not_ignored() _ "." _ name:identifier_not_ignored() {
195 Expression::Atom(Atom::NamespaceVariable{namespace, name})
196 }
197 array:identifier_not_ignored() _ "[" _ index:nat_ctc() _ "]" {
200 Expression::Indexing(AtIndex {
201 array, index
202 })
203 }
204 "[" _ elements:expression() ** (_ "," _) _ "]" {
205 Expression::AdHocArray(AdHocArrayExpression{ elements })
206 }
207 atom:atom() {
208 Expression::Atom(atom)
209 }
210 "(" _ expr:expression() _ ")" { expr }
211 }
212
213 rule atom() -> Atom
215 = x:numeric_literal() { Atom::Literal(x) }
216 / name:identifier_not_ignored() { Atom::Variable(name) }
217
218 rule statement_block() -> Vec<Statement>
219 = "{" _ statements:statement() ** ( _ ";" _ ) _ ";" _ "}" {
220 statements
221 }
222 / "{" _ "}" { vec![] }
223
224 pub rule statement() -> Statement
226 = "let" __ name:identifier_not_ignored() _ ":" _ typ:typ() _ "=" _ initial:expression() {
227 Statement::Let(LetStatement {
228 name, initial, typ
229 })
230 }
231 / "update" __ interaction:identifier_not_ignored()
232 quantity:(_ "." _ q:identifier_not_ignored() {q})?
233 {
234 Statement::Update(UpdateStatement {
235 interaction, quantity
236 })
237 }
238 / "call" __ name:identifier_not_ignored() {
239 Statement::Call(CallStatement {
240 name
241 })
242 }
243 / assignee:identifier_not_ignored() _ index:("[" _ idx:nat_ctc() _ "]" {idx})? _ "=" _ value:expression() {
244 Statement::Assign(AssignStatement {
245 assignee, value, index
246 })
247 }
248
249 rule typ_or_position() -> (FipsType, bool)
254 = "position" {
255 (FipsType::Array {
256 typ: Box::new(FipsType::Double),
257 length: CompileTimeConstant::Identifier(BUILTIN_CONST_NDIM.into())
258 }, true)
259 }
260 / typ:typ() { (typ, false) }
261
262 rule typ() -> FipsType
263 = "f64" { FipsType::Double }
264 / "i64" { FipsType::Int64 }
265 / "[" _ typ:typ() _ ";" _ length:nat_ctc() _ "]" {
266 FipsType::Array {
267 typ: Box::new(typ),
268 length
269 }
270 }
271
272 rule nat() -> usize
274 = s:$("0" / (['1'..='9']['0'..='9']*))
275 { ?
276 s.parse::<usize>().or_else(|err| {
277 match err.kind() {
278 IntErrorKind::Empty => unreachable!(),
279 IntErrorKind::InvalidDigit => unreachable!(),
280 IntErrorKind::PosOverflow => Err("Cannot parse integer number (positive overflow)"),
281 IntErrorKind::NegOverflow => unreachable!(),
282 IntErrorKind::Zero => unreachable!(),
283 _ => Err("Cannot parse integer number (unknown reason)")
284 }
285 } )
286 }
287
288 rule numeric_literal() -> Literal
290 = ['n'|'N'] ['a'|'A'] ['n'|'N'] { Literal::Double(f64::NAN) }
291 / "+"? ['i'|'I'] ['n'|'N'] ['f'|'F'] { Literal::Double(f64::INFINITY) }
292 / "-" ['i'|'I'] ['n'|'N'] ['f'|'F'] { Literal::Double(f64::NEG_INFINITY) }
293 / s:$(['+'|'-']? ['0'..='9']+ ("." ['0'..='9']*)? ((['e'|'E'] ['+'|'-']? ['0'..='9']+)?)?)
294 { ?
295 match s.parse::<i64>() {
297 Ok(n) => Ok(Literal::Int64(n)),
298 Err(e) => {
299 match e.kind() {
300 IntErrorKind::PosOverflow => Err("Positive integer overflow"),
302 IntErrorKind::NegOverflow => Err("Negative integer overflow"),
303 _ => {
305 s.parse::<f64>()
306 .or_else(|err| { Err("Cannot parse float number") } )
307 .map(|x| Literal::Double(x))
308 },
309 }
310 },
311 }
312 }
313
314 rule float_ctc() -> CompileTimeConstant<f64>
316 = x:numeric_literal() {
317 let x = match x {
319 Literal::Double(x) => x,
320 Literal::Int64(n) => n as f64,
321 };
322 CompileTimeConstant::Literal(x)
323 }
324 / name:identifier_not_ignored() { CompileTimeConstant::Identifier(name) }
325
326 rule nat_ctc() -> CompileTimeConstant<usize>
328 = n:nat() { CompileTimeConstant::Literal(n) }
329 / name:identifier_not_ignored() { CompileTimeConstant::Identifier(name) }
330
331 rule identifier() -> Identifier
332 = !keyword() s:$(['a'..='z'|'A'..='Z']['a'..='z'|'A'..='Z'|'0'..='9'|'_']*) {
333 match s {
334 "_" => Identifier::Ignored,
335 _ => Identifier::Named(String::from(s))
336 }
337 }
338
339 rule identifier_not_ignored() -> String
340 = ident:identifier() { ?
341 match ident {
342 Identifier::Ignored => Err("Cannot use _ here"),
343 Identifier::Named(name) => Ok(name)
344 }
345 }
346
347 rule keyword() -> ()
348 = "particle" / "once" / "step" / "call" / "let" / "update"
349
350 rule _() -> ()
351 = quiet!{(
352 [' '|'\n'|'\t'|'\r'] _) / ("//" (!['\n'][_])* ['\n'] _) / ""}
355
356 rule __() -> ()
357 = quiet!{[' '|'\n'|'\t'|'\r'] _}
358 }
359}
360
361#[cfg(test)]
362mod test {
363 use super::*;
364
365 #[test]
366 fn global_state() {
367 assert!(fips_parser::unit_member("global foo: i64").is_err(),
369 "Missing semicolon in global state member declaration not caught");
370 assert!(fips_parser::unit_member("global foo;").is_err(),
371 "Missing type specification in global state member declaration not caught");
372 assert!(fips_parser::unit_member("global : i64;").is_err(),
373 "Missing member name in global state member declaration not caught");
374 assert!(fips_parser::unit_member("global foo : i64 = 42;").is_err(),
375 "Invalid assignment in global state member declaration not caught");
376 assert_eq!(fips_parser::unit_member("global foo: f64;")
378 .expect("Cannot parse immutable global state member"),
379 UnitMember::GlobalStateMember(GlobalStateMember {
380 name: "foo".into(),
381 mutable: false,
382 typ: FipsType::Double
383 })
384 );
385 assert_eq!(fips_parser::unit_member("global foo: mut f64;")
386 .expect("Cannot parse mutable global state member"),
387 UnitMember::GlobalStateMember(GlobalStateMember {
388 name: "foo".into(),
389 mutable: true,
390 typ: FipsType::Double
391 })
392 );
393 }
394
395 #[test]
396 fn statements_let() {
397 assert!(fips_parser::statement("let foo = 42").is_err(),
398 "Let statement without type was not caught");
399 assert!(fips_parser::statement("let : f64 = 42").is_err(),
400 "Let statement without name was not caught");
401 assert!(fips_parser::statement("let _: f64 = 42").is_err(),
402 "Let statement with omitted name was not caught");
403 assert!(fips_parser::statement("let _: f64").is_err(),
404 "Let statement without initial value not caught");
405 let parser_result = fips_parser::statement("let foo: f64 = 42")
406 .expect("Cannot parse valid let statement");
407 assert_eq!(parser_result, Statement::Let(LetStatement{
408 name: "foo".into(),
409 typ: FipsType::Double,
410 initial: Expression::Atom(
411 Atom::Literal(
412 Literal::Int64(42)
413 )
414 )
415 }));
416 }
417
418 #[test]
419 fn statements_assign() {
420 assert!(fips_parser::statement(" = 1337").is_err(),
421 "Assign statement without assignee not caught");
422 assert!(fips_parser::statement("foo[] = 1337").is_err(),
423 "Indexed assign statement with empty index not caught");
424 assert!(fips_parser::statement("foo[1.2] = 1337").is_err(),
425 "Indexed assign statement with invalid index not caught");
426 assert!(fips_parser::statement("foo = ").is_err(),
427 "Assign statement without value not caught");
428 assert!(fips_parser::statement("foo[123] = ").is_err(),
429 "Indexed assign statement without value not caught");
430 let parser_result = fips_parser::statement("foo = 1337")
431 .expect("Cannot parse valid assign statement");
432 assert_eq!(parser_result, Statement::Assign(AssignStatement {
433 assignee: "foo".into(),
434 value: Expression::Atom(Atom::Literal(Literal::Int64(1337))),
435 index: None
436 }));
437 let parser_result = fips_parser::statement("foo[123] = 1337")
438 .expect("Cannot parse valid assign statement");
439 assert_eq!(parser_result, Statement::Assign(AssignStatement {
440 assignee: "foo".into(),
441 value: Expression::Atom(Atom::Literal(Literal::Int64(1337))),
442 index: Some(CompileTimeConstant::Literal(123))
443 }));
444 }
445
446 #[test]
447 fn statements_update() {
448 assert!(fips_parser::statement("update").is_err(),
449 "Update statement without interaction name not caught");
450 let parser_result = fips_parser::statement("update myinteraction")
451 .expect("Cannot parse valid update statement without quantity");
452 assert_eq!(parser_result, Statement::Update(UpdateStatement {
453 interaction: "myinteraction".into(),
454 quantity: None
455 }));
456 let parser_result = fips_parser::statement("update myinteraction.myquantity")
457 .expect("Cannot parse valid update statement with quantity");
458 assert_eq!(parser_result, Statement::Update(UpdateStatement {
459 interaction: "myinteraction".into(),
460 quantity: Some("myquantity".into())
461 }));
462
463 }
464
465 #[test]
466 fn statements_call() {
467 assert!(fips_parser::statement("call").is_err(),
468 "Call statement without callback name not caught");
469 let parser_result = fips_parser::statement("call mycallback")
470 .expect("Cannot parse valid call statement");
471 assert_eq!(parser_result, Statement::Call(CallStatement {
472 name: "mycallback".into()
473 }));
474
475 }
476
477 #[test]
478 fn expression_atom() {
479 assert!(fips_parser::expression("41foobar").is_err(),
481 "Invalid identifier atom not caught");
482 assert!(fips_parser::expression("41.2.3").is_err(),
483 "Invalid numeric literal not caught");
484 assert!(fips_parser::expression("9223372036854775808").is_err(),
485 "Overflowing integer literal not caught");
486 let integer_test_set: Vec<(_, i64)> = vec![
488 ("0", 0),
489 ("+0", 0),
490 ("-0", 0),
491 ("123", 123),
492 ("-123", -123),
493 ("(123)", 123),
494 ("(-123)", -123),
495 ("9223372036854775807", 9223372036854775807),
496 ("-9223372036854775808", -9223372036854775808)
497 ];
498 for (s,n) in integer_test_set {
499 assert_eq!(fips_parser::expression(s)
500 .expect(&format!("Cannot parse integer literal {} as expression", s)),
501 Expression::Atom(Atom::Literal(Literal::Int64(n)))
502 );
503 }
504 let float_test_set = vec![
506 ("0.0", 0.0),
507 ("+0.0", 0.0),
508 ("-0.0", -0.0),
509 ("123.456", 123.456),
510 ("-123.456", -123.456),
511 ("123456e-3", 123456e-3),
512 ("123456.789e-3", 123456.789e-3),
513 ("inf", f64::INFINITY),
514 ("+inf", f64::INFINITY),
515 ("-inf", f64::NEG_INFINITY),
516 ("nan", f64::NAN),
517 ];
518 for (s,x) in float_test_set {
519 let parser_result = fips_parser::expression(s)
520 .expect(&format!("Cannot parse float literal {} as expression", s));
521 if x.is_nan() {
522 match parser_result {
523 Expression::Atom(Atom::Literal(Literal::Double(y))) => {
524 assert!(y.is_nan(), "Expected NaN but got {}", y);
525 },
526 _ => panic!("Expected Atom(Literal(Double(NaN))) but got {:?}", parser_result)
527 }
528 }
529 else {
530 assert_eq!(parser_result,
531 Expression::Atom(Atom::Literal(Literal::Double(x)))
532 );
533 }
534 }
535 assert_eq!(fips_parser::expression("foobar")
537 .expect(&format!("Cannot parse identifier as expression")),
538 Expression::Atom(Atom::Variable("foobar".into()))
539 );
540 assert_eq!(fips_parser::expression("foo.bar")
541 .expect(&format!("Cannot parse identifier as expression")),
542 Expression::Atom(Atom::NamespaceVariable { namespace: "foo".into(), name: "bar".into() })
543 );
544 }
545
546 #[test]
547 fn expression_arrays() {
548 let make_int_atom = |i| {
550 Expression::Atom(Atom::Literal(Literal::Int64(i)))
551 };
552 let make_ident_atom = |name: &str| {
553 Expression::Atom(Atom::Variable(name.into()))
554 };
555 assert!(fips_parser::expression("[1,2,3").is_err(),
557 "Unbalanced array brackets not caught");
558 assert!(fips_parser::expression("[,1,2,3]").is_err(),
559 "Missing array element at the start not caught");
560 assert!(fips_parser::expression("[1,2,,3]").is_err(),
561 "Missing array element inbetween not caught");
562 assert!(fips_parser::expression("[1,2,3,]").is_err(),
563 "Missing array element at the end not caught");
564 assert_eq!(fips_parser::expression("[]")
566 .expect(&format!("Cannot parse empty array")),
567 Expression::AdHocArray(AdHocArrayExpression { elements: vec![] })
568 );
569 assert_eq!(fips_parser::expression("[1]")
570 .expect(&format!("Cannot parse one-element array")),
571 Expression::AdHocArray(AdHocArrayExpression {
572 elements: vec![make_int_atom(1)]
573 })
574 );
575 assert_eq!(fips_parser::expression("[1,2,a,b]")
576 .expect(&format!("Cannot parse multi-element array")),
577 Expression::AdHocArray(AdHocArrayExpression {
578 elements: vec![
579 make_int_atom(1), make_int_atom(2),
580 make_ident_atom("a"), make_ident_atom("b")
581 ]})
582 );
583 }
584
585 #[test]
586 fn expression_indexing() {
587 assert!(fips_parser::expression("myarr[]").is_err(),
589 "Empty array index not caught");
590 assert!(fips_parser::expression("myarr[-1]").is_err(),
591 "Negative array index not caught");
592 assert_eq!(fips_parser::expression("foo[0]")
594 .expect(&format!("Cannot parse valid array indexing")),
595 Expression::Indexing(AtIndex {
596 array: "foo".into(),
597 index: CompileTimeConstant::Literal(0)
598 })
599 );
600 }
601
602 #[test]
603 fn expression_identifier_namespace() {
604 assert!(fips_parser::expression("myparticle.").is_err(),
606 "Missing identifier not caught");
607 assert!(fips_parser::expression(".myident").is_err(),
608 "Missing namespace not caught");
609 assert!(fips_parser::expression(".").is_err(),
610 "Missing identifier and namespace not caught");
611 assert_eq!(fips_parser::expression("myparticle.myident")
613 .expect(&format!("Cannot parse empty array")),
614 Expression::Atom(Atom::NamespaceVariable {
615 namespace: "myparticle".into(),
616 name: "myident".into()
617 })
618 );
619 }
620
621 #[test]
622 fn expression_function_call() {
623 assert!(fips_parser::expression("foo(").is_err(),
625 "Missing closing parenthesis not caught");
626 assert!(fips_parser::expression("foo(bar,").is_err(),
627 "Missing closing parenthesis with parameters not caught");
628 assert!(fips_parser::expression("foo(bar,)").is_err(),
629 "Missing function argument not caught");
630 assert!(fips_parser::expression("foo)").is_err(),
631 "Missing opening parenthesis not caught");
632 assert_eq!(fips_parser::expression("foo(bar)")
634 .expect(&format!("Cannot parse empty array")),
635 Expression::FunctionCall(FunctionCall {
636 fn_name: "foo".into(),
637 parameters: vec![
638 Expression::Atom(Atom::Variable("bar".into()))
639 ]
640 })
641 );
642 }
643
644 #[ignore]
645 #[test]
646 fn euler() {
647 let input = r#"// Simple Euler integrator for point-like particles
648
649// Format for a particle:
650particle PointLike {
651 // Position is special and cannot be redefined, but aliased (array specifier is not allowed,
652 // as position is always NDIM dimensional)
653 x : mut position,
654 v : mut [f64; NDIM], // NDIM is dimensionality of problem
655 F : mut [f64; NDIM], // Quantities bound to interaction calculations later also need to be declared
656 mass: f64 // Constants have to be defined in Rust code (can be either per-particle or
657 // per-type)
658}
659
660// Particles can also inherit members
661particle Orientable extends PointLike {
662 phi : mut [f64; ROTDIM], // ROTDIM is user defined (1 for 2D, 3 for 3D)
663 omega : mut [f64; ROTDIM],
664 torque : mut [f64; ROTDIM],
665 inertia : f64
666}
667
668// Format for an interaction block:
669// The "dyn" marks an interaction between the first type and all types extending the second
670// (similar to the meaning of dyn in Rust)
671// Here we also bind names to the two interacting particles so we can access their members
672// (i.e. the carge for E/M interactions)
673interaction myinteraction (p1: PointLike, p2: dyn PointLike) for r < CUTOFF {
674 // Sole argument is the distance (commonly used and already calculated in neighbour checking)
675 // (p1 and p2 can be used too, obviously)
676 // Syntax: quantity <name> -[<reduction method>]-> <member in first>, <member in second> { <body> }
677 quantity myforce -[sum]-> (F, F) {
678 // Rust like expression syntax
679 4.0*EPSILON*((SIGMA/r))
680 }
681}
682// Interactions are always mutual (this is enforced in syntax to avoid accidental violations of e.g.
683// Newton's third law)!
684
685// Types of value reduction:
686// * sum: (Vector) sum all contributions together
687
688// Simulation block
689simulation MySim {
690 // Indicate that PointLike is the default particle for step blocks
691 default particle PointLike;
692 // Once blocks are executed once in the given timestep
693 once 0 {
694
695 }
696 // Step blocks contain a single time step
697 // Multiple step blocks can be defined in one simulation, so they are interleaved as necessary
698 // Step blocks are executed every x timesteps (default is 1, i.e. every timestep):
699 // step x {...}
700 // If multiple step blocks are to be executed in the same timestep, they are executed in the order of definition
701 step {
702 // This block can be omitted, since PointLike is the default particle type
703 particle PointLike {
704 update myinteraction; // This causes all quantities of myinteraction to be updated across all processors
705 // All members of the particle are accessible without extra scoping
706 // Members bound in interactions are technically write-accessible, but this should issue a warning
707 // since their value will be overwritten by any update command
708 v = v + F / m * DT; // Euler step for velocity (DT is the timestep constant)
709 x = x + v * DT;
710 }
711 }
712}"#;
713
714 fips_parser::unit(input).expect("Cannot parse input");
715 }
716}